]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmsdigest.c
Security-59754.41.1.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 <security_utilities/simulatecrash_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 <Security/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
81 digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
82
83 cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr));
84 if (cmsdigcx == NULL) {
85 goto loser;
86 }
87 cmsdigcx->poolp = poolp;
88
89 if (digcnt > 0) {
90 /* Security check to prevent under-allocation */
91 if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
92 goto loser;
93 }
94 cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
95 if (cmsdigcx->digobjs == NULL) {
96 goto loser;
97 }
98 cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp, (digcnt + 1) * sizeof(SECAlgorithmID *));
99 if (cmsdigcx->digestalgs == NULL) {
100 goto loser;
101 }
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 }
130 cmsdigcx->digcnt++;
131 }
132
133 cmsdigcx->saw_contents = PR_FALSE;
134
135 return cmsdigcx;
136
137 loser:
138 if (poolp) {
139 PORT_FreeArena(poolp, PR_FALSE);
140 }
141
142 return NULL;
143 }
144
145 /*
146 * SecCmsDigestContextStartSingle - same as SecCmsDigestContextStartMultiple, but
147 * only one algorithm.
148 */
149 SecCmsDigestContextRef
150 SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
151 {
152 SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
153
154 digestalgs[0] = digestalg;
155 return SecCmsDigestContextStartMultiple(digestalgs);
156 }
157
158 /*
159 * SecCmsDigestContextUpdate - feed more data into the digest machine
160 */
161 void
162 SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
163 {
164 SecAsn1Item dataBuf;
165 int i;
166
167 dataBuf.Length = len;
168 dataBuf.Data = (uint8_t *)data;
169 cmsdigcx->saw_contents = PR_TRUE;
170 for (i = 0; i < cmsdigcx->digcnt; i++) {
171 if (cmsdigcx->digobjs[i]) {
172 /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
173 This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
174 possible security issue. There is no way to return an error here, but a check at
175 the upper level may happen. */
176 /*
177 rdar://problem/20642513
178 Let's just die a horrible death rather than have the security issue.
179 CMS blob over 4GB? Oh well.
180 */
181 if (len > UINT32_MAX) {
182 /* Ugh. */
183 abort();
184 }
185 assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
186 switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
187 case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
188 case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
189 case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
190 case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
191 case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
192 case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
193 default:
194 break;
195 }
196 }
197 }
198 }
199
200 /*
201 * SecCmsDigestContextCancel - cancel digesting operation
202 */
203 void
204 SecCmsDigestContextCancel(SecCmsDigestContextRef cmsdigcx)
205 {
206 int i;
207
208 for (i = 0; i < cmsdigcx->digcnt; i++) {
209 if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
210 free(cmsdigcx->digobjs[i]);
211 cmsdigcx->digobjs[i] = NULL;
212 }
213 }
214
215 PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
216 }
217
218 /*
219 * SecCmsDigestContextDestroy - delete a digesting operation
220 */
221 void
222 SecCmsDigestContextDestroy(SecCmsDigestContextRef cmsdigcx)
223 {
224 SecCmsDigestContextCancel(cmsdigcx);
225 }
226
227 /*
228 * SecCmsDigestContextFinishMultiple - finish the digests
229 * Note that on iOS, this call only frees the digest objects and requires a call to SecCmsDisgestContextDestroy
230 * or SecCmsDisgestContextCancel (because the digests are allocated out of the context's pool).
231 * The macOS version cancels and frees the digest context (because the digests are allocated from an input arena pool).
232 */
233 OSStatus
234 SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
235 SECAlgorithmID ***digestalgsp,
236 SecAsn1Item * **digestsp)
237 {
238 void * digobj;
239 SecAsn1Item **digests, *digest;
240 SECAlgorithmID **digestalgs;
241 int i;
242 void *mark;
243 OSStatus rv = SECFailure;
244
245 assert(cmsdigcx != NULL);
246
247 /* A message with no contents (just signed attributes) is used within SCEP */
248 #if 0
249 /* no contents? do not update digests */
250 if (digestsp == NULL || !cmsdigcx->saw_contents) {
251 for (i = 0; i < cmsdigcx->digcnt; i++) {
252 if (cmsdigcx->digobjs[i]) {
253 free(cmsdigcx->digobjs[i]);
254 }
255 }
256 rv = SECSuccess;
257 if (digestsp) {
258 *digestsp = NULL;
259 }
260 goto cleanup;
261 }
262 #endif
263
264 assert(digestsp != NULL);
265 assert(digestalgsp != NULL);
266
267 mark = PORT_ArenaMark (cmsdigcx->poolp);
268
269 /* Security check to prevent under-allocation */
270 if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX_OF_3(sizeof(SECAlgorithmID *),sizeof(SecAsn1Item *),sizeof(SecAsn1Item))))-1)) {
271 goto loser;
272 }
273 /* allocate digest array & SecAsn1Items on arena */
274 digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SECAlgorithmID *));
275 digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
276 digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
277 if (digestalgs == NULL || digests == NULL || digest == NULL) {
278 goto loser;
279 }
280
281 for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
282 SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
283 int diglength = 0;
284
285 switch (hash_alg) {
286 case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
287 case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
288 case SEC_OID_SHA224: diglength = CC_SHA224_DIGEST_LENGTH; break;
289 case SEC_OID_SHA256: diglength = CC_SHA256_DIGEST_LENGTH; break;
290 case SEC_OID_SHA384: diglength = CC_SHA384_DIGEST_LENGTH; break;
291 case SEC_OID_SHA512: diglength = CC_SHA512_DIGEST_LENGTH; break;
292 default: goto loser;
293 }
294
295 digobj = cmsdigcx->digobjs[i];
296 if (digobj) {
297 digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
298 if (digest->Data == NULL)
299 goto loser;
300 digest->Length = diglength;
301 switch (hash_alg) {
302 case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
303 case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
304 case SEC_OID_SHA224: CC_SHA224_Final(digest->Data, digobj); break;
305 case SEC_OID_SHA256: CC_SHA256_Final(digest->Data, digobj); break;
306 case SEC_OID_SHA384: CC_SHA384_Final(digest->Data, digobj); break;
307 case SEC_OID_SHA512: CC_SHA512_Final(digest->Data, digobj); break;
308 default: goto loser;
309 }
310
311 free(digobj);
312 digestalgs[i] = cmsdigcx->digestalgs[i];
313 digests[i] = digest;
314 } else {
315 digest->Data = NULL;
316 digest->Length = 0;
317 }
318 }
319 digestalgs[i] = NULL;
320 digests[i] = NULL;
321 *digestalgsp = digestalgs;
322 *digestsp = digests;
323
324 rv = SECSuccess;
325
326 loser:
327 if (rv == SECSuccess) {
328 PORT_ArenaUnmark(cmsdigcx->poolp, mark);
329 } else {
330 PORT_ArenaRelease(cmsdigcx->poolp, mark);
331 }
332
333 /*cleanup:*/
334 /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
335 cmsdigcx->digcnt = 0;
336
337 return rv;
338 }
339
340 /*
341 * SecCmsDigestContextFinishSingle - same as SecCmsDigestContextFinishMultiple,
342 * but for one digest.
343 */
344 OSStatus
345 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
346 SecAsn1Item * digest)
347 {
348 OSStatus rv = SECFailure;
349 SecAsn1Item * *dp;
350 SECAlgorithmID **ap;
351
352 /* get the digests into arena, then copy the first digest into poolp */
353 if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess) {
354 goto loser;
355 }
356
357 /* Return the first element in the digest array. */
358 if (digest) {
359 *digest = *dp[0];
360 }
361
362 rv = SECSuccess;
363
364 loser:
365 return rv;
366 }