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