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