1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/smime/cmsdigest.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,260 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * CMS digesting. 1.10 + */ 1.11 + 1.12 +#include "cmslocal.h" 1.13 + 1.14 +#include "cert.h" 1.15 +#include "key.h" 1.16 +#include "secitem.h" 1.17 +#include "secoid.h" 1.18 +#include "pk11func.h" 1.19 +#include "prtime.h" 1.20 +#include "secerr.h" 1.21 + 1.22 +/* #define CMS_FIND_LEAK_MULTIPLE 1 */ 1.23 +#ifdef CMS_FIND_LEAK_MULTIPLE 1.24 +static int stop_on_err = 1; 1.25 +static int global_num_digests = 0; 1.26 +#endif 1.27 + 1.28 +struct digestPairStr { 1.29 + const SECHashObject * digobj; 1.30 + void * digcx; 1.31 +}; 1.32 +typedef struct digestPairStr digestPair; 1.33 + 1.34 +struct NSSCMSDigestContextStr { 1.35 + PRBool saw_contents; 1.36 + PLArenaPool * pool; 1.37 + int digcnt; 1.38 + digestPair * digPairs; 1.39 +}; 1.40 + 1.41 + 1.42 +/* 1.43 + * NSS_CMSDigestContext_StartMultiple - start digest calculation using all the 1.44 + * digest algorithms in "digestalgs" in parallel. 1.45 + */ 1.46 +NSSCMSDigestContext * 1.47 +NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs) 1.48 +{ 1.49 + PLArenaPool * pool; 1.50 + NSSCMSDigestContext *cmsdigcx; 1.51 + int digcnt; 1.52 + int i; 1.53 + 1.54 +#ifdef CMS_FIND_LEAK_MULTIPLE 1.55 + PORT_Assert(global_num_digests == 0 || !stop_on_err); 1.56 +#endif 1.57 + 1.58 + digcnt = (digestalgs == NULL) ? 0 : NSS_CMSArray_Count((void **)digestalgs); 1.59 + /* It's OK if digcnt is zero. We have to allow this for "certs only" 1.60 + ** messages. 1.61 + */ 1.62 + pool = PORT_NewArena(2048); 1.63 + if (!pool) 1.64 + return NULL; 1.65 + 1.66 + cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext); 1.67 + if (cmsdigcx == NULL) 1.68 + goto loser; 1.69 + 1.70 + cmsdigcx->saw_contents = PR_FALSE; 1.71 + cmsdigcx->pool = pool; 1.72 + cmsdigcx->digcnt = digcnt; 1.73 + 1.74 + cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt); 1.75 + if (cmsdigcx->digPairs == NULL) { 1.76 + goto loser; 1.77 + } 1.78 + 1.79 + /* 1.80 + * Create a digest object context for each algorithm. 1.81 + */ 1.82 + for (i = 0; i < digcnt; i++) { 1.83 + const SECHashObject *digobj; 1.84 + void *digcx; 1.85 + 1.86 + digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]); 1.87 + /* 1.88 + * Skip any algorithm we do not even recognize; obviously, 1.89 + * this could be a problem, but if it is critical then the 1.90 + * result will just be that the signature does not verify. 1.91 + * We do not necessarily want to error out here, because 1.92 + * the particular algorithm may not actually be important, 1.93 + * but we cannot know that until later. 1.94 + */ 1.95 + if (digobj == NULL) 1.96 + continue; 1.97 + 1.98 + digcx = (*digobj->create)(); 1.99 + if (digcx != NULL) { 1.100 + (*digobj->begin) (digcx); 1.101 + cmsdigcx->digPairs[i].digobj = digobj; 1.102 + cmsdigcx->digPairs[i].digcx = digcx; 1.103 +#ifdef CMS_FIND_LEAK_MULTIPLE 1.104 + global_num_digests++; 1.105 +#endif 1.106 + } 1.107 + } 1.108 + return cmsdigcx; 1.109 + 1.110 +loser: 1.111 + /* no digest objects have been created, or need to be destroyed. */ 1.112 + if (pool) { 1.113 + PORT_FreeArena(pool, PR_FALSE); 1.114 + } 1.115 + return NULL; 1.116 +} 1.117 + 1.118 +/* 1.119 + * NSS_CMSDigestContext_StartSingle - same as 1.120 + * NSS_CMSDigestContext_StartMultiple, but only one algorithm. 1.121 + */ 1.122 +NSSCMSDigestContext * 1.123 +NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg) 1.124 +{ 1.125 + SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */ 1.126 + 1.127 + digestalgs[0] = digestalg; 1.128 + return NSS_CMSDigestContext_StartMultiple(digestalgs); 1.129 +} 1.130 + 1.131 +/* 1.132 + * NSS_CMSDigestContext_Update - feed more data into the digest machine 1.133 + */ 1.134 +void 1.135 +NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx, 1.136 + const unsigned char *data, int len) 1.137 +{ 1.138 + int i; 1.139 + digestPair *pair = cmsdigcx->digPairs; 1.140 + 1.141 + cmsdigcx->saw_contents = PR_TRUE; 1.142 + 1.143 + for (i = 0; i < cmsdigcx->digcnt; i++, pair++) { 1.144 + if (pair->digcx) { 1.145 + (*pair->digobj->update)(pair->digcx, data, len); 1.146 + } 1.147 + } 1.148 +} 1.149 + 1.150 +/* 1.151 + * NSS_CMSDigestContext_Cancel - cancel digesting operation 1.152 + */ 1.153 +void 1.154 +NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx) 1.155 +{ 1.156 + int i; 1.157 + digestPair *pair = cmsdigcx->digPairs; 1.158 + 1.159 + for (i = 0; i < cmsdigcx->digcnt; i++, pair++) { 1.160 + if (pair->digcx) { 1.161 + (*pair->digobj->destroy)(pair->digcx, PR_TRUE); 1.162 +#ifdef CMS_FIND_LEAK_MULTIPLE 1.163 + --global_num_digests; 1.164 +#endif 1.165 + } 1.166 + } 1.167 +#ifdef CMS_FIND_LEAK_MULTIPLE 1.168 + PORT_Assert(global_num_digests == 0 || !stop_on_err); 1.169 +#endif 1.170 + PORT_FreeArena(cmsdigcx->pool, PR_FALSE); 1.171 +} 1.172 + 1.173 +/* 1.174 + * NSS_CMSDigestContext_FinishMultiple - finish the digests and put them 1.175 + * into an array of SECItems (allocated on poolp) 1.176 + */ 1.177 +SECStatus 1.178 +NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx, 1.179 + PLArenaPool *poolp, 1.180 + SECItem ***digestsp) 1.181 +{ 1.182 + SECItem ** digests = NULL; 1.183 + digestPair *pair; 1.184 + void * mark; 1.185 + int i; 1.186 + SECStatus rv; 1.187 + 1.188 + /* no contents? do not finish digests */ 1.189 + if (digestsp == NULL || !cmsdigcx->saw_contents) { 1.190 + rv = SECSuccess; 1.191 + goto cleanup; 1.192 + } 1.193 + 1.194 + mark = PORT_ArenaMark (poolp); 1.195 + 1.196 + /* allocate digest array & SECItems on arena */ 1.197 + digests = PORT_ArenaNewArray( poolp, SECItem *, cmsdigcx->digcnt + 1); 1.198 + 1.199 + rv = ((digests == NULL) ? SECFailure : SECSuccess); 1.200 + pair = cmsdigcx->digPairs; 1.201 + for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) { 1.202 + SECItem digest; 1.203 + unsigned char hash[HASH_LENGTH_MAX]; 1.204 + 1.205 + if (!pair->digcx) { 1.206 + digests[i] = NULL; 1.207 + continue; 1.208 + } 1.209 + 1.210 + digest.type = siBuffer; 1.211 + digest.data = hash; 1.212 + digest.len = pair->digobj->length; 1.213 + (* pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len); 1.214 + digests[i] = SECITEM_ArenaDupItem(poolp, &digest); 1.215 + if (!digests[i]) { 1.216 + rv = SECFailure; 1.217 + } 1.218 + } 1.219 + digests[i] = NULL; 1.220 + if (rv == SECSuccess) { 1.221 + PORT_ArenaUnmark(poolp, mark); 1.222 + } else 1.223 + PORT_ArenaRelease(poolp, mark); 1.224 + 1.225 +cleanup: 1.226 + NSS_CMSDigestContext_Cancel(cmsdigcx); 1.227 + /* Don't change the caller's digests pointer if we have no digests. 1.228 + ** NSS_CMSSignedData_Encode_AfterData depends on this behavior. 1.229 + */ 1.230 + if (rv == SECSuccess && digestsp && digests) { 1.231 + *digestsp = digests; 1.232 + } 1.233 + return rv; 1.234 +} 1.235 + 1.236 +/* 1.237 + * NSS_CMSDigestContext_FinishSingle - same as 1.238 + * NSS_CMSDigestContext_FinishMultiple, but for one digest. 1.239 + */ 1.240 +SECStatus 1.241 +NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx, 1.242 + PLArenaPool *poolp, 1.243 + SECItem *digest) 1.244 +{ 1.245 + SECStatus rv = SECFailure; 1.246 + SECItem **dp; 1.247 + PLArenaPool *arena = NULL; 1.248 + 1.249 + if ((arena = PORT_NewArena(1024)) == NULL) 1.250 + goto loser; 1.251 + 1.252 + /* get the digests into arena, then copy the first digest into poolp */ 1.253 + rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp); 1.254 + if (rv == SECSuccess) { 1.255 + /* now copy it into poolp */ 1.256 + rv = SECITEM_CopyItem(poolp, digest, dp[0]); 1.257 + } 1.258 +loser: 1.259 + if (arena) 1.260 + PORT_FreeArena(arena, PR_FALSE); 1.261 + 1.262 + return rv; 1.263 +}