]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmssiginfo.c
Security-58286.51.6.tar.gz
[apple/security.git] / OSX / 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 "secitem.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/SecKeychain.h>
51 #include <Security/SecIdentity.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecKeyPriv.h>
54 #include <CoreFoundation/CFTimeZone.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <AssertMacros.h>
57 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
58 #include <Security/SecPolicyPriv.h>
59 #include <Security/SecItem.h>
60
61 #include "tsaSupport.h"
62 #include "tsaSupportPriv.h"
63
64 #include <syslog.h>
65
66 #define HIDIGIT(v) (((v) / 10) + '0')
67 #define LODIGIT(v) (((v) % 10) + '0')
68
69 #define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
70 #define CAPTURE(var,p,label) \
71 { \
72 if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
73 (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0'); \
74 }
75
76 #ifndef NDEBUG
77 #define SIGINFO_DEBUG 1
78 #endif
79
80 #if SIGINFO_DEBUG
81 #define dprintf(args...) fprintf(stderr, args)
82 #else
83 #define dprintf(args...)
84 #endif
85
86 #if RELEASECOUNTDEBUG
87 #define dprintfRC(args...) dprintf(args)
88 #else
89 #define dprintfRC(args...)
90 #endif
91
92 static OSStatus
93 DER_UTCTimeToCFDate(const CSSM_DATA_PTR utcTime, CFAbsoluteTime *date)
94 {
95 CFGregorianDate gdate;
96 char *string = (char *)utcTime->Data;
97 long year, month, mday, hour, minute, second, hourOff, minOff;
98 CFTimeZoneRef timeZone;
99
100 /* Verify time is formatted properly and capture information */
101 second = 0;
102 hourOff = 0;
103 minOff = 0;
104 CAPTURE(year,string+0,loser);
105 if (year < 50) {
106 /* ASSUME that year # is in the 2000's, not the 1900's */
107 year += 100;
108 }
109 CAPTURE(month,string+2,loser);
110 if ((month == 0) || (month > 12)) goto loser;
111 CAPTURE(mday,string+4,loser);
112 if ((mday == 0) || (mday > 31)) goto loser;
113 CAPTURE(hour,string+6,loser);
114 if (hour > 23) goto loser;
115 CAPTURE(minute,string+8,loser);
116 if (minute > 59) goto loser;
117 if (ISDIGIT(string[10])) {
118 CAPTURE(second,string+10,loser);
119 if (second > 59) goto loser;
120 string += 2;
121 }
122 if (string[10] == '+') {
123 CAPTURE(hourOff,string+11,loser);
124 if (hourOff > 23) goto loser;
125 CAPTURE(minOff,string+13,loser);
126 if (minOff > 59) goto loser;
127 } else if (string[10] == '-') {
128 CAPTURE(hourOff,string+11,loser);
129 if (hourOff > 23) goto loser;
130 hourOff = -hourOff;
131 CAPTURE(minOff,string+13,loser);
132 if (minOff > 59) goto loser;
133 minOff = -minOff;
134 } else if (string[10] != 'Z') {
135 goto loser;
136 }
137
138 gdate.year = (SInt32)(year + 1900);
139 gdate.month = month;
140 gdate.day = mday;
141 gdate.hour = hour;
142 gdate.minute = minute;
143 gdate.second = second;
144
145 if (hourOff == 0 && minOff == 0)
146 timeZone = NULL; /* GMT */
147 else
148 {
149 timeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, (hourOff * 60 + minOff) * 60);
150 }
151
152 *date = CFGregorianDateGetAbsoluteTime(gdate, timeZone);
153 if (timeZone)
154 CFRelease(timeZone);
155
156 return SECSuccess;
157
158 loser:
159 return SECFailure;
160 }
161
162 static OSStatus
163 DER_CFDateToUTCTime(CFAbsoluteTime date, CSSM_DATA_PTR utcTime)
164 {
165 CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(date, NULL /* GMT */);
166 unsigned char *d;
167 SInt8 second;
168
169 utcTime->Length = 13;
170 utcTime->Data = d = PORT_Alloc(13);
171 if (!utcTime->Data)
172 return SECFailure;
173
174 /* UTC time does not handle the years before 1950 */
175 if (gdate.year < 1950)
176 return SECFailure;
177
178 /* remove the century since it's added to the year by the
179 CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
180 gdate.year %= 100;
181 second = gdate.second + 0.5;
182
183 d[0] = HIDIGIT(gdate.year);
184 d[1] = LODIGIT(gdate.year);
185 d[2] = HIDIGIT(gdate.month);
186 d[3] = LODIGIT(gdate.month);
187 d[4] = HIDIGIT(gdate.day);
188 d[5] = LODIGIT(gdate.day);
189 d[6] = HIDIGIT(gdate.hour);
190 d[7] = LODIGIT(gdate.hour);
191 d[8] = HIDIGIT(gdate.minute);
192 d[9] = LODIGIT(gdate.minute);
193 d[10] = HIDIGIT(second);
194 d[11] = LODIGIT(second);
195 d[12] = 'Z';
196 return SECSuccess;
197 }
198
199 /* =============================================================================
200 * SIGNERINFO
201 */
202 SecCmsSignerInfoRef
203 nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
204
205 SecCmsSignerInfoRef
206 SecCmsSignerInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
207 {
208 return nss_cmssignerinfo_create(cmsg, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag);
209 }
210
211 SecCmsSignerInfoRef
212 SecCmsSignerInfoCreate(SecCmsMessageRef cmsg, SecIdentityRef identity, SECOidTag digestalgtag)
213 {
214 SecCmsSignerInfoRef signerInfo = NULL;
215 SecCertificateRef cert = NULL;
216 SecPrivateKeyRef signingKey = NULL;
217 CFDictionaryRef keyAttrs = NULL;
218
219 if (SecIdentityCopyCertificate(identity, &cert))
220 goto loser;
221 if (SecIdentityCopyPrivateKey(identity, &signingKey))
222 goto loser;
223
224 /* In some situations, the "Private Key" in the identity is actually a public key. */
225 keyAttrs = SecKeyCopyAttributes(signingKey);
226 if (!keyAttrs)
227 goto loser;
228 CFTypeRef class = CFDictionaryGetValue(keyAttrs, kSecAttrKeyClass);
229 if (!class || (CFGetTypeID(class) != CFStringGetTypeID()) || !CFEqual(class, kSecAttrKeyClassPrivate))
230 goto loser;
231
232
233 signerInfo = nss_cmssignerinfo_create(cmsg, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
234
235 loser:
236 if (cert)
237 CFRelease(cert);
238 if (signingKey)
239 CFRelease(signingKey);
240 if (keyAttrs)
241 CFRelease(keyAttrs);
242
243 return signerInfo;
244 }
245
246 SecCmsSignerInfoRef
247 nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
248 {
249 void *mark;
250 SecCmsSignerInfoRef signerinfo;
251 int version;
252 PLArenaPool *poolp;
253
254 poolp = cmsg->poolp;
255
256 mark = PORT_ArenaMark(poolp);
257
258 signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo));
259 if (signerinfo == NULL) {
260 PORT_ArenaRelease(poolp, mark);
261 return NULL;
262 }
263
264
265 signerinfo->cmsg = cmsg;
266
267 switch(type) {
268 case SecCmsSignerIDIssuerSN:
269 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN;
270 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
271 goto loser;
272 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
273 goto loser;
274 dprintfRC("nss_cmssignerinfo_create: SecCmsSignerIDIssuerSN: cert.rc %d\n",
275 (int)CFGetRetainCount(signerinfo->cert));
276 break;
277 case SecCmsSignerIDSubjectKeyID:
278 signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID;
279 PORT_Assert(subjKeyID);
280 if (!subjKeyID)
281 goto loser;
282 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
283 if (SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
284 subjKeyID)) {
285 goto loser;
286 }
287 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
288 if (!signerinfo->pubKey)
289 goto loser;
290 break;
291 default:
292 goto loser;
293 }
294
295 if (!signingKey)
296 goto loser;
297
298 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
299 if (!signerinfo->signingKey)
300 goto loser;
301
302 /* set version right now */
303 version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN;
304 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
305 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)
306 version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY;
307 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
308
309 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
310 goto loser;
311
312 PORT_ArenaUnmark(poolp, mark);
313 return signerinfo;
314
315 loser:
316 PORT_ArenaRelease(poolp, mark);
317 return NULL;
318 }
319
320 /*
321 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
322 */
323 void
324 SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
325 {
326 if (si->cert != NULL) {
327 dprintfRC("SecCmsSignerInfoDestroy top: certp %p cert.rc %d\n",
328 si->cert, (int)CFGetRetainCount(si->cert));
329 CERT_DestroyCertificate(si->cert);
330 }
331 if (si->certList != NULL) {
332 dprintfRC("SecCmsSignerInfoDestroy top: certList.rc %d\n",
333 (int)CFGetRetainCount(si->certList));
334 CFRelease(si->certList);
335 }
336 if (si->timestampCertList != NULL) {
337 dprintfRC("SecCmsSignerInfoDestroy top: timestampCertList.rc %d\n",
338 (int)CFGetRetainCount(si->timestampCertList));
339 CFRelease(si->timestampCertList);
340 }
341 if (si->timestampCert != NULL) {
342 dprintfRC("SecCmsSignerInfoDestroy top: timestampCert.rc %d\n",
343 (int)CFGetRetainCount(si->timestampCert));
344 CFRelease(si->timestampCert);
345 }
346 if (si->hashAgilityAttrValue != NULL) {
347 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityAttrValue.rc %d\n",
348 (int)CFGetRetainCount(si->hashAgilityAttrValue));
349 CFRelease(si->hashAgilityAttrValue);
350 }
351 if (si->hashAgilityV2AttrValues != NULL) {
352 dprintfRC("SecCmsSignerInfoDestroy top: hashAgilityV2AttrValues.rc %d\n",
353 (int)CFGetRetainCount(si->hashAgilityV2AttrValues));
354 CFRelease(si->hashAgilityV2AttrValues);
355 }
356 /* XXX storage ??? */
357 }
358
359 /*
360 * SecCmsSignerInfoSign - sign something
361 *
362 */
363 OSStatus
364 SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
365 {
366 SecCertificateRef cert;
367 SecPrivateKeyRef privkey = NULL;
368 SECOidTag digestalgtag;
369 SECOidTag pubkAlgTag;
370 CSSM_DATA signature = { 0 };
371 OSStatus rv;
372 PLArenaPool *poolp, *tmppoolp = NULL;
373 const SECAlgorithmID *algID;
374 SECAlgorithmID freeAlgID;
375 //CERTSubjectPublicKeyInfo *spki;
376
377 PORT_Assert (digest != NULL);
378
379 poolp = signerinfo->cmsg->poolp;
380
381 switch (signerinfo->signerIdentifier.identifierType) {
382 case SecCmsSignerIDIssuerSN:
383 privkey = signerinfo->signingKey;
384 signerinfo->signingKey = NULL;
385 cert = signerinfo->cert;
386 if (SecCertificateGetAlgorithmID(cert,&algID)) {
387 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
388 goto loser;
389 }
390 break;
391 case SecCmsSignerIDSubjectKeyID:
392 privkey = signerinfo->signingKey;
393 signerinfo->signingKey = NULL;
394 #if 0
395 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
396 SECKEY_DestroyPublicKey(signerinfo->pubKey);
397 signerinfo->pubKey = NULL;
398 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
399 SECKEY_DestroySubjectPublicKeyInfo(spki);
400 algID = &freeAlgID;
401 #else
402
403 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
404 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) {
405 #else
406 /* TBD: Unify this code. Currently, iOS has an incompatible
407 * SecKeyGetAlgorithmID implementation. */
408 if (true) {
409 #endif
410 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
411 goto loser;
412 }
413 CFRelease(signerinfo->pubKey);
414 signerinfo->pubKey = NULL;
415 #endif
416 break;
417 default:
418 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
419 goto loser;
420 }
421 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
422 /*
423 * XXX I think there should be a cert-level interface for this,
424 * so that I do not have to know about subjectPublicKeyInfo...
425 */
426 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
427 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
428 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
429 }
430
431 #if 0
432 // @@@ Not yet
433 /* Fortezza MISSI have weird signature formats.
434 * Map them to standard DSA formats
435 */
436 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
437 #endif
438
439 if (signerinfo->authAttr != NULL) {
440 CSSM_DATA encoded_attrs;
441
442 /* find and fill in the message digest attribute. */
443 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
444 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
445 if (rv != SECSuccess)
446 goto loser;
447
448 if (contentType != NULL) {
449 /* if the caller wants us to, find and fill in the content type attribute. */
450 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
451 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
452 if (rv != SECSuccess)
453 goto loser;
454 }
455
456 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
457 PORT_SetError(SEC_ERROR_NO_MEMORY);
458 goto loser;
459 }
460
461 /*
462 * Before encoding, reorder the attributes so that when they
463 * are encoded, they will be conforming DER, which is required
464 * to have a specific order and that is what must be used for
465 * the hash/signature. We do this here, rather than building
466 * it into EncodeAttributes, because we do not want to do
467 * such reordering on incoming messages (which also uses
468 * EncodeAttributes) or our old signatures (and other "broken"
469 * implementations) will not verify. So, we want to guarantee
470 * that we send out good DER encodings of attributes, but not
471 * to expect to receive them.
472 */
473 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
474 goto loser;
475
476 encoded_attrs.Data = NULL;
477 encoded_attrs.Length = 0;
478 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
479 &encoded_attrs) == NULL)
480 goto loser;
481
482 rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length,
483 privkey, digestalgtag, pubkAlgTag);
484 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
485 tmppoolp = 0;
486 } else {
487 rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest);
488 }
489 SECKEY_DestroyPrivateKey(privkey);
490 privkey = NULL;
491
492 if (rv != SECSuccess)
493 goto loser;
494
495 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
496 != SECSuccess)
497 goto loser;
498
499 SECITEM_FreeItem(&signature, PR_FALSE);
500
501 if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) {
502 /*
503 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
504 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
505 * as would appear in other forms of signed datas. However Microsoft doesn't
506 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
507 * MS can't verify - presumably because it takes the digest of the digest
508 * before feeding it to ECDSA.
509 * We handle this with a preference; default if it's not there is
510 * "Microsoft compatibility mode".
511 */
512 if(!SecCmsMsEcdsaCompatMode()) {
513 pubkAlgTag = SEC_OID_ECDSA_WithSHA1;
514 }
515 /* else violating the spec for compatibility */
516 }
517
518 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag,
519 NULL) != SECSuccess)
520 goto loser;
521
522 return SECSuccess;
523
524 loser:
525 if (signature.Length != 0)
526 SECITEM_FreeItem (&signature, PR_FALSE);
527 if (privkey)
528 SECKEY_DestroyPrivateKey(privkey);
529 if (tmppoolp)
530 PORT_FreeArena(tmppoolp, PR_FALSE);
531 return SECFailure;
532 }
533
534 OSStatus
535 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
536 CFTypeRef policies, SecTrustRef *trustRef)
537 {
538 SecCertificateRef cert;
539 CFAbsoluteTime stime;
540 OSStatus rv;
541 CSSM_DATA_PTR *otherCerts;
542
543 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
544 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
545 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
546 return SECFailure;
547 }
548
549 /*
550 * Get and convert the signing time; if available, it will be used
551 * both on the cert verification and for importing the sender
552 * email profile.
553 */
554 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies);
555 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess)
556 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
557 stime = CFAbsoluteTimeGetCurrent();
558 CFReleaseSafe(timeStampPolicies);
559
560 rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts);
561 if(rv) {
562 return rv;
563 }
564 rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef);
565 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
566 cert, (int)CFGetRetainCount(cert));
567 if (rv || !trustRef)
568 {
569 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
570 {
571 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
572 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
573 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
574 }
575 }
576 /* FIXME isn't this leaking the cert? */
577 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv);
578 return rv;
579 }
580
581 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo)
582 {
583 #if SIGINFO_DEBUG
584 CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo);
585 if (cn)
586 {
587 char *ccn = cfStringToChar(cn);
588 if (ccn)
589 {
590 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn);
591 free(ccn);
592 }
593 CFRelease(cn);
594 }
595 #endif
596 }
597
598 /*
599 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
600 *
601 * Just verifies the signature. The assumption is that verification of the certificate
602 * is done already.
603 */
604 OSStatus
605 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
606 {
607 return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType);
608 }
609
610 OSStatus
611 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
612 {
613 SecPublicKeyRef publickey = NULL;
614 SecCmsAttribute *attr = NULL;
615 CSSM_DATA encoded_attrs;
616 SecCertificateRef cert = NULL;
617 SecCmsVerificationStatus vs = SecCmsVSUnverified;
618 PLArenaPool *poolp = NULL;
619 SECOidTag digestAlgTag, digestEncAlgTag;
620
621 if (signerinfo == NULL)
622 return SECFailure;
623
624 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
625 /* cert has not been verified */
626 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
627 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
628 vs = SecCmsVSSigningCertNotFound;
629 goto loser;
630 }
631
632 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
633
634 debugShowSigningCertificate(signerinfo);
635
636 OSStatus status;
637 if ((status = SecCertificateCopyPublicKey(cert, &publickey))) {
638 syslog(LOG_ERR, "SecCmsSignerInfoVerifyWithPolicy: copy public key failed %d", (int)status);
639 vs = SecCmsVSProcessingError;
640 goto loser;
641 }
642
643 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
644 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
645
646 /*
647 * Gross hack necessitated by RFC 3278 section 2.1.1, which states
648 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1,
649 * *not* (as in all other algorithms) the raw signature algorithm, e.g.
650 * pkcs1RSAEncryption.
651 */
652 if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) {
653 digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY;
654 }
655
656 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
657 if (contentType) {
658 /*
659 * Check content type
660 *
661 * RFC2630 sez that if there are any authenticated attributes,
662 * then there must be one for content type which matches the
663 * content type of the content being signed, and there must
664 * be one for message digest which matches our message digest.
665 * So check these things first.
666 */
667 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
668 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
669 {
670 vs = SecCmsVSMalformedSignature;
671 goto loser;
672 }
673
674 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
675 vs = SecCmsVSMalformedSignature;
676 goto loser;
677 }
678 }
679
680 /*
681 * Check digest
682 */
683 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
684 {
685 vs = SecCmsVSMalformedSignature;
686 goto loser;
687 }
688 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
689 vs = SecCmsVSDigestMismatch;
690 goto loser;
691 }
692
693 if ((poolp = PORT_NewArena (1024)) == NULL) {
694 vs = SecCmsVSProcessingError;
695 goto loser;
696 }
697
698 /*
699 * Check signature
700 *
701 * The signature is based on a digest of the DER-encoded authenticated
702 * attributes. So, first we encode and then we digest/verify.
703 * we trust the decoder to have the attributes in the right (sorted) order
704 */
705 encoded_attrs.Data = NULL;
706 encoded_attrs.Length = 0;
707
708 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
709 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
710 {
711 vs = SecCmsVSProcessingError;
712 goto loser;
713 }
714
715 vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length,
716 publickey, &(signerinfo->encDigest),
717 digestAlgTag, digestEncAlgTag,
718 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
719
720 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
721 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
722
723 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
724
725 } else {
726 CSSM_DATA_PTR sig;
727
728 /* No authenticated attributes. The signature is based on the plain message digest. */
729 sig = &(signerinfo->encDigest);
730 if (sig->Length == 0)
731 goto loser;
732
733 vs = (VFY_VerifyDigest(digest, publickey, sig,
734 digestAlgTag, digestEncAlgTag,
735 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
736
737 dprintf("VFY_VerifyData (plain message digest): %s\n",
738 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
739 }
740
741 if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr))
742 {
743 dprintf("found an unAuthAttr\n");
744 OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
745 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
746 if (rux) {
747 goto loser;
748 }
749 }
750
751 if (vs == SecCmsVSBadSignature) {
752 /*
753 * XXX Change the generic error into our specific one, because
754 * in that case we get a better explanation out of the Security
755 * Advisor. This is really a bug in our error strings (the
756 * "generic" error has a lousy/wrong message associated with it
757 * which assumes the signature verification was done for the
758 * purposes of checking the issuer signature on a certificate)
759 * but this is at least an easy workaround and/or in the
760 * Security Advisor, which specifically checks for the error
761 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
762 * in that case but does not similarly check for
763 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
764 * probably say the wrong thing in the case that it *was* the
765 * certificate signature check that failed during the cert
766 * verification done above. Our error handling is really a mess.
767 */
768 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
769 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
770 }
771
772 if (publickey != NULL)
773 CFRelease(publickey);
774
775 signerinfo->verificationStatus = vs;
776 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
777 cert, (int)CFGetRetainCount(cert));
778
779 dprintf("verificationStatus: %d\n", vs);
780
781 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
782
783 loser:
784 if (publickey != NULL)
785 SECKEY_DestroyPublicKey (publickey);
786
787 dprintf("verificationStatus2: %d\n", vs);
788 signerinfo->verificationStatus = vs;
789
790 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
791 return SECFailure;
792 }
793
794 OSStatus
795 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) {
796 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL);
797 }
798
799 OSStatus
800 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy)
801 {
802 /*
803 unAuthAttr is an array of attributes; we expect to
804 see just one: the timestamp blob. If we have an unAuthAttr,
805 but don't see a timestamp, return an error since we have
806 no other cases where this would be present.
807 */
808
809 SecCmsAttribute *attr = NULL;
810 OSStatus status = SECFailure;
811
812 require(signerinfo, xit);
813 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr,
814 SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE);
815 if (attr == NULL)
816 {
817 status = errSecTimestampMissing;
818 goto xit;
819 }
820
821 dprintf("found an id-ct-TSTInfo\n");
822 // Don't check the nonce in this case
823 status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0);
824 xit:
825 return status;
826 }
827
828 CSSM_DATA *
829 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo)
830 {
831 return &signerinfo->encDigest;
832 }
833
834 SecCmsVerificationStatus
835 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
836 {
837 return signerinfo->verificationStatus;
838 }
839
840 SECOidData *
841 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
842 {
843 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
844 }
845
846 SECOidTag
847 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
848 {
849 SECOidData *algdata;
850
851 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
852 if (algdata != NULL)
853 return algdata->offset;
854 else
855 return SEC_OID_UNKNOWN;
856 }
857
858 CFArrayRef
859 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
860 {
861 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
862 (int)CFGetRetainCount(signerinfo->certList));
863 return signerinfo->certList;
864 }
865
866 CFArrayRef
867 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
868 {
869 dprintfRC("SecCmsSignerInfoGetTimestampCertList: timestampCertList.rc %d\n",
870 (int)CFGetRetainCount(signerinfo->timestampCertList));
871 return signerinfo->timestampCertList;
872 }
873
874 SecCertificateRef
875 SecCmsSignerInfoGetTimestampSigningCert(SecCmsSignerInfoRef signerinfo)
876 {
877 dprintfRC("SecCmsSignerInfoGetTimestampSigningCert: timestampCert.rc %d\n",
878 (int)CFGetRetainCount(signerinfo->timestampCert));
879 return signerinfo->timestampCert;
880 }
881
882 int
883 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
884 {
885 unsigned long version;
886
887 /* always take apart the CSSM_DATA */
888 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
889 return 0;
890 else
891 return (int)version;
892 }
893
894 /*
895 * SecCmsSignerInfoGetSigningTime - return the signing time,
896 * in UTCTime format, of a CMS signerInfo.
897 *
898 * sinfo - signerInfo data for this signer
899 *
900 * Returns a pointer to XXXX (what?)
901 * A return value of NULL is an error.
902 */
903 OSStatus
904 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
905 {
906 SecCmsAttribute *attr;
907 CSSM_DATA_PTR value;
908
909 if (sinfo == NULL)
910 return paramErr;
911
912 if (sinfo->signingTime != 0) {
913 *stime = sinfo->signingTime; /* cached copy */
914 return SECSuccess;
915 }
916
917 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
918 /* XXXX multi-valued attributes NIH */
919 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
920 return errSecSigningTimeMissing;
921 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
922 return errSecSigningTimeMissing;
923 sinfo->signingTime = *stime; /* make cached copy */
924 return SECSuccess;
925 }
926
927 OSStatus
928 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
929 {
930 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime);
931 }
932
933 OSStatus
934 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime)
935 {
936 OSStatus status = paramErr;
937
938 require(sinfo && stime, xit);
939
940 if (sinfo->timestampTime != 0)
941 {
942 *stime = sinfo->timestampTime; /* cached copy */
943 return noErr;
944 }
945
946 // A bit heavyweight if haven't already called verify
947 status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy);
948 *stime = sinfo->timestampTime;
949 xit:
950 return status;
951 }
952
953 /*!
954 @function
955 @abstract Return the data in the signed Codesigning Hash Agility attribute.
956 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
957 @discussion Returns a CFDataRef containing the value of the attribute
958 @result A return value of errSecInternal is an error trying to look up the oid.
959 A status value of success with null result data indicates the attribute was not present.
960 */
961 OSStatus
962 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
963 {
964 SecCmsAttribute *attr;
965 CSSM_DATA_PTR value;
966
967 if (sinfo == NULL || sdata == NULL)
968 return paramErr;
969
970 *sdata = NULL;
971
972 if (sinfo->hashAgilityAttrValue != NULL) {
973 *sdata = sinfo->hashAgilityAttrValue; /* cached copy */
974 return SECSuccess;
975 }
976
977 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
978
979 /* attribute not found */
980 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
981 return SECSuccess;
982
983 sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */
984 if (sinfo->hashAgilityAttrValue) {
985 *sdata = sinfo->hashAgilityAttrValue;
986 return SECSuccess;
987 }
988 return errSecAllocate;
989 }
990
991 /* AgileHash ::= SEQUENCE {
992 hashType OBJECT IDENTIFIER,
993 hashValues OCTET STRING }
994 */
995 typedef struct {
996 SecAsn1Item digestOID;
997 SecAsn1Item digestValue;
998 } CMSAppleAgileHash;
999
1000 static const SecAsn1Template CMSAppleAgileHashTemplate[] = {
1001 { SEC_ASN1_SEQUENCE,
1002 0, NULL, sizeof(CMSAppleAgileHash) },
1003 { SEC_ASN1_OBJECT_ID,
1004 offsetof(CMSAppleAgileHash, digestOID), },
1005 { SEC_ASN1_OCTET_STRING,
1006 offsetof(CMSAppleAgileHash, digestValue), },
1007 { 0, }
1008 };
1009
1010 static OSStatus CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary, SecAsn1Item *DERAgileHash) {
1011 PLArenaPool *tmppoolp = NULL;
1012 OSStatus status = errSecSuccess;
1013 CMSAppleAgileHash agileHash;
1014 CFDataRef digestValue = NULL;
1015 CFNumberRef digestTag = NULL;
1016
1017 tmppoolp = PORT_NewArena(1024);
1018 if (tmppoolp == NULL) {
1019 return errSecAllocate;
1020 }
1021
1022 if ((status = SEC_ASN1DecodeItem(tmppoolp, &agileHash, CMSAppleAgileHashTemplate, DERAgileHash)) != errSecSuccess) {
1023 goto loser;
1024 }
1025
1026 int64_t tag = SECOID_FindOIDTag(&agileHash.digestOID);
1027 digestTag = CFNumberCreate(NULL, kCFNumberSInt64Type, &tag);
1028 digestValue = CFDataCreate(NULL, agileHash.digestValue.Data, agileHash.digestValue.Length);
1029 CFDictionaryAddValue(dictionary, digestTag, digestValue);
1030
1031 loser:
1032 CFReleaseNull(digestValue);
1033 CFReleaseNull(digestTag);
1034 if (tmppoolp) {
1035 PORT_FreeArena(tmppoolp, PR_FALSE);
1036 }
1037 return status;
1038 }
1039
1040 /*!
1041 @function
1042 @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
1043 @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
1044 @discussion Returns a CFDictionaryRef containing the values of the attribute
1045 @result A return value of errSecInternal is an error trying to look up the oid.
1046 A status value of success with null result data indicates the attribute was not present.
1047 */
1048 OSStatus
1049 SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo, CFDictionaryRef *sdict)
1050 {
1051 SecCmsAttribute *attr;
1052
1053 if (sinfo == NULL || sdict == NULL) {
1054 return errSecParam;
1055 }
1056
1057 *sdict = NULL;
1058
1059 if (sinfo->hashAgilityV2AttrValues != NULL) {
1060 *sdict = sinfo->hashAgilityV2AttrValues; /* cached copy */
1061 return SECSuccess;
1062 }
1063
1064 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY_V2, PR_TRUE);
1065
1066 /* attribute not found */
1067 if (attr == NULL) {
1068 return SECSuccess;
1069 }
1070
1071 /* attrValues SET OF AttributeValue
1072 * AttributeValue ::= ANY
1073 */
1074 CSSM_DATA_PTR *values = attr->values;
1075 if (values == NULL) { /* There must be values */
1076 return errSecDecode;
1077 }
1078
1079 CFMutableDictionaryRef agileHashValues = CFDictionaryCreateMutable(NULL, SecCmsArrayCount((void **)values),
1080 &kCFTypeDictionaryKeyCallBacks,
1081 &kCFTypeDictionaryValueCallBacks);
1082 while (*values != NULL) {
1083 (void)CMSAddAgileHashToDictionary(agileHashValues, *values++);
1084 }
1085 if (CFDictionaryGetCount(agileHashValues) != SecCmsArrayCount((void **)attr->values)) {
1086 CFReleaseNull(agileHashValues);
1087 return errSecDecode;
1088 }
1089
1090 sinfo->hashAgilityV2AttrValues = agileHashValues; /* make cached copy */
1091 if (sinfo->hashAgilityV2AttrValues) {
1092 *sdict = sinfo->hashAgilityV2AttrValues;
1093 return SECSuccess;
1094 }
1095 return errSecAllocate;
1096 }
1097
1098 /*
1099 * Return the signing cert of a CMS signerInfo.
1100 *
1101 * the certs in the enclosing SignedData must have been imported already
1102 */
1103 SecCertificateRef
1104 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
1105 {
1106 SecCertificateRef cert;
1107 SecCmsSignerIdentifier *sid;
1108 OSStatus ortn;
1109 CSSM_DATA_PTR *rawCerts;
1110
1111 if (signerinfo->cert != NULL) {
1112 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
1113 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1114 return signerinfo->cert;
1115 }
1116 ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts);
1117 if(ortn) {
1118 return NULL;
1119 }
1120 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
1121 SecCmsArrayCount((void **)rawCerts));
1122
1123 /*
1124 * This cert will also need to be freed, but since we save it
1125 * in signerinfo for later, we do not want to destroy it when
1126 * we leave this function -- we let the clean-up of the entire
1127 * cinfo structure later do the destroy of this cert.
1128 */
1129 sid = &signerinfo->signerIdentifier;
1130 switch (sid->identifierType) {
1131 case SecCmsSignerIDIssuerSN:
1132 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->sigd->certs, signerinfo->cmsg->poolp,
1133 sid->id.issuerAndSN);
1134 break;
1135 case SecCmsSignerIDSubjectKeyID:
1136 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, signerinfo->sigd->certs, sid->id.subjectKeyID);
1137 break;
1138 default:
1139 cert = NULL;
1140 break;
1141 }
1142
1143 /* cert can be NULL at that point */
1144 signerinfo->cert = cert; /* earmark it */
1145 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
1146 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
1147
1148 return cert;
1149 }
1150
1151 /*
1152 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1153 *
1154 * sinfo - signerInfo data for this signer
1155 *
1156 * Returns a CFStringRef containing the common name of the signer.
1157 * A return value of NULL is an error.
1158 */
1159 CFStringRef
1160 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
1161 {
1162 SecCertificateRef signercert;
1163 CFStringRef commonName = NULL;
1164
1165 /* will fail if cert is not verified */
1166 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1167 return NULL;
1168
1169 if (errSecSuccess != SecCertificateCopyCommonName(signercert, &commonName)) {
1170 return NULL;
1171 }
1172
1173 return commonName;
1174 }
1175
1176 /*
1177 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1178 *
1179 * sinfo - signerInfo data for this signer
1180 *
1181 * Returns a CFStringRef containing the name of the signer.
1182 * A return value of NULL is an error.
1183 */
1184 CFStringRef
1185 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
1186 {
1187 SecCertificateRef signercert;
1188 CFStringRef emailAddress = NULL;
1189
1190 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1191 return NULL;
1192
1193 SecCertificateGetEmailAddress(signercert, &emailAddress);
1194
1195 return emailAddress;
1196 }
1197
1198
1199 /*
1200 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1201 * authenticated (i.e. signed) attributes of "signerinfo".
1202 */
1203 OSStatus
1204 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1205 {
1206 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
1207 }
1208
1209 /*
1210 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1211 * unauthenticated attributes of "signerinfo".
1212 */
1213 OSStatus
1214 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1215 {
1216 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
1217 }
1218
1219 /*
1220 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1221 * authenticated (i.e. signed) attributes of "signerinfo".
1222 *
1223 * This is expected to be included in outgoing signed
1224 * messages for email (S/MIME) but is likely useful in other situations.
1225 *
1226 * This should only be added once; a second call will do nothing.
1227 *
1228 * XXX This will probably just shove the current time into "signerinfo"
1229 * but it will not actually get signed until the entire item is
1230 * processed for encoding. Is this (expected to be small) delay okay?
1231 */
1232 OSStatus
1233 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1234 {
1235 SecCmsAttribute *attr;
1236 CSSM_DATA stime;
1237 void *mark;
1238 PLArenaPool *poolp;
1239
1240 poolp = signerinfo->cmsg->poolp;
1241
1242 mark = PORT_ArenaMark(poolp);
1243
1244 /* create new signing time attribute */
1245 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1246 goto loser;
1247
1248 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1249 SECITEM_FreeItem (&stime, PR_FALSE);
1250 goto loser;
1251 }
1252
1253 SECITEM_FreeItem (&stime, PR_FALSE);
1254
1255 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1256 goto loser;
1257
1258 PORT_ArenaUnmark (poolp, mark);
1259
1260 return SECSuccess;
1261
1262 loser:
1263 PORT_ArenaRelease (poolp, mark);
1264 return SECFailure;
1265 }
1266
1267 /*
1268 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1269 * authenticated (i.e. signed) attributes of "signerinfo".
1270 *
1271 * This is expected to be included in outgoing signed
1272 * messages for email (S/MIME).
1273 */
1274 OSStatus
1275 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1276 {
1277 SecCmsAttribute *attr;
1278 CSSM_DATA_PTR smimecaps = NULL;
1279 void *mark;
1280 PLArenaPool *poolp;
1281
1282 poolp = signerinfo->cmsg->poolp;
1283
1284 mark = PORT_ArenaMark(poolp);
1285
1286 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1287 if (smimecaps == NULL)
1288 goto loser;
1289
1290 /* create new signing time attribute */
1291 #if 1
1292 // @@@ We don't do Fortezza yet.
1293 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess)
1294 #else
1295 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1296 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1297 #endif
1298 goto loser;
1299
1300 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1301 goto loser;
1302
1303 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1304 goto loser;
1305
1306 PORT_ArenaUnmark (poolp, mark);
1307 return SECSuccess;
1308
1309 loser:
1310 PORT_ArenaRelease (poolp, mark);
1311 return SECFailure;
1312 }
1313
1314 /*
1315 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1316 * authenticated (i.e. signed) attributes of "signerinfo".
1317 *
1318 * This is expected to be included in outgoing signed messages for email (S/MIME).
1319 */
1320 OSStatus
1321 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1322 {
1323 SecCmsAttribute *attr;
1324 CSSM_DATA_PTR smimeekp = NULL;
1325 void *mark;
1326 PLArenaPool *poolp;
1327
1328 #if 0
1329 CFTypeRef policy;
1330
1331 /* verify this cert for encryption */
1332 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1333 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1334 CFRelease(policy);
1335 return SECFailure;
1336 }
1337 CFRelease(policy);
1338 #endif
1339
1340 poolp = signerinfo->cmsg->poolp;
1341 mark = PORT_ArenaMark(poolp);
1342
1343 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1344 if (smimeekp == NULL)
1345 goto loser;
1346
1347 /* create new signing time attribute */
1348 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1349 goto loser;
1350
1351 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1352 goto loser;
1353
1354 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1355 goto loser;
1356
1357 PORT_ArenaUnmark (poolp, mark);
1358 return SECSuccess;
1359
1360 loser:
1361 PORT_ArenaRelease (poolp, mark);
1362 return SECFailure;
1363 }
1364
1365 /*
1366 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1367 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
1368 *
1369 * This is expected to be included in outgoing signed messages for email (S/MIME),
1370 * if compatibility with Microsoft mail clients is wanted.
1371 */
1372 OSStatus
1373 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1374 {
1375 SecCmsAttribute *attr;
1376 CSSM_DATA_PTR smimeekp = NULL;
1377 void *mark;
1378 PLArenaPool *poolp;
1379
1380 #if 0
1381 CFTypeRef policy;
1382
1383 /* verify this cert for encryption */
1384 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1385 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1386 CFRelease(policy);
1387 return SECFailure;
1388 }
1389 CFRelease(policy);
1390 #endif
1391
1392 poolp = signerinfo->cmsg->poolp;
1393 mark = PORT_ArenaMark(poolp);
1394
1395 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1396 if (smimeekp == NULL)
1397 goto loser;
1398
1399 /* create new signing time attribute */
1400 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1401 goto loser;
1402
1403 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1404 goto loser;
1405
1406 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1407 goto loser;
1408
1409 PORT_ArenaUnmark (poolp, mark);
1410 return SECSuccess;
1411
1412 loser:
1413 PORT_ArenaRelease (poolp, mark);
1414 return SECFailure;
1415 }
1416
1417 /*
1418 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1419 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1420 *
1421 * This will initially be used for time stamping signed applications
1422 * by using a Time Stamping Authority. It may also be included in outgoing signed
1423 * messages for email (S/MIME), and may be useful in other situations.
1424 *
1425 * This should only be added once; a second call will do nothing.
1426 *
1427 */
1428
1429 /*
1430 Countersignature attribute values have ASN.1 type Countersignature:
1431 Countersignature ::= SignerInfo
1432 Countersignature values have the same meaning as SignerInfo values
1433 for ordinary signatures, except that:
1434 1. The signedAttributes field MUST NOT contain a content-type
1435 attribute; there is no content type for countersignatures.
1436 2. The signedAttributes field MUST contain a message-digest
1437 attribute if it contains any other attributes.
1438 3. The input to the message-digesting process is the contents octets
1439 of the DER encoding of the signatureValue field of the SignerInfo
1440 value with which the attribute is associated.
1441 */
1442
1443 /*!
1444 @function
1445 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1446 */
1447
1448 OSStatus
1449 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken)
1450 {
1451 SecCmsAttribute *attr;
1452 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1453 void *mark = PORT_ArenaMark(poolp);
1454
1455 // We have already encoded this ourselves, so last param is PR_TRUE
1456 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL)
1457 goto loser;
1458
1459 if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess)
1460 goto loser;
1461
1462 PORT_ArenaUnmark (poolp, mark);
1463
1464 return SECSuccess;
1465
1466 loser:
1467 PORT_ArenaRelease (poolp, mark);
1468 return SECFailure;
1469 }
1470
1471 /*
1472 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1473 *
1474 * 1. digest the DER-encoded signature value of the original signerinfo
1475 * 2. create new signerinfo with correct version, sid, digestAlg
1476 * 3. add message-digest authAttr, but NO content-type
1477 * 4. sign the authAttrs
1478 * 5. DER-encode the new signerInfo
1479 * 6. add the whole thing to original signerInfo's unAuthAttrs
1480 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1481 *
1482 * XXXX give back the new signerinfo?
1483 */
1484 OSStatus
1485 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1486 SECOidTag digestalg, SecIdentityRef identity)
1487 {
1488 /* XXXX TBD XXXX */
1489 return SECFailure;
1490 }
1491
1492 /*!
1493 @function
1494 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1495 @discussion This is expected to be included in outgoing Apple code signatures.
1496 */
1497 OSStatus
1498 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
1499 {
1500 SecCmsAttribute *attr;
1501 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1502 void *mark = PORT_ArenaMark(poolp);
1503 OSStatus status = SECFailure;
1504
1505 /* The value is required for this attribute. */
1506 if (!attrValue) {
1507 status = errSecParam;
1508 goto loser;
1509 }
1510
1511 /*
1512 * SecCmsAttributeCreate makes a copy of the data in value, so
1513 * we don't need to copy into the CSSM_DATA struct.
1514 */
1515 CSSM_DATA value;
1516 value.Length = CFDataGetLength(attrValue);
1517 value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
1518
1519 if ((attr = SecCmsAttributeCreate(poolp,
1520 SEC_OID_APPLE_HASH_AGILITY,
1521 &value,
1522 PR_FALSE)) == NULL) {
1523 status = errSecAllocate;
1524 goto loser;
1525 }
1526
1527 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1528 status = errSecInternalError;
1529 goto loser;
1530 }
1531
1532 PORT_ArenaUnmark(poolp, mark);
1533 return SECSuccess;
1534
1535 loser:
1536 PORT_ArenaRelease(poolp, mark);
1537 return status;
1538 }
1539
1540 static OSStatus CMSAddAgileHashToAttribute(PLArenaPool *poolp, SecCmsAttribute *attr, CFNumberRef cftag, CFDataRef value) {
1541 PLArenaPool *tmppoolp = NULL;
1542 int64_t tag;
1543 SECOidData *digestOid = NULL;
1544 CMSAppleAgileHash agileHash;
1545 SecAsn1Item attrValue = { .Data = NULL, .Length = 0 };
1546 OSStatus status = errSecSuccess;
1547
1548 memset(&agileHash, 0, sizeof(agileHash));
1549
1550 if(!CFNumberGetValue(cftag, kCFNumberSInt64Type, &tag)) {
1551 return errSecParam;
1552 }
1553 digestOid = SECOID_FindOIDByTag((SECOidTag)tag);
1554
1555 agileHash.digestValue.Data = (uint8_t *)CFDataGetBytePtr(value);
1556 agileHash.digestValue.Length = CFDataGetLength(value);
1557 agileHash.digestOID.Data = digestOid->oid.Data;
1558 agileHash.digestOID.Length = digestOid->oid.Length;
1559
1560 tmppoolp = PORT_NewArena(1024);
1561 if (tmppoolp == NULL) {
1562 return errSecAllocate;
1563 }
1564
1565 if (SEC_ASN1EncodeItem(tmppoolp, &attrValue, &agileHash, CMSAppleAgileHashTemplate) == NULL) {
1566 status = errSecParam;
1567 goto loser;
1568 }
1569
1570 status = SecCmsAttributeAddValue(poolp, attr, &attrValue);
1571
1572 loser:
1573 if (tmppoolp) {
1574 PORT_FreeArena(tmppoolp, PR_FALSE);
1575 }
1576 return status;
1577 }
1578
1579 /*!
1580 @function
1581 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1582 @discussion This is expected to be included in outgoing Apple code signatures.
1583 */
1584 OSStatus
1585 SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo, CFDictionaryRef attrValues)
1586 {
1587 __block SecCmsAttribute *attr;
1588 __block PLArenaPool *poolp = signerinfo->cmsg->poolp;
1589 void *mark = PORT_ArenaMark(poolp);
1590 OSStatus status = SECFailure;
1591
1592 /* The value is required for this attribute. */
1593 if (!attrValues) {
1594 status = errSecParam;
1595 goto loser;
1596 }
1597
1598 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_HASH_AGILITY_V2,
1599 NULL, PR_TRUE)) == NULL) {
1600 status = errSecAllocate;
1601 goto loser;
1602 }
1603
1604 CFDictionaryForEach(attrValues, ^(const void *key, const void *value) {
1605 if (!isNumber(key) || !isData(value)) {
1606 return;
1607 }
1608 (void)CMSAddAgileHashToAttribute(poolp, attr, (CFNumberRef)key, (CFDataRef)value);
1609 });
1610
1611 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1612 status = errSecInternal;
1613 goto loser;
1614 }
1615
1616 PORT_ArenaUnmark(poolp, mark);
1617 return SECSuccess;
1618
1619 loser:
1620 PORT_ArenaRelease(poolp, mark);
1621 return status;
1622 }
1623
1624
1625 SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
1626 SecCertificateRef cert = NULL;
1627 SecCmsAttribute *attr;
1628 CSSM_DATA_PTR ekp;
1629 SecKeychainRef keychainOrArray;
1630
1631 (void)SecKeychainCopyDefault(&keychainOrArray);
1632
1633 /* sanity check - see if verification status is ok (unverified does not count...) */
1634 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1635 return NULL;
1636
1637 /* Prep the raw certs */
1638 CSSM_DATA_PTR *rawCerts = NULL;
1639 if (signerinfo->sigd) {
1640 rawCerts = signerinfo->sigd->rawCerts;
1641 }
1642
1643 /* find preferred encryption cert */
1644 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1645 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1646 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1647 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1648 ekp = SecCmsAttributeGetValue(attr);
1649 if (ekp == NULL)
1650 return NULL;
1651 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1652 }
1653 if(cert) return cert;
1654
1655 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1656 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1657 SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1658 { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1659 ekp = SecCmsAttributeGetValue(attr);
1660 if (ekp == NULL)
1661 return NULL;
1662 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, rawCerts, ekp);
1663 }
1664 return cert;
1665 }
1666
1667 /*
1668 * XXXX the following needs to be done in the S/MIME layer code
1669 * after signature of a signerinfo is verified
1670 */
1671 OSStatus
1672 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1673 {
1674 SecCertificateRef cert = NULL;
1675 CSSM_DATA_PTR profile = NULL;
1676 SecCmsAttribute *attr;
1677 CSSM_DATA_PTR utc_stime = NULL;
1678 CSSM_DATA_PTR ekp;
1679 int save_error;
1680 OSStatus rv;
1681 Boolean must_free_cert = PR_FALSE;
1682 OSStatus status;
1683 SecKeychainRef keychainOrArray;
1684
1685 status = SecKeychainCopyDefault(&keychainOrArray);
1686
1687 /* sanity check - see if verification status is ok (unverified does not count...) */
1688 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1689 return SECFailure;
1690
1691 /* find preferred encryption cert */
1692 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1693 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1694 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1695 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1696 ekp = SecCmsAttributeGetValue(attr);
1697 if (ekp == NULL)
1698 return SECFailure;
1699
1700 /* we assume that all certs coming with the message have been imported to the */
1701 /* temporary database */
1702 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, NULL, ekp);
1703 if (cert == NULL)
1704 return SECFailure;
1705 must_free_cert = PR_TRUE;
1706 }
1707
1708 if (cert == NULL) {
1709 /* no preferred cert found?
1710 * find the cert the signerinfo is signed with instead */
1711 CFStringRef emailAddress=NULL;
1712
1713 cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray);
1714 if (cert == NULL)
1715 return SECFailure;
1716 if (SecCertificateGetEmailAddress(cert,&emailAddress))
1717 return SECFailure;
1718 }
1719
1720 /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert.
1721 * that's OK, we can still save the S/MIME profile. The encryption cert
1722 * should have already been saved */
1723 #ifdef notdef
1724 if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1725 if (must_free_cert)
1726 CERT_DestroyCertificate(cert);
1727 return SECFailure;
1728 }
1729 #endif
1730
1731 /* XXX store encryption cert permanently? */
1732
1733 /*
1734 * Remember the current error set because we do not care about
1735 * anything set by the functions we are about to call.
1736 */
1737 save_error = PORT_GetError();
1738
1739 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
1740 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1741 SEC_OID_PKCS9_SMIME_CAPABILITIES,
1742 PR_TRUE);
1743 profile = SecCmsAttributeGetValue(attr);
1744 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1745 SEC_OID_PKCS9_SIGNING_TIME,
1746 PR_TRUE);
1747 utc_stime = SecCmsAttributeGetValue(attr);
1748 }
1749
1750 rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
1751 if (must_free_cert)
1752 CERT_DestroyCertificate(cert);
1753
1754 /*
1755 * Restore the saved error in case the calls above set a new
1756 * one that we do not actually care about.
1757 */
1758 PORT_SetError (save_error);
1759
1760 return rv;
1761 }
1762
1763 /*
1764 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1765 */
1766 OSStatus
1767 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1768 {
1769 if (signerinfo->cert == NULL)
1770 return SECFailure;
1771
1772 /* don't leak if we get called twice */
1773 if (signerinfo->certList != NULL) {
1774 CFRelease(signerinfo->certList);
1775 signerinfo->certList = NULL;
1776 }
1777
1778 switch (cm) {
1779 case SecCmsCMNone:
1780 signerinfo->certList = NULL;
1781 break;
1782 case SecCmsCMCertOnly:
1783 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1784 break;
1785 case SecCmsCMCertChain:
1786 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1787 break;
1788 case SecCmsCMCertChainWithRoot:
1789 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1790 break;
1791 }
1792
1793 if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1794 return SECFailure;
1795
1796 return SECSuccess;
1797 }