]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmsdigest.c
Security-57740.51.3.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 #include <CommonCrypto/CommonDigest.h>
48
49 #include "SecCmsDigestContext.h"
50
51 /* Return the maximum value between S and T (and U) */
52 #define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
53 #define MAX_OF_3(S, T, U) ({__typeof__(U) _max_st = MAX(S,T); MAX(_max_st,U);})
54
55 struct SecCmsDigestContextStr {
56 PLArenaPool * poolp;
57 Boolean saw_contents;
58 int digcnt;
59 void ** digobjs;
60 SECAlgorithmID ** digestalgs;
61 };
62
63 /*
64 * SecCmsDigestContextStartMultiple - start digest calculation using all the
65 * digest algorithms in "digestalgs" in parallel.
66 */
67 SecCmsDigestContextRef
68 SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
69 {
70 PLArenaPool *poolp;
71 SecCmsDigestContextRef cmsdigcx;
72 void * digobj;
73 int digcnt;
74 int i;
75
76 poolp = PORT_NewArena(1024);
77 if (poolp == NULL)
78 goto loser;
79
80 digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
81
82 cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr));
83 if (cmsdigcx == NULL) {
84 goto loser;
85 }
86 cmsdigcx->poolp = poolp;
87
88 if (digcnt > 0) {
89 /* Security check to prevent under-allocation */
90 if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
91 goto loser;
92 }
93 cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
94
95 if (cmsdigcx->digobjs == NULL)
96 goto loser;
97
98 cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp,
99 (digcnt + 1) * sizeof(SECAlgorithmID *));
100 if (cmsdigcx->digestalgs == NULL)
101 goto loser;
102 }
103
104 cmsdigcx->digcnt = 0;
105
106 /*
107 * Create a digest object context for each algorithm.
108 */
109 for (i = 0; i < digcnt; i++) {
110 digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
111 /*
112 * Skip any algorithm we do not even recognize; obviously,
113 * this could be a problem, but if it is critical then the
114 * result will just be that the signature does not verify.
115 * We do not necessarily want to error out here, because
116 * the particular algorithm may not actually be important,
117 * but we cannot know that until later.
118 */
119
120 cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
121 cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
122 if (SECITEM_CopyItem(poolp,
123 &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
124 &(digestalgs[i]->algorithm))
125 || SECITEM_CopyItem(poolp,
126 &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
127 &(digestalgs[i]->parameters)))
128 goto loser;
129 cmsdigcx->digcnt++;
130 }
131
132 cmsdigcx->saw_contents = PR_FALSE;
133
134 return cmsdigcx;
135
136 loser:
137 if (poolp)
138 PORT_FreeArena(poolp, PR_FALSE);
139
140 return NULL;
141 }
142
143 /*
144 * SecCmsDigestContextStartSingle - same as SecCmsDigestContextStartMultiple, but
145 * only one algorithm.
146 */
147 SecCmsDigestContextRef
148 SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
149 {
150 SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
151
152 digestalgs[0] = digestalg;
153 return SecCmsDigestContextStartMultiple(digestalgs);
154 }
155
156 /*
157 * SecCmsDigestContextUpdate - feed more data into the digest machine
158 */
159 void
160 SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
161 {
162 SecAsn1Item dataBuf;
163 int i;
164
165 dataBuf.Length = len;
166 dataBuf.Data = (uint8_t *)data;
167 cmsdigcx->saw_contents = PR_TRUE;
168 for (i = 0; i < cmsdigcx->digcnt; i++) {
169 if (cmsdigcx->digobjs[i]) {
170 /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
171 This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
172 possible security issue. There is no way to return an error here, but a check at
173 the upper level may happen. */
174 /*
175 rdar://problem/20642513
176 Let's just die a horrible death rather than have the security issue.
177 CMS blob over 4GB? Oh well.
178 */
179 if (len > UINT32_MAX) {
180 /* Ugh. */
181 abort();
182 }
183 assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
184 switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
185 case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
186 case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
187 case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
188 case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
189 case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
190 case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
191 default:
192 break;
193 }
194 }
195 }
196 }
197
198 /*
199 * SecCmsDigestContextCancel - cancel digesting operation
200 */
201 void
202 SecCmsDigestContextCancel(SecCmsDigestContextRef cmsdigcx)
203 {
204 int i;
205
206 for (i = 0; i < cmsdigcx->digcnt; i++)
207 if (cmsdigcx->digobjs[i])
208 free(cmsdigcx->digobjs[i]);
209
210 PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
211 }
212
213 /*
214 * SecCmsDigestContextDestroy - delete a digesting operation
215 */
216 void
217 SecCmsDigestContextDestroy(SecCmsDigestContextRef cmsdigcx)
218 {
219 SecCmsDigestContextCancel(cmsdigcx);
220 }
221
222 /*
223 * SecCmsDigestContextFinishMultiple - finish the digests
224 */
225 OSStatus
226 SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
227 SECAlgorithmID ***digestalgsp,
228 SecAsn1Item * **digestsp)
229 {
230 void * digobj;
231 SecAsn1Item **digests, *digest;
232 SECAlgorithmID **digestalgs;
233 int i;
234 void *mark;
235 OSStatus rv = SECFailure;
236
237 assert(cmsdigcx != NULL);
238 assert(digestsp != NULL);
239 assert(digestalgsp != NULL);
240
241 mark = PORT_ArenaMark (cmsdigcx->poolp);
242
243 /* Security check to prevent under-allocation */
244 if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX_OF_3(sizeof(SECAlgorithmID *),sizeof(SecAsn1Item *),sizeof(SecAsn1Item))))-1)) {
245 goto loser;
246 }
247 /* allocate digest array & SecAsn1Items on arena */
248 digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SECAlgorithmID *));
249 digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
250 digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
251 if (digestalgs == NULL || digests == NULL || digest == NULL) {
252 goto loser;
253 }
254
255 for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
256
257 SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
258 int diglength = 0;
259
260 switch (hash_alg) {
261 case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
262 case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
263 case SEC_OID_SHA224: diglength = CC_SHA224_DIGEST_LENGTH; break;
264 case SEC_OID_SHA256: diglength = CC_SHA256_DIGEST_LENGTH; break;
265 case SEC_OID_SHA384: diglength = CC_SHA384_DIGEST_LENGTH; break;
266 case SEC_OID_SHA512: diglength = CC_SHA512_DIGEST_LENGTH; break;
267 default: goto loser; break;
268 }
269
270 digobj = cmsdigcx->digobjs[i];
271 if (digobj)
272 {
273 digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
274 if (digest->Data == NULL)
275 goto loser;
276 digest->Length = diglength;
277
278 switch (hash_alg) {
279 case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
280 case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
281 case SEC_OID_SHA224: CC_SHA224_Final(digest->Data, digobj); break;
282 case SEC_OID_SHA256: CC_SHA256_Final(digest->Data, digobj); break;
283 case SEC_OID_SHA384: CC_SHA384_Final(digest->Data, digobj); break;
284 case SEC_OID_SHA512: CC_SHA512_Final(digest->Data, digobj); break;
285 default: goto loser; break;
286 }
287
288 free(digobj);
289 digestalgs[i] = cmsdigcx->digestalgs[i];
290 digests[i] = digest;
291 }
292 else
293 {
294 digest->Data = NULL;
295 digest->Length = 0;
296 }
297 }
298 digestalgs[i] = NULL;
299 digests[i] = NULL;
300 *digestalgsp = digestalgs;
301 *digestsp = digests;
302
303 rv = SECSuccess;
304
305 loser:
306 if (rv == SECSuccess)
307 PORT_ArenaUnmark(cmsdigcx->poolp, mark);
308 else
309 PORT_ArenaRelease(cmsdigcx->poolp, mark);
310
311 /*cleanup:*/
312 /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
313 cmsdigcx->digcnt = 0;
314
315 return rv;
316 }
317
318 /*
319 * SecCmsDigestContextFinishSingle - same as SecCmsDigestContextFinishMultiple,
320 * but for one digest.
321 */
322 OSStatus
323 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
324 SecAsn1Item * digest)
325 {
326 OSStatus rv = SECFailure;
327 SecAsn1Item * *dp;
328 SECAlgorithmID **ap;
329
330 /* get the digests into arena, then copy the first digest into poolp */
331 if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess)
332 goto loser;
333
334 /* Return the first element in the digest array. */
335 if (digest) {
336 *digest = *dp[0];
337 }
338
339 rv = SECSuccess;
340
341 loser:
342 return rv;
343 }