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