#include "cmslocal.h"
#include "secitem.h"
#include "secoid.h"
#include <security_asn1/secerr.h>
#include <Security/cssmapi.h>
#include <Security/SecCmsDigestContext.h>
#define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
struct SecCmsDigestContextStr {
Boolean saw_contents;
int digcnt;
CSSM_CC_HANDLE * digobjs;
};
SecCmsDigestContextRef
SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
{
SecCmsDigestContextRef cmsdigcx;
CSSM_CC_HANDLE digobj;
int digcnt;
int i;
digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
cmsdigcx = (SecCmsDigestContextRef)PORT_ZAlloc(sizeof(struct SecCmsDigestContextStr));
if (cmsdigcx == NULL)
return NULL;
if (digcnt > 0) {
if (digcnt >= (int)(INT_MAX/sizeof(CSSM_CC_HANDLE))) {
goto loser;
}
cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ZAlloc(digcnt * sizeof(CSSM_CC_HANDLE));
if (cmsdigcx->digobjs == NULL)
goto loser;
}
cmsdigcx->digcnt = 0;
for (i = 0; i < digcnt; i++) {
digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
if (digobj)
{
CSSM_RETURN result;
result = CSSM_DigestDataInit(digobj);
if (result != CSSM_OK)
{
goto loser;
}
}
cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
cmsdigcx->digcnt++;
}
cmsdigcx->saw_contents = PR_FALSE;
return cmsdigcx;
loser:
if (cmsdigcx) {
if (cmsdigcx->digobjs) {
PORT_Free(cmsdigcx->digobjs);
cmsdigcx->digobjs = NULL;
cmsdigcx->digcnt = 0;
}
}
return NULL;
}
SecCmsDigestContextRef
SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
{
SECAlgorithmID *digestalgs[] = { NULL, NULL };
digestalgs[0] = digestalg;
return SecCmsDigestContextStartMultiple(digestalgs);
}
void
SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
{
CSSM_DATA dataBuf;
int i;
dataBuf.Length = len;
dataBuf.Data = (uint8 *)data;
cmsdigcx->saw_contents = PR_TRUE;
for (i = 0; i < cmsdigcx->digcnt; i++)
if (cmsdigcx->digobjs && cmsdigcx->digobjs[i])
CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
}
void
SecCmsDigestContextCancel(SecCmsDigestContextRef cmsdigcx)
{
int i;
for (i = 0; i < cmsdigcx->digcnt; i++)
if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
CSSM_DeleteContext(cmsdigcx->digobjs[i]);
cmsdigcx->digobjs[i] = 0;
}
}
OSStatus
SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
CSSM_DATA_PTR **digestsp)
{
CSSM_CC_HANDLE digobj;
CSSM_DATA_PTR *digests, digest;
int i;
void *mark;
OSStatus rv = SECFailure;
if (digestsp == NULL || !cmsdigcx->saw_contents) {
for (i = 0; i < cmsdigcx->digcnt; i++)
if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
CSSM_DeleteContext(cmsdigcx->digobjs[i]);
cmsdigcx->digobjs[i] = 0;
}
rv = SECSuccess;
goto cleanup;
}
mark = PORT_ArenaMark ((PLArenaPool *)poolp);
if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_DATA_PTR),sizeof(CSSM_DATA))))-1)) {
goto loser;
}
digests = (CSSM_DATA_PTR *)PORT_ArenaAlloc((PLArenaPool *)poolp, (cmsdigcx->digcnt+1) * sizeof(CSSM_DATA_PTR));
digest = (CSSM_DATA_PTR)PORT_ArenaZAlloc((PLArenaPool *)poolp, cmsdigcx->digcnt * sizeof(CSSM_DATA));
if (digests == NULL || digest == NULL) {
goto loser;
}
for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
if (cmsdigcx->digobjs) {
digobj = cmsdigcx->digobjs[i];
} else {
digobj = 0;
}
CSSM_QUERY_SIZE_DATA dataSize;
rv = CSSM_QuerySize(digobj, CSSM_FALSE, 1, &dataSize);
if (rv != CSSM_OK)
{
goto loser;
}
int diglength = dataSize.SizeOutputBlock;
if (digobj)
{
digest->Data = (unsigned char*)PORT_ArenaAlloc((PLArenaPool *)poolp, diglength);
if (digest->Data == NULL)
goto loser;
digest->Length = diglength;
rv = CSSM_DigestDataFinal(digobj, digest);
if (rv != CSSM_OK)
{
goto loser;
}
CSSM_DeleteContext(digobj);
cmsdigcx->digobjs[i] = 0;
}
else
{
digest->Data = NULL;
digest->Length = 0;
}
digests[i] = digest;
}
digests[i] = NULL;
*digestsp = digests;
rv = SECSuccess;
loser:
if (rv == SECSuccess)
PORT_ArenaUnmark((PLArenaPool *)poolp, mark);
else
PORT_ArenaRelease((PLArenaPool *)poolp, mark);
cleanup:
if (cmsdigcx->digcnt > 0) {
SecCmsDigestContextCancel(cmsdigcx);
PORT_Free(cmsdigcx->digobjs);
cmsdigcx->digobjs = NULL;
cmsdigcx->digcnt = 0;
}
PORT_Free(cmsdigcx);
return rv;
}
OSStatus
SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
CSSM_DATA_PTR digest)
{
OSStatus rv = SECFailure;
CSSM_DATA_PTR *dp;
PLArenaPool *arena = NULL;
if ((arena = PORT_NewArena(1024)) == NULL)
goto loser;
if (SecCmsDigestContextFinishMultiple(cmsdigcx, (SecArenaPoolRef)arena, &dp) != SECSuccess)
goto loser;
if (SECITEM_CopyItem((PLArenaPool *)poolp, digest, dp[0]) != SECSuccess)
goto loser;
rv = SECSuccess;
loser:
if (arena)
PORT_FreeArena(arena, PR_FALSE);
return rv;
}