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