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