#include <Security/SecCmsEnvelopedData.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCmsRecipientInfo.h>
#include <Security/SecRandom.h>
#include "cmslocal.h"
#include "SecAsn1Item.h"
#include "secoid.h"
#include "cryptohi.h"
#include <security_asn1/secasn1.h>
#include <security_asn1/secerr.h>
#include <security_asn1/secport.h>
#include <Security/SecKeyPriv.h>
#include <CommonCrypto/CommonCryptor.h>
#include <AssertMacros.h>
SecCmsEnvelopedDataRef
SecCmsEnvelopedDataCreate(SecCmsMessageRef cmsg, SECOidTag algorithm, int keysize)
{
void *mark;
SecCmsEnvelopedDataRef envd;
PLArenaPool *poolp;
OSStatus rv;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
envd = (SecCmsEnvelopedDataRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsEnvelopedData));
if (envd == NULL)
goto loser;
envd->contentInfo.cmsg = cmsg;
rv = SecCmsContentInfoSetContentEncAlg(&(envd->contentInfo), algorithm, NULL, keysize);
if (rv != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return envd;
loser:
PORT_ArenaRelease(poolp, mark);
return NULL;
}
void
SecCmsEnvelopedDataDestroy(SecCmsEnvelopedDataRef edp)
{
SecCmsRecipientInfoRef *recipientinfos;
SecCmsRecipientInfoRef ri;
if (edp == NULL)
return;
recipientinfos = edp->recipientInfos;
if (recipientinfos == NULL)
return;
while ((ri = *recipientinfos++) != NULL)
SecCmsRecipientInfoDestroy(ri);
SecCmsContentInfoDestroy(&(edp->contentInfo));
}
SecCmsContentInfoRef
SecCmsEnvelopedDataGetContentInfo(SecCmsEnvelopedDataRef envd)
{
return &(envd->contentInfo);
}
OSStatus
SecCmsEnvelopedDataAddRecipient(SecCmsEnvelopedDataRef edp, SecCmsRecipientInfoRef rip)
{
void *mark;
OSStatus rv;
PR_ASSERT(edp != NULL);
PR_ASSERT(rip != NULL);
mark = PORT_ArenaMark(edp->contentInfo.cmsg->poolp);
rv = SecCmsArrayAdd(edp->contentInfo.cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
if (rv != SECSuccess) {
PORT_ArenaRelease(edp->contentInfo.cmsg->poolp, mark);
return SECFailure;
}
PORT_ArenaUnmark (edp->contentInfo.cmsg->poolp, mark);
return SECSuccess;
}
OSStatus
SecCmsEnvelopedDataEncodeBeforeStart(SecCmsEnvelopedDataRef envd)
{
int version;
SecCmsRecipientInfoRef *recipientinfos;
SecCmsContentInfoRef cinfo;
SecSymmetricKeyRef bulkkey = NULL;
#if USE_CDSA_CRYPTO
SecAsn1AlgId algorithm;
#endif
SECOidTag bulkalgtag;
OSStatus rv;
SecAsn1Item * dummy;
PLArenaPool *poolp;
extern const SecAsn1Template SecCmsRecipientInfoTemplate[];
void *mark = NULL;
int i;
cinfo = &(envd->contentInfo);
poolp = cinfo->cmsg->poolp;
recipientinfos = envd->recipientInfos;
if (recipientinfos == NULL) {
PORT_SetError(SEC_ERROR_BAD_DATA);
#if 0
PORT_SetErrorString("Cannot find recipientinfos to encode.");
#endif
goto loser;
}
version = SEC_CMS_ENVELOPED_DATA_VERSION_REG;
if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
version = SEC_CMS_ENVELOPED_DATA_VERSION_ADV;
} else {
for (i = 0; recipientinfos[i] != NULL; i++) {
if (SecCmsRecipientInfoGetVersion(recipientinfos[i]) != 0) {
version = SEC_CMS_ENVELOPED_DATA_VERSION_ADV;
break;
}
}
}
dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
if (dummy == NULL)
goto loser;
if ((bulkalgtag = SecCmsContentInfoGetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
rv = SecCmsContentInfoSetContentEncAlg(cinfo, SEC_OID_DES_EDE3_CBC, NULL, 192);
if (rv != SECSuccess)
goto loser;
bulkalgtag = SEC_OID_DES_EDE3_CBC;
}
#if USE_CDSA_CRYPTO
algorithm = SECOID_FindyCssmAlgorithmByTag(bulkalgtag);
if (!algorithm)
goto loser;
rv = SecKeyGenerate(NULL,
algorithm,
SecCmsContentInfoGetBulkKeySize(cinfo),
0,
CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
CSSM_KEYATTR_EXTRACTABLE,
NULL,
&bulkkey);
if (rv)
goto loser;
#else
{
size_t keysize = (cinfo->keysize + 7)/8;
uint8_t *key_material = (uint8_t *)malloc(keysize);
if (!key_material) {
goto loser;
}
require_noerr(SecRandomCopyBytes(kSecRandomDefault, keysize, key_material), loser);
bulkkey = (SecSymmetricKeyRef)CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, key_material, keysize, kCFAllocatorMalloc);
}
#endif
mark = PORT_ArenaMark(poolp);
for (i = 0; recipientinfos[i] != NULL; i++) {
rv = SecCmsRecipientInfoWrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
if (rv != SECSuccess)
goto loser;
}
rv = SecCmsArraySortByDER((void **)envd->recipientInfos, SecCmsRecipientInfoTemplate, NULL);
if (rv != SECSuccess)
goto loser;
SecCmsContentInfoSetBulkKey(cinfo, bulkkey);
PORT_ArenaUnmark(poolp, mark);
CFRelease(bulkkey);
return SECSuccess;
loser:
if (mark != NULL)
PORT_ArenaRelease (poolp, mark);
if (bulkkey)
CFRelease(bulkkey);
return SECFailure;
}
OSStatus
SecCmsEnvelopedDataEncodeBeforeData(SecCmsEnvelopedDataRef envd)
{
SecCmsContentInfoRef cinfo;
SecSymmetricKeyRef bulkkey;
SECAlgorithmID *algid;
cinfo = &(envd->contentInfo);
bulkkey = SecCmsContentInfoGetBulkKey(cinfo);
if (bulkkey == NULL)
return SECFailure;
algid = SecCmsContentInfoGetContentEncAlg(cinfo);
if (algid == NULL)
return SECFailure;
cinfo->ciphcx = SecCmsCipherContextStartEncrypt(cinfo->cmsg->poolp, bulkkey, algid);
CFRelease(bulkkey);
if (cinfo->ciphcx == NULL)
return SECFailure;
return SECSuccess;
}
OSStatus
SecCmsEnvelopedDataEncodeAfterData(SecCmsEnvelopedDataRef envd)
{
if (envd->contentInfo.ciphcx) {
SecCmsCipherContextDestroy(envd->contentInfo.ciphcx);
envd->contentInfo.ciphcx = NULL;
}
return SECSuccess;
}
OSStatus
SecCmsEnvelopedDataDecodeBeforeData(SecCmsEnvelopedDataRef envd)
{
SecCmsRecipientInfoRef ri;
SecSymmetricKeyRef bulkkey = NULL;
SECOidTag bulkalgtag;
SECAlgorithmID *bulkalg;
OSStatus rv = SECFailure;
SecCmsContentInfoRef cinfo;
SecCmsRecipient **recipient_list = NULL;
SecCmsRecipient *recipient;
int rlIndex;
if (SecCmsArrayCount((void **)envd->recipientInfos) == 0) {
PORT_SetError(SEC_ERROR_BAD_DATA);
#if 0
PORT_SetErrorString("No recipient data in envelope.");
#endif
goto loser;
}
recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
if (recipient_list == NULL)
goto loser;
cinfo = &(envd->contentInfo);
rlIndex = nss_cms_FindCertAndKeyByRecipientList(recipient_list, cinfo->cmsg->pwfn_arg);
if (rlIndex < 0) {
PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
#if 0
PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
#endif
goto loser;
}
recipient = recipient_list[rlIndex];
if (!recipient->cert || !recipient->privkey) {
goto loser;
}
ri = envd->recipientInfos[recipient->riIndex];
bulkalgtag = SecCmsContentInfoGetContentEncAlgTag(cinfo);
if (bulkalgtag == SEC_OID_UNKNOWN) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
} else
bulkkey = SecCmsRecipientInfoUnwrapBulkKey(ri,recipient->subIndex,
recipient->cert,
recipient->privkey,
bulkalgtag);
if (bulkkey == NULL) {
goto loser;
}
SecCmsContentInfoSetBulkKey(cinfo, bulkkey);
CFRetain(recipient->privkey);
cinfo->privkey = recipient->privkey;
bulkalg = SecCmsContentInfoGetContentEncAlg(cinfo);
cinfo->ciphcx = SecCmsCipherContextStartDecrypt(bulkkey, bulkalg);
if (cinfo->ciphcx == NULL)
goto loser;
#if 1
#else
if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) {
SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
bulkkey = keyPwd->key;
}
#endif
rv = SECSuccess;
loser:
if (bulkkey)
CFRelease(bulkkey);
if (recipient_list != NULL)
nss_cms_recipient_list_destroy(recipient_list);
return rv;
}
OSStatus
SecCmsEnvelopedDataDecodeAfterData(SecCmsEnvelopedDataRef envd)
{
if (envd && envd->contentInfo.ciphcx) {
SecCmsCipherContextDestroy(envd->contentInfo.ciphcx);
envd->contentInfo.ciphcx = NULL;
}
return SECSuccess;
}
OSStatus
SecCmsEnvelopedDataDecodeAfterEnd(SecCmsEnvelopedDataRef envd)
{
return SECSuccess;
}