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