SSL/TLS Library
CyaSSL is SSL/TLS library for embedded systems.
Diff: ctaocrypt/src/pkcs7.c
- Revision:
- 0:9d17e4342598
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ctaocrypt/src/pkcs7.c Sun Apr 20 12:40:57 2014 +0000 @@ -0,0 +1,1392 @@ +/* pkcs7.c + * + * Copyright (C) 2006-2013 wolfSSL Inc. + * + * This file is part of CyaSSL. + * + * CyaSSL 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. + * + * CyaSSL 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <cyassl/ctaocrypt/settings.h> + +#ifdef HAVE_PKCS7 + +#include <cyassl/ctaocrypt/pkcs7.h> +#include <cyassl/ctaocrypt/error-crypt.h> +#include <cyassl/ctaocrypt/logging.h> + +#ifndef min + static INLINE word32 min(word32 a, word32 b) + { + return a > b ? b : a; + } +#endif + + +/* placed ASN.1 contentType OID into *output, return idx on success, + * 0 upon failure */ +CYASSL_LOCAL int SetContentType(int pkcs7TypeOID, byte* output) +{ + /* PKCS#7 content types, RFC 2315, section 14 */ + static const byte pkcs7[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x07 }; + static const byte data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x07, 0x01 }; + static const byte signedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x07, 0x02}; + static const byte envelopedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x07, 0x03 }; + static const byte signedAndEnveloped[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x07, 0x04 }; + static const byte digestedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x07, 0x05 }; + static 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: + CYASSL_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 */ +int GetContentType(const byte* input, word32* inOutIdx, word32* oid, + word32 maxIdx) +{ + int length; + word32 i = *inOutIdx; + byte b; + *oid = 0; + + CYASSL_ENTER("GetContentType"); + + b = input[i++]; + if (b != ASN_OBJECT_ID) + return ASN_OBJECT_ID_E; + + if (GetLength(input, &i, &length, maxIdx) < 0) + return ASN_PARSE_E; + + while(length--) { + *oid += input[i]; + i++; + } + + *inOutIdx = i; + + return 0; +} + + +/* init PKCS7 struct with recipient cert, decode into DecodedCert */ +int PKCS7_InitWithCert(PKCS7* pkcs7, byte* cert, word32 certSz) +{ + int ret = 0; + + XMEMSET(pkcs7, 0, sizeof(PKCS7)); + if (cert != NULL && certSz > 0) { + DecodedCert dCert; + + pkcs7->singleCert = cert; + pkcs7->singleCertSz = certSz; + InitDecodedCert(&dCert, cert, certSz, 0); + + ret = ParseCert(&dCert, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) { + FreeDecodedCert(&dCert); + return ret; + } + XMEMCPY(pkcs7->publicKey, dCert.publicKey, dCert.pubKeySize); + pkcs7->publicKeySz = dCert.pubKeySize; + XMEMCPY(pkcs7->issuerHash, dCert.issuerHash, SHA_SIZE); + pkcs7->issuer = dCert.issuerRaw; + pkcs7->issuerSz = dCert.issuerRawLen; + XMEMCPY(pkcs7->issuerSn, dCert.serial, dCert.serialSz); + pkcs7->issuerSnSz = dCert.serialSz; + FreeDecodedCert(&dCert); + } + + return ret; +} + + +/* releases any memory allocated by a PKCS7 initializer */ +void PKCS7_Free(PKCS7* pkcs7) +{ + (void)pkcs7; +} + + +/* build PKCS#7 data content type */ +int 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 { + Sha sha; + byte contentDigest[SHA_DIGEST_SIZE + 2]; /* content only + ASN.1 heading */ + byte contentAttribsDigest[SHA_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; +} + + +/* build PKCS#7 signedData content type */ +int 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 }; + + ESD esd; + word32 signerInfoSz = 0; + word32 totalSz = 0; + int idx = 0, ret = 0; + byte* flatSignedAttribs = NULL; + word32 flatSignedAttribsSz = 0; + word32 innerOidSz = sizeof(innerOid); + word32 outerOidSz = sizeof(outerOid); + + if (pkcs7 == NULL || pkcs7->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; + + XMEMSET(&esd, 0, sizeof(esd)); + ret = InitSha(&esd.sha); + if (ret != 0) + return ret; + + if (pkcs7->contentSz != 0) + { + ShaUpdate(&esd.sha, pkcs7->content, pkcs7->contentSz); + esd.contentDigest[0] = ASN_OCTET_STRING; + esd.contentDigest[1] = SHA_DIGEST_SIZE; + ShaFinal(&esd.sha, &esd.contentDigest[2]); + } + + esd.innerOctetsSz = SetOctetString(pkcs7->contentSz, esd.innerOctets); + esd.innerContSeqSz = SetExplicit(0, esd.innerOctetsSz + pkcs7->contentSz, + esd.innerContSeq); + esd.contentInfoSeqSz = SetSequence(pkcs7->contentSz + esd.innerOctetsSz + + innerOidSz + esd.innerContSeqSz, + esd.contentInfoSeq); + + esd.issuerSnSz = SetSerialNumber(pkcs7->issuerSn, pkcs7->issuerSnSz, + esd.issuerSn); + signerInfoSz += esd.issuerSnSz; + esd.issuerNameSz = SetSequence(pkcs7->issuerSz, esd.issuerName); + signerInfoSz += esd.issuerNameSz + pkcs7->issuerSz; + esd.issuerSnSeqSz = SetSequence(signerInfoSz, esd.issuerSnSeq); + signerInfoSz += esd.issuerSnSeqSz; + esd.signerVersionSz = SetMyVersion(1, esd.signerVersion, 0); + signerInfoSz += esd.signerVersionSz; + esd.signerDigAlgoIdSz = SetAlgoID(pkcs7->hashOID, esd.signerDigAlgoId, + hashType, 0); + signerInfoSz += esd.signerDigAlgoIdSz; + esd.digEncAlgoIdSz = SetAlgoID(pkcs7->encryptOID, esd.digEncAlgoId, + keyType, 0); + signerInfoSz += esd.digEncAlgoIdSz; + + if (pkcs7->signedAttribsSz != 0) { + byte contentTypeOid[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0d, 0x01, + 0x09, 0x03 }; + byte contentType[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x07, 0x01 }; + byte messageDigestOid[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x04 }; + + PKCS7Attrib cannedAttribs[2] = + { + { contentTypeOid, sizeof(contentTypeOid), + contentType, sizeof(contentType) }, + { messageDigestOid, sizeof(messageDigestOid), + esd.contentDigest, sizeof(esd.contentDigest) } + }; + word32 cannedAttribsCount = sizeof(cannedAttribs)/sizeof(PKCS7Attrib); + + 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); + + flatSignedAttribs = (byte*)XMALLOC(esd.signedAttribsSz, 0, NULL); + flatSignedAttribsSz = esd.signedAttribsSz; + if (flatSignedAttribs == NULL) + 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. */ + { + RsaKey privKey; + int result; + word32 scratch = 0; + + byte digestInfo[MAX_SEQ_SZ + MAX_ALGO_SZ + + MAX_OCTET_STR_SZ + SHA_DIGEST_SIZE]; + byte digestInfoSeq[MAX_SEQ_SZ]; + byte digestStr[MAX_OCTET_STR_SZ]; + word32 digestInfoSeqSz, digestStrSz; + int digIdx = 0; + + if (pkcs7->signedAttribsSz != 0) { + byte attribSet[MAX_SET_SZ]; + word32 attribSetSz; + + attribSetSz = SetSet(flatSignedAttribsSz, attribSet); + + ret = InitSha(&esd.sha); + if (ret < 0) { + XFREE(flatSignedAttribs, 0, NULL); + return ret; + } + ShaUpdate(&esd.sha, attribSet, attribSetSz); + ShaUpdate(&esd.sha, flatSignedAttribs, flatSignedAttribsSz); + } + ShaFinal(&esd.sha, esd.contentAttribsDigest); + + digestStrSz = SetOctetString(SHA_DIGEST_SIZE, digestStr); + digestInfoSeqSz = SetSequence(esd.signerDigAlgoIdSz + + digestStrSz + SHA_DIGEST_SIZE, + digestInfoSeq); + + XMEMCPY(digestInfo + digIdx, digestInfoSeq, digestInfoSeqSz); + digIdx += digestInfoSeqSz; + XMEMCPY(digestInfo + digIdx, + esd.signerDigAlgoId, esd.signerDigAlgoIdSz); + digIdx += esd.signerDigAlgoIdSz; + XMEMCPY(digestInfo + digIdx, digestStr, digestStrSz); + digIdx += digestStrSz; + XMEMCPY(digestInfo + digIdx, esd.contentAttribsDigest, SHA_DIGEST_SIZE); + digIdx += SHA_DIGEST_SIZE; + + result = InitRsaKey(&privKey, NULL); + if (result == 0) + result = RsaPrivateKeyDecode(pkcs7->privateKey, &scratch, &privKey, + pkcs7->privateKeySz); + if (result < 0) { + XFREE(flatSignedAttribs, 0, NULL); + return PUBLIC_KEY_E; + } + result = RsaSSL_Sign(digestInfo, digIdx, + esd.encContentDigest, sizeof(esd.encContentDigest), + &privKey, pkcs7->rng); + FreeRsaKey(&privKey); + if (result < 0) { + XFREE(flatSignedAttribs, 0, NULL); + return result; + } + esd.encContentDigestSz = (word32)result; + } + 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, + hashType, 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) + 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 (pkcs7->signedAttribsSz != 0) { + XMEMCPY(output + idx, esd.signedAttribSet, esd.signedAttribSetSz); + idx += esd.signedAttribSetSz; + XMEMCPY(output + idx, flatSignedAttribs, flatSignedAttribsSz); + idx += flatSignedAttribsSz; + XFREE(flatSignedAttribs, 0, NULL); + } + + 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; + + return idx; +} + + +/* Finds the certificates in the message and saves it. */ +int PKCS7_VerifySignedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz) +{ + word32 idx, contentType; + int length, version, ret; + byte* content = NULL; + byte* sig = NULL; + byte* cert = NULL; + byte* signedAttr = NULL; + int contentSz = 0, sigSz = 0, certSz = 0, signedAttrSz = 0; + + (void)signedAttr; /* not used yet, just set */ + (void)signedAttrSz; + + 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 (GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (contentType != SIGNED_DATA) { + CYASSL_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) < 0) + return ASN_PARSE_E; + + if (version != 1) { + CYASSL_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 (GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (contentType != DATA) { + CYASSL_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); + } + 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) { + RsaKey key; + word32 scratch = 0; + int plainSz = 0; + byte digest[MAX_SEQ_SZ+MAX_ALGO_SZ+MAX_OCTET_STR_SZ+SHA_DIGEST_SIZE]; + + /* Get the sequence of the first signerInfo */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* Get the version */ + if (GetMyVersion(pkiMsg, &idx, &version) < 0) + return ASN_PARSE_E; + + if (version != 1) { + CYASSL_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 (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* Skip it */ + idx += length; + + /* 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 */ + signedAttr = &pkiMsg[idx]; + signedAttrSz = 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; + } + + XMEMSET(digest, 0, sizeof(digest)); + pkcs7->content = content; + pkcs7->contentSz = contentSz; + + ret = InitRsaKey(&key, NULL); + if (ret != 0) return ret; + if (RsaPublicKeyDecode(pkcs7->publicKey, &scratch, &key, + pkcs7->publicKeySz) < 0) { + CYASSL_MSG("ASN RSA key decode error"); + return PUBLIC_KEY_E; + } + plainSz = RsaSSL_Verify(sig, sigSz, digest, sizeof(digest), &key); + FreeRsaKey(&key); + if (plainSz < 0) + return plainSz; + } + + return 0; +} + + +/* create ASN.1 fomatted RecipientInfo structure, returns sequence size */ +CYASSL_LOCAL int CreateRecipientInfo(const byte* cert, word32 certSz, + int keyEncAlgo, int blockKeySz, + RNG* rng, byte* contentKeyPlain, + byte* contentKeyEnc, + int* keyEncSz, byte* out, word32 outSz) +{ + 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 serial[MAX_SN_SZ]; + byte issuerSerialSeq[MAX_SEQ_SZ]; + byte recipSeq[MAX_SEQ_SZ]; + byte issuerSeq[MAX_SEQ_SZ]; + byte keyAlgArray[MAX_ALGO_SZ]; + byte encKeyOctetStr[MAX_OCTET_STR_SZ]; + + RsaKey pubKey; + DecodedCert decoded; + + InitDecodedCert(&decoded, (byte*)cert, certSz, 0); + ret = ParseCert(&decoded, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) { + FreeDecodedCert(&decoded); + return ret; + } + + /* version */ + verSz = SetMyVersion(0, ver, 0); + + /* IssuerAndSerialNumber */ + if (decoded.issuerRaw == NULL || decoded.issuerRawLen == 0) { + CYASSL_MSG("DecodedCert lacks raw issuer pointer and length"); + FreeDecodedCert(&decoded); + return -1; + } + issuerSz = decoded.issuerRawLen; + issuerSeqSz = SetSequence(issuerSz, issuerSeq); + + if (decoded.serial == NULL || decoded.serialSz == 0) { + CYASSL_MSG("DecodedCert missing serial number"); + FreeDecodedCert(&decoded); + return -1; + } + snSz = SetSerialNumber(decoded.serial, decoded.serialSz, serial); + + issuerSerialSeqSz = SetSequence(issuerSeqSz + issuerSz + snSz, + issuerSerialSeq); + + /* KeyEncryptionAlgorithmIdentifier, only support RSA now */ + if (keyEncAlgo != RSAk) + return ALGO_ID_E; + + keyEncAlgSz = SetAlgoID(keyEncAlgo, keyAlgArray, keyType, 0); + if (keyEncAlgSz == 0) + return BAD_FUNC_ARG; + + /* EncryptedKey */ + ret = InitRsaKey(&pubKey, 0); + if (ret != 0) return ret; + if (RsaPublicKeyDecode(decoded.publicKey, &idx, &pubKey, + decoded.pubKeySize) < 0) { + CYASSL_MSG("ASN RSA key decode error"); + return PUBLIC_KEY_E; + } + + *keyEncSz = RsaPublicEncrypt(contentKeyPlain, blockKeySz, contentKeyEnc, + MAX_ENCRYPTED_KEY_SZ, &pubKey, rng); + FreeRsaKey(&pubKey); + if (*keyEncSz < 0) { + CYASSL_MSG("RSA Public Encrypt failed"); + 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) { + CYASSL_MSG("RecipientInfo output buffer too small"); + 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); + + return totalSz; +} + + +/* build PKCS#7 envelopedData content type, return enveloped size */ +int PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, byte* output, word32 outputSz) +{ + int i, ret = 0, idx = 0; + int totalSz = 0, padSz = 0, desOutSz = 0; + + 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]; + + RNG rng; + int contentKeyEncSz, blockKeySz; + int dynamicFlag = 0; + byte contentKeyPlain[MAX_CONTENT_KEY_LEN]; + byte contentKeyEnc[MAX_ENCRYPTED_KEY_SZ]; + byte* plain; + byte* encryptedContent; + + int recipSz, recipSetSz; + byte recip[MAX_RECIP_SZ]; + 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[DES_BLOCK_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) + return BAD_FUNC_ARG; + + if (output == NULL || outputSz == 0) + return BAD_FUNC_ARG; + + /* PKCS#7 only supports DES, 3DES for now */ + switch (pkcs7->encryptOID) { + case DESb: + blockKeySz = DES_KEYLEN; + break; + + case DES3b: + blockKeySz = DES3_KEYLEN; + break; + + default: + CYASSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + }; + + /* outer content type */ + outerContentTypeSz = SetContentType(ENVELOPED_DATA, outerContentType); + + /* version, defined as 0 in RFC 2315 */ + verSz = SetMyVersion(0, ver, 0); + + /* generate random content encryption key */ + InitRng(&rng); + RNG_GenerateBlock(&rng, contentKeyPlain, blockKeySz); + + /* build RecipientInfo, only handle 1 for now */ + recipSz = CreateRecipientInfo(pkcs7->singleCert, pkcs7->singleCertSz, RSAk, + blockKeySz, &rng, contentKeyPlain, + contentKeyEnc, &contentKeyEncSz, recip, + MAX_RECIP_SZ); + + if (recipSz < 0) { + CYASSL_MSG("Failed to create RecipientInfo"); + return recipSz; + } + recipSetSz = SetSet(recipSz, recipSet); + + /* EncryptedContentInfo */ + contentTypeSz = SetContentType(pkcs7->contentOID, contentType); + if (contentTypeSz == 0) + return BAD_FUNC_ARG; + + /* allocate encrypted content buffer, pad if necessary, PKCS#7 padding */ + padSz = DES_BLOCK_SIZE - (pkcs7->contentSz % DES_BLOCK_SIZE); + desOutSz = pkcs7->contentSz + padSz; + + if (padSz != 0) { + plain = XMALLOC(desOutSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (plain == NULL) { + return MEMORY_E; + } + XMEMCPY(plain, pkcs7->content, pkcs7->contentSz); + dynamicFlag = 1; + + for (i = 0; i < padSz; i++) { + plain[pkcs7->contentSz + i] = padSz; + } + + } else { + plain = pkcs7->content; + desOutSz = pkcs7->contentSz; + } + + encryptedContent = XMALLOC(desOutSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedContent == NULL) { + if (dynamicFlag) + XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + + /* generate IV for block cipher */ + RNG_GenerateBlock(&rng, tmpIv, DES_BLOCK_SIZE); + + /* put together IV OCTET STRING */ + ivOctetStringSz = SetOctetString(DES_BLOCK_SIZE, ivOctetString); + + /* build up our ContentEncryptionAlgorithmIdentifier sequence, + * adding (ivOctetStringSz + DES_BLOCK_SIZE) for IV OCTET STRING */ + contentEncAlgoSz = SetAlgoID(pkcs7->encryptOID, contentEncAlgo, + blkType, ivOctetStringSz + DES_BLOCK_SIZE); + if (contentEncAlgoSz == 0) + return BAD_FUNC_ARG; + + /* encrypt content */ + if (pkcs7->encryptOID == DESb) { + Des des; + + ret = Des_SetKey(&des, contentKeyPlain, tmpIv, DES_ENCRYPTION); + + if (ret == 0) + Des_CbcEncrypt(&des, encryptedContent, plain, desOutSz); + + if (ret != 0) { + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (dynamicFlag) + XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + } + else if (pkcs7->encryptOID == DES3b) { + Des3 des3; + + ret = Des3_SetKey(&des3, contentKeyPlain, tmpIv, DES_ENCRYPTION); + + if (ret == 0) + ret = Des3_CbcEncrypt(&des3, encryptedContent, plain, desOutSz); + + if (ret != 0) { + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (dynamicFlag) + XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + } + + encContentOctetSz = SetImplicit(ASN_OCTET_STRING, 0, + desOutSz, encContentOctet); + + encContentSeqSz = SetSequence(contentTypeSz + contentEncAlgoSz + + ivOctetStringSz + DES_BLOCK_SIZE + + encContentOctetSz + desOutSz, encContentSeq); + + /* keep track of sizes for outer wrapper layering */ + totalSz = verSz + recipSetSz + recipSz + encContentSeqSz + contentTypeSz + + contentEncAlgoSz + ivOctetStringSz + DES_BLOCK_SIZE + + encContentOctetSz + desOutSz; + + /* 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) { + CYASSL_MSG("Pkcs7_encrypt output buffer too small"); + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (dynamicFlag) + XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); + 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, DES_BLOCK_SIZE); + idx += DES_BLOCK_SIZE; + XMEMCPY(output + idx, encContentOctet, encContentOctetSz); + idx += encContentOctetSz; + XMEMCPY(output + idx, encryptedContent, desOutSz); + idx += desOutSz; + +#ifdef NO_RC4 + FreeRng(&rng); +#endif + + XMEMSET(contentKeyPlain, 0, MAX_CONTENT_KEY_LEN); + XMEMSET(contentKeyEnc, 0, MAX_ENCRYPTED_KEY_SZ); + + if (dynamicFlag) + XFREE(plain, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return idx; +} + +/* unwrap and decrypt PKCS#7 envelopedData object, return decoded size */ +CYASSL_API int PKCS7_DecodeEnvelopedData(PKCS7* pkcs7, byte* pkiMsg, + word32 pkiMsgSz, byte* output, + word32 outputSz) +{ + int recipFound = 0; + int ret, version, length; + word32 savedIdx = 0, idx = 0; + word32 contentType, encOID; + byte issuerHash[SHA_DIGEST_SIZE]; + mp_int serialNum; + + int encryptedKeySz, keySz; + byte tmpIv[DES_BLOCK_SIZE]; + byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; + byte* decryptedKey = NULL; + + RsaKey privKey; + 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; + + /* load private key */ + ret = InitRsaKey(&privKey, 0); + if (ret != 0) return ret; + ret = RsaPrivateKeyDecode(pkcs7->privateKey, &idx, &privKey, + pkcs7->privateKeySz); + if (ret != 0) { + CYASSL_MSG("Failed to decode RSA private key"); + return ret; + } + + idx = 0; + + /* read past ContentInfo, verify type is envelopedData */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (contentType != ENVELOPED_DATA) { + CYASSL_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) < 0) + return ASN_PARSE_E; + + if (version != 0) { + CYASSL_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; + + savedIdx = idx; + recipFound = 0; + + /* 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) { + idx = savedIdx; + break; + } + + if (GetMyVersion(pkiMsg, &idx, &version) < 0) { + idx = savedIdx; + break; + } + + if (version != 0) + return ASN_VERSION_E; + + /* 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; + } + + if (GetInt(&serialNum, pkiMsg, &idx, pkiMsgSz) < 0) + return ASN_PARSE_E; + mp_clear(&serialNum); + + if (GetAlgoId(pkiMsg, &idx, &encOID, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* key encryption algorithm must be RSA for now */ + if (encOID != RSAk) + return ALGO_ID_E; + + /* read encryptedKey */ + if (pkiMsg[idx++] != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &encryptedKeySz, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (recipFound == 1) + XMEMCPY(encryptedKey, &pkiMsg[idx], encryptedKeySz); + idx += encryptedKeySz; + + /* update good idx */ + savedIdx = idx; + } + + if (recipFound == 0) { + CYASSL_MSG("No recipient found in envelopedData that matches input"); + return PKCS7_RECIP_E; + } + + /* remove EncryptedContentInfo */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetAlgoId(pkiMsg, &idx, &encOID, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* 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 != DES_BLOCK_SIZE) { + CYASSL_MSG("Incorrect IV length, must be of DES_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 = XMALLOC(encryptedContentSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + + XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); + + /* decrypt encryptedKey */ + keySz = RsaPrivateDecryptInline(encryptedKey, encryptedKeySz, + &decryptedKey, &privKey); + FreeRsaKey(&privKey); + if (keySz <= 0) + return keySz; + + /* decrypt encryptedContent */ + if (encOID == DESb) { + Des des; + ret = Des_SetKey(&des, decryptedKey, tmpIv, DES_DECRYPTION); + + if (ret == 0) + Des_CbcDecrypt(&des, encryptedContent, encryptedContent, + encryptedContentSz); + + if (ret != 0) { + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + } + else if (encOID == DES3b) { + Des3 des; + ret = Des3_SetKey(&des, decryptedKey, tmpIv, DES_DECRYPTION); + if (ret == 0) + ret = Des3_CbcDecrypt(&des, encryptedContent, encryptedContent, + encryptedContentSz); + + if (ret != 0) { + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + } else { + CYASSL_MSG("Unsupported content encryption OID type"); + return ALGO_ID_E; + } + + padLen = encryptedContent[encryptedContentSz-1]; + + /* copy plaintext to output */ + XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); + + /* free memory, zero out keys */ + XMEMSET(encryptedKey, 0, MAX_ENCRYPTED_KEY_SZ); + XMEMSET(encryptedContent, 0, encryptedContentSz); + XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return encryptedContentSz - padLen; +} + + +#else /* HAVE_PKCS7 */ + + +#ifdef _MSC_VER + /* 4206 warning for blank file */ + #pragma warning(disable: 4206) +#endif + + +#endif /* HAVE_PKCS7 */ +