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