]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmssiginfo.c
Security-57336.10.29.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmssiginfo.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 signerInfo methods.
36 */
37
38 #include <Security/SecCmsSignerInfo.h>
39 #include "SecSMIMEPriv.h"
40
41 #include "cmslocal.h"
42
43 #include "cert.h"
44 #include "SecAsn1Item.h"
45 #include "secoid.h"
46 #include "cryptohi.h"
47
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <security_asn1/secport.h>
51
52 #if USE_CDSA_CRYPTO
53 #include <Security/SecKeychain.h>
54 #endif
55
56 #include <Security/SecIdentity.h>
57 #include <Security/SecCertificateInternal.h>
58 #include <Security/SecInternal.h>
59 #include <Security/SecKeyPriv.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <CoreFoundation/CFTimeZone.h>
62
63
64 #define HIDIGIT(v) (((v) / 10) + '0')
65 #define LODIGIT(v) (((v) % 10) + '0')
66
67 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
68 #define CAPTURE(var,p,label) \
69 { \
70 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
71 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
72 }
73
74
75 static OSStatus
76 DER_UTCTimeToCFDate(const SecAsn1Item * utcTime, CFAbsoluteTime *date)
77 {
78 char *string = (char *)utcTime->Data;
79 int year, month, mday, hour, minute, second, hourOff, minOff;
80
81 /* Verify time is formatted properly and capture information */
82 second = 0;
83 hourOff = 0;
84 minOff = 0;
85 CAPTURE(year,string+0,loser);
86 if (year < 50) {
87 /* ASSUME that year # is in the 2000's, not the 1900's */
88 year += 2000;
89 } else {
90 year += 1900;
91 }
92 CAPTURE(month,string+2,loser);
93 if ((month == 0) || (month > 12)) goto loser;
94 CAPTURE(mday,string+4,loser);
95 if ((mday == 0) || (mday > 31)) goto loser;
96 CAPTURE(hour,string+6,loser);
97 if (hour > 23) goto loser;
98 CAPTURE(minute,string+8,loser);
99 if (minute > 59) goto loser;
100 if (ISDIGIT(string[10])) {
101 CAPTURE(second,string+10,loser);
102 if (second > 59) goto loser;
103 string += 2;
104 }
105 if (string[10] == '+') {
106 CAPTURE(hourOff,string+11,loser);
107 if (hourOff > 23) goto loser;
108 CAPTURE(minOff,string+13,loser);
109 if (minOff > 59) goto loser;
110 } else if (string[10] == '-') {
111 CAPTURE(hourOff,string+11,loser);
112 if (hourOff > 23) goto loser;
113 hourOff = -hourOff;
114 CAPTURE(minOff,string+13,loser);
115 if (minOff > 59) goto loser;
116 minOff = -minOff;
117 } else if (string[10] != 'Z') {
118 goto loser;
119 }
120
121 if (hourOff == 0 && minOff == 0) {
122 *date = CFAbsoluteTimeForGregorianZuluMoment(year, month, mday, hour, minute, second);
123 } else {
124 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, (hourOff * 60 + minOff) * 60);
125 *date = CFAbsoluteTimeForGregorianMoment(tz, year, month, mday, hour, minute, second);
126 CFReleaseSafe(tz);
127 }
128
129 return SECSuccess;
130
131 loser:
132 return SECFailure;
133 }
134
135 static OSStatus
136 DER_CFDateToUTCTime(CFAbsoluteTime date, SecAsn1Item * utcTime)
137 {
138 unsigned char *d;
139
140 utcTime->Length = 13;
141 utcTime->Data = d = PORT_Alloc(13);
142 if (!utcTime->Data)
143 return SECFailure;
144
145 __block int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
146 __block bool result;
147 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) {
148 result = CFCalendarDecomposeAbsoluteTime(zuluCalendar, date, "yMdHms", &year, &month, &day, &hour, &minute, &second);
149 });
150 if (!result)
151 return SECFailure;
152
153 /* UTC time does not handle the years before 1950 */
154 if (year < 1950)
155 return SECFailure;
156
157 /* remove the century since it's added to the year by the
158 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
159 year %= 100;
160
161 d[0] = HIDIGIT(year);
162 d[1] = LODIGIT(year);
163 d[2] = HIDIGIT(month);
164 d[3] = LODIGIT(month);
165 d[4] = HIDIGIT(day);
166 d[5] = LODIGIT(day);
167 d[6] = HIDIGIT(hour);
168 d[7] = LODIGIT(hour);
169 d[8] = HIDIGIT(minute);
170 d[9] = LODIGIT(minute);
171 d[10] = HIDIGIT(second);
172 d[11] = LODIGIT(second);
173 d[12] = 'Z';
174 return SECSuccess;
175 }
176
177 /* =============================================================================
178 * SIGNERINFO
179 */
180 SecCmsSignerInfoRef
181 nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
182
183 SecCmsSignerInfoRef
184 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsSignedDataRef sigd, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
185 {
186 return nss_cmssignerinfo_create(sigd, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag);
187 }
188
189 SecCmsSignerInfoRef
190 SecCmsSignerInfoCreate(SecCmsSignedDataRef sigd, SecIdentityRef identity, SECOidTag digestalgtag)
191 {
192 SecCmsSignerInfoRef signerInfo = NULL;
193 SecCertificateRef cert = NULL;
194 SecPrivateKeyRef signingKey = NULL;
195
196 if (SecIdentityCopyCertificate(identity, &cert))
197 goto loser;
198 if (SecIdentityCopyPrivateKey(identity, &signingKey))
199 goto loser;
200
201 signerInfo = nss_cmssignerinfo_create(sigd, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
202
203 loser:
204 if (cert)
205 CFRelease(cert);
206 if (signingKey)
207 CFRelease(signingKey);
208
209 return signerInfo;
210 }
211
212 SecCmsSignerInfoRef
213 nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
214 {
215 void *mark;
216 SecCmsSignerInfoRef signerinfo;
217 int version;
218 PLArenaPool *poolp;
219
220 poolp = sigd->contentInfo.cmsg->poolp;
221
222 mark = PORT_ArenaMark(poolp);
223
224 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo));
225 if (signerinfo == NULL) {
226 PORT_ArenaRelease(poolp, mark);
227 return NULL;
228 }
229
230
231 signerinfo->signedData = sigd;
232
233 switch(type) {
234 case SecCmsSignerIDIssuerSN:
235 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN;
236 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
237 goto loser;
238 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
239 goto loser;
240 break;
241 case SecCmsSignerIDSubjectKeyID:
242 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID;
243 PORT_Assert(subjKeyID);
244 if (!subjKeyID)
245 goto loser;
246 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
247 SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
248 subjKeyID);
249 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
250 if (!signerinfo->pubKey)
251 goto loser;
252 break;
253 default:
254 goto loser;
255 }
256
257 if (!signingKey)
258 goto loser;
259
260 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
261 if (!signerinfo->signingKey)
262 goto loser;
263
264 /* set version right now */
265 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN;
266 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
267 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)
268 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY;
269 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
270
271 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
272 goto loser;
273
274 if (SecCmsSignedDataAddSignerInfo(sigd, signerinfo))
275 goto loser;
276
277 PORT_ArenaUnmark(poolp, mark);
278 return signerinfo;
279
280 loser:
281 PORT_ArenaRelease(poolp, mark);
282 return NULL;
283 }
284
285 /*
286 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
287 */
288 void
289 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
290 {
291 if (si->cert != NULL)
292 CERT_DestroyCertificate(si->cert);
293
294 if (si->certList != NULL)
295 CFRelease(si->certList);
296
297 /* XXX storage ??? */
298 }
299
300 static SecAsn1AlgId SecCertificateGetPublicKeyAlgorithmID(SecCertificateRef cert)
301 {
302 const DERAlgorithmId *length_data_swapped = SecCertificateGetPublicKeyAlgorithm(cert);
303 SecAsn1AlgId temp = {
304 { length_data_swapped->oid.length, length_data_swapped->oid.data },
305 { length_data_swapped->params.length, length_data_swapped->params.data } };
306
307 return temp;
308 }
309
310 /*
311 * SecCmsSignerInfoSign - sign something
312 *
313 */
314 OSStatus
315 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
316 {
317 SecCertificateRef cert;
318 SecPrivateKeyRef privkey = NULL;
319 SECOidTag digestalgtag;
320 SECOidTag pubkAlgTag;
321 SecAsn1Item signature = { 0 };
322 OSStatus rv;
323 PLArenaPool *poolp, *tmppoolp = NULL;
324 const SECAlgorithmID *algID = NULL;
325 //CERTSubjectPublicKeyInfo *spki;
326
327 PORT_Assert (digest != NULL);
328
329 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
330
331 switch (signerinfo->signerIdentifier.identifierType) {
332 case SecCmsSignerIDIssuerSN:
333 privkey = signerinfo->signingKey;
334 signerinfo->signingKey = NULL;
335 cert = signerinfo->cert;
336 #if USE_CDSA_CRYPTO
337 if (SecCertificateGetAlgorithmID(cert,&algID)) {
338 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
339 goto loser;
340 }
341 #else
342 SecAsn1AlgId _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
343 algID = &_algID;
344 #endif
345 break;
346 case SecCmsSignerIDSubjectKeyID:
347 privkey = signerinfo->signingKey;
348 signerinfo->signingKey = NULL;
349 #if 0
350 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
351 SECKEY_DestroyPublicKey(signerinfo->pubKey);
352 signerinfo->pubKey = NULL;
353 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
354 SECKEY_DestroySubjectPublicKeyInfo(spki);
355 algID = &freeAlgID;
356 #else
357 #if USE_CDSA_CRYPTO
358 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) {
359 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
360 goto loser;
361 }
362 #endif
363 #endif
364 CFRelease(signerinfo->pubKey);
365 signerinfo->pubKey = NULL;
366 break;
367 default:
368 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
369 goto loser;
370 }
371 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
372 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
373 #if USE_CDSA_CRYPTO
374 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
375 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
376 }
377 #endif
378 #if 0
379 // @@@ Not yet
380 /* Fortezza MISSI have weird signature formats.
381 * Map them to standard DSA formats
382 */
383 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
384 #endif
385
386 if (signerinfo->authAttr != NULL) {
387 SecAsn1Item encoded_attrs;
388
389 /* find and fill in the message digest attribute. */
390 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
391 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
392 if (rv != SECSuccess)
393 goto loser;
394
395 if (contentType != NULL) {
396 /* if the caller wants us to, find and fill in the content type attribute. */
397 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
398 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
399 if (rv != SECSuccess)
400 goto loser;
401 }
402
403 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
404 PORT_SetError(SEC_ERROR_NO_MEMORY);
405 goto loser;
406 }
407
408 /*
409 * Before encoding, reorder the attributes so that when they
410 * are encoded, they will be conforming DER, which is required
411 * to have a specific order and that is what must be used for
412 * the hash/signature. We do this here, rather than building
413 * it into EncodeAttributes, because we do not want to do
414 * such reordering on incoming messages (which also uses
415 * EncodeAttributes) or our old signatures (and other "broken"
416 * implementations) will not verify. So, we want to guarantee
417 * that we send out good DER encodings of attributes, but not
418 * to expect to receive them.
419 */
420 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
421 goto loser;
422
423 encoded_attrs.Data = NULL;
424 encoded_attrs.Length = 0;
425 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
426 &encoded_attrs) == NULL)
427 goto loser;
428
429 #if USE_CDSA_CRYPTO
430 rv = SEC_SignData(&signature, encoded_attrs.Data, encoded_attrs.Length,
431 privkey, digestalgtag, pubkAlgTag);
432 #else
433 signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
434 signature.Data = PORT_ZAlloc(signature.Length);
435 if (!signature.Data) {
436 signature.Length = 0;
437 goto loser;
438 }
439 rv = SecKeyDigestAndSign(privkey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signature.Data, &signature.Length);
440 if (rv) {
441 PORT_ZFree(signature.Data, signature.Length);
442 signature.Length = 0;
443 }
444 #endif
445
446 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
447 tmppoolp = 0;
448 } else {
449 signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
450 signature.Data = PORT_ZAlloc(signature.Length);
451 if (!signature.Data) {
452 signature.Length = 0;
453 goto loser;
454 }
455 rv = SecKeySignDigest(privkey, &signerinfo->digestAlg, digest->Data, digest->Length,
456 signature.Data, &signature.Length);
457 if (rv) {
458 PORT_ZFree(signature.Data, signature.Length);
459 signature.Length = 0;
460 }
461 }
462 SECKEY_DestroyPrivateKey(privkey);
463 privkey = NULL;
464
465 if (rv != SECSuccess)
466 goto loser;
467
468 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
469 != SECSuccess)
470 goto loser;
471
472 SECITEM_FreeItem(&signature, PR_FALSE);
473
474 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag,
475 NULL) != SECSuccess)
476 goto loser;
477
478 return SECSuccess;
479
480 loser:
481 if (signature.Length != 0)
482 SECITEM_FreeItem (&signature, PR_FALSE);
483 if (privkey)
484 SECKEY_DestroyPrivateKey(privkey);
485 if (tmppoolp)
486 PORT_FreeArena(tmppoolp, PR_FALSE);
487 return SECFailure;
488 }
489
490 #if !USE_CDSA_CRYPTO
491 static CFArrayRef
492 SecCmsSignerInfoCopySigningCertificates(SecCmsSignerInfoRef signerinfo)
493 {
494 CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
495 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
496 SecAsn1Item *cert_data;
497 if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
498 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
499 if (cert) {
500 switch (signerinfo->signerIdentifier.identifierType) {
501 case SecCmsSignerIDIssuerSN:
502 if (CERT_CheckIssuerAndSerial(cert,
503 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
504 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
505 CFArrayInsertValueAtIndex(certs, 0, cert);
506 else
507 CFArrayAppendValue(certs, cert);
508 break;
509 case SecCmsSignerIDSubjectKeyID:
510 {
511 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
512 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
513 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
514 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
515 CFArrayInsertValueAtIndex(certs, 0, cert);
516 else
517 CFArrayAppendValue(certs, cert);
518 break;
519 }
520 }
521 CFReleaseNull(cert);
522 }
523 cert_datas++;
524 }
525
526 if ((CFArrayGetCount(certs) == 0) &&
527 (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN))
528 {
529 SecCertificateRef cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
530 if (cert) {
531 CFArrayAppendValue(certs, cert);
532 CFRelease(cert);
533 }
534 }
535 return certs;
536 }
537 #endif
538
539 OSStatus
540 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
541 CFTypeRef policies, SecTrustRef *trustRef)
542 {
543 CFAbsoluteTime stime;
544 OSStatus rv;
545
546 #if USE_CDSA_CRYPTO
547 SecCertificateRef cert;
548
549 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
550 #else
551 CFArrayRef certs;
552
553 if ((certs = SecCmsSignerInfoCopySigningCertificates(signerinfo)) == NULL) {
554 #endif
555 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
556 return SECFailure;
557 }
558 /*
559 * Get and convert the signing time; if available, it will be used
560 * both on the cert verification and for importing the sender
561 * email profile.
562 */
563 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
564 stime = CFAbsoluteTimeGetCurrent();
565
566 #if USE_CDSA_CRYPTO
567 rv = CERT_VerifyCert(keychainOrArray, cert, policies, stime, trustRef);
568 #else
569 rv = CERT_VerifyCert(keychainOrArray, certs, policies, stime, trustRef);
570 CFRelease(certs);
571 #endif
572 if (rv || !trustRef)
573 {
574 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
575 {
576 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
577 #if 0
578 #warning DEBUG - SecCmsSignerInfoVerifyCertificate trusts everything!
579 if (signerinfo->verificationStatus == SecCmsVSGoodSignature) {
580 syslog(LOG_ERR, "SecCmsSignerInfoVerifyCertificate ignoring SEC_ERROR_UNTRUSTED_CERT");
581 rv = SECSuccess;
582 }
583 #else
584 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
585 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
586 #endif
587 }
588 }
589
590 return rv;
591 }
592
593 /*
594 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
595 *
596 * Just verifies the signature. The assumption is that verification of the certificate
597 * is done already.
598 */
599 OSStatus
600 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
601 {
602 SecPublicKeyRef publickey = NULL;
603 SecCmsAttribute *attr;
604 SecAsn1Item encoded_attrs;
605 SecCertificateRef cert;
606 SecCmsVerificationStatus vs = SecCmsVSUnverified;
607 PLArenaPool *poolp;
608 SECOidTag digestAlgTag, digestEncAlgTag;
609
610 if (signerinfo == NULL)
611 return SECFailure;
612
613 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
614 /* cert has not been verified */
615 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
616 vs = SecCmsVSSigningCertNotFound;
617 goto loser;
618 }
619
620 #if USE_CDSA_CRYPTO
621 if (SecCertificateCopyPublicKey(cert, &publickey)) {
622 vs = SecCmsVSProcessingError;
623 goto loser;
624 }
625 #else
626 publickey = SecCertificateCopyPublicKey(cert);
627 if (publickey == NULL)
628 goto loser;
629 #endif
630
631 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
632 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
633 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
634 if (contentType) {
635 /*
636 * Check content type
637 *
638 * RFC2630 sez that if there are any authenticated attributes,
639 * then there must be one for content type which matches the
640 * content type of the content being signed, and there must
641 * be one for message digest which matches our message digest.
642 * So check these things first.
643 */
644 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
645 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
646 {
647 vs = SecCmsVSMalformedSignature;
648 goto loser;
649 }
650
651 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
652 vs = SecCmsVSMalformedSignature;
653 goto loser;
654 }
655 }
656
657 /*
658 * Check digest
659 */
660 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
661 {
662 vs = SecCmsVSMalformedSignature;
663 goto loser;
664 }
665 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
666 vs = SecCmsVSDigestMismatch;
667 goto loser;
668 }
669
670 if ((poolp = PORT_NewArena (1024)) == NULL) {
671 vs = SecCmsVSProcessingError;
672 goto loser;
673 }
674
675 /*
676 * Check signature
677 *
678 * The signature is based on a digest of the DER-encoded authenticated
679 * attributes. So, first we encode and then we digest/verify.
680 * we trust the decoder to have the attributes in the right (sorted) order
681 */
682 encoded_attrs.Data = NULL;
683 encoded_attrs.Length = 0;
684
685 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
686 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
687 {
688 vs = SecCmsVSProcessingError;
689 goto loser;
690 }
691 if (errSecSuccess == SecKeyDigestAndVerify(publickey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signerinfo->encDigest.Data, signerinfo->encDigest.Length))
692 vs = SecCmsVSGoodSignature;
693 else
694 vs = SecCmsVSBadSignature;
695
696 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
697
698 } else {
699 SecAsn1Item * sig;
700
701 /* No authenticated attributes. The signature is based on the plain message digest. */
702 sig = &(signerinfo->encDigest);
703 if (sig->Length == 0)
704 goto loser;
705
706 if (SecKeyVerifyDigest(publickey, &signerinfo->digestAlg, digest->Data, digest->Length, sig->Data, sig->Length))
707 vs = SecCmsVSBadSignature;
708 else
709 vs = SecCmsVSGoodSignature;
710 }
711
712 if (vs == SecCmsVSBadSignature) {
713 /*
714 * XXX Change the generic error into our specific one, because
715 * in that case we get a better explanation out of the Security
716 * Advisor. This is really a bug in our error strings (the
717 * "generic" error has a lousy/wrong message associated with it
718 * which assumes the signature verification was done for the
719 * purposes of checking the issuer signature on a certificate)
720 * but this is at least an easy workaround and/or in the
721 * Security Advisor, which specifically checks for the error
722 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
723 * in that case but does not similarly check for
724 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
725 * probably say the wrong thing in the case that it *was* the
726 * certificate signature check that failed during the cert
727 * verification done above. Our error handling is really a mess.
728 */
729 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
730 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
731 }
732
733 if (publickey != NULL)
734 CFRelease(publickey);
735
736 signerinfo->verificationStatus = vs;
737
738 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
739
740 loser:
741 if (publickey != NULL)
742 SECKEY_DestroyPublicKey (publickey);
743
744 signerinfo->verificationStatus = vs;
745
746 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
747 return SECFailure;
748 }
749
750 SecCmsVerificationStatus
751 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
752 {
753 return signerinfo->verificationStatus;
754 }
755
756 SECOidData *
757 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
758 {
759 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
760 }
761
762 SECOidTag
763 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
764 {
765 SECOidData *algdata;
766
767 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
768 if (algdata != NULL)
769 return algdata->offset;
770 else
771 return SEC_OID_UNKNOWN;
772 }
773
774 CFArrayRef
775 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
776 {
777 return signerinfo->certList;
778 }
779
780 int
781 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
782 {
783 unsigned long version;
784
785 /* always take apart the SecAsn1Item */
786 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
787 return 0;
788 else
789 return (int)version;
790 }
791
792 /*
793 * SecCmsSignerInfoGetSigningTime - return the signing time,
794 * in UTCTime format, of a CMS signerInfo.
795 *
796 * sinfo - signerInfo data for this signer
797 *
798 * Returns a pointer to XXXX (what?)
799 * A return value of NULL is an error.
800 */
801 OSStatus
802 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
803 {
804 SecCmsAttribute *attr;
805 SecAsn1Item * value;
806
807 if (sinfo == NULL)
808 return SECFailure;
809
810 if (sinfo->signingTime != 0) {
811 *stime = sinfo->signingTime; /* cached copy */
812 return SECSuccess;
813 }
814
815 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
816 /* XXXX multi-valued attributes NIH */
817 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
818 return SECFailure;
819 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
820 return SECFailure;
821 sinfo->signingTime = *stime; /* make cached copy */
822 return SECSuccess;
823 }
824
825 /*
826 * Return the signing cert of a CMS signerInfo.
827 *
828 * the certs in the enclosing SignedData must have been imported already
829 */
830 SecCertificateRef
831 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
832 {
833 SecCertificateRef cert = NULL;
834
835 if (signerinfo->cert != NULL)
836 return signerinfo->cert;
837
838 /* @@@ Make sure we search though all the certs in the cms message itself as well, it's silly
839 to require them to be added to a keychain first. */
840
841 #if USE_CDSA_CRYPTO
842 SecCmsSignerIdentifier *sid;
843
844 /*
845 * This cert will also need to be freed, but since we save it
846 * in signerinfo for later, we do not want to destroy it when
847 * we leave this function -- we let the clean-up of the entire
848 * cinfo structure later do the destroy of this cert.
849 */
850 sid = &signerinfo->signerIdentifier;
851 switch (sid->identifierType) {
852 case SecCmsSignerIDIssuerSN:
853 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, sid->id.issuerAndSN);
854 break;
855 case SecCmsSignerIDSubjectKeyID:
856 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, sid->id.subjectKeyID);
857 break;
858 default:
859 cert = NULL;
860 break;
861 }
862
863 /* cert can be NULL at that point */
864 signerinfo->cert = cert; /* earmark it */
865 #else
866 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
867 SecAsn1Item *cert_data;
868 if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
869 cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
870 if (cert) {
871 switch (signerinfo->signerIdentifier.identifierType) {
872 case SecCmsSignerIDIssuerSN:
873 if (CERT_CheckIssuerAndSerial(cert,
874 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
875 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
876 signerinfo->cert = cert;
877 break;
878 case SecCmsSignerIDSubjectKeyID: {
879 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
880 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
881 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
882 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
883 signerinfo->cert = cert;
884 }
885 }
886 if (signerinfo->cert)
887 break;
888 CFReleaseNull(cert);
889 }
890 cert_datas++;
891 }
892
893 if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN)) {
894 cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
895 signerinfo->cert = cert;
896 }
897 #endif
898
899 return cert;
900 }
901
902
903 /*
904 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
905 *
906 * sinfo - signerInfo data for this signer
907 *
908 * Returns a CFStringRef containing the common name of the signer.
909 * A return value of NULL is an error.
910 */
911 CFStringRef
912 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
913 {
914 SecCertificateRef signercert;
915 CFStringRef commonName = NULL;
916
917 /* will fail if cert is not verified */
918 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
919 return NULL;
920
921 #if USE_CDSA_CRYPTO
922 SecCertificateGetCommonName(signercert, &commonName);
923 #else
924 CFArrayRef commonNames = SecCertificateCopyCommonNames(signercert);
925 if (commonNames) {
926 /* SecCertificateCopyCommonNames doesn't return empty arrays */
927 commonName = (CFStringRef)CFArrayGetValueAtIndex(commonNames, CFArrayGetCount(commonNames) - 1);
928 CFRetain(commonName);
929 CFRelease(commonNames);
930 }
931 #endif
932
933 return commonName;
934 }
935
936 /*
937 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
938 *
939 * sinfo - signerInfo data for this signer
940 *
941 * Returns a CFStringRef containing the name of the signer.
942 * A return value of NULL is an error.
943 */
944 CFStringRef
945 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
946 {
947 SecCertificateRef signercert;
948 CFStringRef emailAddress = NULL;
949
950 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
951 return NULL;
952
953 #if USE_CDSA_CRYPTO
954 SecCertificateGetEmailAddress(signercert, &emailAddress);
955 #else
956 CFArrayRef names = SecCertificateCopyRFC822Names(signercert);
957 if (names) {
958 if (CFArrayGetCount(names) > 0)
959 emailAddress = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
960 if (emailAddress)
961 CFRetain(emailAddress);
962 CFRelease(names);
963 }
964 #endif
965 return emailAddress;
966 }
967
968
969 /*
970 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
971 * authenticated (i.e. signed) attributes of "signerinfo".
972 */
973 OSStatus
974 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
975 {
976 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->authAttr), attr);
977 }
978
979 /*
980 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
981 * unauthenticated attributes of "signerinfo".
982 */
983 OSStatus
984 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
985 {
986 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->unAuthAttr), attr);
987 }
988
989 /*
990 * SecCmsSignerInfoAddSigningTime - add the signing time to the
991 * authenticated (i.e. signed) attributes of "signerinfo".
992 *
993 * This is expected to be included in outgoing signed
994 * messages for email (S/MIME) but is likely useful in other situations.
995 *
996 * This should only be added once; a second call will do nothing.
997 *
998 * XXX This will probably just shove the current time into "signerinfo"
999 * but it will not actually get signed until the entire item is
1000 * processed for encoding. Is this (expected to be small) delay okay?
1001 */
1002 OSStatus
1003 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1004 {
1005 SecCmsAttribute *attr;
1006 SecAsn1Item stime;
1007 void *mark;
1008 PLArenaPool *poolp;
1009
1010 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1011
1012 mark = PORT_ArenaMark(poolp);
1013
1014 /* create new signing time attribute */
1015 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1016 goto loser;
1017
1018 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1019 SECITEM_FreeItem (&stime, PR_FALSE);
1020 goto loser;
1021 }
1022
1023 SECITEM_FreeItem (&stime, PR_FALSE);
1024
1025 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1026 goto loser;
1027
1028 PORT_ArenaUnmark (poolp, mark);
1029
1030 return SECSuccess;
1031
1032 loser:
1033 PORT_ArenaRelease (poolp, mark);
1034 return SECFailure;
1035 }
1036
1037 /*
1038 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1039 * authenticated (i.e. signed) attributes of "signerinfo".
1040 *
1041 * This is expected to be included in outgoing signed
1042 * messages for email (S/MIME).
1043 */
1044 OSStatus
1045 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1046 {
1047 SecCmsAttribute *attr;
1048 SecAsn1Item * smimecaps = NULL;
1049 void *mark;
1050 PLArenaPool *poolp;
1051
1052 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1053
1054 mark = PORT_ArenaMark(poolp);
1055
1056 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1057 if (smimecaps == NULL)
1058 goto loser;
1059
1060 /* create new signing time attribute */
1061 #if 1
1062 // @@@ We don't do Fortezza yet.
1063 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, PR_FALSE) != SECSuccess)
1064 #else
1065 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1066 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1067 #endif
1068 goto loser;
1069
1070 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1071 goto loser;
1072
1073 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1074 goto loser;
1075
1076 PORT_ArenaUnmark (poolp, mark);
1077 return SECSuccess;
1078
1079 loser:
1080 PORT_ArenaRelease (poolp, mark);
1081 return SECFailure;
1082 }
1083
1084 /*
1085 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1086 * authenticated (i.e. signed) attributes of "signerinfo".
1087 *
1088 * This is expected to be included in outgoing signed messages for email (S/MIME).
1089 */
1090 OSStatus
1091 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1092 {
1093 SecCmsAttribute *attr;
1094 SecAsn1Item * smimeekp = NULL;
1095 void *mark;
1096 PLArenaPool *poolp;
1097
1098 #if 0
1099 CFTypeRef policy;
1100
1101 /* verify this cert for encryption */
1102 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1103 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1104 CFRelease(policy);
1105 return SECFailure;
1106 }
1107 CFRelease(policy);
1108 #endif
1109
1110 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1111 mark = PORT_ArenaMark(poolp);
1112
1113 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1114 if (smimeekp == NULL)
1115 goto loser;
1116
1117 /* create new signing time attribute */
1118 if (SecSMIMECreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
1119 goto loser;
1120
1121 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1122 goto loser;
1123
1124 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1125 goto loser;
1126
1127 PORT_ArenaUnmark (poolp, mark);
1128 return SECSuccess;
1129
1130 loser:
1131 PORT_ArenaRelease (poolp, mark);
1132 return SECFailure;
1133 }
1134
1135 /*
1136 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1137 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
1138 *
1139 * This is expected to be included in outgoing signed messages for email (S/MIME),
1140 * if compatibility with Microsoft mail clients is wanted.
1141 */
1142 OSStatus
1143 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1144 {
1145 SecCmsAttribute *attr;
1146 SecAsn1Item * smimeekp = NULL;
1147 void *mark;
1148 PLArenaPool *poolp;
1149
1150 #if 0
1151 CFTypeRef policy;
1152
1153 /* verify this cert for encryption */
1154 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1155 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1156 CFRelease(policy);
1157 return SECFailure;
1158 }
1159 CFRelease(policy);
1160 #endif
1161
1162 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1163 mark = PORT_ArenaMark(poolp);
1164
1165 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1166 if (smimeekp == NULL)
1167 goto loser;
1168
1169 /* create new signing time attribute */
1170 if (SecSMIMECreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
1171 goto loser;
1172
1173 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1174 goto loser;
1175
1176 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1177 goto loser;
1178
1179 PORT_ArenaUnmark (poolp, mark);
1180 return SECSuccess;
1181
1182 loser:
1183 PORT_ArenaRelease (poolp, mark);
1184 return SECFailure;
1185 }
1186
1187 /*
1188 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1189 *
1190 * 1. digest the DER-encoded signature value of the original signerinfo
1191 * 2. create new signerinfo with correct version, sid, digestAlg
1192 * 3. add message-digest authAttr, but NO content-type
1193 * 4. sign the authAttrs
1194 * 5. DER-encode the new signerInfo
1195 * 6. add the whole thing to original signerInfo's unAuthAttrs
1196 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1197 *
1198 * XXXX give back the new signerinfo?
1199 */
1200 OSStatus
1201 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1202 SECOidTag digestalg, SecIdentityRef identity)
1203 {
1204 /* XXXX TBD XXXX */
1205 return SECFailure;
1206 }
1207
1208 /*
1209 * XXXX the following needs to be done in the S/MIME layer code
1210 * after signature of a signerinfo is verified
1211 */
1212 OSStatus
1213 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1214 {
1215 return -4 /*unImp*/;
1216 }
1217
1218 /*
1219 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1220 */
1221 OSStatus
1222 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1223 {
1224 if (signerinfo->cert == NULL)
1225 return SECFailure;
1226
1227 /* don't leak if we get called twice */
1228 if (signerinfo->certList != NULL) {
1229 CFRelease(signerinfo->certList);
1230 signerinfo->certList = NULL;
1231 }
1232
1233 switch (cm) {
1234 case SecCmsCMNone:
1235 signerinfo->certList = NULL;
1236 break;
1237 case SecCmsCMCertOnly:
1238 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1239 break;
1240 case SecCmsCMCertChain:
1241 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1242 break;
1243 case SecCmsCMCertChainWithRoot:
1244 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1245 break;
1246 }
1247
1248 if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1249 return SECFailure;
1250
1251 return SECSuccess;
1252 }