]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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 | */ | |
d8f41ccd | 37 | #include <assert.h> |
b1ab9ed8 A |
38 | |
39 | #include "cmslocal.h" | |
40 | ||
d8f41ccd | 41 | #include "SecAsn1Item.h" |
b1ab9ed8 A |
42 | #include "secoid.h" |
43 | ||
44 | #include <security_asn1/secerr.h> | |
d8f41ccd | 45 | #include <security_asn1/secport.h> |
b1ab9ed8 | 46 | |
866f8763 A |
47 | #if USE_CDSA_CRYPTO |
48 | #include <Security/cssmapi.h> | |
49 | #else | |
d8f41ccd | 50 | #include <CommonCrypto/CommonDigest.h> |
866f8763 | 51 | #endif |
427c49bc | 52 | |
d8f41ccd | 53 | #include "SecCmsDigestContext.h" |
b1ab9ed8 | 54 | |
60c433a9 A |
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 | ||
b1ab9ed8 | 59 | struct SecCmsDigestContextStr { |
d8f41ccd | 60 | PLArenaPool * poolp; |
b1ab9ed8 | 61 | Boolean saw_contents; |
d8f41ccd | 62 | int digcnt; |
866f8763 A |
63 | #if USE_CDSA_CRYPTO |
64 | CSSM_CC_HANDLE * digobjs; | |
65 | #else | |
d8f41ccd | 66 | void ** digobjs; |
866f8763 | 67 | #endif |
d8f41ccd | 68 | SECAlgorithmID ** digestalgs; |
b1ab9ed8 A |
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 | { | |
d8f41ccd | 78 | PLArenaPool *poolp; |
b1ab9ed8 | 79 | SecCmsDigestContextRef cmsdigcx; |
866f8763 A |
80 | #if USE_CDSA_CRYPTO |
81 | CSSM_CC_HANDLE digobj; | |
82 | #else | |
d8f41ccd | 83 | void * digobj; |
866f8763 | 84 | #endif |
b1ab9ed8 A |
85 | int digcnt; |
86 | int i; | |
87 | ||
d8f41ccd A |
88 | poolp = PORT_NewArena(1024); |
89 | if (poolp == NULL) | |
90 | goto loser; | |
91 | ||
b1ab9ed8 A |
92 | digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs); |
93 | ||
d8f41ccd | 94 | cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr)); |
6b200bc3 A |
95 | if (cmsdigcx == NULL) { |
96 | goto loser; | |
97 | } | |
d8f41ccd | 98 | cmsdigcx->poolp = poolp; |
b1ab9ed8 A |
99 | |
100 | if (digcnt > 0) { | |
866f8763 A |
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 | |
60c433a9 A |
110 | /* Security check to prevent under-allocation */ |
111 | if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) { | |
112 | goto loser; | |
113 | } | |
d8f41ccd | 114 | cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *)); |
b1ab9ed8 A |
115 | if (cmsdigcx->digobjs == NULL) |
116 | goto loser; | |
866f8763 | 117 | #endif |
d8f41ccd A |
118 | cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp, |
119 | (digcnt + 1) * sizeof(SECAlgorithmID *)); | |
120 | if (cmsdigcx->digestalgs == NULL) | |
121 | goto loser; | |
b1ab9ed8 A |
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 | */ | |
866f8763 A |
139 | #if USE_CDSA_CRYPTO |
140 | if (digobj) | |
141 | if (CSSM_DigestDataInit(digobj)) | |
142 | goto loser; | |
143 | #endif | |
d8f41ccd | 144 | |
b1ab9ed8 | 145 | cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj; |
d8f41ccd A |
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; | |
b1ab9ed8 A |
154 | cmsdigcx->digcnt++; |
155 | } | |
156 | ||
157 | cmsdigcx->saw_contents = PR_FALSE; | |
158 | ||
159 | return cmsdigcx; | |
160 | ||
161 | loser: | |
d8f41ccd A |
162 | if (poolp) |
163 | PORT_FreeArena(poolp, PR_FALSE); | |
164 | ||
b1ab9ed8 A |
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 | { | |
d8f41ccd | 187 | SecAsn1Item dataBuf; |
b1ab9ed8 A |
188 | int i; |
189 | ||
190 | dataBuf.Length = len; | |
d8f41ccd | 191 | dataBuf.Data = (uint8_t *)data; |
b1ab9ed8 | 192 | cmsdigcx->saw_contents = PR_TRUE; |
d8f41ccd A |
193 | for (i = 0; i < cmsdigcx->digcnt; i++) { |
194 | if (cmsdigcx->digobjs[i]) { | |
866f8763 A |
195 | #if USE_CDSA_CRYPTO |
196 | CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1); | |
197 | #else | |
d8f41ccd A |
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. */ | |
60c433a9 A |
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 | } | |
d8f41ccd A |
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 | } | |
866f8763 | 222 | #endif |
d8f41ccd A |
223 | } |
224 | } | |
b1ab9ed8 A |
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]) | |
866f8763 A |
237 | #if USE_CDSA_CRYPTO |
238 | CSSM_DeleteContext(cmsdigcx->digobjs[i]); | |
239 | #else | |
d8f41ccd | 240 | free(cmsdigcx->digobjs[i]); |
866f8763 | 241 | #endif |
d8f41ccd | 242 | |
6b200bc3 | 243 | PORT_FreeArena(cmsdigcx->poolp, PR_TRUE); |
b1ab9ed8 A |
244 | } |
245 | ||
246 | /* | |
d8f41ccd A |
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 | |
b1ab9ed8 A |
257 | */ |
258 | OSStatus | |
d8f41ccd A |
259 | SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx, |
260 | SECAlgorithmID ***digestalgsp, | |
261 | SecAsn1Item * **digestsp) | |
b1ab9ed8 | 262 | { |
866f8763 A |
263 | #if USE_CDSA_CRYPTO |
264 | CSSM_CC_HANDLE digboj; | |
265 | #else | |
d8f41ccd | 266 | void * digobj; |
866f8763 | 267 | #endif |
d8f41ccd A |
268 | SecAsn1Item **digests, *digest; |
269 | SECAlgorithmID **digestalgs; | |
b1ab9ed8 A |
270 | int i; |
271 | void *mark; | |
272 | OSStatus rv = SECFailure; | |
273 | ||
d8f41ccd | 274 | assert(cmsdigcx != NULL); |
866f8763 A |
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 | ||
d8f41ccd A |
294 | assert(digestsp != NULL); |
295 | assert(digestalgsp != NULL); | |
b1ab9ed8 | 296 | |
d8f41ccd | 297 | mark = PORT_ArenaMark (cmsdigcx->poolp); |
b1ab9ed8 | 298 | |
60c433a9 A |
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 | } | |
d8f41ccd A |
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) { | |
b1ab9ed8 A |
308 | goto loser; |
309 | } | |
310 | ||
311 | for (i = 0; i < cmsdigcx->digcnt; i++, digest++) { | |
d8f41ccd A |
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; | |
427c49bc A |
324 | } |
325 | ||
d8f41ccd | 326 | digobj = cmsdigcx->digobjs[i]; |
b1ab9ed8 A |
327 | if (digobj) |
328 | { | |
d8f41ccd | 329 | digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength); |
b1ab9ed8 A |
330 | if (digest->Data == NULL) |
331 | goto loser; | |
332 | digest->Length = diglength; | |
866f8763 A |
333 | #if USE_CDSA_CRYPTO |
334 | CSSM_DigestDataFinal(digobj, digest); | |
335 | CSSM_DeleteContext(digobj); | |
336 | #else | |
d8f41ccd A |
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); | |
866f8763 | 348 | #endif |
d8f41ccd A |
349 | digestalgs[i] = cmsdigcx->digestalgs[i]; |
350 | digests[i] = digest; | |
b1ab9ed8 A |
351 | } |
352 | else | |
353 | { | |
354 | digest->Data = NULL; | |
355 | digest->Length = 0; | |
356 | } | |
d8f41ccd A |
357 | } |
358 | digestalgs[i] = NULL; | |
b1ab9ed8 | 359 | digests[i] = NULL; |
d8f41ccd | 360 | *digestalgsp = digestalgs; |
b1ab9ed8 A |
361 | *digestsp = digests; |
362 | ||
363 | rv = SECSuccess; | |
364 | ||
365 | loser: | |
366 | if (rv == SECSuccess) | |
d8f41ccd | 367 | PORT_ArenaUnmark(cmsdigcx->poolp, mark); |
b1ab9ed8 | 368 | else |
d8f41ccd | 369 | PORT_ArenaRelease(cmsdigcx->poolp, mark); |
b1ab9ed8 | 370 | |
d8f41ccd A |
371 | /*cleanup:*/ |
372 | /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */ | |
373 | cmsdigcx->digcnt = 0; | |
b1ab9ed8 A |
374 | |
375 | return rv; | |
376 | } | |
377 | ||
378 | /* | |
379 | * SecCmsDigestContextFinishSingle - same as SecCmsDigestContextFinishMultiple, | |
380 | * but for one digest. | |
381 | */ | |
382 | OSStatus | |
d8f41ccd A |
383 | SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx, |
384 | SecAsn1Item * digest) | |
b1ab9ed8 A |
385 | { |
386 | OSStatus rv = SECFailure; | |
d8f41ccd A |
387 | SecAsn1Item * *dp; |
388 | SECAlgorithmID **ap; | |
b1ab9ed8 A |
389 | |
390 | /* get the digests into arena, then copy the first digest into poolp */ | |
d8f41ccd | 391 | if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess) |
b1ab9ed8 A |
392 | goto loser; |
393 | ||
d8f41ccd | 394 | /* Return the first element in the digest array. */ |
6b200bc3 A |
395 | if (digest) { |
396 | *digest = *dp[0]; | |
397 | } | |
b1ab9ed8 A |
398 | |
399 | rv = SECSuccess; | |
400 | ||
401 | loser: | |
b1ab9ed8 A |
402 | return rv; |
403 | } |