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/
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.
12 * The Original Code is the Netscape security libraries.
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
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
35 * CMS signedData methods.
38 #include <Security/SecCmsSignedData.h>
40 #include <Security/SecCmsContentInfo.h>
41 #include <Security/SecCmsDigestContext.h>
42 #include <Security/SecCmsSignerInfo.h>
49 #include "tsaTemplates.h"
51 #include <security_asn1/secasn1.h>
52 #include <security_asn1/secerr.h>
53 #include <Security/SecBase.h>
54 #include <CommonCrypto/CommonRandomSPI.h>
55 #include <Security/SecPolicy.h>
56 #include <Security/SecPolicyPriv.h>
57 #include <utilities/SecCFWrappers.h>
61 #define SIGDATA_DEBUG 1
65 #define dprintf(args...) fprintf(stderr, args)
67 #define dprintf(args...)
71 SecCmsSignedDataCreate(SecCmsMessageRef cmsg
)
74 SecCmsSignedDataRef sigd
;
79 mark
= PORT_ArenaMark(poolp
);
81 sigd
= (SecCmsSignedDataRef
)PORT_ArenaZAlloc (poolp
, sizeof(SecCmsSignedData
));
87 /* signerInfos, certs, certlists, crls are all empty */
88 /* version is set in SecCmsSignedDataFinalize() */
90 PORT_ArenaUnmark(poolp
, mark
);
94 PORT_ArenaRelease(poolp
, mark
);
99 SecCmsSignedDataDestroy(SecCmsSignedDataRef sigd
)
101 SecCmsSignerInfoRef
*signerinfos
, si
;
106 if (sigd
->certs
!= NULL
)
107 CFRelease(sigd
->certs
);
109 signerinfos
= sigd
->signerInfos
;
110 if (signerinfos
!= NULL
) {
111 while ((si
= *signerinfos
++) != NULL
)
112 SecCmsSignerInfoDestroy(si
);
115 /* everything's in a pool, so don't worry about the storage */
116 SecCmsContentInfoDestroy(&(sigd
->contentInfo
));
120 * SecCmsSignedDataEncodeBeforeStart - do all the necessary things to a SignedData
121 * before start of encoding.
124 * - find out about the right value to put into sigd->version
125 * - come up with a list of digestAlgorithms (which should be the union of the algorithms
126 * in the signerinfos).
127 * If we happen to have a pre-set list of algorithms (and digest values!), we
128 * check if we have all the signerinfos' algorithms. If not, this is an error.
131 SecCmsSignedDataEncodeBeforeStart(SecCmsSignedDataRef sigd
)
133 SecCmsSignerInfoRef signerinfo
;
134 SECOidTag digestalgtag
;
138 Boolean haveDigests
= PR_FALSE
;
142 poolp
= sigd
->cmsg
->poolp
;
144 /* we assume that we have precomputed digests if there is a list of algorithms, and */
145 /* a chunk of data for each of those algorithms */
146 if (sigd
->digestAlgorithms
!= NULL
&& sigd
->digests
!= NULL
) {
147 for (i
=0; sigd
->digestAlgorithms
[i
] != NULL
; i
++) {
148 if (sigd
->digests
[i
] == NULL
)
151 if (sigd
->digestAlgorithms
[i
] == NULL
) /* reached the end of the array? */
152 haveDigests
= PR_TRUE
; /* yes: we must have all the digests */
155 version
= SEC_CMS_SIGNED_DATA_VERSION_BASIC
;
157 /* RFC2630 5.1 "version is the syntax version number..." */
158 if (SecCmsContentInfoGetContentTypeTag(&(sigd
->contentInfo
)) != SEC_OID_PKCS7_DATA
)
159 version
= SEC_CMS_SIGNED_DATA_VERSION_EXT
;
161 /* prepare all the SignerInfos (there may be none) */
162 for (i
=0; i
< SecCmsSignedDataSignerInfoCount(sigd
); i
++) {
163 signerinfo
= SecCmsSignedDataGetSignerInfo(sigd
, i
);
165 /* RFC2630 5.1 "version is the syntax version number..." */
166 if (SecCmsSignerInfoGetVersion(signerinfo
) != SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN
)
167 version
= SEC_CMS_SIGNED_DATA_VERSION_EXT
;
169 /* collect digestAlgorithms from SignerInfos */
170 /* (we need to know which algorithms we have when the content comes in) */
171 /* do not overwrite any existing digestAlgorithms (and digest) */
172 digestalgtag
= SecCmsSignerInfoGetDigestAlgTag(signerinfo
);
174 n
= SecCmsAlgArrayGetIndexByAlgTag(sigd
->digestAlgorithms
, digestalgtag
);
175 if (n
< 0 && haveDigests
) {
176 /* oops, there is a digestalg we do not have a digest for */
177 /* but we were supposed to have all the digests already... */
180 /* add the digestAlgorithm & a NULL digest */
181 rv
= SecCmsSignedDataAddDigest((SecArenaPoolRef
)poolp
, sigd
, digestalgtag
, NULL
);
182 if (rv
!= SECSuccess
)
185 /* found it, nothing to do */
189 dummy
= SEC_ASN1EncodeInteger(poolp
, &(sigd
->version
), (long)version
);
193 /* this is a SET OF, so we need to sort them guys */
194 rv
= SecCmsArraySortByDER((void **)sigd
->digestAlgorithms
,
195 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate
),
196 (void **)sigd
->digests
);
197 if (rv
!= SECSuccess
)
207 SecCmsSignedDataEncodeBeforeData(SecCmsSignedDataRef sigd
)
209 /* set up the digests */
210 if (sigd
->digestAlgorithms
!= NULL
) {
211 sigd
->contentInfo
.digcx
= SecCmsDigestContextStartMultiple(sigd
->digestAlgorithms
);
212 if (sigd
->contentInfo
.digcx
== NULL
)
218 #include <AssertMacros.h>
219 #include "tsaSupport.h"
220 #include "tsaSupportPriv.h"
221 #include "tsaTemplates.h"
222 #include <security_keychain/tsaDERUtilities.h>
224 extern const SecAsn1Template kSecAsn1TSATSTInfoTemplate
;
226 OSStatus
createTSAMessageImprint(SecCmsSignedDataRef signedData
, CSSM_DATA_PTR encDigest
,
227 SecAsn1TSAMessageImprint
*messageImprint
)
229 // Calculate hash of encDigest and put in messageImprint.hashedMessage
230 // We pass in encDigest, since in the verification case, it comes from a different signedData
232 OSStatus status
= SECFailure
;
234 require(signedData
&& messageImprint
, xit
);
236 SECAlgorithmID
**digestAlgorithms
= SecCmsSignedDataGetDigestAlgs(signedData
);
237 require(digestAlgorithms
, xit
);
239 SecCmsDigestContextRef digcx
= SecCmsDigestContextStartMultiple(digestAlgorithms
);
241 require(encDigest
, xit
);
243 SecCmsSignerInfoRef signerinfo
= SecCmsSignedDataGetSignerInfo(signedData
, 0); // NB - assume 1 signer only!
244 messageImprint
->hashAlgorithm
= signerinfo
->digestAlg
;
246 SecCmsDigestContextUpdate(digcx
, encDigest
->Data
, encDigest
->Length
);
248 require_noerr(SecCmsDigestContextFinishSingle(digcx
, (SecArenaPoolRef
)signedData
->cmsg
->poolp
,
249 &messageImprint
->hashedMessage
), xit
);
256 #include <Security/SecAsn1Templates.h>
259 static OSStatus
decodeDERUTF8String(const CSSM_DATA_PTR content
, char *statusstr
, size_t strsz
)
261 // The statusString should use kSecAsn1SequenceOfUTF8StringTemplate, but doesn't
262 OSStatus status
= SECFailure
;
263 SecAsn1CoderRef coder
= NULL
;
264 CSSM_DATA statusString
;
267 require(content
&& statusstr
, xit
);
269 require_noerr(SecAsn1CoderCreate(&coder
), xit
);
270 require_noerr(SecAsn1Decode(coder
, content
->Data
, content
->Length
, kSecAsn1UTF8StringTemplate
, &statusString
), xit
);
272 len
= (statusString
.Length
< strsz
)?(int)statusString
.Length
:strsz
;
273 if (statusstr
&& statusString
.Data
)
274 strncpy(statusstr
, (const char *)statusString
.Data
, len
);
278 SecAsn1CoderRelease(coder
);
283 static OSStatus
validateTSAResponseAndAddTimeStamp(SecCmsSignerInfoRef signerinfo
, CSSM_DATA_PTR tsaResponse
,
284 uint64_t expectedNonce
)
286 OSStatus status
= SECFailure
;
287 SecAsn1CoderRef coder
= NULL
;
288 SecAsn1TimeStampRespDER respDER
= {{{0}},};
289 SecAsn1TSAPKIStatusInfo
*tsastatus
= NULL
;
295 require_action(tsaResponse
&& tsaResponse
->Data
&& tsaResponse
->Length
, xit
, status
= errSecTimestampMissing
);
297 require_noerr(SecAsn1CoderCreate(&coder
), xit
);
298 require_noerr(SecTSAResponseCopyDEREncoding(coder
, tsaResponse
, &respDER
), xit
);
301 tsaWriteFileX("/tmp/tsa-timeStampToken.der", respDER
.timeStampTokenDER
.Data
, respDER
.timeStampTokenDER
.Length
);
304 tsastatus
= (SecAsn1TSAPKIStatusInfo
*)&respDER
.status
;
305 require_action(tsastatus
->status
.Data
, xit
, status
= errSecTimestampBadDataFormat
);
306 respstatus
= (int)tsaDER_ToInt(&tsastatus
->status
);
310 if (tsastatus
->failInfo
.Data
) // e.g. FI_BadRequest
311 failinfo
= (int)tsaDER_ToInt(&tsastatus
->failInfo
);
313 if (tsastatus
->statusString
.Data
&& tsastatus
->statusString
.Length
)
315 OSStatus strrx
= decodeDERUTF8String(&tsastatus
->statusString
, buf
, sizeof(buf
));
316 dprintf("decodeDERUTF8String status: %d\n", (int)strrx
);
319 dprintf("validateTSAResponse: status: %d, failinfo: %d, %s\n", respstatus
, failinfo
, buf
);
325 case PKIS_GrantedWithMods
: // Success
329 status
= errSecTimestampRejection
;
332 status
= errSecTimestampWaiting
;
334 case PKIS_RevocationWarning
:
335 status
= errSecTimestampRevocationWarning
;
337 case PKIS_RevocationNotification
:
338 status
= errSecTimestampRevocationNotification
;
341 status
= errSecTimestampSystemFailure
;
344 require_noerr(status
, xit
);
346 // If we succeeded, then we must have a TimeStampToken
348 require_action(respDER
.timeStampTokenDER
.Data
&& respDER
.timeStampTokenDER
.Length
, xit
, status
= errSecTimestampBadDataFormat
);
350 dprintf("timestamp full expected nonce: %lld\n", expectedNonce
);
353 The bytes in respDER are a full CMS message, which we need to check now for validity.
354 The code for this is essentially the same code taht is done during a timestamp
355 verify, except that we also need to check the nonce.
357 require_noerr(status
= decodeTimeStampToken(signerinfo
, &respDER
.timeStampTokenDER
, NULL
, expectedNonce
), xit
);
359 status
= SecCmsSignerInfoAddTimeStamp(signerinfo
, &respDER
.timeStampTokenDER
);
363 SecAsn1CoderRelease(coder
);
367 static OSStatus
getRandomNonce(uint64_t *nonce
)
369 return nonce
? CCRandomCopyBytes(kCCRandomDevRandom
, (void *)nonce
, sizeof(*nonce
)) : SECFailure
;
372 static OSStatus
remapTimestampError(OSStatus inStatus
)
375 Since communicating with the timestamp server is perhaps an unexpected
376 dependency on the network, map unknown errors into something to indicate
377 that signing without a timestamp may be a workaround. In particular, the
378 private CFURL errors (see CFNetworkErrorsPriv.i)
380 kCFURLErrorTimedOut = -1001,
381 kCFURLErrorNotConnectedToInternet = -1009,
383 are remapped to errSecTimestampServiceNotAvailable.
388 case errSecTimestampMissing
:
389 case errSecTimestampInvalid
:
390 case errSecTimestampNotTrusted
:
391 case errSecTimestampServiceNotAvailable
:
392 case errSecTimestampBadAlg
:
393 case errSecTimestampBadRequest
:
394 case errSecTimestampBadDataFormat
:
395 case errSecTimestampTimeNotAvailable
:
396 case errSecTimestampUnacceptedPolicy
:
397 case errSecTimestampUnacceptedExtension
:
398 case errSecTimestampAddInfoNotAvailable
:
399 case errSecTimestampSystemFailure
:
400 case errSecSigningTimeMissing
:
401 case errSecTimestampRejection
:
402 case errSecTimestampWaiting
:
403 case errSecTimestampRevocationWarning
:
404 case errSecTimestampRevocationNotification
:
407 return errSecTimestampServiceNotAvailable
;
409 return errSecTimestampServiceNotAvailable
;
413 * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData
414 * after all the encapsulated data was passed through the encoder.
417 * - create the signatures in all the SignerInfos
419 * Please note that nothing is done to the Certificates and CRLs in the message - this
420 * is entirely the responsibility of our callers.
423 SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd
)
425 SecCmsSignerInfoRef
*signerinfos
, signerinfo
;
426 SecCmsContentInfoRef cinfo
;
427 SECOidTag digestalgtag
;
428 OSStatus ret
= SECFailure
;
430 CSSM_DATA_PTR contentType
;
432 int i
, ci
, n
, rci
, si
;
435 extern const SecAsn1Template SecCmsSignerInfoTemplate
[];
437 poolp
= sigd
->cmsg
->poolp
;
438 cinfo
= &(sigd
->contentInfo
);
440 /* did we have digest calculation going on? */
442 rv
= SecCmsDigestContextFinishMultiple(cinfo
->digcx
, (SecArenaPoolRef
)poolp
, &(sigd
->digests
));
443 if (rv
!= SECSuccess
)
444 goto loser
; /* error has been set by SecCmsDigestContextFinishMultiple */
448 signerinfos
= sigd
->signerInfos
;
451 /* prepare all the SignerInfos (there may be none) */
452 for (i
=0; i
< SecCmsSignedDataSignerInfoCount(sigd
); i
++) {
453 signerinfo
= SecCmsSignedDataGetSignerInfo(sigd
, i
);
455 /* find correct digest for this signerinfo */
456 digestalgtag
= SecCmsSignerInfoGetDigestAlgTag(signerinfo
);
457 n
= SecCmsAlgArrayGetIndexByAlgTag(sigd
->digestAlgorithms
, digestalgtag
);
458 if (n
< 0 || sigd
->digests
== NULL
|| sigd
->digests
[n
] == NULL
) {
459 /* oops - digest not found */
460 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND
);
464 /* XXX if our content is anything else but data, we need to force the
465 * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
468 /* pass contentType here as we want a contentType attribute */
469 if ((contentType
= SecCmsContentInfoGetContentTypeOID(cinfo
)) == NULL
)
473 rv
= SecCmsSignerInfoSign(signerinfo
, sigd
->digests
[n
], contentType
);
474 if (rv
!= SECSuccess
)
477 /* while we're at it, count number of certs in certLists */
478 certlist
= SecCmsSignerInfoGetCertList(signerinfo
);
480 certcount
+= CFArrayGetCount(certlist
);
483 /* Now we can get a timestamp, since we have all the digests */
485 // We force the setting of a callback, since this is the most usual case
486 if (!sigd
->cmsg
->tsaCallback
)
487 SecCmsMessageSetTSACallback(sigd
->cmsg
, (SecCmsTSACallback
)SecCmsTSADefaultCallback
);
489 if (sigd
->cmsg
->tsaCallback
&& sigd
->cmsg
->tsaContext
)
491 CSSM_DATA tsaResponse
= {0,};
492 SecAsn1TSAMessageImprint messageImprint
= {{{0},},{0,}};
493 // <rdar://problem/11073466> Add nonce support for timestamping client
497 require_noerr(getRandomNonce(&nonce
), tsxit
);
498 dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd
));
500 // Calculate hash of encDigest and put in messageImprint.hashedMessage
501 SecCmsSignerInfoRef signerinfo
= SecCmsSignedDataGetSignerInfo(sigd
, 0); // NB - assume 1 signer only!
502 CSSM_DATA
*encDigest
= SecCmsSignerInfoGetEncDigest(signerinfo
);
503 require_noerr(createTSAMessageImprint(sigd
, encDigest
, &messageImprint
), tsxit
);
505 // Callback to fire up XPC service to talk to TimeStamping server, etc.
506 require_noerr(rv
=(*sigd
->cmsg
->tsaCallback
)(sigd
->cmsg
->tsaContext
, &messageImprint
,
507 nonce
, &tsaResponse
), tsxit
);
509 require_noerr(rv
= validateTSAResponseAndAddTimeStamp(signerinfo
, &tsaResponse
, nonce
), tsxit
);
512 It is likely that every occurrence of "goto loser" in this file should
513 also do a PORT_SetError. Since it is not clear what might depend on this
514 behavior, we just do this in the timestamping case.
519 dprintf("Original timestamp error: %d\n", (int)rv
);
520 rv
= remapTimestampError(rv
);
526 /* this is a SET OF, so we need to sort them guys */
527 rv
= SecCmsArraySortByDER((void **)signerinfos
, SecCmsSignerInfoTemplate
, NULL
);
528 if (rv
!= SECSuccess
)
532 * now prepare certs & crls
535 /* count the rest of the certs */
536 if (sigd
->certs
!= NULL
)
537 certcount
+= CFArrayGetCount(sigd
->certs
);
539 if (certcount
== 0) {
540 sigd
->rawCerts
= NULL
;
543 * Combine all of the certs and cert chains into rawcerts.
544 * Note: certcount is an upper bound; we may not need that many slots
545 * but we will allocate anyway to avoid having to do another pass.
546 * (The temporary space saving is not worth it.)
548 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
549 * SetOfDERcertficates implementation
551 sigd
->rawCerts
= (CSSM_DATA_PTR
*)PORT_ArenaAlloc(poolp
, (certcount
+ 1) * sizeof(CSSM_DATA_PTR
));
552 if (sigd
->rawCerts
== NULL
)
556 * XXX Want to check for duplicates and not add *any* cert that is
557 * already in the set. This will be more important when we start
558 * dealing with larger sets of certs, dual-key certs (signing and
559 * encryption), etc. For the time being we can slide by...
561 * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
562 * SetOfDERcertficates implementation
565 if (signerinfos
!= NULL
) {
566 for (si
= 0; signerinfos
[si
] != NULL
; si
++) {
567 signerinfo
= signerinfos
[si
];
568 for (ci
= 0; ci
< CFArrayGetCount(signerinfo
->certList
); ci
++) {
569 sigd
->rawCerts
[rci
] = PORT_ArenaZAlloc(poolp
, sizeof(CSSM_DATA
));
570 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(signerinfo
->certList
, ci
);
571 SecCertificateGetData(cert
, sigd
->rawCerts
[rci
++]);
576 if (sigd
->certs
!= NULL
) {
577 for (ci
= 0; ci
< CFArrayGetCount(sigd
->certs
); ci
++) {
578 sigd
->rawCerts
[rci
] = PORT_ArenaZAlloc(poolp
, sizeof(CSSM_DATA
));
579 SecCertificateRef cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(sigd
->certs
, ci
);
580 SecCertificateGetData(cert
, sigd
->rawCerts
[rci
++]);
584 sigd
->rawCerts
[rci
] = NULL
;
586 /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
587 SecCmsArraySort((void **)sigd
->rawCerts
, SecCmsUtilDERCompare
, NULL
, NULL
);
594 dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret
, (long)rv
);
599 SecCmsSignedDataDecodeBeforeData(SecCmsSignedDataRef sigd
)
601 /* set up the digests */
602 if (sigd
->digestAlgorithms
!= NULL
&& sigd
->digests
== NULL
) {
603 /* if digests are already there, do nothing */
604 sigd
->contentInfo
.digcx
= SecCmsDigestContextStartMultiple(sigd
->digestAlgorithms
);
605 if (sigd
->contentInfo
.digcx
== NULL
)
612 * SecCmsSignedDataDecodeAfterData - do all the necessary things to a SignedData
613 * after all the encapsulated data was passed through the decoder.
616 SecCmsSignedDataDecodeAfterData(SecCmsSignedDataRef sigd
)
618 /* did we have digest calculation going on? */
619 if (sigd
->contentInfo
.digcx
) {
620 if (SecCmsDigestContextFinishMultiple(sigd
->contentInfo
.digcx
, (SecArenaPoolRef
)sigd
->cmsg
->poolp
, &(sigd
->digests
)) != SECSuccess
)
621 return SECFailure
; /* error has been set by SecCmsDigestContextFinishMultiple */
622 sigd
->contentInfo
.digcx
= NULL
;
628 * SecCmsSignedDataDecodeAfterEnd - do all the necessary things to a SignedData
629 * after all decoding is finished.
632 SecCmsSignedDataDecodeAfterEnd(SecCmsSignedDataRef sigd
)
634 SecCmsSignerInfoRef
*signerinfos
;
637 signerinfos
= sigd
->signerInfos
;
639 /* set cmsg and sigd backpointers for all the signerinfos */
641 for (i
= 0; signerinfos
[i
] != NULL
; i
++) {
642 signerinfos
[i
]->cmsg
= sigd
->cmsg
;
643 signerinfos
[i
]->sigd
= sigd
;
651 * SecCmsSignedDataGetSignerInfos - retrieve the SignedData's signer list
653 SecCmsSignerInfoRef
*
654 SecCmsSignedDataGetSignerInfos(SecCmsSignedDataRef sigd
)
656 return sigd
->signerInfos
;
660 SecCmsSignedDataSignerInfoCount(SecCmsSignedDataRef sigd
)
662 return SecCmsArrayCount((void **)sigd
->signerInfos
);
666 SecCmsSignedDataGetSignerInfo(SecCmsSignedDataRef sigd
, int i
)
668 return sigd
->signerInfos
[i
];
672 * SecCmsSignedDataGetDigestAlgs - retrieve the SignedData's digest algorithm list
675 SecCmsSignedDataGetDigestAlgs(SecCmsSignedDataRef sigd
)
677 return sigd
->digestAlgorithms
;
681 * SecCmsSignedDataGetContentInfo - return pointer to this signedData's contentinfo
684 SecCmsSignedDataGetContentInfo(SecCmsSignedDataRef sigd
)
686 return &(sigd
->contentInfo
);
690 * SecCmsSignedDataGetCertificateList - retrieve the SignedData's certificate list
693 SecCmsSignedDataGetCertificateList(SecCmsSignedDataRef sigd
)
695 return sigd
->rawCerts
;
699 SecCmsSignedDataImportCerts(SecCmsSignedDataRef sigd
, SecKeychainRef keychain
,
700 SECCertUsage certusage
, Boolean keepcerts
)
706 certcount
= SecCmsArrayCount((void **)sigd
->rawCerts
);
708 rv
= CERT_ImportCerts(keychain
, certusage
, certcount
, sigd
->rawCerts
, NULL
,
709 keepcerts
, PR_FALSE
, NULL
);
711 /* XXX CRL handling */
713 if (sigd
->signerInfos
!= NULL
) {
714 /* fill in all signerinfo's certs */
715 for (i
= 0; sigd
->signerInfos
[i
] != NULL
; i
++)
716 (void)SecCmsSignerInfoGetSigningCertificate(sigd
->signerInfos
[i
], keychain
);
723 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
724 * of external signatures!
728 * SecCmsSignedDataVerifySignerInfo - check the signatures.
730 * The digests were either calculated during decoding (and are stored in the
731 * signedData itself) or set after decoding using SecCmsSignedDataSetDigests.
733 * The verification checks if the signing cert is valid and has a trusted chain
734 * for the purpose specified by "policies".
736 * If trustRef is NULL the cert chain is verified and the VerificationStatus is set accordingly.
737 * Otherwise a SecTrust object is returned for the caller to evaluate using SecTrustEvaluate().
740 SecCmsSignedDataVerifySignerInfo(SecCmsSignedDataRef sigd
, int i
,
741 SecKeychainRef keychainOrArray
, CFTypeRef policies
, SecTrustRef
*trustRef
)
743 SecCmsSignerInfoRef signerinfo
;
744 SecCmsContentInfoRef cinfo
;
745 SECOidData
*algiddata
;
746 CSSM_DATA_PTR contentType
, digest
;
747 OSStatus status
, status2
;
749 cinfo
= &(sigd
->contentInfo
);
751 signerinfo
= sigd
->signerInfos
[i
];
753 /* Signature or digest level verificationStatus errors should supercede
754 certificate level errors, so check the digest and signature first. */
756 /* Find digest and contentType for signerinfo */
757 algiddata
= SecCmsSignerInfoGetDigestAlg(signerinfo
);
758 if (algiddata
== NULL
) {
759 return errSecInternalError
; // shouldn't have happened, this is likely due to corrupted data
762 digest
= SecCmsSignedDataGetDigestByAlgTag(sigd
, algiddata
->offset
);
765 * No digests; this probably had detached content the caller has to
767 * FIXME: need some error return for this (as well as many
768 * other places in this library).
770 return errSecDataNotAvailable
;
772 contentType
= SecCmsContentInfoGetContentTypeOID(cinfo
);
774 /* verify signature */
776 #warning STU: <rdar://21328501>
777 // timestamp policy is currently unsupported; use codesign policy only
779 syslog(LOG_ERR
, "SecCmsSignedDataVerifySignerInfo: using codesign policy without timestamp verification");
781 CFTypeRef timeStampPolicies
=SecPolicyCreateWithProperties(kSecPolicyAppleCodeSigning
, NULL
);
783 CFTypeRef timeStampPolicies
=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies
);
785 status
= SecCmsSignerInfoVerifyWithPolicy(signerinfo
, timeStampPolicies
, digest
, contentType
);
786 CFReleaseSafe(timeStampPolicies
);
788 /* Now verify the certificate. We do this even if the signature failed to verify so we can
789 return a trustRef to the caller for display purposes. */
790 status2
= SecCmsSignerInfoVerifyCertificate(signerinfo
, keychainOrArray
,
792 dprintf("SecCmsSignedDataVerifySignerInfo: status %d status2 %d\n", (int) status
, (int)status2
);
793 /* The error from SecCmsSignerInfoVerify() supercedes error from SecCmsSignerInfoVerifyCertificate(). */
801 * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message
804 SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd
,
805 SecKeychainRef keychainOrArray
,
808 SecCertificateRef cert
;
809 OSStatus rv
= SECSuccess
;
813 if (!sigd
|| !keychainOrArray
|| !sigd
->rawCerts
) {
814 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
818 count
= SecCmsArrayCount((void**)sigd
->rawCerts
);
819 for (i
=0; i
< count
; i
++) {
820 if (sigd
->certs
&& CFArrayGetCount(sigd
->certs
) > i
) {
821 cert
= (SecCertificateRef
)CFArrayGetValueAtIndex(sigd
->certs
, i
);
824 cert
= CERT_FindCertByDERCert(keychainOrArray
, sigd
->rawCerts
[i
]);
830 rv
|= CERT_VerifyCert(keychainOrArray
, cert
, sigd
->rawCerts
,
831 policies
, CFAbsoluteTimeGetCurrent(), NULL
);
839 * SecCmsSignedDataHasDigests - see if we have digests in place
842 SecCmsSignedDataHasDigests(SecCmsSignedDataRef sigd
)
844 return (sigd
->digests
!= NULL
);
848 SecCmsSignedDataAddCertList(SecCmsSignedDataRef sigd
, CFArrayRef certlist
)
850 PORT_Assert(certlist
!= NULL
);
852 if (certlist
== NULL
)
856 sigd
->certs
= CFArrayCreateMutableCopy(NULL
, 0, certlist
);
859 CFRange certlistRange
= { 0, CFArrayGetCount(certlist
) };
860 CFArrayAppendArray(sigd
->certs
, certlist
, certlistRange
);
867 * SecCmsSignedDataAddCertChain - add cert and its entire chain to the set of certs
870 SecCmsSignedDataAddCertChain(SecCmsSignedDataRef sigd
, SecCertificateRef cert
)
876 usage
= certUsageEmailSigner
;
878 /* do not include root */
879 certlist
= CERT_CertChainFromCert(cert
, usage
, PR_FALSE
);
880 if (certlist
== NULL
)
883 rv
= SecCmsSignedDataAddCertList(sigd
, certlist
);
890 SecCmsSignedDataAddCertificate(SecCmsSignedDataRef sigd
, SecCertificateRef cert
)
892 PORT_Assert(cert
!= NULL
);
898 sigd
->certs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
900 CFArrayAppendValue(sigd
->certs
, cert
);
906 SecCmsSignedDataContainsCertsOrCrls(SecCmsSignedDataRef sigd
)
908 if (sigd
->rawCerts
!= NULL
&& sigd
->rawCerts
[0] != NULL
)
910 else if (sigd
->rawCrls
!= NULL
&& sigd
->rawCrls
[0] != NULL
)
917 SecCmsSignedDataAddSignerInfo(SecCmsSignedDataRef sigd
,
918 SecCmsSignerInfoRef signerinfo
)
922 SECOidTag digestalgtag
;
925 poolp
= sigd
->cmsg
->poolp
;
927 mark
= PORT_ArenaMark(poolp
);
930 rv
= SecCmsArrayAdd(poolp
, (void ***)&(sigd
->signerInfos
), (void *)signerinfo
);
931 if (rv
!= SECSuccess
)
934 signerinfo
->sigd
= sigd
;
938 * Empty because we don't have it yet. Either it gets created during encoding
939 * (if the data is present) or has to be set externally.
940 * XXX maybe pass it in optionally?
942 digestalgtag
= SecCmsSignerInfoGetDigestAlgTag(signerinfo
);
943 rv
= SecCmsSignedDataSetDigestValue(sigd
, digestalgtag
, NULL
);
944 if (rv
!= SECSuccess
)
948 * The last thing to get consistency would be adding the digest.
951 PORT_ArenaUnmark(poolp
, mark
);
955 PORT_ArenaRelease (poolp
, mark
);
960 SecCmsSignedDataGetDigestByAlgTag(SecCmsSignedDataRef sigd
, SECOidTag algtag
)
964 if(sigd
->digests
== NULL
) {
967 idx
= SecCmsAlgArrayGetIndexByAlgTag(sigd
->digestAlgorithms
, algtag
);
968 return sigd
->digests
[idx
];
972 * SecCmsSignedDataSetDigests - set a signedData's digests member
974 * "digestalgs" - array of digest algorithm IDs
975 * "digests" - array of digests corresponding to the digest algorithms
978 SecCmsSignedDataSetDigests(SecCmsSignedDataRef sigd
,
979 SECAlgorithmID
**digestalgs
,
980 CSSM_DATA_PTR
*digests
)
984 if (sigd
->digestAlgorithms
== NULL
) {
985 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
989 /* we assume that the digests array is just not there yet */
990 PORT_Assert(sigd
->digests
== NULL
);
991 if (sigd
->digests
!= NULL
) {
992 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE
);
996 /* now allocate one (same size as digestAlgorithms) */
997 cnt
= SecCmsArrayCount((void **)sigd
->digestAlgorithms
);
998 sigd
->digests
= PORT_ArenaZAlloc(sigd
->cmsg
->poolp
, (cnt
+ 1) * sizeof(CSSM_DATA_PTR
));
999 if (sigd
->digests
== NULL
) {
1000 PORT_SetError(SEC_ERROR_NO_MEMORY
);
1004 for (i
= 0; sigd
->digestAlgorithms
[i
] != NULL
; i
++) {
1005 /* try to find the sigd's i'th digest algorithm in the array we passed in */
1006 idx
= SecCmsAlgArrayGetIndexByAlgID(digestalgs
, sigd
->digestAlgorithms
[i
]);
1008 PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND
);
1012 /* found it - now set it */
1013 if ((sigd
->digests
[i
] = SECITEM_AllocItem(sigd
->cmsg
->poolp
, NULL
, 0)) == NULL
||
1014 SECITEM_CopyItem(sigd
->cmsg
->poolp
, sigd
->digests
[i
], digests
[idx
]) != SECSuccess
)
1016 PORT_SetError(SEC_ERROR_NO_MEMORY
);
1024 SecCmsSignedDataSetDigestValue(SecCmsSignedDataRef sigd
,
1025 SECOidTag digestalgtag
,
1026 CSSM_DATA_PTR digestdata
)
1028 CSSM_DATA_PTR digest
= NULL
;
1033 poolp
= sigd
->cmsg
->poolp
;
1035 mark
= PORT_ArenaMark(poolp
);
1039 digest
= (CSSM_DATA_PTR
) PORT_ArenaZAlloc(poolp
,sizeof(CSSM_DATA
));
1041 /* copy digestdata item to arena (in case we have it and are not only making room) */
1042 if (SECITEM_CopyItem(poolp
, digest
, digestdata
) != SECSuccess
)
1046 /* now allocate one (same size as digestAlgorithms) */
1047 if (sigd
->digests
== NULL
) {
1048 cnt
= SecCmsArrayCount((void **)sigd
->digestAlgorithms
);
1049 sigd
->digests
= PORT_ArenaZAlloc(sigd
->cmsg
->poolp
, (cnt
+ 1) * sizeof(CSSM_DATA_PTR
));
1050 if (sigd
->digests
== NULL
) {
1051 PORT_SetError(SEC_ERROR_NO_MEMORY
);
1057 if (sigd
->digestAlgorithms
!= NULL
)
1058 n
= SecCmsAlgArrayGetIndexByAlgTag(sigd
->digestAlgorithms
, digestalgtag
);
1060 /* if not found, add a digest */
1062 if (SecCmsSignedDataAddDigest((SecArenaPoolRef
)poolp
, sigd
, digestalgtag
, digest
) != SECSuccess
)
1065 /* replace NULL pointer with digest item (and leak previous value) */
1066 sigd
->digests
[n
] = digest
;
1069 PORT_ArenaUnmark(poolp
, mark
);
1073 PORT_ArenaRelease(poolp
, mark
);
1078 SecCmsSignedDataAddDigest(SecArenaPoolRef pool
,
1079 SecCmsSignedDataRef sigd
,
1080 SECOidTag digestalgtag
,
1081 CSSM_DATA_PTR digest
)
1083 PRArenaPool
*poolp
= (PRArenaPool
*)pool
;
1084 SECAlgorithmID
*digestalg
;
1087 mark
= PORT_ArenaMark(poolp
);
1089 digestalg
= PORT_ArenaZAlloc(poolp
, sizeof(SECAlgorithmID
));
1090 if (digestalg
== NULL
)
1093 if (SECOID_SetAlgorithmID (poolp
, digestalg
, digestalgtag
, NULL
) != SECSuccess
) /* no params */
1096 if (SecCmsArrayAdd(poolp
, (void ***)&(sigd
->digestAlgorithms
), (void *)digestalg
) != SECSuccess
||
1097 /* even if digest is NULL, add dummy to have same-size array */
1098 SecCmsArrayAdd(poolp
, (void ***)&(sigd
->digests
), (void *)digest
) != SECSuccess
)
1103 PORT_ArenaUnmark(poolp
, mark
);
1107 PORT_ArenaRelease(poolp
, mark
);
1112 SecCmsSignedDataGetDigestValue(SecCmsSignedDataRef sigd
, SECOidTag digestalgtag
)
1116 if (sigd
->digestAlgorithms
== NULL
)
1119 n
= SecCmsAlgArrayGetIndexByAlgTag(sigd
->digestAlgorithms
, digestalgtag
);
1121 return (n
< 0) ? NULL
: sigd
->digests
[n
];
1124 /* =============================================================================
1125 * Misc. utility functions
1129 * SecCmsSignedDataCreateCertsOnly - create a certs-only SignedData.
1131 * cert - base certificates that will be included
1132 * include_chain - if true, include the complete cert chain for cert
1134 * More certs and chains can be added via AddCertificate and AddCertChain.
1136 * An error results in a return value of NULL and an error set.
1141 SecCmsSignedDataCreateCertsOnly(SecCmsMessageRef cmsg
, SecCertificateRef cert
, Boolean include_chain
)
1143 SecCmsSignedDataRef sigd
;
1148 poolp
= cmsg
->poolp
;
1149 mark
= PORT_ArenaMark(poolp
);
1151 sigd
= SecCmsSignedDataCreate(cmsg
);
1155 /* no signerinfos, thus no digestAlgorithms */
1158 if (include_chain
) {
1159 rv
= SecCmsSignedDataAddCertChain(sigd
, cert
);
1161 rv
= SecCmsSignedDataAddCertificate(sigd
, cert
);
1163 if (rv
!= SECSuccess
)
1167 * In the degenerate case where there are no signers, the
1168 * EncapsulatedContentInfo value being "signed" is irrelevant. In this
1169 * case, the content type within the EncapsulatedContentInfo value being
1170 * "signed" should be id-data (as defined in section 4), and the content
1171 * field of the EncapsulatedContentInfo value should be omitted.
1173 rv
= SecCmsContentInfoSetContentData(cmsg
, &(sigd
->contentInfo
), NULL
, PR_TRUE
);
1174 if (rv
!= SECSuccess
)
1177 PORT_ArenaUnmark(poolp
, mark
);
1182 SecCmsSignedDataDestroy(sigd
);
1183 PORT_ArenaRelease(poolp
, mark
);
1188 * Get SecCmsSignedDataRawCerts - obtain raw certs as a NULL_terminated array
1191 extern OSStatus
SecCmsSignedDataRawCerts(SecCmsSignedDataRef sigd
,
1192 CSSM_DATA_PTR
**rawCerts
)
1194 *rawCerts
= sigd
->rawCerts
;
1199 * SecCmsSignerInfoGetReceiptRequest()
1200 * SecCmsSignedDataHasReceiptRequest()
1201 * easy way to iterate over signers