cmsdigest.c (7319B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * CMS digesting. 7 */ 8 9 #include "cmslocal.h" 10 11 #include "cert.h" 12 #include "keyhi.h" 13 #include "secitem.h" 14 #include "secoid.h" 15 #include "pk11func.h" 16 #include "prtime.h" 17 #include "secerr.h" 18 #include "smime.h" 19 20 /* #define CMS_FIND_LEAK_MULTIPLE 1 */ 21 #ifdef CMS_FIND_LEAK_MULTIPLE 22 static int stop_on_err = 1; 23 static int global_num_digests = 0; 24 #endif 25 26 struct digestPairStr { 27 const SECHashObject *digobj; 28 void *digcx; 29 }; 30 typedef struct digestPairStr digestPair; 31 32 struct NSSCMSDigestContextStr { 33 PRBool saw_contents; 34 PLArenaPool *pool; 35 int digcnt; 36 digestPair *digPairs; 37 }; 38 39 /* 40 * NSS_CMSDigestContext_StartMultiple - start digest calculation using all the 41 * digest algorithms in "digestalgs" in parallel. 42 */ 43 NSSCMSDigestContext * 44 NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs) 45 { 46 PLArenaPool *pool; 47 NSSCMSDigestContext *cmsdigcx; 48 int digcnt; 49 int i; 50 51 #ifdef CMS_FIND_LEAK_MULTIPLE 52 PORT_Assert(global_num_digests == 0 || !stop_on_err); 53 #endif 54 55 digcnt = (digestalgs == NULL) ? 0 : NSS_CMSArray_Count((void **)digestalgs); 56 /* It's OK if digcnt is zero. We have to allow this for "certs only" 57 ** messages. 58 */ 59 pool = PORT_NewArena(2048); 60 if (!pool) 61 return NULL; 62 63 cmsdigcx = PORT_ArenaNew(pool, NSSCMSDigestContext); 64 if (cmsdigcx == NULL) 65 goto loser; 66 67 cmsdigcx->saw_contents = PR_FALSE; 68 cmsdigcx->pool = pool; 69 cmsdigcx->digcnt = digcnt; 70 71 cmsdigcx->digPairs = PORT_ArenaZNewArray(pool, digestPair, digcnt); 72 if (cmsdigcx->digPairs == NULL) { 73 goto loser; 74 } 75 76 /* 77 * Create a digest object context for each algorithm. 78 */ 79 for (i = 0; i < digcnt; i++) { 80 const SECHashObject *digobj; 81 void *digcx; 82 83 if (!NSS_SMIMEUtil_SigningAllowed(digestalgs[i])) { 84 goto loser; 85 } 86 digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]); 87 /* 88 * Skip any algorithm we do not even recognize; obviously, 89 * this could be a problem, but if it is critical then the 90 * result will just be that the signature does not verify. 91 * We do not necessarily want to error out here, because 92 * the particular algorithm may not actually be important, 93 * but we cannot know that until later. 94 */ 95 if (digobj == NULL) 96 continue; 97 98 digcx = (*digobj->create)(); 99 if (digcx != NULL) { 100 (*digobj->begin)(digcx); 101 cmsdigcx->digPairs[i].digobj = digobj; 102 cmsdigcx->digPairs[i].digcx = digcx; 103 #ifdef CMS_FIND_LEAK_MULTIPLE 104 global_num_digests++; 105 #endif 106 } 107 } 108 return cmsdigcx; 109 110 loser: 111 /* free any earlier digest objects that may have bee allocated. */ 112 for (i = 0; i < digcnt; i++) { 113 digestPair *pair = &cmsdigcx->digPairs[i]; 114 if (pair->digobj) { 115 (*pair->digobj->destroy)(pair->digcx, PR_TRUE); 116 #ifdef CMS_FIND_LEAK_MULTIPLE 117 --global_num_digests; 118 #endif 119 } 120 } 121 if (pool) { 122 PORT_FreeArena(pool, PR_FALSE); 123 } 124 return NULL; 125 } 126 127 /* 128 * NSS_CMSDigestContext_StartSingle - same as 129 * NSS_CMSDigestContext_StartMultiple, but only one algorithm. 130 */ 131 NSSCMSDigestContext * 132 NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg) 133 { 134 SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */ 135 136 digestalgs[0] = digestalg; 137 return NSS_CMSDigestContext_StartMultiple(digestalgs); 138 } 139 140 /* 141 * NSS_CMSDigestContext_Update - feed more data into the digest machine 142 */ 143 void 144 NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx, 145 const unsigned char *data, int len) 146 { 147 int i; 148 digestPair *pair = cmsdigcx->digPairs; 149 150 cmsdigcx->saw_contents = PR_TRUE; 151 152 for (i = 0; i < cmsdigcx->digcnt; i++, pair++) { 153 if (pair->digcx) { 154 (*pair->digobj->update)(pair->digcx, data, len); 155 } 156 } 157 } 158 159 /* 160 * NSS_CMSDigestContext_Cancel - cancel digesting operation 161 */ 162 void 163 NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx) 164 { 165 int i; 166 digestPair *pair = cmsdigcx->digPairs; 167 168 for (i = 0; i < cmsdigcx->digcnt; i++, pair++) { 169 if (pair->digcx) { 170 (*pair->digobj->destroy)(pair->digcx, PR_TRUE); 171 #ifdef CMS_FIND_LEAK_MULTIPLE 172 --global_num_digests; 173 #endif 174 } 175 } 176 #ifdef CMS_FIND_LEAK_MULTIPLE 177 PORT_Assert(global_num_digests == 0 || !stop_on_err); 178 #endif 179 PORT_FreeArena(cmsdigcx->pool, PR_FALSE); 180 } 181 182 /* 183 * NSS_CMSDigestContext_FinishMultiple - finish the digests and put them 184 * into an array of SECItems (allocated on poolp) 185 */ 186 SECStatus 187 NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx, 188 PLArenaPool *poolp, 189 SECItem ***digestsp) 190 { 191 SECItem **digests = NULL; 192 digestPair *pair; 193 void *mark; 194 int i; 195 SECStatus rv; 196 197 /* no contents? do not finish digests */ 198 if (digestsp == NULL || !cmsdigcx->saw_contents) { 199 rv = SECSuccess; 200 goto cleanup; 201 } 202 203 mark = PORT_ArenaMark(poolp); 204 205 /* allocate digest array & SECItems on arena */ 206 digests = PORT_ArenaNewArray(poolp, SECItem *, cmsdigcx->digcnt + 1); 207 208 rv = ((digests == NULL) ? SECFailure : SECSuccess); 209 pair = cmsdigcx->digPairs; 210 for (i = 0; rv == SECSuccess && i < cmsdigcx->digcnt; i++, pair++) { 211 SECItem digest; 212 unsigned char hash[HASH_LENGTH_MAX]; 213 214 if (!pair->digcx) { 215 digests[i] = NULL; 216 continue; 217 } 218 219 digest.type = siBuffer; 220 digest.data = hash; 221 digest.len = pair->digobj->length; 222 (*pair->digobj->end)(pair->digcx, hash, &digest.len, digest.len); 223 digests[i] = SECITEM_ArenaDupItem(poolp, &digest); 224 if (!digests[i]) { 225 rv = SECFailure; 226 } 227 } 228 digests[i] = NULL; 229 if (rv == SECSuccess) { 230 PORT_ArenaUnmark(poolp, mark); 231 } else 232 PORT_ArenaRelease(poolp, mark); 233 234 cleanup: 235 NSS_CMSDigestContext_Cancel(cmsdigcx); 236 /* Don't change the caller's digests pointer if we have no digests. 237 ** NSS_CMSSignedData_Encode_AfterData depends on this behavior. 238 */ 239 if (rv == SECSuccess && digestsp && digests) { 240 *digestsp = digests; 241 } 242 return rv; 243 } 244 245 /* 246 * NSS_CMSDigestContext_FinishSingle - same as 247 * NSS_CMSDigestContext_FinishMultiple, but for one digest. 248 */ 249 SECStatus 250 NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx, 251 PLArenaPool *poolp, 252 SECItem *digest) 253 { 254 SECStatus rv = SECFailure; 255 SECItem **dp = NULL; 256 PLArenaPool *arena = NULL; 257 258 if ((arena = PORT_NewArena(1024)) == NULL) 259 goto loser; 260 261 /* get the digests into arena, then copy the first digest into poolp */ 262 rv = NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp); 263 if (rv == SECSuccess && dp && dp[0]) { 264 /* now copy it into poolp */ 265 rv = SECITEM_CopyItem(poolp, digest, dp[0]); 266 } 267 loser: 268 if (arena) 269 PORT_FreeArena(arena, PR_FALSE); 270 271 return rv; 272 }