#include "cmslocal.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "secerr.h"
struct SecCmsDecoderContextStr {
SEC_ASN1DecoderContext * dcx;
SecCmsMessage * cmsg;
SECOidTag type;
SecCmsContent content;
SecCmsDecoderContext * childp7dcx;
PRBool saw_contents;
int error;
SecCmsContentCallback cb;
void * cb_arg;
};
static void nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind);
static OSStatus nss_cms_before_data(SecCmsDecoderContext *p7dcx);
static OSStatus nss_cms_after_data(SecCmsDecoderContext *p7dcx);
static OSStatus nss_cms_after_end(SecCmsDecoderContext *p7dcx);
static void nss_cms_decoder_work_data(SecCmsDecoderContext *p7dcx,
const unsigned char *data, unsigned long len, PRBool final);
extern const SEC_ASN1Template SecCmsMessageTemplate[];
static void
nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
{
SecCmsDecoderContext *p7dcx;
SecCmsContentInfo *rootcinfo, *cinfo;
PRBool after = !before;
p7dcx = (SecCmsDecoderContext *)arg;
rootcinfo = &(p7dcx->cmsg->contentInfo);
#ifdef CMSDEBUG
fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
#endif
switch (p7dcx->type) {
case SEC_OID_UNKNOWN:
if (after && dest == &(rootcinfo->contentType)) {
p7dcx->type = SecCmsContentInfoGetContentTypeTag(rootcinfo);
p7dcx->content = rootcinfo->content;
}
break;
case SEC_OID_PKCS7_DATA:
if (before && dest == &(rootcinfo->content)) {
SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
nss_cms_decoder_update_filter,
p7dcx,
(PRBool)(p7dcx->cb != NULL));
break;
}
if (after && dest == &(rootcinfo->content.data)) {
SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
}
break;
case SEC_OID_PKCS7_SIGNED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
if (before && dest == &(rootcinfo->content))
break;
if (p7dcx->content.pointer == NULL)
p7dcx->content = rootcinfo->content;
cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
if (before && dest == &(cinfo->contentType)) {
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
p7dcx->content.signedData->cmsg = p7dcx->cmsg;
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
break;
default:
PORT_Assert(0);
break;
}
}
if (before && dest == &(cinfo->rawContent)) {
SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, nss_cms_decoder_update_filter,
p7dcx, (PRBool)(p7dcx->cb != NULL));
if (nss_cms_before_data(p7dcx) != SECSuccess) {
SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
p7dcx->error = PORT_GetError();
}
}
if (after && dest == &(cinfo->rawContent)) {
if (nss_cms_after_data(p7dcx) != SECSuccess)
p7dcx->error = PORT_GetError();
SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
}
break;
#if 0
case SEC_OID_PKCS7_AUTHENTICATED_DATA:
#endif
default:
p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
break;
}
}
static OSStatus
nss_cms_before_data(SecCmsDecoderContext *p7dcx)
{
OSStatus rv;
SECOidTag childtype;
PLArenaPool *poolp;
SecCmsDecoderContext *childp7dcx;
SecCmsContentInfo *cinfo;
const SEC_ASN1Template *template;
void *mark = NULL;
size_t size;
poolp = p7dcx->cmsg->poolp;
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
rv = SecCmsSignedDataDecodeBeforeData(p7dcx->content.signedData);
if (rv != SECSuccess)
return SECFailure;
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = SecCmsDigestedDataDecodeBeforeData(p7dcx->content.digestedData);
if (rv != SECSuccess)
return SECFailure;
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = SecCmsEnvelopedDataDecodeBeforeData(p7dcx->content.envelopedData);
if (rv != SECSuccess)
return SECFailure;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = SecCmsEncryptedDataDecodeBeforeData(p7dcx->content.encryptedData);
if (rv != SECSuccess)
return SECFailure;
break;
default:
return SECFailure;
}
cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
if (childtype == SEC_OID_PKCS7_DATA) {
cinfo->content.data = SECITEM_AllocItem(poolp, NULL, 0);
if (cinfo->content.data == NULL)
return SECFailure;
p7dcx->childp7dcx = NULL;
return SECSuccess;
}
if ((template = SecCmsUtilGetTemplateByTypeTag(childtype)) == NULL)
return SECFailure;
childp7dcx = (SecCmsDecoderContext *)PORT_ZAlloc(sizeof(SecCmsDecoderContext));
if (childp7dcx == NULL)
return SECFailure;
mark = PORT_ArenaMark(poolp);
size = SecCmsUtilGetSizeByTypeTag(childtype);
childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
if (childp7dcx->content.pointer == NULL)
goto loser;
childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL);
if (childp7dcx->dcx == NULL)
goto loser;
SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, childp7dcx);
p7dcx->childp7dcx = childp7dcx;
childp7dcx->type = childtype;
childp7dcx->cmsg = p7dcx->cmsg;
childp7dcx->cb = p7dcx->cb;
childp7dcx->cb_arg = p7dcx->cb_arg;
p7dcx->cb = (SecCmsContentCallback)SecCmsDecoderUpdate;
p7dcx->cb_arg = childp7dcx;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
if (mark)
PORT_ArenaRelease(poolp, mark);
if (childp7dcx)
PORT_Free(childp7dcx);
p7dcx->childp7dcx = NULL;
return SECFailure;
}
static OSStatus
nss_cms_after_data(SecCmsDecoderContext *p7dcx)
{
PLArenaPool *poolp;
SecCmsDecoderContext *childp7dcx;
OSStatus rv = SECFailure;
poolp = p7dcx->cmsg->poolp;
nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
if (p7dcx->childp7dcx != NULL) {
childp7dcx = p7dcx->childp7dcx;
if (childp7dcx->dcx != NULL) {
if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
rv = SECFailure;
} else {
rv = nss_cms_after_end(childp7dcx);
}
if (rv != SECSuccess)
goto done;
}
PORT_Free(p7dcx->childp7dcx);
p7dcx->childp7dcx = NULL;
}
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
rv = SecCmsSignedDataDecodeAfterData(p7dcx->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = SecCmsEnvelopedDataDecodeAfterData(p7dcx->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = SecCmsDigestedDataDecodeAfterData(p7dcx->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = SecCmsEncryptedDataDecodeAfterData(p7dcx->content.encryptedData);
break;
case SEC_OID_PKCS7_DATA:
break;
default:
rv = SECFailure;
break;
}
done:
return rv;
}
static OSStatus
nss_cms_after_end(SecCmsDecoderContext *p7dcx)
{
OSStatus rv;
PLArenaPool *poolp;
poolp = p7dcx->cmsg->poolp;
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
rv = SecCmsSignedDataDecodeAfterEnd(p7dcx->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = SecCmsEnvelopedDataDecodeAfterEnd(p7dcx->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = SecCmsDigestedDataDecodeAfterEnd(p7dcx->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = SecCmsEncryptedDataDecodeAfterEnd(p7dcx->content.encryptedData);
break;
case SEC_OID_PKCS7_DATA:
rv = SECSuccess;
break;
default:
rv = SECFailure;
break;
}
return rv;
}
static void
nss_cms_decoder_work_data(SecCmsDecoderContext *p7dcx,
const unsigned char *data, unsigned long len,
PRBool final)
{
SecCmsContentInfo *cinfo;
unsigned char *buf = NULL;
unsigned char *dest;
unsigned int offset;
OSStatus rv;
CSSM_DATA *storage;
PORT_Assert ((data != NULL && len) || final);
if (!p7dcx->content.pointer) return;
cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
if (cinfo->ciphcx != NULL) {
unsigned int outlen = 0;
unsigned int buflen;
buflen = SecCmsCipherContextDecryptLength(cinfo->ciphcx, len, final);
if (buflen == 0 && len == 0)
goto loser;
if (buflen != 0) {
buf = (unsigned char *)PORT_Alloc(buflen);
if (buf == NULL) {
p7dcx->error = SEC_ERROR_NO_MEMORY;
goto loser;
}
}
rv = SecCmsCipherContextDecrypt(cinfo->ciphcx, buf, &outlen, buflen,
data, len, final);
if (rv != SECSuccess) {
p7dcx->error = PORT_GetError();
goto loser;
}
PORT_Assert (final || outlen == buflen);
data = buf;
len = outlen;
}
if (len == 0)
goto done;
if (cinfo->digcx)
SecCmsDigestContextUpdate(cinfo->digcx, data, len);
if (p7dcx->cb != NULL) {
(*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
}
#if 1
else
#endif
if (SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
storage = cinfo->content.data;
offset = storage->Length;
if (storage->Length == 0) {
dest = (unsigned char *)PORT_ArenaAlloc(p7dcx->cmsg->poolp, len);
} else {
dest = (unsigned char *)PORT_ArenaGrow(p7dcx->cmsg->poolp,
storage->Data,
storage->Length,
storage->Length + len);
}
if (dest == NULL) {
p7dcx->error = SEC_ERROR_NO_MEMORY;
goto loser;
}
storage->Data = dest;
storage->Length += len;
PORT_Memcpy(storage->Data + offset, data, len);
}
done:
loser:
if (buf)
PORT_Free (buf);
}
static void
nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind)
{
SecCmsDecoderContext *p7dcx;
PORT_Assert (len);
if (len == 0)
return;
p7dcx = (SecCmsDecoderContext*)arg;
p7dcx->saw_contents = PR_TRUE;
if (data_kind == SEC_ASN1_Contents)
nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, PR_FALSE);
}
SecCmsDecoderContext *
SecCmsDecoderStart(PRArenaPool *poolp,
SecCmsContentCallback cb, void *cb_arg,
PK11PasswordFunc pwfn, void *pwfn_arg,
SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg)
{
SecCmsDecoderContext *p7dcx;
SecCmsMessage *cmsg;
cmsg = SecCmsMessageCreate(poolp);
if (cmsg == NULL)
return NULL;
SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
NULL, NULL);
p7dcx = (SecCmsDecoderContext*)PORT_ZAlloc(sizeof(SecCmsDecoderContext));
if (p7dcx == NULL) {
SecCmsMessageDestroy(cmsg);
return NULL;
}
p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL);
if (p7dcx->dcx == NULL) {
PORT_Free (p7dcx);
SecCmsMessageDestroy(cmsg);
return NULL;
}
SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
p7dcx->cmsg = cmsg;
p7dcx->type = SEC_OID_UNKNOWN;
p7dcx->cb = cb;
p7dcx->cb_arg = cb_arg;
return p7dcx;
}
OSStatus
SecCmsDecoderUpdate(SecCmsDecoderContext *p7dcx, const char *buf, unsigned long len)
{
if (p7dcx->dcx != NULL && p7dcx->error == 0) {
if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
p7dcx->error = PORT_GetError();
PORT_Assert (p7dcx->error);
if (p7dcx->error == 0)
p7dcx->error = -1;
}
}
if (p7dcx->error == 0)
return SECSuccess;
if (p7dcx->dcx != NULL) {
(void) SEC_ASN1DecoderFinish (p7dcx->dcx);
p7dcx->dcx = NULL;
}
PORT_SetError (p7dcx->error);
return SECFailure;
}
void
SecCmsDecoderCancel(SecCmsDecoderContext *p7dcx)
{
SecCmsMessageDestroy(p7dcx->cmsg);
(void)SEC_ASN1DecoderFinish(p7dcx->dcx);
PORT_Free(p7dcx);
}
SecCmsMessage *
SecCmsDecoderFinish(SecCmsDecoderContext *p7dcx)
{
SecCmsMessage *cmsg;
cmsg = p7dcx->cmsg;
if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
nss_cms_after_end(p7dcx) != SECSuccess)
{
SecCmsMessageDestroy(cmsg);
cmsg = NULL;
}
PORT_Free(p7dcx);
return cmsg;
}
SecCmsMessage *
SecCmsMessageCreateFromDER(CSSM_DATA *DERmessage,
SecCmsContentCallback cb, void *cb_arg,
PK11PasswordFunc pwfn, void *pwfn_arg,
SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg)
{
SecCmsDecoderContext *p7dcx;
p7dcx = SecCmsDecoderStart(NULL, cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg);
(void) SecCmsDecoderUpdate(p7dcx, (char *)DERmessage->Data, DERmessage->Length);
return SecCmsDecoderFinish(p7dcx);
}