/*
* CMS digesting.
*/
+#include <assert.h>
#include "cmslocal.h"
-#include "secitem.h"
+#include "SecAsn1Item.h"
#include "secoid.h"
#include <security_asn1/secerr.h>
+#include <security_asn1/secport.h>
+
+#if USE_CDSA_CRYPTO
#include <Security/cssmapi.h>
+#else
+#include <CommonCrypto/CommonDigest.h>
+#endif
-#include <Security/SecCmsDigestContext.h>
+#include "SecCmsDigestContext.h"
+/* Return the maximum value between S and T (and U) */
+#define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
+#define MAX_OF_3(S, T, U) ({__typeof__(U) _max_st = MAX(S,T); MAX(_max_st,U);})
struct SecCmsDigestContextStr {
+ PLArenaPool * poolp;
Boolean saw_contents;
- int digcnt;
+ int digcnt;
+#if USE_CDSA_CRYPTO
CSSM_CC_HANDLE * digobjs;
+#else
+ void ** digobjs;
+#endif
+ SECAlgorithmID ** digestalgs;
};
/*
SecCmsDigestContextRef
SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
{
+ PLArenaPool *poolp;
SecCmsDigestContextRef cmsdigcx;
+#if USE_CDSA_CRYPTO
CSSM_CC_HANDLE digobj;
+#else
+ void * digobj;
+#endif
int digcnt;
int i;
+ poolp = PORT_NewArena(1024);
+ if (poolp == NULL)
+ goto loser;
+
digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
- cmsdigcx = (SecCmsDigestContextRef)PORT_Alloc(sizeof(struct SecCmsDigestContextStr));
- if (cmsdigcx == NULL)
- return NULL;
+ cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr));
+ if (cmsdigcx == NULL) {
+ goto loser;
+ }
+ cmsdigcx->poolp = poolp;
if (digcnt > 0) {
- cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_Alloc(digcnt * sizeof(CSSM_CC_HANDLE));
+#if USE_CDSA_CRYPTO
+ /* Security check to prevent under-allocation */
+ if (digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_CC_HANDLE),sizeof(SECAlgorithmID *))))-1)) {
+ goto loser;
+ }
+ cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ArenaAlloc(poolp, digcnt * sizeof(CSSM_CC_HANDLE));
if (cmsdigcx->digobjs == NULL)
goto loser;
+#else
+ /* Security check to prevent under-allocation */
+ if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
+ goto loser;
+ }
+ cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
+ if (cmsdigcx->digobjs == NULL)
+ goto loser;
+#endif
+ cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp,
+ (digcnt + 1) * sizeof(SECAlgorithmID *));
+ if (cmsdigcx->digestalgs == NULL)
+ goto loser;
}
cmsdigcx->digcnt = 0;
* the particular algorithm may not actually be important,
* but we cannot know that until later.
*/
+#if USE_CDSA_CRYPTO
if (digobj)
- {
- CSSM_RETURN result;
- result = CSSM_DigestDataInit(digobj);
- if (result != CSSM_OK)
- {
- goto loser;
- }
- }
-
+ if (CSSM_DigestDataInit(digobj))
+ goto loser;
+#endif
+
cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
+ cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
+ if (SECITEM_CopyItem(poolp,
+ &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
+ &(digestalgs[i]->algorithm))
+ || SECITEM_CopyItem(poolp,
+ &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
+ &(digestalgs[i]->parameters)))
+ goto loser;
cmsdigcx->digcnt++;
}
return cmsdigcx;
loser:
- if (cmsdigcx) {
- if (cmsdigcx->digobjs)
- PORT_Free(cmsdigcx->digobjs);
- }
+ if (poolp)
+ PORT_FreeArena(poolp, PR_FALSE);
+
return NULL;
}
void
SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
{
- CSSM_DATA dataBuf;
+ SecAsn1Item dataBuf;
int i;
dataBuf.Length = len;
- dataBuf.Data = (uint8 *)data;
+ dataBuf.Data = (uint8_t *)data;
cmsdigcx->saw_contents = PR_TRUE;
- for (i = 0; i < cmsdigcx->digcnt; i++)
- if (cmsdigcx->digobjs[i])
+ for (i = 0; i < cmsdigcx->digcnt; i++) {
+ if (cmsdigcx->digobjs[i]) {
+#if USE_CDSA_CRYPTO
CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
+#else
+ /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
+ This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
+ possible security issue. There is no way to return an error here, but a check at
+ the upper level may happen. */
+ /*
+ rdar://problem/20642513
+ Let's just die a horrible death rather than have the security issue.
+ CMS blob over 4GB? Oh well.
+ */
+ if (len > UINT32_MAX) {
+ /* Ugh. */
+ abort();
+ }
+ assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
+ switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
+ case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+ default:
+ break;
+ }
+#endif
+ }
+ }
}
/*
for (i = 0; i < cmsdigcx->digcnt; i++)
if (cmsdigcx->digobjs[i])
+#if USE_CDSA_CRYPTO
CSSM_DeleteContext(cmsdigcx->digobjs[i]);
+#else
+ free(cmsdigcx->digobjs[i]);
+#endif
+
+ PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
+}
+
+/*
+ * SecCmsDigestContextDestroy - delete a digesting operation
+ */
+void
+SecCmsDigestContextDestroy(SecCmsDigestContextRef cmsdigcx)
+{
+ SecCmsDigestContextCancel(cmsdigcx);
}
/*
- * SecCmsDigestContextFinishMultiple - finish the digests and put them
- * into an array of CSSM_DATAs (allocated on poolp)
+ * SecCmsDigestContextFinishMultiple - finish the digests
*/
OSStatus
-SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
- CSSM_DATA_PTR **digestsp)
+SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
+ SECAlgorithmID ***digestalgsp,
+ SecAsn1Item * **digestsp)
{
- CSSM_CC_HANDLE digobj;
- CSSM_DATA_PTR *digests, digest;
+#if USE_CDSA_CRYPTO
+ CSSM_CC_HANDLE digboj;
+#else
+ void * digobj;
+#endif
+ SecAsn1Item **digests, *digest;
+ SECAlgorithmID **digestalgs;
int i;
void *mark;
OSStatus rv = SECFailure;
+ assert(cmsdigcx != NULL);
+
+ /* A message with no contents (just signed attributes) is used within SCEP */
+#if 0
/* no contents? do not update digests */
if (digestsp == NULL || !cmsdigcx->saw_contents) {
for (i = 0; i < cmsdigcx->digcnt; i++)
if (cmsdigcx->digobjs[i])
+#if USE_CDSA_CRYPTO
CSSM_DeleteContext(cmsdigcx->digobjs[i]);
+#else
+ free(cmsdigcx->digobjs[i]);
+#endif
rv = SECSuccess;
if (digestsp)
*digestsp = NULL;
goto cleanup;
}
+#endif
- mark = PORT_ArenaMark ((PLArenaPool *)poolp);
+ assert(digestsp != NULL);
+ assert(digestalgsp != NULL);
- /* allocate digest array & CSSM_DATAs on arena */
- 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) {
+ mark = PORT_ArenaMark (cmsdigcx->poolp);
+
+ /* Security check to prevent under-allocation */
+ if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX_OF_3(sizeof(SECAlgorithmID *),sizeof(SecAsn1Item *),sizeof(SecAsn1Item))))-1)) {
+ goto loser;
+ }
+ /* allocate digest array & SecAsn1Items on arena */
+ digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SECAlgorithmID *));
+ digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
+ digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
+ if (digestalgs == NULL || digests == NULL || digest == NULL) {
goto loser;
}
for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
- digobj = cmsdigcx->digobjs[i];
- CSSM_QUERY_SIZE_DATA dataSize;
- rv = CSSM_QuerySize(digobj, CSSM_FALSE, 1, &dataSize);
- if (rv != CSSM_OK)
- {
- goto loser;
+
+ SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
+ int diglength = 0;
+
+ switch (hash_alg) {
+ case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
+ case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
+ case SEC_OID_SHA224: diglength = CC_SHA224_DIGEST_LENGTH; break;
+ case SEC_OID_SHA256: diglength = CC_SHA256_DIGEST_LENGTH; break;
+ case SEC_OID_SHA384: diglength = CC_SHA384_DIGEST_LENGTH; break;
+ case SEC_OID_SHA512: diglength = CC_SHA512_DIGEST_LENGTH; break;
+ default: goto loser; break;
}
- int diglength = dataSize.SizeOutputBlock;
-
+ digobj = cmsdigcx->digobjs[i];
if (digobj)
{
- digest->Data = (unsigned char*)PORT_ArenaAlloc((PLArenaPool *)poolp, diglength);
+ digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
if (digest->Data == NULL)
goto loser;
digest->Length = diglength;
- rv = CSSM_DigestDataFinal(digobj, digest);
- if (rv != CSSM_OK)
- {
- goto loser;
- }
-
+#if USE_CDSA_CRYPTO
+ CSSM_DigestDataFinal(digobj, digest);
CSSM_DeleteContext(digobj);
+#else
+ switch (hash_alg) {
+ case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
+ case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA224: CC_SHA224_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA256: CC_SHA256_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA384: CC_SHA384_Final(digest->Data, digobj); break;
+ case SEC_OID_SHA512: CC_SHA512_Final(digest->Data, digobj); break;
+ default: goto loser; break;
+ }
+
+ free(digobj);
+#endif
+ digestalgs[i] = cmsdigcx->digestalgs[i];
+ digests[i] = digest;
}
else
{
digest->Data = NULL;
digest->Length = 0;
}
-
- digests[i] = digest;
- }
+ }
+ digestalgs[i] = NULL;
digests[i] = NULL;
+ *digestalgsp = digestalgs;
*digestsp = digests;
rv = SECSuccess;
loser:
if (rv == SECSuccess)
- PORT_ArenaUnmark((PLArenaPool *)poolp, mark);
+ PORT_ArenaUnmark(cmsdigcx->poolp, mark);
else
- PORT_ArenaRelease((PLArenaPool *)poolp, mark);
+ PORT_ArenaRelease(cmsdigcx->poolp, mark);
-cleanup:
- if (cmsdigcx->digcnt > 0) {
- PORT_Free(cmsdigcx->digobjs);
- }
- PORT_Free(cmsdigcx);
+/*cleanup:*/
+ /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
+ cmsdigcx->digcnt = 0;
return rv;
}
* but for one digest.
*/
OSStatus
-SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
- CSSM_DATA_PTR digest)
+SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
+ SecAsn1Item * digest)
{
OSStatus rv = SECFailure;
- CSSM_DATA_PTR *dp;
- PLArenaPool *arena = NULL;
-
- if ((arena = PORT_NewArena(1024)) == NULL)
- goto loser;
+ SecAsn1Item * *dp;
+ SECAlgorithmID **ap;
/* get the digests into arena, then copy the first digest into poolp */
- if (SecCmsDigestContextFinishMultiple(cmsdigcx, (SecArenaPoolRef)arena, &dp) != SECSuccess)
+ if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess)
goto loser;
- /* now copy it into poolp */
- if (SECITEM_CopyItem((PLArenaPool *)poolp, digest, dp[0]) != SECSuccess)
- goto loser;
+ /* Return the first element in the digest array. */
+ if (digest) {
+ *digest = *dp[0];
+ }
rv = SECSuccess;
loser:
- if (arena)
- PORT_FreeArena(arena, PR_FALSE);
-
return rv;
}