]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmssigdata.c
Security-55471.tar.gz
[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 "secitem.h"
48 #include "secoid.h"
49 #include "tsaTemplates.h"
50
51 #include <security_asn1/secasn1.h>
52 #include <security_asn1/secerr.h>
53 #include <Security/SecBase.h>
54 #include <CommonCrypto/CommonRandomSPI.h>
55
56 #ifndef NDEBUG
57 #define SIGDATA_DEBUG 1
58 #endif
59
60 #if SIGDATA_DEBUG
61 #define dprintf(args...) printf(args)
62 #else
63 #define dprintf(args...)
64 #endif
65
66 SecCmsSignedDataRef
67 SecCmsSignedDataCreate(SecCmsMessageRef cmsg)
68 {
69 void *mark;
70 SecCmsSignedDataRef sigd;
71 PLArenaPool *poolp;
72
73 poolp = cmsg->poolp;
74
75 mark = PORT_ArenaMark(poolp);
76
77 sigd = (SecCmsSignedDataRef)PORT_ArenaZAlloc (poolp, sizeof(SecCmsSignedData));
78 if (sigd == NULL)
79 goto loser;
80
81 sigd->cmsg = cmsg;
82
83 /* signerInfos, certs, certlists, crls are all empty */
84 /* version is set in SecCmsSignedDataFinalize() */
85
86 PORT_ArenaUnmark(poolp, mark);
87 return sigd;
88
89 loser:
90 PORT_ArenaRelease(poolp, mark);
91 return NULL;
92 }
93
94 void
95 SecCmsSignedDataDestroy(SecCmsSignedDataRef sigd)
96 {
97 SecCmsSignerInfoRef *signerinfos, si;
98
99 if (sigd == NULL)
100 return;
101
102 if (sigd->certs != NULL)
103 CFRelease(sigd->certs);
104
105 signerinfos = sigd->signerInfos;
106 if (signerinfos != NULL) {
107 while ((si = *signerinfos++) != NULL)
108 SecCmsSignerInfoDestroy(si);
109 }
110
111 /* everything's in a pool, so don't worry about the storage */
112 SecCmsContentInfoDestroy(&(sigd->contentInfo));
113 }
114
115 /*
116 * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData
117 * before start of encoding.
118 *
119 * In detail:
120 * - find out about the right value to put into sigd->version
121 * - come up with a list of digestAlgorithms (which should be the union of the algorithms
122 * in the signerinfos).
123 * If we happen to have a pre-set list of algorithms (and digest values!), we
124 * check if we have all the signerinfos' algorithms. If not, this is an error.
125 */
126 OSStatus
127 SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd)
128 {
129 SecCmsSignerInfoRef signerinfo;
130 SECOidTag digestalgtag;
131 CSSM_DATA_PTR dummy;
132 int version;
133 OSStatus rv;
134 Boolean haveDigests = PR_FALSE;
135 int n, i;
136 PLArenaPool *poolp;
137
138 poolp = sigd->cmsg->poolp;
139
140 /* we assume that we have precomputed digests if there is a list of algorithms, and */
141 /* a chunk of data for each of those algorithms */
142 if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
143 for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
144 if (sigd->digests[i] == NULL)
145 break;
146 }
147 if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
148 haveDigests = PR_TRUE; /* yes: we must have all the digests */
149 }
150
151 version = SEC_CMS_SIGNED_DATA_VERSION_BASIC;
152
153 /* RFC2630 5.1 "version is the syntax version number..." */
154 if (SecCmsContentInfoGetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
155 version = SEC_CMS_SIGNED_DATA_VERSION_EXT;
156
157 /* prepare all the SignerInfos (there may be none) */
158 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
159 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
160
161 /* RFC2630 5.1 "version is the syntax version number..." */
162 if (SecCmsSignerInfoGetVersion(signerinfo) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN)
163 version = SEC_CMS_SIGNED_DATA_VERSION_EXT;
164
165 /* collect digestAlgorithms from SignerInfos */
166 /* (we need to know which algorithms we have when the content comes in) */
167 /* do not overwrite any existing digestAlgorithms (and digest) */
168 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
169
170 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
171 if (n < 0 && haveDigests) {
172 /* oops, there is a digestalg we do not have a digest for */
173 /* but we were supposed to have all the digests already... */
174 goto loser;
175 } else if (n < 0) {
176 /* add the digestAlgorithm & a NULL digest */
177 rv = SecCmsSignedDataAddDigest((SecArenaPoolRef)poolp, sigd, digestalgtag, NULL);
178 if (rv != SECSuccess)
179 goto loser;
180 } else {
181 /* found it, nothing to do */
182 }
183 }
184
185 dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
186 if (dummy == NULL)
187 return SECFailure;
188
189 /* this is a SET OF, so we need to sort them guys */
190 rv = SecCmsArraySortByDER((void **)sigd->digestAlgorithms,
191 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
192 (void **)sigd->digests);
193 if (rv != SECSuccess)
194 return SECFailure;
195
196 return SECSuccess;
197
198 loser:
199 return SECFailure;
200 }
201
202 OSStatus
203 SecCmsSignedDataEncodeBeforeData(SecCmsSignedDataRef sigd)
204 {
205 /* set up the digests */
206 if (sigd->digestAlgorithms != NULL) {
207 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms);
208 if (sigd->contentInfo.digcx == NULL)
209 return SECFailure;
210 }
211 return SECSuccess;
212 }
213
214 #include <AssertMacros.h>
215 #include "tsaSupport.h"
216 #include "tsaSupportPriv.h"
217 #include "tsaTemplates.h"
218 #include <security_keychain/tsaDERUtilities.h>
219
220 extern const SecAsn1Template kSecAsn1TSATSTInfoTemplate;
221
222 OSStatus createTSAMessageImprint(SecCmsSignedDataRef signedData, CSSM_DATA_PTR encDigest,
223 SecAsn1TSAMessageImprint *messageImprint)
224 {
225 // Calculate hash of encDigest and put in messageImprint.hashedMessage
226 // We pass in encDigest, since in the verification case, it comes from a different signedData
227
228 OSStatus status = SECFailure;
229
230 require(signedData && messageImprint, xit);
231
232 SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
233 require(digestAlgorithms, xit);
234
235 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms);
236 require(digcx, xit);
237 require(encDigest, xit);
238
239 SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(signedData, 0); // NB - assume 1 signer only!
240 messageImprint->hashAlgorithm = signerinfo->digestAlg;
241
242 SecCmsDigestContextUpdate(digcx, encDigest->Data, encDigest->Length);
243
244 require_noerr(SecCmsDigestContextFinishSingle(digcx, (SecArenaPoolRef)signedData->cmsg->poolp,
245 &messageImprint->hashedMessage), xit);
246
247 status = SECSuccess;
248 xit:
249 return status;
250 }
251
252 #include <Security/SecAsn1Templates.h>
253
254 #ifndef NDEBUG
255 static OSStatus decodeDERUTF8String(const CSSM_DATA_PTR content, char *statusstr, size_t strsz)
256 {
257 // The statusString should use kSecAsn1SequenceOfUTF8StringTemplate, but doesn't
258 OSStatus status = SECFailure;
259 SecAsn1CoderRef coder = NULL;
260 CSSM_DATA statusString;
261 size_t len = 0;
262
263 require(content && statusstr, xit);
264
265 require_noerr(SecAsn1CoderCreate(&coder), xit);
266 require_noerr(SecAsn1Decode(coder, content->Data, content->Length, kSecAsn1UTF8StringTemplate, &statusString), xit);
267 status = 0;
268 len = (statusString.Length < strsz)?(int)statusString.Length:strsz;
269 if (statusstr && statusString.Data)
270 strncpy(statusstr, (const char *)statusString.Data, len);
271
272 xit:
273 if (coder)
274 SecAsn1CoderRelease(coder);
275 return status;
276 }
277 #endif
278
279 static OSStatus validateTSAResponseAndAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR tsaResponse,
280 uint64_t expectedNonce)
281 {
282 OSStatus status = SECFailure;
283 SecAsn1CoderRef coder = NULL;
284 SecAsn1TimeStampRespDER respDER = {{{0}},};
285 SecAsn1TSAPKIStatusInfo *tsastatus = NULL;
286 int respstatus = -1;
287 #ifndef NDEBUG
288 int failinfo = -1;
289 #endif
290
291 require_action(tsaResponse && tsaResponse->Data && tsaResponse->Length, xit, status = errSecTimestampMissing);
292
293 require_noerr(SecAsn1CoderCreate(&coder), xit);
294 require_noerr(SecTSAResponseCopyDEREncoding(coder, tsaResponse, &respDER), xit);
295
296 #ifndef NDEBUG
297 tsaWriteFileX("/tmp/tsa-timeStampToken.der", respDER.timeStampTokenDER.Data, respDER.timeStampTokenDER.Length);
298 #endif
299
300 tsastatus = (SecAsn1TSAPKIStatusInfo *)&respDER.status;
301 require_action(tsastatus->status.Data, xit, status = errSecTimestampBadDataFormat);
302 respstatus = (int)tsaDER_ToInt(&tsastatus->status);
303
304 #ifndef NDEBUG
305 char buf[80]={0,};
306 if (tsastatus->failInfo.Data) // e.g. FI_BadRequest
307 failinfo = (int)tsaDER_ToInt(&tsastatus->failInfo);
308
309 if (tsastatus->statusString.Data && tsastatus->statusString.Length)
310 {
311 OSStatus strrx = decodeDERUTF8String(&tsastatus->statusString, buf, sizeof(buf));
312 dprintf("decodeDERUTF8String status: %d\n", (int)strrx);
313 }
314
315 dprintf("validateTSAResponse: status: %d, failinfo: %d, %s\n", respstatus, failinfo, buf);
316 #endif
317
318 switch (respstatus)
319 {
320 case PKIS_Granted:
321 case PKIS_GrantedWithMods: // Success
322 status = noErr;
323 break;
324 case PKIS_Rejection:
325 status = errSecTimestampRejection;
326 break;
327 case PKIS_Waiting:
328 status = errSecTimestampWaiting;
329 break;
330 case PKIS_RevocationWarning:
331 status = errSecTimestampRevocationWarning;
332 break;
333 case PKIS_RevocationNotification:
334 status = errSecTimestampRevocationNotification;
335 break;
336 default:
337 status = errSecTimestampSystemFailure;
338 break;
339 }
340 require_noerr(status, xit);
341
342 // If we succeeded, then we must have a TimeStampToken
343
344 require_action(respDER.timeStampTokenDER.Data && respDER.timeStampTokenDER.Length, xit, status = errSecTimestampBadDataFormat);
345
346 dprintf("timestamp full expected nonce: %lld\n", expectedNonce);
347
348 /*
349 The bytes in respDER are a full CMS message, which we need to check now for validity.
350 The code for this is essentially the same code taht is done during a timestamp
351 verify, except that we also need to check the nonce.
352 */
353 require_noerr(status = decodeTimeStampToken(signerinfo, &respDER.timeStampTokenDER, NULL, expectedNonce), xit);
354
355 status = SecCmsSignerInfoAddTimeStamp(signerinfo, &respDER.timeStampTokenDER);
356
357 xit:
358 if (coder)
359 SecAsn1CoderRelease(coder);
360 return status;
361 }
362
363 static OSStatus getRandomNonce(uint64_t *nonce)
364 {
365 return nonce ? CCRandomCopyBytes(kCCRandomDevRandom, (void *)nonce, sizeof(*nonce)) : SECFailure;
366 }
367
368 static OSStatus remapTimestampError(OSStatus inStatus)
369 {
370 /*
371 Since communicating with the timestamp server is perhaps an unexpected
372 dependency on the network, map unknown errors into something to indicate
373 that signing without a timestamp may be a workaround. In particular, the
374 private CFURL errors (see CFNetworkErrorsPriv.i)
375
376 kCFURLErrorTimedOut = -1001,
377 kCFURLErrorNotConnectedToInternet = -1009,
378
379 are remapped to errSecTimestampServiceNotAvailable.
380 */
381
382 switch (inStatus)
383 {
384 case errSecTimestampMissing:
385 case errSecTimestampInvalid:
386 case errSecTimestampNotTrusted:
387 case errSecTimestampServiceNotAvailable:
388 case errSecTimestampBadAlg:
389 case errSecTimestampBadRequest:
390 case errSecTimestampBadDataFormat:
391 case errSecTimestampTimeNotAvailable:
392 case errSecTimestampUnacceptedPolicy:
393 case errSecTimestampUnacceptedExtension:
394 case errSecTimestampAddInfoNotAvailable:
395 case errSecTimestampSystemFailure:
396 case errSecSigningTimeMissing:
397 case errSecTimestampRejection:
398 case errSecTimestampWaiting:
399 case errSecTimestampRevocationWarning:
400 case errSecTimestampRevocationNotification:
401 return inStatus;
402 default:
403 return errSecTimestampServiceNotAvailable;
404 }
405 return errSecTimestampServiceNotAvailable;
406 }
407
408 /*
409 * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData
410 * after all the encapsulated data was passed through the encoder.
411 *
412 * In detail:
413 * - create the signatures in all the SignerInfos
414 *
415 * Please note that nothing is done to the Certificates and CRLs in the message - this
416 * is entirely the responsibility of our callers.
417 */
418 OSStatus
419 SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd)
420 {
421 SecCmsSignerInfoRef *signerinfos, signerinfo;
422 SecCmsContentInfoRef cinfo;
423 SECOidTag digestalgtag;
424 OSStatus ret = SECFailure;
425 OSStatus rv;
426 CSSM_DATA_PTR contentType;
427 int certcount;
428 int i, ci, n, rci, si;
429 PLArenaPool *poolp;
430 CFArrayRef certlist;
431 extern const SecAsn1Template SecCmsSignerInfoTemplate[];
432
433 poolp = sigd->cmsg->poolp;
434 cinfo = &(sigd->contentInfo);
435
436 /* did we have digest calculation going on? */
437 if (cinfo->digcx) {
438 rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, (SecArenaPoolRef)poolp, &(sigd->digests));
439 if (rv != SECSuccess)
440 goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */
441 cinfo->digcx = NULL;
442 }
443
444 signerinfos = sigd->signerInfos;
445 certcount = 0;
446
447 /* prepare all the SignerInfos (there may be none) */
448 for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) {
449 signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i);
450
451 /* find correct digest for this signerinfo */
452 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
453 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
454 if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
455 /* oops - digest not found */
456 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
457 goto loser;
458 }
459
460 /* XXX if our content is anything else but data, we need to force the
461 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
462 * collection...") */
463
464 /* pass contentType here as we want a contentType attribute */
465 if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL)
466 goto loser;
467
468 /* sign the thing */
469 rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType);
470 if (rv != SECSuccess)
471 goto loser;
472
473 /* while we're at it, count number of certs in certLists */
474 certlist = SecCmsSignerInfoGetCertList(signerinfo);
475 if (certlist)
476 certcount += CFArrayGetCount(certlist);
477 }
478
479 /* Now we can get a timestamp, since we have all the digests */
480
481 // We force the setting of a callback, since this is the most usual case
482 if (!sigd->cmsg->tsaCallback)
483 SecCmsMessageSetTSACallback(sigd->cmsg, (SecCmsTSACallback)SecCmsTSADefaultCallback);
484
485 if (sigd->cmsg->tsaCallback && sigd->cmsg->tsaContext)
486 {
487 CSSM_DATA tsaResponse = {0,};
488 SecAsn1TSAMessageImprint messageImprint = {{{0},},{0,}};
489 // <rdar://problem/11073466> Add nonce support for timestamping client
490
491 uint64_t nonce = 0;
492
493 require_noerr(getRandomNonce(&nonce), tsxit);
494 dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd));
495
496 // Calculate hash of encDigest and put in messageImprint.hashedMessage
497 SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(sigd, 0); // NB - assume 1 signer only!
498 CSSM_DATA *encDigest = SecCmsSignerInfoGetEncDigest(signerinfo);
499 require_noerr(createTSAMessageImprint(sigd, encDigest, &messageImprint), tsxit);
500
501 // Callback to fire up XPC service to talk to TimeStamping server, etc.
502 require_noerr(rv =(*sigd->cmsg->tsaCallback)(sigd->cmsg->tsaContext, &messageImprint,
503 nonce, &tsaResponse), tsxit);
504
505 require_noerr(rv = validateTSAResponseAndAddTimeStamp(signerinfo, &tsaResponse, nonce), tsxit);
506
507 /*
508 It is likely that every occurrence of "goto loser" in this file should
509 also do a PORT_SetError. Since it is not clear what might depend on this
510 behavior, we just do this in the timestamping case.
511 */
512 tsxit:
513 if (rv)
514 {
515 dprintf("Original timestamp error: %d\n", (int)rv);
516 rv = remapTimestampError(rv);
517 PORT_SetError(rv);
518 goto loser;
519 }
520 }
521
522 /* this is a SET OF, so we need to sort them guys */
523 rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL);
524 if (rv != SECSuccess)
525 goto loser;
526
527 /*
528 * now prepare certs & crls
529 */
530
531 /* count the rest of the certs */
532 if (sigd->certs != NULL)
533 certcount += CFArrayGetCount(sigd->certs);
534
535 if (certcount == 0) {
536 sigd->rawCerts = NULL;
537 } else {
538 /*
539 * Combine all of the certs and cert chains into rawcerts.
540 * Note: certcount is an upper bound; we may not need that many slots
541 * but we will allocate anyway to avoid having to do another pass.
542 * (The temporary space saving is not worth it.)
543 *
544 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
545 * SetOfDERcertficates implementation
546 */
547 sigd->rawCerts = (CSSM_DATA_PTR *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(CSSM_DATA_PTR));
548 if (sigd->rawCerts == NULL)
549 return SECFailure;
550
551 /*
552 * XXX Want to check for duplicates and not add *any* cert that is
553 * already in the set. This will be more important when we start
554 * dealing with larger sets of certs, dual-key certs (signing and
555 * encryption), etc. For the time being we can slide by...
556 *
557 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
558 * SetOfDERcertficates implementation
559 */
560 rci = 0;
561 if (signerinfos != NULL) {
562 for (si = 0; signerinfos[si] != NULL; si++) {
563 signerinfo = signerinfos[si];
564 for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) {
565 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA));
566 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci);
567 SecCertificateGetData(cert, sigd->rawCerts[rci++]);
568 }
569 }
570 }
571
572 if (sigd->certs != NULL) {
573 for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) {
574 sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA));
575 SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci);
576 SecCertificateGetData(cert, sigd->rawCerts[rci++]);
577 }
578 }
579
580 sigd->rawCerts[rci] = NULL;
581
582 /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
583 SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL);
584 }
585
586 ret = SECSuccess;
587
588 loser:
589
590 dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret, (long)rv);
591 return ret;
592 }
593
594 OSStatus
595 SecCmsSignedDataDecodeBeforeData(SecCmsSignedDataRef sigd)
596 {
597 /* set up the digests */
598 if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
599 /* if digests are already there, do nothing */
600 sigd->contentInfo.digcx = SecCmsDigestContextStartMultiple(sigd->digestAlgorithms);
601 if (sigd->contentInfo.digcx == NULL)
602 return SECFailure;
603 }
604 return SECSuccess;
605 }
606
607 /*
608 * SecCmsSignedDataDecodeAfterData - do all the necessary things to a SignedData
609 * after all the encapsulated data was passed through the decoder.
610 */
611 OSStatus
612 SecCmsSignedDataDecodeAfterData(SecCmsSignedDataRef sigd)
613 {
614 /* did we have digest calculation going on? */
615 if (sigd->contentInfo.digcx) {
616 if (SecCmsDigestContextFinishMultiple(sigd->contentInfo.digcx, (SecArenaPoolRef)sigd->cmsg->poolp, &(sigd->digests)) != SECSuccess)
617 return SECFailure; /* error has been set by SecCmsDigestContextFinishMultiple */
618 sigd->contentInfo.digcx = NULL;
619 }
620 return SECSuccess;
621 }
622
623 /*
624 * SecCmsSignedDataDecodeAfterEnd - do all the necessary things to a SignedData
625 * after all decoding is finished.
626 */
627 OSStatus
628 SecCmsSignedDataDecodeAfterEnd(SecCmsSignedDataRef sigd)
629 {
630 SecCmsSignerInfoRef *signerinfos;
631 int i;
632
633 signerinfos = sigd->signerInfos;
634
635 /* set cmsg and sigd backpointers for all the signerinfos */
636 if (signerinfos) {
637 for (i = 0; signerinfos[i] != NULL; i++) {
638 signerinfos[i]->cmsg = sigd->cmsg;
639 signerinfos[i]->sigd = sigd;
640 }
641 }
642
643 return SECSuccess;
644 }
645
646 /*
647 * SecCmsSignedDataGetSignerInfos - retrieve the SignedData's signer list
648 */
649 SecCmsSignerInfoRef *
650 SecCmsSignedDataGetSignerInfos(SecCmsSignedDataRef sigd)
651 {
652 return sigd->signerInfos;
653 }
654
655 int
656 SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd)
657 {
658 return SecCmsArrayCount((void **)sigd->signerInfos);
659 }
660
661 SecCmsSignerInfoRef
662 SecCmsSignedDataGetSignerInfo(SecCmsSignedDataRef sigd, int i)
663 {
664 return sigd->signerInfos[i];
665 }
666
667 /*
668 * SecCmsSignedDataGetDigestAlgs - retrieve the SignedData's digest algorithm list
669 */
670 SECAlgorithmID **
671 SecCmsSignedDataGetDigestAlgs(SecCmsSignedDataRef sigd)
672 {
673 return sigd->digestAlgorithms;
674 }
675
676 /*
677 * SecCmsSignedDataGetContentInfo - return pointer to this signedData's contentinfo
678 */
679 SecCmsContentInfoRef
680 SecCmsSignedDataGetContentInfo(SecCmsSignedDataRef sigd)
681 {
682 return &(sigd->contentInfo);
683 }
684
685 /*
686 * SecCmsSignedDataGetCertificateList - retrieve the SignedData's certificate list
687 */
688 CSSM_DATA_PTR *
689 SecCmsSignedDataGetCertificateList(SecCmsSignedDataRef sigd)
690 {
691 return sigd->rawCerts;
692 }
693
694 OSStatus
695 SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd, SecKeychainRef keychain,
696 SECCertUsage certusage, Boolean keepcerts)
697 {
698 int certcount;
699 OSStatus rv;
700 int i;
701
702 certcount = SecCmsArrayCount((void **)sigd->rawCerts);
703
704 rv = CERT_ImportCerts(keychain, certusage, certcount, sigd->rawCerts, NULL,
705 keepcerts, PR_FALSE, NULL);
706
707 /* XXX CRL handling */
708
709 if (sigd->signerInfos != NULL) {
710 /* fill in all signerinfo's certs */
711 for (i = 0; sigd->signerInfos[i] != NULL; i++)
712 (void)SecCmsSignerInfoGetSigningCertificate(sigd->signerInfos[i], keychain);
713 }
714
715 return rv;
716 }
717
718 /*
719 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
720 * of external signatures!
721 */
722
723 /*
724 * SecCmsSignedDataVerifySignerInfo - check the signatures.
725 *
726 * The digests were either calculated during decoding (and are stored in the
727 * signedData itself) or set after decoding using SecCmsSignedDataSetDigests.
728 *
729 * The verification checks if the signing cert is valid and has a trusted chain
730 * for the purpose specified by "policies".
731 *
732 * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly.
733 * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate().
734 */
735 OSStatus
736 SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd, int i,
737 SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef)
738 {
739 SecCmsSignerInfoRef signerinfo;
740 SecCmsContentInfoRef cinfo;
741 SECOidData *algiddata;
742 CSSM_DATA_PTR contentType, digest;
743 OSStatus status, status2;
744
745 cinfo = &(sigd->contentInfo);
746
747 signerinfo = sigd->signerInfos[i];
748
749 /* Signature or digest level verificationStatus errors should supercede
750 certificate level errors, so check the digest and signature first. */
751
752 /* Find digest and contentType for signerinfo */
753 algiddata = SecCmsSignerInfoGetDigestAlg(signerinfo);
754 if (algiddata == NULL) {
755 return errSecInternalError; // shouldn't have happened, this is likely due to corrupted data
756 }
757
758 digest = SecCmsSignedDataGetDigestByAlgTag(sigd, algiddata->offset);
759 if(digest == NULL) {
760 /*
761 * No digests; this probably had detached content the caller has to
762 * deal with.
763 * FIXME: need some error return for this (as well as many
764 * other places in this library).
765 */
766 return errSecDataNotAvailable;
767 }
768 contentType = SecCmsContentInfoGetContentTypeOID(cinfo);
769
770 /* verify signature */
771 status = SecCmsSignerInfoVerify(signerinfo, digest, contentType);
772
773 /* Now verify the certificate. We do this even if the signature failed to verify so we can
774 return a trustRef to the caller for display purposes. */
775 status2 = SecCmsSignerInfoVerifyCertificate(signerinfo, keychainOrArray,
776 policies, trustRef);
777 dprintf("SecCmsSignedDataVerifySignerInfo: status %d status2 %d\n", (int) status, (int)status2);
778 /* The error from SecCmsSignerInfoVerify() supercedes error from SecCmsSignerInfoVerifyCertificate(). */
779 if (status)
780 return status;
781
782 return status2;
783 }
784
785 /*
786 * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message
787 */
788 OSStatus
789 SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd,
790 SecKeychainRef keychainOrArray,
791 CFTypeRef policies)
792 {
793 SecCertificateRef cert;
794 OSStatus rv = SECSuccess;
795 int i;
796 int count;
797
798 if (!sigd || !keychainOrArray || !sigd->rawCerts) {
799 PORT_SetError(SEC_ERROR_INVALID_ARGS);
800 return SECFailure;
801 }
802
803 count = SecCmsArrayCount((void**)sigd->rawCerts);
804 for (i=0; i < count; i++) {
805 if (sigd->certs && CFArrayGetCount(sigd->certs) > i) {
806 cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i);
807 CFRetain(cert);
808 } else {
809 cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]);
810 if (!cert) {
811 rv = SECFailure;
812 break;
813 }
814 }
815 rv |= CERT_VerifyCert(keychainOrArray, cert, sigd->rawCerts,
816 policies, CFAbsoluteTimeGetCurrent(), NULL);
817 CFRelease(cert);
818 }
819
820 return rv;
821 }
822
823 /*
824 * SecCmsSignedDataHasDigests - see if we have digests in place
825 */
826 Boolean
827 SecCmsSignedDataHasDigests(SecCmsSignedDataRef sigd)
828 {
829 return (sigd->digests != NULL);
830 }
831
832 OSStatus
833 SecCmsSignedDataAddCertList(SecCmsSignedDataRef sigd, CFArrayRef certlist)
834 {
835 PORT_Assert(certlist != NULL);
836
837 if (certlist == NULL)
838 return SECFailure;
839
840 if (!sigd->certs)
841 sigd->certs = CFArrayCreateMutableCopy(NULL, 0, certlist);
842 else
843 {
844 CFRange certlistRange = { 0, CFArrayGetCount(certlist) };
845 CFArrayAppendArray(sigd->certs, certlist, certlistRange);
846 }
847
848 return SECSuccess;
849 }
850
851 /*
852 * SecCmsSignedDataAddCertChain - add cert and its entire chain to the set of certs
853 */
854 OSStatus
855 SecCmsSignedDataAddCertChain(SecCmsSignedDataRef sigd, SecCertificateRef cert)
856 {
857 CFArrayRef certlist;
858 SECCertUsage usage;
859 OSStatus rv;
860
861 usage = certUsageEmailSigner;
862
863 /* do not include root */
864 certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
865 if (certlist == NULL)
866 return SECFailure;
867
868 rv = SecCmsSignedDataAddCertList(sigd, certlist);
869 CFRelease(certlist);
870
871 return rv;
872 }
873
874 OSStatus
875 SecCmsSignedDataAddCertificate(SecCmsSignedDataRef sigd, SecCertificateRef cert)
876 {
877 PORT_Assert(cert != NULL);
878
879 if (cert == NULL)
880 return SECFailure;
881
882 if (!sigd->certs)
883 sigd->certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
884
885 CFArrayAppendValue(sigd->certs, cert);
886
887 return SECSuccess;
888 }
889
890 Boolean
891 SecCmsSignedDataContainsCertsOrCrls(SecCmsSignedDataRef sigd)
892 {
893 if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
894 return PR_TRUE;
895 else if (sigd->rawCrls != NULL && sigd->rawCrls[0] != NULL)
896 return PR_TRUE;
897 else
898 return PR_FALSE;
899 }
900
901 OSStatus
902 SecCmsSignedDataAddSignerInfo(SecCmsSignedDataRef sigd,
903 SecCmsSignerInfoRef signerinfo)
904 {
905 void *mark;
906 OSStatus rv;
907 SECOidTag digestalgtag;
908 PLArenaPool *poolp;
909
910 poolp = sigd->cmsg->poolp;
911
912 mark = PORT_ArenaMark(poolp);
913
914 /* add signerinfo */
915 rv = SecCmsArrayAdd(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
916 if (rv != SECSuccess)
917 goto loser;
918
919 signerinfo->sigd = sigd;
920
921 /*
922 * add empty digest
923 * Empty because we don't have it yet. Either it gets created during encoding
924 * (if the data is present) or has to be set externally.
925 * XXX maybe pass it in optionally?
926 */
927 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
928 rv = SecCmsSignedDataSetDigestValue(sigd, digestalgtag, NULL);
929 if (rv != SECSuccess)
930 goto loser;
931
932 /*
933 * The last thing to get consistency would be adding the digest.
934 */
935
936 PORT_ArenaUnmark(poolp, mark);
937 return SECSuccess;
938
939 loser:
940 PORT_ArenaRelease (poolp, mark);
941 return SECFailure;
942 }
943
944 CSSM_DATA_PTR
945 SecCmsSignedDataGetDigestByAlgTag(SecCmsSignedDataRef sigd, SECOidTag algtag)
946 {
947 int idx;
948
949 if(sigd->digests == NULL) {
950 return NULL;
951 }
952 idx = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, algtag);
953 return sigd->digests[idx];
954 }
955
956 /*
957 * SecCmsSignedDataSetDigests - set a signedData's digests member
958 *
959 * "digestalgs" - array of digest algorithm IDs
960 * "digests" - array of digests corresponding to the digest algorithms
961 */
962 OSStatus
963 SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd,
964 SECAlgorithmID **digestalgs,
965 CSSM_DATA_PTR *digests)
966 {
967 int cnt, i, idx;
968
969 if (sigd->digestAlgorithms == NULL) {
970 PORT_SetError(SEC_ERROR_INVALID_ARGS);
971 return SECFailure;
972 }
973
974 /* we assume that the digests array is just not there yet */
975 PORT_Assert(sigd->digests == NULL);
976 if (sigd->digests != NULL) {
977 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
978 return SECFailure;
979 }
980
981 /* now allocate one (same size as digestAlgorithms) */
982 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
983 sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(CSSM_DATA_PTR));
984 if (sigd->digests == NULL) {
985 PORT_SetError(SEC_ERROR_NO_MEMORY);
986 return SECFailure;
987 }
988
989 for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
990 /* try to find the sigd's i'th digest algorithm in the array we passed in */
991 idx = SecCmsAlgArrayGetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
992 if (idx < 0) {
993 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
994 return SECFailure;
995 }
996
997 /* found it - now set it */
998 if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
999 SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
1000 {
1001 PORT_SetError(SEC_ERROR_NO_MEMORY);
1002 return SECFailure;
1003 }
1004 }
1005 return SECSuccess;
1006 }
1007
1008 OSStatus
1009 SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd,
1010 SECOidTag digestalgtag,
1011 CSSM_DATA_PTR digestdata)
1012 {
1013 CSSM_DATA_PTR digest = NULL;
1014 PLArenaPool *poolp;
1015 void *mark;
1016 int n, cnt;
1017
1018 poolp = sigd->cmsg->poolp;
1019
1020 mark = PORT_ArenaMark(poolp);
1021
1022
1023 if (digestdata) {
1024 digest = (CSSM_DATA_PTR) PORT_ArenaZAlloc(poolp,sizeof(CSSM_DATA));
1025
1026 /* copy digestdata item to arena (in case we have it and are not only making room) */
1027 if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
1028 goto loser;
1029 }
1030
1031 /* now allocate one (same size as digestAlgorithms) */
1032 if (sigd->digests == NULL) {
1033 cnt = SecCmsArrayCount((void **)sigd->digestAlgorithms);
1034 sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(CSSM_DATA_PTR));
1035 if (sigd->digests == NULL) {
1036 PORT_SetError(SEC_ERROR_NO_MEMORY);
1037 return SECFailure;
1038 }
1039 }
1040
1041 n = -1;
1042 if (sigd->digestAlgorithms != NULL)
1043 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
1044
1045 /* if not found, add a digest */
1046 if (n < 0) {
1047 if (SecCmsSignedDataAddDigest((SecArenaPoolRef)poolp, sigd, digestalgtag, digest) != SECSuccess)
1048 goto loser;
1049 } else {
1050 /* replace NULL pointer with digest item (and leak previous value) */
1051 sigd->digests[n] = digest;
1052 }
1053
1054 PORT_ArenaUnmark(poolp, mark);
1055 return SECSuccess;
1056
1057 loser:
1058 PORT_ArenaRelease(poolp, mark);
1059 return SECFailure;
1060 }
1061
1062 OSStatus
1063 SecCmsSignedDataAddDigest(SecArenaPoolRef pool,
1064 SecCmsSignedDataRef sigd,
1065 SECOidTag digestalgtag,
1066 CSSM_DATA_PTR digest)
1067 {
1068 PRArenaPool *poolp = (PRArenaPool *)pool;
1069 SECAlgorithmID *digestalg;
1070 void *mark;
1071
1072 mark = PORT_ArenaMark(poolp);
1073
1074 digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
1075 if (digestalg == NULL)
1076 goto loser;
1077
1078 if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
1079 goto loser;
1080
1081 if (SecCmsArrayAdd(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess ||
1082 /* even if digest is NULL, add dummy to have same-size array */
1083 SecCmsArrayAdd(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess)
1084 {
1085 goto loser;
1086 }
1087
1088 PORT_ArenaUnmark(poolp, mark);
1089 return SECSuccess;
1090
1091 loser:
1092 PORT_ArenaRelease(poolp, mark);
1093 return SECFailure;
1094 }
1095
1096 CSSM_DATA_PTR
1097 SecCmsSignedDataGetDigestValue(SecCmsSignedDataRef sigd, SECOidTag digestalgtag)
1098 {
1099 int n;
1100
1101 if (sigd->digestAlgorithms == NULL)
1102 return NULL;
1103
1104 n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
1105
1106 return (n < 0) ? NULL : sigd->digests[n];
1107 }
1108
1109 /* =============================================================================
1110 * Misc. utility functions
1111 */
1112
1113 /*
1114 * SecCmsSignedDataCreateCertsOnly - create a certs-only SignedData.
1115 *
1116 * cert - base certificates that will be included
1117 * include_chain - if true, include the complete cert chain for cert
1118 *
1119 * More certs and chains can be added via AddCertificate and AddCertChain.
1120 *
1121 * An error results in a return value of NULL and an error set.
1122 *
1123 * XXXX CRLs
1124 */
1125 SecCmsSignedDataRef
1126 SecCmsSignedDataCreateCertsOnly(SecCmsMessageRef cmsg, SecCertificateRef cert, Boolean include_chain)
1127 {
1128 SecCmsSignedDataRef sigd;
1129 void *mark;
1130 PLArenaPool *poolp;
1131 OSStatus rv;
1132
1133 poolp = cmsg->poolp;
1134 mark = PORT_ArenaMark(poolp);
1135
1136 sigd = SecCmsSignedDataCreate(cmsg);
1137 if (sigd == NULL)
1138 goto loser;
1139
1140 /* no signerinfos, thus no digestAlgorithms */
1141
1142 /* but certs */
1143 if (include_chain) {
1144 rv = SecCmsSignedDataAddCertChain(sigd, cert);
1145 } else {
1146 rv = SecCmsSignedDataAddCertificate(sigd, cert);
1147 }
1148 if (rv != SECSuccess)
1149 goto loser;
1150
1151 /* RFC2630 5.2 sez:
1152 * In the degenerate case where there are no signers, the
1153 * EncapsulatedContentInfo value being "signed" is irrelevant. In this
1154 * case, the content type within the EncapsulatedContentInfo value being
1155 * "signed" should be id-data (as defined in section 4), and the content
1156 * field of the EncapsulatedContentInfo value should be omitted.
1157 */
1158 rv = SecCmsContentInfoSetContentData(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
1159 if (rv != SECSuccess)
1160 goto loser;
1161
1162 PORT_ArenaUnmark(poolp, mark);
1163 return sigd;
1164
1165 loser:
1166 if (sigd)
1167 SecCmsSignedDataDestroy(sigd);
1168 PORT_ArenaRelease(poolp, mark);
1169 return NULL;
1170 }
1171
1172 /*
1173 * Get SecCmsSignedDataRawCerts - obtain raw certs as a NULL_terminated array
1174 * of pointers.
1175 */
1176 extern OSStatus SecCmsSignedDataRawCerts(SecCmsSignedDataRef sigd,
1177 CSSM_DATA_PTR **rawCerts)
1178 {
1179 *rawCerts = sigd->rawCerts;
1180 return noErr;
1181 }
1182
1183 /* TODO:
1184 * SecCmsSignerInfoGetReceiptRequest()
1185 * SecCmsSignedDataHasReceiptRequest()
1186 * easy way to iterate over signers
1187 */
1188