]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmsdigest.c
Security-58286.1.32.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmsdigest.c
1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation. Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above. If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL. If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34 /*
35 * CMS digesting.
36 */
37 #include <assert.h>
38
39 #include "cmslocal.h"
40
41 #include "SecAsn1Item.h"
42 #include "secoid.h"
43
44 #include <security_asn1/secerr.h>
45 #include <security_asn1/secport.h>
46
47 #if USE_CDSA_CRYPTO
48 #include <Security/cssmapi.h>
49 #else
50 #include <CommonCrypto/CommonDigest.h>
51 #endif
52
53 #include "SecCmsDigestContext.h"
54
55 /* Return the maximum value between S and T (and U) */
56 #define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
57 #define MAX_OF_3(S, T, U) ({__typeof__(U) _max_st = MAX(S,T); MAX(_max_st,U);})
58
59 struct SecCmsDigestContextStr {
60 PLArenaPool * poolp;
61 Boolean saw_contents;
62 int digcnt;
63 #if USE_CDSA_CRYPTO
64 CSSM_CC_HANDLE * digobjs;
65 #else
66 void ** digobjs;
67 #endif
68 SECAlgorithmID ** digestalgs;
69 };
70
71 /*
72 * SecCmsDigestContextStartMultiple - start digest calculation using all the
73 * digest algorithms in "digestalgs" in parallel.
74 */
75 SecCmsDigestContextRef
76 SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
77 {
78 PLArenaPool *poolp;
79 SecCmsDigestContextRef cmsdigcx;
80 #if USE_CDSA_CRYPTO
81 CSSM_CC_HANDLE digobj;
82 #else
83 void * digobj;
84 #endif
85 int digcnt;
86 int i;
87
88 poolp = PORT_NewArena(1024);
89 if (poolp == NULL)
90 goto loser;
91
92 digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
93
94 cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr));
95 if (cmsdigcx == NULL) {
96 goto loser;
97 }
98 cmsdigcx->poolp = poolp;
99
100 if (digcnt > 0) {
101 #if USE_CDSA_CRYPTO
102 /* Security check to prevent under-allocation */
103 if (digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_CC_HANDLE),sizeof(SECAlgorithmID *))))-1)) {
104 goto loser;
105 }
106 cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ArenaAlloc(poolp, digcnt * sizeof(CSSM_CC_HANDLE));
107 if (cmsdigcx->digobjs == NULL)
108 goto loser;
109 #else
110 /* Security check to prevent under-allocation */
111 if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
112 goto loser;
113 }
114 cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
115 if (cmsdigcx->digobjs == NULL)
116 goto loser;
117 #endif
118 cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp,
119 (digcnt + 1) * sizeof(SECAlgorithmID *));
120 if (cmsdigcx->digestalgs == NULL)
121 goto loser;
122 }
123
124 cmsdigcx->digcnt = 0;
125
126 /*
127 * Create a digest object context for each algorithm.
128 */
129 for (i = 0; i < digcnt; i++) {
130 digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
131 /*
132 * Skip any algorithm we do not even recognize; obviously,
133 * this could be a problem, but if it is critical then the
134 * result will just be that the signature does not verify.
135 * We do not necessarily want to error out here, because
136 * the particular algorithm may not actually be important,
137 * but we cannot know that until later.
138 */
139 #if USE_CDSA_CRYPTO
140 if (digobj)
141 if (CSSM_DigestDataInit(digobj))
142 goto loser;
143 #endif
144
145 cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
146 cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
147 if (SECITEM_CopyItem(poolp,
148 &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
149 &(digestalgs[i]->algorithm))
150 || SECITEM_CopyItem(poolp,
151 &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
152 &(digestalgs[i]->parameters)))
153 goto loser;
154 cmsdigcx->digcnt++;
155 }
156
157 cmsdigcx->saw_contents = PR_FALSE;
158
159 return cmsdigcx;
160
161 loser:
162 if (poolp)
163 PORT_FreeArena(poolp, PR_FALSE);
164
165 return NULL;
166 }
167
168 /*
169 * SecCmsDigestContextStartSingle - same as SecCmsDigestContextStartMultiple, but
170 * only one algorithm.
171 */
172 SecCmsDigestContextRef
173 SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
174 {
175 SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
176
177 digestalgs[0] = digestalg;
178 return SecCmsDigestContextStartMultiple(digestalgs);
179 }
180
181 /*
182 * SecCmsDigestContextUpdate - feed more data into the digest machine
183 */
184 void
185 SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
186 {
187 SecAsn1Item dataBuf;
188 int i;
189
190 dataBuf.Length = len;
191 dataBuf.Data = (uint8_t *)data;
192 cmsdigcx->saw_contents = PR_TRUE;
193 for (i = 0; i < cmsdigcx->digcnt; i++) {
194 if (cmsdigcx->digobjs[i]) {
195 #if USE_CDSA_CRYPTO
196 CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
197 #else
198 /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
199 This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
200 possible security issue. There is no way to return an error here, but a check at
201 the upper level may happen. */
202 /*
203 rdar://problem/20642513
204 Let's just die a horrible death rather than have the security issue.
205 CMS blob over 4GB? Oh well.
206 */
207 if (len > UINT32_MAX) {
208 /* Ugh. */
209 abort();
210 }
211 assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
212 switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
213 case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
214 case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
215 case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
216 case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
217 case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
218 case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
219 default:
220 break;
221 }
222 #endif
223 }
224 }
225 }
226
227 /*
228 * SecCmsDigestContextCancel - cancel digesting operation
229 */
230 void
231 SecCmsDigestContextCancel(SecCmsDigestContextRef cmsdigcx)
232 {
233 int i;
234
235 for (i = 0; i < cmsdigcx->digcnt; i++)
236 if (cmsdigcx->digobjs[i])
237 #if USE_CDSA_CRYPTO
238 CSSM_DeleteContext(cmsdigcx->digobjs[i]);
239 #else
240 free(cmsdigcx->digobjs[i]);
241 #endif
242
243 PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
244 }
245
246 /*
247 * SecCmsDigestContextDestroy - delete a digesting operation
248 */
249 void
250 SecCmsDigestContextDestroy(SecCmsDigestContextRef cmsdigcx)
251 {
252 SecCmsDigestContextCancel(cmsdigcx);
253 }
254
255 /*
256 * SecCmsDigestContextFinishMultiple - finish the digests
257 */
258 OSStatus
259 SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
260 SECAlgorithmID ***digestalgsp,
261 SecAsn1Item * **digestsp)
262 {
263 #if USE_CDSA_CRYPTO
264 CSSM_CC_HANDLE digboj;
265 #else
266 void * digobj;
267 #endif
268 SecAsn1Item **digests, *digest;
269 SECAlgorithmID **digestalgs;
270 int i;
271 void *mark;
272 OSStatus rv = SECFailure;
273
274 assert(cmsdigcx != NULL);
275
276 /* A message with no contents (just signed attributes) is used within SCEP */
277 #if 0
278 /* no contents? do not update digests */
279 if (digestsp == NULL || !cmsdigcx->saw_contents) {
280 for (i = 0; i < cmsdigcx->digcnt; i++)
281 if (cmsdigcx->digobjs[i])
282 #if USE_CDSA_CRYPTO
283 CSSM_DeleteContext(cmsdigcx->digobjs[i]);
284 #else
285 free(cmsdigcx->digobjs[i]);
286 #endif
287 rv = SECSuccess;
288 if (digestsp)
289 *digestsp = NULL;
290 goto cleanup;
291 }
292 #endif
293
294 assert(digestsp != NULL);
295 assert(digestalgsp != NULL);
296
297 mark = PORT_ArenaMark (cmsdigcx->poolp);
298
299 /* Security check to prevent under-allocation */
300 if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX_OF_3(sizeof(SECAlgorithmID *),sizeof(SecAsn1Item *),sizeof(SecAsn1Item))))-1)) {
301 goto loser;
302 }
303 /* allocate digest array & SecAsn1Items on arena */
304 digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SECAlgorithmID *));
305 digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
306 digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
307 if (digestalgs == NULL || digests == NULL || digest == NULL) {
308 goto loser;
309 }
310
311 for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
312
313 SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
314 int diglength = 0;
315
316 switch (hash_alg) {
317 case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
318 case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
319 case SEC_OID_SHA224: diglength = CC_SHA224_DIGEST_LENGTH; break;
320 case SEC_OID_SHA256: diglength = CC_SHA256_DIGEST_LENGTH; break;
321 case SEC_OID_SHA384: diglength = CC_SHA384_DIGEST_LENGTH; break;
322 case SEC_OID_SHA512: diglength = CC_SHA512_DIGEST_LENGTH; break;
323 default: goto loser; break;
324 }
325
326 digobj = cmsdigcx->digobjs[i];
327 if (digobj)
328 {
329 digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
330 if (digest->Data == NULL)
331 goto loser;
332 digest->Length = diglength;
333 #if USE_CDSA_CRYPTO
334 CSSM_DigestDataFinal(digobj, digest);
335 CSSM_DeleteContext(digobj);
336 #else
337 switch (hash_alg) {
338 case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
339 case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
340 case SEC_OID_SHA224: CC_SHA224_Final(digest->Data, digobj); break;
341 case SEC_OID_SHA256: CC_SHA256_Final(digest->Data, digobj); break;
342 case SEC_OID_SHA384: CC_SHA384_Final(digest->Data, digobj); break;
343 case SEC_OID_SHA512: CC_SHA512_Final(digest->Data, digobj); break;
344 default: goto loser; break;
345 }
346
347 free(digobj);
348 #endif
349 digestalgs[i] = cmsdigcx->digestalgs[i];
350 digests[i] = digest;
351 }
352 else
353 {
354 digest->Data = NULL;
355 digest->Length = 0;
356 }
357 }
358 digestalgs[i] = NULL;
359 digests[i] = NULL;
360 *digestalgsp = digestalgs;
361 *digestsp = digests;
362
363 rv = SECSuccess;
364
365 loser:
366 if (rv == SECSuccess)
367 PORT_ArenaUnmark(cmsdigcx->poolp, mark);
368 else
369 PORT_ArenaRelease(cmsdigcx->poolp, mark);
370
371 /*cleanup:*/
372 /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
373 cmsdigcx->digcnt = 0;
374
375 return rv;
376 }
377
378 /*
379 * SecCmsDigestContextFinishSingle - same as SecCmsDigestContextFinishMultiple,
380 * but for one digest.
381 */
382 OSStatus
383 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
384 SecAsn1Item * digest)
385 {
386 OSStatus rv = SECFailure;
387 SecAsn1Item * *dp;
388 SECAlgorithmID **ap;
389
390 /* get the digests into arena, then copy the first digest into poolp */
391 if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess)
392 goto loser;
393
394 /* Return the first element in the digest array. */
395 if (digest) {
396 *digest = *dp[0];
397 }
398
399 rv = SECSuccess;
400
401 loser:
402 return rv;
403 }