]> git.saurik.com Git - apple/security.git/blame - libsecurity_smime/lib/cmssigdata.c
Security-58286.1.32.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmssigdata.c
CommitLineData
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 signedData methods.
36 */
37
38#include <Security/SecCmsSignedData.h>
39
40#include <Security/SecCmsContentInfo.h>
41#include <Security/SecCmsDigestContext.h>
42#include <Security/SecCmsSignerInfo.h>
43
44#include "cmslocal.h"
45
46#include "cert.h"
d8f41ccd 47#include "SecAsn1Item.h"
b1ab9ed8 48#include "secoid.h"
b1ab9ed8
A
49
50#include <security_asn1/secasn1.h>
51#include <security_asn1/secerr.h>
d8f41ccd 52#include <security_asn1/secport.h>
b1ab9ed8 53
866f8763 54#if !USE_CDSA_CRYPTO
d8f41ccd 55#include <Security/SecCertificatePriv.h>
866f8763 56#endif
b1ab9ed8
A
57
58SecCmsSignedDataRef
59SecCmsSignedDataCreate(SecCmsMessageRef cmsg)
60{
61 void *mark;
62 SecCmsSignedDataRef sigd;
63 PLArenaPool *poolp;
64
65 poolp = cmsg->poolp;
66
67 mark = PORT_ArenaMark(poolp);
68
69 sigd = (SecCmsSignedDataRef)PORT_ArenaZAlloc (poolp, sizeof(SecCmsSignedData));
70 if (sigd == NULL)
71 goto loser;
72
d8f41ccd 73 sigd->contentInfo.cmsg = cmsg;
b1ab9ed8
A
74
75 /* signerInfos, certs, certlists, crls are all empty */
76 /* version is set in SecCmsSignedDataFinalize() */
77
78 PORT_ArenaUnmark(poolp, mark);
79 return sigd;
80
81loser:
82 PORT_ArenaRelease(poolp, mark);
83 return NULL;
84}
85
86void
87SecCmsSignedDataDestroy(SecCmsSignedDataRef sigd)
88{
89 SecCmsSignerInfoRef *signerinfos, si;
90
91 if (sigd == NULL)
92 return;
93
94 if (sigd->certs != NULL)
95 CFRelease(sigd->certs);
96
97 signerinfos = sigd->signerInfos;
98 if (signerinfos != NULL) {
99 while ((si = *signerinfos++) != NULL)
100 SecCmsSignerInfoDestroy(si);
101 }
102
103 /* everything's in a pool, so don't worry about the storage */
104 SecCmsContentInfoDestroy(&(sigd->contentInfo));
105}
106
107/*
108 * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData
109 * before start of encoding.
110 *
111 * In detail:
112 * - find out about the right value to put into sigd->version
113 * - come up with a list of digestAlgorithms (which should be the union of the algorithms
114 * in the signerinfos).
115 * If we happen to have a pre-set list of algorithms (and digest values!), we
116 * check if we have all the signerinfos' algorithms. If not, this is an error.
117 */
118OSStatus
119SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd)
120{
121 SecCmsSignerInfoRef signerinfo;
122 SECOidTag digestalgtag;
d8f41ccd 123 SecAsn1Item * dummy;
b1ab9ed8
A
124 int version;
125 OSStatus rv;
126 Boolean haveDigests = PR_FALSE;
127 int n, i;
128 PLArenaPool *poolp;
129
d8f41ccd 130 poolp = sigd->contentInfo.cmsg->poolp;
b1ab9ed8
A
131
132 /* we assume that we have precomputed digests if there is a list of algorithms, and */
133 /* a chunk of data for each of those algorithms */
134 if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
135 for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
136 if (sigd->digests[i] == NULL)
137 break;
138 }
139 if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
140 haveDigests = PR_TRUE; /* yes: we must have all the digests */
141 }
142
143 version = SEC_CMS_SIGNED_DATA_VERSION_BASIC;
144
145 /* RFC2630 5.1 "version is the syntax version number..." */
146 if (SecCmsContentInfoGetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
147 version = SEC_CMS_SIGNED_DATA_VERSION_EXT;
148
149 /* prepare all the SignerInfos (there may be none) */
150 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
151 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
152
153 /* RFC2630 5.1 "version is the syntax version number..." */
154 if (SecCmsSignerInfoGetVersion(signerinfo) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN)
155 version = SEC_CMS_SIGNED_DATA_VERSION_EXT;
156
157 /* collect digestAlgorithms from SignerInfos */
158 /* (we need to know which algorithms we have when the content comes in) */
159 /* do not overwrite any existing digestAlgorithms (and digest) */
160 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
b1ab9ed8
A
161 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
162 if (n < 0 && haveDigests) {
163 /* oops, there is a digestalg we do not have a digest for */
164 /* but we were supposed to have all the digests already... */
165 goto loser;
166 } else if (n < 0) {
167 /* add the digestAlgorithm & a NULL digest */
d8f41ccd 168 rv = SecCmsSignedDataAddDigest(poolp, sigd, digestalgtag, NULL);
b1ab9ed8
A
169 if (rv != SECSuccess)
170 goto loser;
171 } else {
172 /* found it, nothing to do */
173 }
174 }
175
176 dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
177 if (dummy == NULL)
178 return SECFailure;
179
180 /* this is a SET OF, so we need to sort them guys */
181 rv = SecCmsArraySortByDER((void **)sigd->digestAlgorithms,
182 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
183 (void **)sigd->digests);
184 if (rv != SECSuccess)
185 return SECFailure;
186
187 return SECSuccess;
188
189loser:
190 return SECFailure;
191}
192
193OSStatus
194SecCmsSignedDataEncodeBeforeData(SecCmsSignedDataRef sigd)
195{
196 /* set up the digests */
197 if (sigd->digestAlgorithms != NULL) {
198 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms);
199 if (sigd->contentInfo.digcx == NULL)
200 return SECFailure;
201 }
202 return SECSuccess;
203}
204
b1ab9ed8
A
205/*
206 * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData
207 * after all the encapsulated data was passed through the encoder.
208 *
209 * In detail:
210 * - create the signatures in all the SignerInfos
211 *
212 * Please note that nothing is done to the Certificates and CRLs in the message - this
213 * is entirely the responsibility of our callers.
214 */
215OSStatus
216SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd)
217{
218 SecCmsSignerInfoRef *signerinfos, signerinfo;
219 SecCmsContentInfoRef cinfo;
220 SECOidTag digestalgtag;
221 OSStatus ret = SECFailure;
222 OSStatus rv;
d8f41ccd
A
223 SecAsn1Item * contentType;
224 CFIndex certcount;
b1ab9ed8
A
225 int i, ci, n, rci, si;
226 PLArenaPool *poolp;
227 CFArrayRef certlist;
228 extern const SecAsn1Template SecCmsSignerInfoTemplate[];
229
b1ab9ed8 230 cinfo = &(sigd->contentInfo);
d8f41ccd 231 poolp = cinfo->cmsg->poolp;
b1ab9ed8
A
232
233 /* did we have digest calculation going on? */
234 if (cinfo->digcx) {
d8f41ccd
A
235 SecAsn1Item **digests = NULL;
236 SECAlgorithmID **digestalgs = NULL;
237 rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, &digestalgs, &digests);
b1ab9ed8
A
238 if (rv != SECSuccess)
239 goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */
d8f41ccd
A
240 if (digestalgs && digests) {
241 rv = SecCmsSignedDataSetDigests(sigd, digestalgs, digests);
242 if (rv != SECSuccess)
243 goto loser; /* error has been set by SecCmsSignedDataSetDigests */
244 }
245 SecCmsDigestContextDestroy(cinfo->digcx);
b1ab9ed8
A
246 cinfo->digcx = NULL;
247 }
248
249 signerinfos = sigd->signerInfos;
250 certcount = 0;
251
252 /* prepare all the SignerInfos (there may be none) */
253 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
254 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
d8f41ccd 255
b1ab9ed8
A
256 /* find correct digest for this signerinfo */
257 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
258 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
259 if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
260 /* oops - digest not found */
261 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
262 goto loser;
263 }
264
265 /* XXX if our content is anything else but data, we need to force the
266 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
267 * collection...") */
268
269 /* pass contentType here as we want a contentType attribute */
270 if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL)
271 goto loser;
272
273 /* sign the thing */
274 rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType);
275 if (rv != SECSuccess)
276 goto loser;
277
278 /* while we're at it, count number of certs in certLists */
279 certlist = SecCmsSignerInfoGetCertList(signerinfo);
280 if (certlist)
281 certcount += CFArrayGetCount(certlist);
282 }
283
b1ab9ed8
A
284 /* this is a SET OF, so we need to sort them guys */
285 rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL);
286 if (rv != SECSuccess)
287 goto loser;
288
289 /*
290 * now prepare certs & crls
291 */
292
293 /* count the rest of the certs */
294 if (sigd->certs != NULL)
295 certcount += CFArrayGetCount(sigd->certs);
296
297 if (certcount == 0) {
298 sigd->rawCerts = NULL;
299 } else {
300 /*
301 * Combine all of the certs and cert chains into rawcerts.
302 * Note: certcount is an upper bound; we may not need that many slots
303 * but we will allocate anyway to avoid having to do another pass.
304 * (The temporary space saving is not worth it.)
305 *
306 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
307 * SetOfDERcertficates implementation
308 */
d8f41ccd 309 sigd->rawCerts = (SecAsn1Item * *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SecAsn1Item *));
b1ab9ed8
A
310 if (sigd->rawCerts == NULL)
311 return SECFailure;
312
313 /*
314 * XXX Want to check for duplicates and not add *any* cert that is
315 * already in the set. This will be more important when we start
316 * dealing with larger sets of certs, dual-key certs (signing and
317 * encryption), etc. For the time being we can slide by...
318 *
319 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
320 * SetOfDERcertficates implementation
321 */
322 rci = 0;
323 if (signerinfos != NULL) {
324 for (si = 0; signerinfos[si] != NULL; si++) {
325 signerinfo = signerinfos[si];
326 for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) {
d8f41ccd 327 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(SecAsn1Item));
866f8763
A
328 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci);
329#if USE_CDSA_CRYPTO
330 SecCertificateGetData(cert, sigd->rawCerts[rci++]);
331#else
d8f41ccd
A
332 SecAsn1Item cert_data = { SecCertificateGetLength(cert),
333 (uint8_t *)SecCertificateGetBytePtr(cert) };
334 *(sigd->rawCerts[rci++]) = cert_data;
866f8763 335#endif
b1ab9ed8
A
336 }
337 }
338 }
339
340 if (sigd->certs != NULL) {
341 for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) {
d8f41ccd 342 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(SecAsn1Item));
b1ab9ed8 343 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci);
866f8763
A
344#if USE_CDSA_CRYPTO
345 SecCertificateGetData(cert, sigd->rawCerts[rci++]);
346#else
d8f41ccd
A
347 SecAsn1Item cert_data = { SecCertificateGetLength(cert),
348 (uint8_t *)SecCertificateGetBytePtr(cert) };
349 *(sigd->rawCerts[rci++]) = cert_data;
866f8763 350#endif
b1ab9ed8
A
351 }
352 }
353
354 sigd->rawCerts[rci] = NULL;
355
356 /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
357 SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL);
358 }
359
360 ret = SECSuccess;
361
362loser:
b1ab9ed8
A
363 return ret;
364}
365
366OSStatus
367SecCmsSignedDataDecodeBeforeData(SecCmsSignedDataRef sigd)
368{
d8f41ccd
A
369 /* set up the digests, if we have digest algorithms, no digests yet, and content is attached */
370 if (sigd->digestAlgorithms != NULL && sigd->digests == NULL /* && sigd->contentInfo.content.pointer != NULL*/) {
b1ab9ed8
A
371 /* if digests are already there, do nothing */
372 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms);
373 if (sigd->contentInfo.digcx == NULL)
374 return SECFailure;
375 }
376 return SECSuccess;
377}
378
379/*
380 * SecCmsSignedDataDecodeAfterData - do all the necessary things to a SignedData
381 * after all the encapsulated data was passed through the decoder.
382 */
383OSStatus
384SecCmsSignedDataDecodeAfterData(SecCmsSignedDataRef sigd)
385{
d8f41ccd
A
386 OSStatus rv = SECSuccess;
387
b1ab9ed8
A
388 /* did we have digest calculation going on? */
389 if (sigd->contentInfo.digcx) {
d8f41ccd
A
390 /* @@@ we should see if data was absent vs. zero length */
391 if (sigd->contentInfo.content.data && sigd->contentInfo.content.data->Length) {
392 SecAsn1Item * *digests = NULL;
393 SECAlgorithmID **digestalgs = NULL;
394 rv = SecCmsDigestContextFinishMultiple(sigd->contentInfo.digcx, &digestalgs, &digests);
395 if (rv != SECSuccess)
396 goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */
397 rv = SecCmsSignedDataSetDigests(sigd, digestalgs, digests);
398 if (rv != SECSuccess)
399 goto loser; /* error has been set by SecCmsSignedDataSetDigests */
400 }
401 SecCmsDigestContextDestroy(sigd->contentInfo.digcx);
b1ab9ed8
A
402 sigd->contentInfo.digcx = NULL;
403 }
d8f41ccd
A
404
405loser:
406 return rv;
b1ab9ed8
A
407}
408
409/*
410 * SecCmsSignedDataDecodeAfterEnd - do all the necessary things to a SignedData
411 * after all decoding is finished.
412 */
413OSStatus
414SecCmsSignedDataDecodeAfterEnd(SecCmsSignedDataRef sigd)
415{
416 SecCmsSignerInfoRef *signerinfos;
417 int i;
418
fa7225c8
A
419 if (!sigd) {
420 return SECFailure;
421 }
422
d8f41ccd 423 /* set cmsg for all the signerinfos */
b1ab9ed8
A
424 signerinfos = sigd->signerInfos;
425
d8f41ccd 426 /* set signedData for all the signerinfos */
b1ab9ed8 427 if (signerinfos) {
d8f41ccd
A
428 for (i = 0; signerinfos[i] != NULL; i++)
429 signerinfos[i]->signedData = sigd;
b1ab9ed8
A
430 }
431
432 return SECSuccess;
433}
434
435/*
436 * SecCmsSignedDataGetSignerInfos - retrieve the SignedData's signer list
437 */
438SecCmsSignerInfoRef *
439SecCmsSignedDataGetSignerInfos(SecCmsSignedDataRef sigd)
440{
441 return sigd->signerInfos;
442}
443
444int
445SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd)
446{
447 return SecCmsArrayCount((void **)sigd->signerInfos);
448}
449
450SecCmsSignerInfoRef
451SecCmsSignedDataGetSignerInfo(SecCmsSignedDataRef sigd, int i)
452{
453 return sigd->signerInfos[i];
454}
455
456/*
457 * SecCmsSignedDataGetDigestAlgs - retrieve the SignedData's digest algorithm list
458 */
459SECAlgorithmID **
460SecCmsSignedDataGetDigestAlgs(SecCmsSignedDataRef sigd)
461{
462 return sigd->digestAlgorithms;
463}
464
465/*
466 * SecCmsSignedDataGetContentInfo - return pointer to this signedData's contentinfo
467 */
468SecCmsContentInfoRef
469SecCmsSignedDataGetContentInfo(SecCmsSignedDataRef sigd)
470{
471 return &(sigd->contentInfo);
472}
473
474/*
475 * SecCmsSignedDataGetCertificateList - retrieve the SignedData's certificate list
476 */
d8f41ccd 477SecAsn1Item * *
b1ab9ed8
A
478SecCmsSignedDataGetCertificateList(SecCmsSignedDataRef sigd)
479{
480 return sigd->rawCerts;
481}
482
483OSStatus
484SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd, SecKeychainRef keychain,
485 SECCertUsage certusage, Boolean keepcerts)
486{
d8f41ccd 487 OSStatus rv = -1;
866f8763
A
488
489#if USE_CDSA_CRYPTO
490 int ix, certcount = SecCmsArrayCount((void **)sigd->rawCerts);
491 rv = CERT_ImportCerts(keychain, certusage, certcount, sigd->rawCerts, NULL,
492 keepcerts, PR_FALSE, NULL);
493 /* XXX CRL handling */
494
495 if (sigd->signerInfos != NULL) {
496 /* fill in all signerinfo's certs */
497 for (ix = 0; sigd->signerInfos[ix] != NULL; i++)
498 (void)SecCmsSignerInfoGetSigningCertificate(sigd->signerInfos[ix], keychain);
499 }
500#else
501 // XXX we should only ever import certs for a cert only data blob
502#endif
503
b1ab9ed8
A
504 return rv;
505}
506
507/*
508 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
509 * of external signatures!
510 */
511
d8f41ccd 512
b1ab9ed8
A
513/*
514 * SecCmsSignedDataVerifySignerInfo - check the signatures.
515 *
516 * The digests were either calculated during decoding (and are stored in the
517 * signedData itself) or set after decoding using SecCmsSignedDataSetDigests.
518 *
519 * The verification checks if the signing cert is valid and has a trusted chain
520 * for the purpose specified by "policies".
521 *
522 * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly.
523 * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate().
524 */
525OSStatus
526SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd, int i,
527 SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef)
528{
529 SecCmsSignerInfoRef signerinfo;
530 SecCmsContentInfoRef cinfo;
531 SECOidData *algiddata;
d8f41ccd
A
532 SecAsn1Item *contentType, *digest;
533 OSStatus status;
b1ab9ed8
A
534
535 cinfo = &(sigd->contentInfo);
536
537 signerinfo = sigd->signerInfos[i];
538
539 /* Signature or digest level verificationStatus errors should supercede
540 certificate level errors, so check the digest and signature first. */
541
542 /* Find digest and contentType for signerinfo */
543 algiddata = SecCmsSignerInfoGetDigestAlg(signerinfo);
d8f41ccd
A
544
545 if (!sigd->digests) {
546 SECAlgorithmID **digestalgs = SecCmsSignedDataGetDigestAlgs(sigd);
547 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestalgs);
548 SecCmsSignedDataSetDigestContext(sigd, digcx);
549 SecCmsDigestContextDestroy(digcx);
b1ab9ed8 550 }
d8f41ccd 551
b1ab9ed8 552 digest = SecCmsSignedDataGetDigestByAlgTag(sigd, algiddata->offset);
d8f41ccd 553
b1ab9ed8
A
554 contentType = SecCmsContentInfoGetContentTypeOID(cinfo);
555
556 /* verify signature */
557 status = SecCmsSignerInfoVerify(signerinfo, digest, contentType);
5c19dc3a
A
558#if SECTRUST_VERBOSE_DEBUG
559 syslog(LOG_ERR, "SecCmsSignedDataVerifySignerInfo: SecCmsSignerInfoVerify returned %d, will %sverify cert",
560 (int)status, (status) ? "NOT " : "");
561#endif
562 if (status) {
563 return status;
564 }
b1ab9ed8 565
6b200bc3
A
566 /* Now verify the certificate. We only do this when the signature verification succeeds. Note that this
567 behavior is different than the macOS code. */
d8f41ccd 568 status = SecCmsSignerInfoVerifyCertificate(signerinfo, keychainOrArray, policies, trustRef);
5c19dc3a
A
569#if SECTRUST_VERBOSE_DEBUG
570 syslog(LOG_ERR, "SecCmsSignedDataVerifySignerInfo: SecCmsSignerInfoVerifyCertificate returned %d", (int)status);
571#endif
d8f41ccd
A
572
573 return status;
b1ab9ed8
A
574}
575
866f8763
A
576#if USE_CDSA_CRYPTO
577
578/*
579 * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message
580 */
581OSStatus
582SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd,
583 SecKeychainRef keychainOrArray,
584 CFTypeRef policies)
585{
586 SecCertificateRef cert;
587 OSStatus rv = SECSuccess;
588 int i;
589 int count;
590
591 if (!sigd || !keychainOrArray || !sigd->rawCerts) {
592 PORT_SetError(SEC_ERROR_INVALID_ARGS);
593 return SECFailure;
594 }
595
596 count = SecCmsArrayCount((void**)sigd->rawCerts);
597 for (i=0; i < count; i++) {
598 if (sigd->certs && CFArrayGetCount(sigd->certs) > i) {
599 cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i);
600 CFRetain(cert);
601 } else {
602 cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]);
603 if (!cert) {
604 rv = SECFailure;
605 break;
606 }
607 }
608 rv |= CERT_VerifyCert(keychainOrArray, cert, policies, CFAbsoluteTimeGetCurrent(), NULL);
609 CFRelease(cert);
610 }
611
612 return rv;
613}
614#else
d8f41ccd
A
615OSStatus
616SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd,
617 SecKeychainRef keychainOrArray,
618 CFTypeRef policies)
619{
620 OSStatus rv = SECSuccess;
621
622 if (!sigd || !keychainOrArray || !sigd->rawCerts) {
623 PORT_SetError(SEC_ERROR_INVALID_ARGS);
624 return SECFailure;
625 }
626
627 SecAsn1Item **cert_datas = sigd->rawCerts;
628 SecAsn1Item *cert_data;
629 while ((cert_data = *cert_datas++) != NULL) {
630 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
631 if (cert) {
632 CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **)&cert, 1, NULL);
633 rv |= CERT_VerifyCert(keychainOrArray, certs, policies, CFAbsoluteTimeGetCurrent(), NULL);
634 CFRelease(certs);
635 CFRelease(cert);
636 }
637 else
638 rv |= SECFailure;
639 }
640
641 return rv;
642}
866f8763 643#endif
b1ab9ed8
A
644
645/*
646 * SecCmsSignedDataHasDigests - see if we have digests in place
647 */
648Boolean
649SecCmsSignedDataHasDigests(SecCmsSignedDataRef sigd)
650{
651 return (sigd->digests != NULL);
652}
653
654OSStatus
655SecCmsSignedDataAddCertList(SecCmsSignedDataRef sigd, CFArrayRef certlist)
656{
657 PORT_Assert(certlist != NULL);
658
659 if (certlist == NULL)
660 return SECFailure;
661
662 if (!sigd->certs)
663 sigd->certs = CFArrayCreateMutableCopy(NULL, 0, certlist);
664 else
665 {
666 CFRange certlistRange = { 0, CFArrayGetCount(certlist) };
667 CFArrayAppendArray(sigd->certs, certlist, certlistRange);
668 }
669
670 return SECSuccess;
671}
672
673/*
674 * SecCmsSignedDataAddCertChain - add cert and its entire chain to the set of certs
675 */
676OSStatus
677SecCmsSignedDataAddCertChain(SecCmsSignedDataRef sigd, SecCertificateRef cert)
678{
679 CFArrayRef certlist;
680 SECCertUsage usage;
681 OSStatus rv;
682
683 usage = certUsageEmailSigner;
684
685 /* do not include root */
686 certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
687 if (certlist == NULL)
688 return SECFailure;
689
690 rv = SecCmsSignedDataAddCertList(sigd, certlist);
691 CFRelease(certlist);
692
693 return rv;
694}
695
696OSStatus
697SecCmsSignedDataAddCertificate(SecCmsSignedDataRef sigd, SecCertificateRef cert)
698{
699 PORT_Assert(cert != NULL);
700
701 if (cert == NULL)
702 return SECFailure;
703
704 if (!sigd->certs)
705 sigd->certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
706
707 CFArrayAppendValue(sigd->certs, cert);
708
709 return SECSuccess;
710}
711
712Boolean
713SecCmsSignedDataContainsCertsOrCrls(SecCmsSignedDataRef sigd)
714{
715 if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
716 return PR_TRUE;
717 else if (sigd->rawCrls != NULL && sigd->rawCrls[0] != NULL)
718 return PR_TRUE;
719 else
720 return PR_FALSE;
721}
722
723OSStatus
724SecCmsSignedDataAddSignerInfo(SecCmsSignedDataRef sigd,
d8f41ccd 725 SecCmsSignerInfoRef signerinfo)
b1ab9ed8
A
726{
727 void *mark;
728 OSStatus rv;
729 SECOidTag digestalgtag;
730 PLArenaPool *poolp;
731
d8f41ccd 732 poolp = sigd->contentInfo.cmsg->poolp;
b1ab9ed8
A
733
734 mark = PORT_ArenaMark(poolp);
735
736 /* add signerinfo */
737 rv = SecCmsArrayAdd(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
738 if (rv != SECSuccess)
739 goto loser;
740
b1ab9ed8
A
741 /*
742 * add empty digest
743 * Empty because we don't have it yet. Either it gets created during encoding
744 * (if the data is present) or has to be set externally.
745 * XXX maybe pass it in optionally?
746 */
747 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
748 rv = SecCmsSignedDataSetDigestValue(sigd, digestalgtag, NULL);
749 if (rv != SECSuccess)
750 goto loser;
751
752 /*
753 * The last thing to get consistency would be adding the digest.
754 */
755
756 PORT_ArenaUnmark(poolp, mark);
757 return SECSuccess;
758
759loser:
760 PORT_ArenaRelease (poolp, mark);
761 return SECFailure;
762}
763
d8f41ccd 764SecAsn1Item *
b1ab9ed8
A
765SecCmsSignedDataGetDigestByAlgTag(SecCmsSignedDataRef sigd, SECOidTag algtag)
766{
767 int idx;
768
6b200bc3
A
769 if(sigd == NULL || sigd->digests == NULL) {
770 return NULL;
771 }
b1ab9ed8 772 idx = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, algtag);
d8f41ccd
A
773 return (idx >= 0)?(sigd->digests)[idx]:NULL;
774}
775
776OSStatus
777SecCmsSignedDataSetDigestContext(SecCmsSignedDataRef sigd,
778 SecCmsDigestContextRef digestContext)
779{
780 SECAlgorithmID **digestalgs;
781 SecAsn1Item * *digests;
782
783 if (SecCmsDigestContextFinishMultiple(digestContext, &digestalgs, &digests) != SECSuccess)
784 goto loser;
785 if (SecCmsSignedDataSetDigests(sigd, digestalgs, digests) != SECSuccess)
786 goto loser;
787
788 return 0;
789loser:
790 return PORT_GetError();
b1ab9ed8
A
791}
792
793/*
794 * SecCmsSignedDataSetDigests - set a signedData's digests member
795 *
796 * "digestalgs" - array of digest algorithm IDs
797 * "digests" - array of digests corresponding to the digest algorithms
798 */
799OSStatus
800SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd,
801 SECAlgorithmID **digestalgs,
d8f41ccd 802 SecAsn1Item * *digests)
b1ab9ed8
A
803{
804 int cnt, i, idx;
805
6b200bc3
A
806 /* Check input structure and items in structure */
807 if (sigd == NULL || sigd->digestAlgorithms == NULL || sigd->contentInfo.cmsg == NULL ||
808 sigd->contentInfo.cmsg->poolp == NULL) {
809 PORT_SetError(SEC_ERROR_INVALID_ARGS);
810 return SECFailure;
b1ab9ed8
A
811 }
812
d8f41ccd
A
813 /* Since we'll generate a empty digest for content-less messages
814 whether or not they're detached, we have to avoid overwriting
815 externally set digest for detached content => return early */
816 if (sigd->digests && sigd->digests[0])
817 return 0;
818
b1ab9ed8 819 /* we assume that the digests array is just not there yet */
d8f41ccd 820/*
b1ab9ed8
A
821 PORT_Assert(sigd->digests == NULL);
822 if (sigd->digests != NULL) {
823 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
824 return SECFailure;
825 }
d8f41ccd 826*/
b1ab9ed8 827 /* now allocate one (same size as digestAlgorithms) */
b1ab9ed8 828 if (sigd->digests == NULL) {
d8f41ccd
A
829 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
830 sigd->digests = PORT_ArenaZAlloc(sigd->contentInfo.cmsg->poolp, (cnt + 1) * sizeof(SecAsn1Item *));
831 if (sigd->digests == NULL) {
832 PORT_SetError(SEC_ERROR_NO_MEMORY);
833 return SECFailure;
834 }
b1ab9ed8 835 }
d8f41ccd 836
b1ab9ed8
A
837 for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
838 /* try to find the sigd's i'th digest algorithm in the array we passed in */
839 idx = SecCmsAlgArrayGetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
840 if (idx < 0) {
841 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
842 return SECFailure;
843 }
844
845 /* found it - now set it */
d8f41ccd
A
846 if ((sigd->digests[i] = SECITEM_AllocItem(sigd->contentInfo.cmsg->poolp, NULL, 0)) == NULL ||
847 SECITEM_CopyItem(sigd->contentInfo.cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
b1ab9ed8
A
848 {
849 PORT_SetError(SEC_ERROR_NO_MEMORY);
850 return SECFailure;
851 }
852 }
853 return SECSuccess;
854}
855
856OSStatus
857SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd,
858 SECOidTag digestalgtag,
d8f41ccd 859 SecAsn1Item * digestdata)
b1ab9ed8 860{
d8f41ccd 861 SecAsn1Item * digest = NULL;
b1ab9ed8
A
862 PLArenaPool *poolp;
863 void *mark;
864 int n, cnt;
865
d8f41ccd 866 poolp = sigd->contentInfo.cmsg->poolp;
b1ab9ed8
A
867
868 mark = PORT_ArenaMark(poolp);
869
870
871 if (digestdata) {
d8f41ccd 872 digest = (SecAsn1Item *) PORT_ArenaZAlloc(poolp,sizeof(SecAsn1Item));
b1ab9ed8
A
873
874 /* copy digestdata item to arena (in case we have it and are not only making room) */
875 if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
876 goto loser;
877 }
878
879 /* now allocate one (same size as digestAlgorithms) */
880 if (sigd->digests == NULL) {
881 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
d8f41ccd 882 sigd->digests = PORT_ArenaZAlloc(sigd->contentInfo.cmsg->poolp, (cnt + 1) * sizeof(SecAsn1Item *));
b1ab9ed8
A
883 if (sigd->digests == NULL) {
884 PORT_SetError(SEC_ERROR_NO_MEMORY);
885 return SECFailure;
886 }
887 }
888
889 n = -1;
890 if (sigd->digestAlgorithms != NULL)
891 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
892
893 /* if not found, add a digest */
894 if (n < 0) {
d8f41ccd 895 if (SecCmsSignedDataAddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
b1ab9ed8
A
896 goto loser;
897 } else {
898 /* replace NULL pointer with digest item (and leak previous value) */
899 sigd->digests[n] = digest;
900 }
901
902 PORT_ArenaUnmark(poolp, mark);
903 return SECSuccess;
904
905loser:
906 PORT_ArenaRelease(poolp, mark);
907 return SECFailure;
908}
909
910OSStatus
d8f41ccd 911SecCmsSignedDataAddDigest(PRArenaPool *poolp,
b1ab9ed8
A
912 SecCmsSignedDataRef sigd,
913 SECOidTag digestalgtag,
d8f41ccd 914 SecAsn1Item * digest)
b1ab9ed8 915{
b1ab9ed8
A
916 SECAlgorithmID *digestalg;
917 void *mark;
918
919 mark = PORT_ArenaMark(poolp);
920
921 digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
922 if (digestalg == NULL)
923 goto loser;
924
925 if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
926 goto loser;
927
928 if (SecCmsArrayAdd(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess ||
929 /* even if digest is NULL, add dummy to have same-size array */
930 SecCmsArrayAdd(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess)
931 {
932 goto loser;
933 }
934
935 PORT_ArenaUnmark(poolp, mark);
936 return SECSuccess;
937
938loser:
939 PORT_ArenaRelease(poolp, mark);
940 return SECFailure;
941}
942
d8f41ccd 943SecAsn1Item *
b1ab9ed8
A
944SecCmsSignedDataGetDigestValue(SecCmsSignedDataRef sigd, SECOidTag digestalgtag)
945{
946 int n;
947
948 if (sigd->digestAlgorithms == NULL)
949 return NULL;
950
951 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
952
953 return (n < 0) ? NULL : sigd->digests[n];
954}
955
956/* =============================================================================
957 * Misc. utility functions
958 */
959
960/*
961 * SecCmsSignedDataCreateCertsOnly - create a certs-only SignedData.
962 *
963 * cert - base certificates that will be included
964 * include_chain - if true, include the complete cert chain for cert
965 *
966 * More certs and chains can be added via AddCertificate and AddCertChain.
967 *
968 * An error results in a return value of NULL and an error set.
969 *
970 * XXXX CRLs
971 */
972SecCmsSignedDataRef
973SecCmsSignedDataCreateCertsOnly(SecCmsMessageRef cmsg, SecCertificateRef cert, Boolean include_chain)
974{
975 SecCmsSignedDataRef sigd;
976 void *mark;
977 PLArenaPool *poolp;
978 OSStatus rv;
979
980 poolp = cmsg->poolp;
981 mark = PORT_ArenaMark(poolp);
982
983 sigd = SecCmsSignedDataCreate(cmsg);
984 if (sigd == NULL)
985 goto loser;
986
987 /* no signerinfos, thus no digestAlgorithms */
988
989 /* but certs */
990 if (include_chain) {
991 rv = SecCmsSignedDataAddCertChain(sigd, cert);
992 } else {
993 rv = SecCmsSignedDataAddCertificate(sigd, cert);
994 }
995 if (rv != SECSuccess)
996 goto loser;
997
998 /* RFC2630 5.2 sez:
999 * In the degenerate case where there are no signers, the
1000 * EncapsulatedContentInfo value being "signed" is irrelevant. In this
1001 * case, the content type within the EncapsulatedContentInfo value being
1002 * "signed" should be id-data (as defined in section 4), and the content
1003 * field of the EncapsulatedContentInfo value should be omitted.
1004 */
d8f41ccd 1005 rv = SecCmsContentInfoSetContentData(&(sigd->contentInfo), NULL, PR_TRUE);
b1ab9ed8
A
1006 if (rv != SECSuccess)
1007 goto loser;
1008
1009 PORT_ArenaUnmark(poolp, mark);
1010 return sigd;
1011
1012loser:
1013 if (sigd)
1014 SecCmsSignedDataDestroy(sigd);
1015 PORT_ArenaRelease(poolp, mark);
1016 return NULL;
1017}
1018
b1ab9ed8
A
1019/* TODO:
1020 * SecCmsSignerInfoGetReceiptRequest()
1021 * SecCmsSignedDataHasReceiptRequest()
1022 * easy way to iterate over signers
1023 */
1024