Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* CMS digesting.
*/
#include "cmslocal.h"
#include "cert.h"
#include "keyhi.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "prtime.h"
#include "secerr.h"
#include "smime.h"
/* #define CMS_FIND_LEAK_MULTIPLE 1 */
#ifdef CMS_FIND_LEAK_MULTIPLE
static int stop_on_err = 1;
static int global_num_digests = 0;
#endif
struct digestPairStr {
const SECHashObject *digobj;
void *digcx;
};
typedef struct digestPairStr digestPair;
struct NSSCMSDigestContextStr {
PRBool saw_contents;
PLArenaPool *pool;
int digcnt;
digestPair *digPairs;
};
/*
* NSS_CMSDigestContext_StartMultiple - start digest calculation using all the
* digest algorithms in "digestalgs" in parallel.
*/
NSSCMSDigestContext *
NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs)
{
PLArenaPool *pool;
NSSCMSDigestContext *cmsdigcx;
int digcnt;
int i;
#ifdef CMS_FIND_LEAK_MULTIPLE
PORT_Assert(global_num_digests == 0 || !stop_on_err);
#endif
digcnt = (digestalgs == NULL) ? 0 : NSS_CMSArray_Count((void **)digestalgs);
/* It's OK if digcnt is zero. We have to allow this for "certs only"
** messages.
*/
pool = PORT_NewArena(2048);
if (!pool)
return NULL;
cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext);
if (cmsdigcx == NULL)
goto loser;
cmsdigcx->saw_contents = PR_FALSE;
cmsdigcx->pool = pool;
cmsdigcx->digcnt = digcnt;
cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt);
if (cmsdigcx->digPairs == NULL) {
goto loser;
}
/*
* Create a digest object context for each algorithm.
*/
for (i = 0; i < digcnt; i++) {
const SECHashObject *digobj;
void *digcx;
if (!NSS_SMIMEUtil_SigningAllowed(digestalgs[i])) {
goto loser;
}
digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]);
/*
* Skip any algorithm we do not even recognize; obviously,
* this could be a problem, but if it is critical then the
* result will just be that the signature does not verify.
* We do not necessarily want to error out here, because
* the particular algorithm may not actually be important,
* but we cannot know that until later.
*/
if (digobj == NULL)
continue;
digcx = (*digobj->create)();
if (digcx != NULL) {
(*digobj->begin)(digcx);
cmsdigcx->digPairs[i].digobj = digobj;
cmsdigcx->digPairs[i].digcx = digcx;
#ifdef CMS_FIND_LEAK_MULTIPLE
global_num_digests++;
#endif
}
}
return cmsdigcx;
loser:
/* free any earlier digest objects that may have bee allocated. */
for (i = 0; i < digcnt; i++) {
digestPair *pair = &cmsdigcx->digPairs[i];
if (pair->digobj) {
(*pair->digobj->destroy)(pair->digcx, PR_TRUE);
#ifdef CMS_FIND_LEAK_MULTIPLE
--global_num_digests;
#endif
}
}
if (pool) {
PORT_FreeArena(pool, PR_FALSE);
}
return NULL;
}
/*
* NSS_CMSDigestContext_StartSingle - same as
* NSS_CMSDigestContext_StartMultiple, but only one algorithm.
*/
NSSCMSDigestContext *
NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg)
{
SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
digestalgs[0] = digestalg;
return NSS_CMSDigestContext_StartMultiple(digestalgs);
}
/*
* NSS_CMSDigestContext_Update - feed more data into the digest machine
*/
void
NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx,
const unsigned char *data, int len)
{
int i;
digestPair *pair = cmsdigcx->digPairs;
cmsdigcx->saw_contents = PR_TRUE;
for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
if (pair->digcx) {
(*pair->digobj->update)(pair->digcx, data, len);
}
}
}
/*
* NSS_CMSDigestContext_Cancel - cancel digesting operation
*/
void
NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx)
{
int i;
digestPair *pair = cmsdigcx->digPairs;
for (i = 0; i < cmsdigcx->digcnt; i++, pair++) {
if (pair->digcx) {
(*pair->digobj->destroy)(pair->digcx, PR_TRUE);
#ifdef CMS_FIND_LEAK_MULTIPLE
--global_num_digests;
#endif
}
}
#ifdef CMS_FIND_LEAK_MULTIPLE
PORT_Assert(global_num_digests == 0 || !stop_on_err);
#endif
PORT_FreeArena(cmsdigcx->pool, PR_FALSE);
}
/*
* NSS_CMSDigestContext_FinishMultiple - finish the digests and put them
* into an array of SECItems (allocated on poolp)
*/
SECStatus
NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx,
PLArenaPool *poolp,
SECItem ***digestsp)
{
SECItem **digests = NULL;
digestPair *pair;
void *mark;
int i;
SECStatus rv;
/* no contents? do not finish digests */
if (digestsp == NULL || !cmsdigcx->saw_contents) {
rv = SECSuccess;
goto cleanup;
}
mark = PORT_ArenaMark(poolp);
/* allocate digest array & SECItems on arena */
digests = PORT_ArenaNewArray(poolp, SECItem *, cmsdigcx->digcnt + 1);
rv = ((digests == NULL) ? SECFailure : SECSuccess);
pair = cmsdigcx->digPairs;
for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) {
SECItem digest;
unsigned char hash[HASH_LENGTH_MAX];
if (!pair->digcx) {
digests[i] = NULL;
continue;
}
digest.type = siBuffer;
digest.data = hash;
digest.len = pair->digobj->length;
(*pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len);
digests[i] = SECITEM_ArenaDupItem(poolp, &digest);
if (!digests[i]) {
rv = SECFailure;
}
}
digests[i] = NULL;
if (rv == SECSuccess) {
PORT_ArenaUnmark(poolp, mark);
} else
PORT_ArenaRelease(poolp, mark);
cleanup:
NSS_CMSDigestContext_Cancel(cmsdigcx);
/* Don't change the caller's digests pointer if we have no digests.
** NSS_CMSSignedData_Encode_AfterData depends on this behavior.
*/
if (rv == SECSuccess && digestsp && digests) {
*digestsp = digests;
}
return rv;
}
/*
* NSS_CMSDigestContext_FinishSingle - same as
* NSS_CMSDigestContext_FinishMultiple, but for one digest.
*/
SECStatus
NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx,
PLArenaPool *poolp,
SECItem *digest)
{
SECStatus rv = SECFailure;
SECItem **dp = NULL;
PLArenaPool *arena = NULL;
if ((arena = PORT_NewArena(1024)) == NULL)
goto loser;
/* get the digests into arena, then copy the first digest into poolp */
rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp);
if (rv == SECSuccess && dp) {
/* now copy it into poolp */
rv = SECITEM_CopyItem(poolp, digest, dp[0]);
}
loser:
if (arena)
PORT_FreeArena(arena, PR_FALSE);
return rv;
}