]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmssiginfo.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmssiginfo.c
1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation. Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above. If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL. If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34 /*
35 * CMS signerInfo methods.
36 */
37
38 #include <Security/SecCmsSignerInfo.h>
39 #include "SecSMIMEPriv.h"
40
41 #include "cmslocal.h"
42
43 #include "cert.h"
44 #include "secitem.h"
45 #include "secoid.h"
46 #include "cryptohi.h"
47
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <Security/SecKeychain.h>
51 #include <Security/SecIdentity.h>
52 #include <Security/SecCertificatePriv.h>
53 #include <Security/SecKeyPriv.h>
54 #include <CoreFoundation/CFTimeZone.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <AssertMacros.h>
57 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
58 #include <Security/SecPolicyPriv.h>
59
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...) fprintf(stderr, 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
371 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
372 if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) {
373 #else
374 /* TBD: Unify this code. Currently, iOS has an incompatible
375 * SecKeyGetAlgorithmID implementation. */
376 if (true) {
377 #endif
378 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
379 goto loser;
380 }
381 CFRelease(signerinfo->pubKey);
382 signerinfo->pubKey = NULL;
383 #endif
384 break;
385 default:
386 PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
387 goto loser;
388 }
389 digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
390 /*
391 * XXX I think there should be a cert-level interface for this,
392 * so that I do not have to know about subjectPublicKeyInfo...
393 */
394 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
395 if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
396 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
397 }
398
399 #if 0
400 // @@@ Not yet
401 /* Fortezza MISSI have weird signature formats.
402 * Map them to standard DSA formats
403 */
404 pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
405 #endif
406
407 if (signerinfo->authAttr != NULL) {
408 CSSM_DATA encoded_attrs;
409
410 /* find and fill in the message digest attribute. */
411 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
412 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
413 if (rv != SECSuccess)
414 goto loser;
415
416 if (contentType != NULL) {
417 /* if the caller wants us to, find and fill in the content type attribute. */
418 rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
419 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
420 if (rv != SECSuccess)
421 goto loser;
422 }
423
424 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
425 PORT_SetError(SEC_ERROR_NO_MEMORY);
426 goto loser;
427 }
428
429 /*
430 * Before encoding, reorder the attributes so that when they
431 * are encoded, they will be conforming DER, which is required
432 * to have a specific order and that is what must be used for
433 * the hash/signature. We do this here, rather than building
434 * it into EncodeAttributes, because we do not want to do
435 * such reordering on incoming messages (which also uses
436 * EncodeAttributes) or our old signatures (and other "broken"
437 * implementations) will not verify. So, we want to guarantee
438 * that we send out good DER encodings of attributes, but not
439 * to expect to receive them.
440 */
441 if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
442 goto loser;
443
444 encoded_attrs.Data = NULL;
445 encoded_attrs.Length = 0;
446 if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
447 &encoded_attrs) == NULL)
448 goto loser;
449
450 rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length,
451 privkey, digestalgtag, pubkAlgTag);
452 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
453 tmppoolp = 0;
454 } else {
455 rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest);
456 }
457 SECKEY_DestroyPrivateKey(privkey);
458 privkey = NULL;
459
460 if (rv != SECSuccess)
461 goto loser;
462
463 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
464 != SECSuccess)
465 goto loser;
466
467 SECITEM_FreeItem(&signature, PR_FALSE);
468
469 if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) {
470 /*
471 * RFC 3278 section section 2.1.1 states that the signatureAlgorithm
472 * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey
473 * as would appear in other forms of signed datas. However Microsoft doesn't
474 * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there,
475 * MS can't verify - presumably because it takes the digest of the digest
476 * before feeding it to ECDSA.
477 * We handle this with a preference; default if it's not there is
478 * "Microsoft compatibility mode".
479 */
480 if(!SecCmsMsEcdsaCompatMode()) {
481 pubkAlgTag = SEC_OID_ECDSA_WithSHA1;
482 }
483 /* else violating the spec for compatibility */
484 }
485
486 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag,
487 NULL) != SECSuccess)
488 goto loser;
489
490 return SECSuccess;
491
492 loser:
493 if (signature.Length != 0)
494 SECITEM_FreeItem (&signature, PR_FALSE);
495 if (privkey)
496 SECKEY_DestroyPrivateKey(privkey);
497 if((algID != NULL) & (algID != &freeAlgID)) {
498 /* this is dicey - this was actually mallocd by either SecCertificate or
499 * by SecKey...it all boils down to a free() in the end though. */
500 SECOID_DestroyAlgorithmID((SECAlgorithmID *)algID, PR_FALSE);
501 }
502 if (tmppoolp)
503 PORT_FreeArena(tmppoolp, PR_FALSE);
504 return SECFailure;
505 }
506
507 OSStatus
508 SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
509 CFTypeRef policies, SecTrustRef *trustRef)
510 {
511 SecCertificateRef cert;
512 CFAbsoluteTime stime;
513 OSStatus rv;
514 CSSM_DATA_PTR *otherCerts;
515
516 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
517 dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n");
518 signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
519 return SECFailure;
520 }
521
522 /*
523 * Get and convert the signing time; if available, it will be used
524 * both on the cert verification and for importing the sender
525 * email profile.
526 */
527 CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies);
528 if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess)
529 if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
530 stime = CFAbsoluteTimeGetCurrent();
531 CFReleaseSafe(timeStampPolicies);
532
533 rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts);
534 if(rv) {
535 return rv;
536 }
537 rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef);
538 dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n",
539 cert, (int)CFGetRetainCount(cert));
540 if (rv || !trustRef)
541 {
542 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
543 {
544 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
545 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
546 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
547 }
548 }
549 /* FIXME isn't this leaking the cert? */
550 dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv);
551 return rv;
552 }
553
554 static void debugShowSigningCertificate(SecCmsSignerInfoRef signerinfo)
555 {
556 #if SIGINFO_DEBUG
557 CFStringRef cn = SecCmsSignerInfoGetSignerCommonName(signerinfo);
558 if (cn)
559 {
560 char *ccn = cfStringToChar(cn);
561 if (ccn)
562 {
563 dprintf("SecCmsSignerInfoVerify: cn: %s\n", ccn);
564 free(ccn);
565 }
566 CFRelease(cn);
567 }
568 #endif
569 }
570
571 /*
572 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
573 *
574 * Just verifies the signature. The assumption is that verification of the certificate
575 * is done already.
576 */
577 OSStatus
578 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
579 {
580 return SecCmsSignerInfoVerifyWithPolicy(signerinfo,NULL, digest,contentType);
581 }
582
583 OSStatus
584 SecCmsSignerInfoVerifyWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType)
585 {
586 SecPublicKeyRef publickey = NULL;
587 SecCmsAttribute *attr;
588 CSSM_DATA encoded_attrs;
589 SecCertificateRef cert;
590 SecCmsVerificationStatus vs = SecCmsVSUnverified;
591 PLArenaPool *poolp;
592 SECOidTag digestAlgTag, digestEncAlgTag;
593
594 if (signerinfo == NULL)
595 return SECFailure;
596
597 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
598 /* cert has not been verified */
599 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
600 dprintf("SecCmsSignerInfoVerify: no signing cert\n");
601 vs = SecCmsVSSigningCertNotFound;
602 goto loser;
603 }
604
605 dprintfRC("SecCmsSignerInfoVerify top: cert %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert));
606
607 debugShowSigningCertificate(signerinfo);
608
609 if (SecCertificateCopyPublicKey(cert, &publickey)) {
610 vs = SecCmsVSProcessingError;
611 goto loser;
612 }
613
614 digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
615 digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
616
617 /*
618 * Gross hack necessitated by RFC 3278 section 2.1.1, which states
619 * that the signature algorithm (here, digestEncAlg) contains ecdsa_with-SHA1,
620 * *not* (as in all other algorithms) the raw signature algorithm, e.g.
621 * pkcs1RSAEncryption.
622 */
623 if(digestEncAlgTag == SEC_OID_ECDSA_WithSHA1) {
624 digestEncAlgTag = SEC_OID_EC_PUBLIC_KEY;
625 }
626
627 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
628 if (contentType) {
629 /*
630 * Check content type
631 *
632 * RFC2630 sez that if there are any authenticated attributes,
633 * then there must be one for content type which matches the
634 * content type of the content being signed, and there must
635 * be one for message digest which matches our message digest.
636 * So check these things first.
637 */
638 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
639 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
640 {
641 vs = SecCmsVSMalformedSignature;
642 goto loser;
643 }
644
645 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
646 vs = SecCmsVSMalformedSignature;
647 goto loser;
648 }
649 }
650
651 /*
652 * Check digest
653 */
654 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
655 {
656 vs = SecCmsVSMalformedSignature;
657 goto loser;
658 }
659 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
660 vs = SecCmsVSDigestMismatch;
661 goto loser;
662 }
663
664 if ((poolp = PORT_NewArena (1024)) == NULL) {
665 vs = SecCmsVSProcessingError;
666 goto loser;
667 }
668
669 /*
670 * Check signature
671 *
672 * The signature is based on a digest of the DER-encoded authenticated
673 * attributes. So, first we encode and then we digest/verify.
674 * we trust the decoder to have the attributes in the right (sorted) order
675 */
676 encoded_attrs.Data = NULL;
677 encoded_attrs.Length = 0;
678
679 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
680 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
681 {
682 vs = SecCmsVSProcessingError;
683 goto loser;
684 }
685
686 vs = (VFY_VerifyData (encoded_attrs.Data, (int)encoded_attrs.Length,
687 publickey, &(signerinfo->encDigest),
688 digestAlgTag, digestEncAlgTag,
689 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
690
691 dprintf("VFY_VerifyData (authenticated attributes): %s\n",
692 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
693
694 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
695
696 } else {
697 CSSM_DATA_PTR sig;
698
699 /* No authenticated attributes. The signature is based on the plain message digest. */
700 sig = &(signerinfo->encDigest);
701 if (sig->Length == 0)
702 goto loser;
703
704 vs = (VFY_VerifyDigest(digest, publickey, sig,
705 digestAlgTag, digestEncAlgTag,
706 signerinfo->cmsg->pwfn_arg) != SECSuccess) ? SecCmsVSBadSignature : SecCmsVSGoodSignature;
707
708 dprintf("VFY_VerifyData (plain message digest): %s\n",
709 (vs == SecCmsVSGoodSignature)?"SecCmsVSGoodSignature":"SecCmsVSBadSignature");
710 }
711
712 if (!SecCmsArrayIsEmpty((void **)signerinfo->unAuthAttr))
713 {
714 dprintf("found an unAuthAttr\n");
715 OSStatus rux = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo,timeStampPolicy);
716 dprintf("SecCmsSignerInfoVerifyUnAuthAttrs Status: %ld\n", (long)rux);
717 if (rux)
718 goto loser;
719 }
720
721 if (vs == SecCmsVSBadSignature) {
722 /*
723 * XXX Change the generic error into our specific one, because
724 * in that case we get a better explanation out of the Security
725 * Advisor. This is really a bug in our error strings (the
726 * "generic" error has a lousy/wrong message associated with it
727 * which assumes the signature verification was done for the
728 * purposes of checking the issuer signature on a certificate)
729 * but this is at least an easy workaround and/or in the
730 * Security Advisor, which specifically checks for the error
731 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
732 * in that case but does not similarly check for
733 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
734 * probably say the wrong thing in the case that it *was* the
735 * certificate signature check that failed during the cert
736 * verification done above. Our error handling is really a mess.
737 */
738 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
739 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
740 }
741
742 if (publickey != NULL)
743 CFRelease(publickey);
744
745 signerinfo->verificationStatus = vs;
746 dprintfRC("SecCmsSignerInfoVerify end: cerp %p cert.rc %d\n",
747 cert, (int)CFGetRetainCount(cert));
748
749 dprintf("verificationStatus: %d\n", vs);
750
751 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
752
753 loser:
754 if (publickey != NULL)
755 SECKEY_DestroyPublicKey (publickey);
756
757 dprintf("verificationStatus2: %d\n", vs);
758 signerinfo->verificationStatus = vs;
759
760 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
761 return SECFailure;
762 }
763
764 OSStatus
765 SecCmsSignerInfoVerifyUnAuthAttrs(SecCmsSignerInfoRef signerinfo) {
766 return SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(signerinfo, NULL);
767 }
768
769 OSStatus
770 SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(SecCmsSignerInfoRef signerinfo,CFTypeRef timeStampPolicy)
771 {
772 /*
773 unAuthAttr is an array of attributes; we expect to
774 see just one: the timestamp blob. If we have an unAuthAttr,
775 but don't see a timestamp, return an error since we have
776 no other cases where this would be present.
777 */
778
779 SecCmsAttribute *attr = NULL;
780 OSStatus status = SECFailure;
781
782 require(signerinfo, xit);
783 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->unAuthAttr,
784 SEC_OID_PKCS9_TIMESTAMP_TOKEN, PR_TRUE);
785 if (attr == NULL)
786 {
787 status = errSecTimestampMissing;
788 goto xit;
789 }
790
791 dprintf("found an id-ct-TSTInfo\n");
792 // Don't check the nonce in this case
793 status = decodeTimeStampTokenWithPolicy(signerinfo, timeStampPolicy, (attr->values)[0], &signerinfo->encDigest, 0);
794 xit:
795 return status;
796 }
797
798 CSSM_DATA *
799 SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo)
800 {
801 return &signerinfo->encDigest;
802 }
803
804 SecCmsVerificationStatus
805 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
806 {
807 return signerinfo->verificationStatus;
808 }
809
810 SECOidData *
811 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
812 {
813 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
814 }
815
816 SECOidTag
817 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
818 {
819 SECOidData *algdata;
820
821 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
822 if (algdata != NULL)
823 return algdata->offset;
824 else
825 return SEC_OID_UNKNOWN;
826 }
827
828 CFArrayRef
829 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
830 {
831 dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
832 (int)CFGetRetainCount(signerinfo->certList));
833 return signerinfo->certList;
834 }
835
836 CFArrayRef
837 SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
838 {
839 dprintfRC("SecCmsSignerInfoGetCertList: timestampCertList.rc %d\n",
840 (int)CFGetRetainCount(signerinfo->timestampCertList));
841 return signerinfo->timestampCertList;
842 }
843
844
845
846 int
847 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
848 {
849 unsigned long version;
850
851 /* always take apart the CSSM_DATA */
852 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
853 return 0;
854 else
855 return (int)version;
856 }
857
858 /*
859 * SecCmsSignerInfoGetSigningTime - return the signing time,
860 * in UTCTime format, of a CMS signerInfo.
861 *
862 * sinfo - signerInfo data for this signer
863 *
864 * Returns a pointer to XXXX (what?)
865 * A return value of NULL is an error.
866 */
867 OSStatus
868 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
869 {
870 SecCmsAttribute *attr;
871 CSSM_DATA_PTR value;
872
873 if (sinfo == NULL)
874 return paramErr;
875
876 if (sinfo->signingTime != 0) {
877 *stime = sinfo->signingTime; /* cached copy */
878 return SECSuccess;
879 }
880
881 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
882 /* XXXX multi-valued attributes NIH */
883 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
884 return errSecSigningTimeMissing;
885 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
886 return errSecSigningTimeMissing;
887 sinfo->signingTime = *stime; /* make cached copy */
888 return SECSuccess;
889 }
890
891 OSStatus
892 SecCmsSignerInfoGetTimestampTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
893 {
894 return SecCmsSignerInfoGetTimestampTimeWithPolicy(sinfo, NULL, stime);
895 }
896
897 OSStatus
898 SecCmsSignerInfoGetTimestampTimeWithPolicy(SecCmsSignerInfoRef sinfo, CFTypeRef timeStampPolicy, CFAbsoluteTime *stime)
899 {
900 OSStatus status = paramErr;
901
902 require(sinfo && stime, xit);
903
904 if (sinfo->timestampTime != 0)
905 {
906 *stime = sinfo->timestampTime; /* cached copy */
907 return noErr;
908 }
909
910 // A bit heavyweight if haven't already called verify
911 status = SecCmsSignerInfoVerifyUnAuthAttrsWithPolicy(sinfo,timeStampPolicy);
912 *stime = sinfo->timestampTime;
913 xit:
914 return status;
915 }
916
917 /*
918 * Return the signing cert of a CMS signerInfo.
919 *
920 * the certs in the enclosing SignedData must have been imported already
921 */
922 SecCertificateRef
923 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
924 {
925 SecCertificateRef cert;
926 SecCmsSignerIdentifier *sid;
927 OSStatus ortn;
928 CSSM_DATA_PTR *rawCerts;
929
930 if (signerinfo->cert != NULL) {
931 dprintfRC("SecCmsSignerInfoGetSigningCertificate top: cert %p cert.rc %d\n",
932 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
933 return signerinfo->cert;
934 }
935 ortn = SecCmsSignedDataRawCerts(signerinfo->sigd, &rawCerts);
936 if(ortn) {
937 return NULL;
938 }
939 dprintf("SecCmsSignerInfoGetSigningCertificate: numRawCerts %d\n",
940 SecCmsArrayCount((void **)rawCerts));
941
942 /*
943 * This cert will also need to be freed, but since we save it
944 * in signerinfo for later, we do not want to destroy it when
945 * we leave this function -- we let the clean-up of the entire
946 * cinfo structure later do the destroy of this cert.
947 */
948 sid = &signerinfo->signerIdentifier;
949 switch (sid->identifierType) {
950 case SecCmsSignerIDIssuerSN:
951 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, rawCerts, signerinfo->cmsg->poolp,
952 sid->id.issuerAndSN);
953 break;
954 case SecCmsSignerIDSubjectKeyID:
955 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, rawCerts, sid->id.subjectKeyID);
956 break;
957 default:
958 cert = NULL;
959 break;
960 }
961
962 /* cert can be NULL at that point */
963 signerinfo->cert = cert; /* earmark it */
964 dprintfRC("SecCmsSignerInfoGetSigningCertificate end: certp %p cert.rc %d\n",
965 signerinfo->cert, (int)CFGetRetainCount(signerinfo->cert));
966
967 return cert;
968 }
969
970 /*
971 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
972 *
973 * sinfo - signerInfo data for this signer
974 *
975 * Returns a CFStringRef containing the common name of the signer.
976 * A return value of NULL is an error.
977 */
978 CFStringRef
979 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
980 {
981 SecCertificateRef signercert;
982 CFStringRef commonName = NULL;
983
984 /* will fail if cert is not verified */
985 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
986 return NULL;
987
988 SecCertificateCopyCommonName(signercert, &commonName);
989
990 return commonName;
991 }
992
993 /*
994 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
995 *
996 * sinfo - signerInfo data for this signer
997 *
998 * Returns a CFStringRef containing the name of the signer.
999 * A return value of NULL is an error.
1000 */
1001 CFStringRef
1002 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
1003 {
1004 SecCertificateRef signercert;
1005 CFStringRef emailAddress = NULL;
1006
1007 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1008 return NULL;
1009
1010 SecCertificateGetEmailAddress(signercert, &emailAddress);
1011
1012 return emailAddress;
1013 }
1014
1015
1016 /*
1017 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1018 * authenticated (i.e. signed) attributes of "signerinfo".
1019 */
1020 OSStatus
1021 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1022 {
1023 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
1024 }
1025
1026 /*
1027 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1028 * unauthenticated attributes of "signerinfo".
1029 */
1030 OSStatus
1031 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1032 {
1033 return SecCmsAttributeArrayAddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
1034 }
1035
1036 /*
1037 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1038 * authenticated (i.e. signed) attributes of "signerinfo".
1039 *
1040 * This is expected to be included in outgoing signed
1041 * messages for email (S/MIME) but is likely useful in other situations.
1042 *
1043 * This should only be added once; a second call will do nothing.
1044 *
1045 * XXX This will probably just shove the current time into "signerinfo"
1046 * but it will not actually get signed until the entire item is
1047 * processed for encoding. Is this (expected to be small) delay okay?
1048 */
1049 OSStatus
1050 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1051 {
1052 SecCmsAttribute *attr;
1053 CSSM_DATA stime;
1054 void *mark;
1055 PLArenaPool *poolp;
1056
1057 poolp = signerinfo->cmsg->poolp;
1058
1059 mark = PORT_ArenaMark(poolp);
1060
1061 /* create new signing time attribute */
1062 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1063 goto loser;
1064
1065 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1066 SECITEM_FreeItem (&stime, PR_FALSE);
1067 goto loser;
1068 }
1069
1070 SECITEM_FreeItem (&stime, PR_FALSE);
1071
1072 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1073 goto loser;
1074
1075 PORT_ArenaUnmark (poolp, mark);
1076
1077 return SECSuccess;
1078
1079 loser:
1080 PORT_ArenaRelease (poolp, mark);
1081 return SECFailure;
1082 }
1083
1084 /*
1085 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1086 * authenticated (i.e. signed) attributes of "signerinfo".
1087 *
1088 * This is expected to be included in outgoing signed
1089 * messages for email (S/MIME).
1090 */
1091 OSStatus
1092 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1093 {
1094 SecCmsAttribute *attr;
1095 CSSM_DATA_PTR smimecaps = NULL;
1096 void *mark;
1097 PLArenaPool *poolp;
1098
1099 poolp = signerinfo->cmsg->poolp;
1100
1101 mark = PORT_ArenaMark(poolp);
1102
1103 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1104 if (smimecaps == NULL)
1105 goto loser;
1106
1107 /* create new signing time attribute */
1108 #if 1
1109 // @@@ We don't do Fortezza yet.
1110 if (SecSMIMECreateSMIMECapabilities((SecArenaPoolRef)poolp, smimecaps, PR_FALSE) != SECSuccess)
1111 #else
1112 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1113 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1114 #endif
1115 goto loser;
1116
1117 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1118 goto loser;
1119
1120 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1121 goto loser;
1122
1123 PORT_ArenaUnmark (poolp, mark);
1124 return SECSuccess;
1125
1126 loser:
1127 PORT_ArenaRelease (poolp, mark);
1128 return SECFailure;
1129 }
1130
1131 /*
1132 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1133 * authenticated (i.e. signed) attributes of "signerinfo".
1134 *
1135 * This is expected to be included in outgoing signed messages for email (S/MIME).
1136 */
1137 OSStatus
1138 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1139 {
1140 SecCmsAttribute *attr;
1141 CSSM_DATA_PTR smimeekp = NULL;
1142 void *mark;
1143 PLArenaPool *poolp;
1144
1145 #if 0
1146 CFTypeRef policy;
1147
1148 /* verify this cert for encryption */
1149 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1150 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1151 CFRelease(policy);
1152 return SECFailure;
1153 }
1154 CFRelease(policy);
1155 #endif
1156
1157 poolp = signerinfo->cmsg->poolp;
1158 mark = PORT_ArenaMark(poolp);
1159
1160 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1161 if (smimeekp == NULL)
1162 goto loser;
1163
1164 /* create new signing time attribute */
1165 if (SecSMIMECreateSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1166 goto loser;
1167
1168 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1169 goto loser;
1170
1171 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1172 goto loser;
1173
1174 PORT_ArenaUnmark (poolp, mark);
1175 return SECSuccess;
1176
1177 loser:
1178 PORT_ArenaRelease (poolp, mark);
1179 return SECFailure;
1180 }
1181
1182 /*
1183 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1184 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
1185 *
1186 * This is expected to be included in outgoing signed messages for email (S/MIME),
1187 * if compatibility with Microsoft mail clients is wanted.
1188 */
1189 OSStatus
1190 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1191 {
1192 SecCmsAttribute *attr;
1193 CSSM_DATA_PTR smimeekp = NULL;
1194 void *mark;
1195 PLArenaPool *poolp;
1196
1197 #if 0
1198 CFTypeRef policy;
1199
1200 /* verify this cert for encryption */
1201 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1202 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1203 CFRelease(policy);
1204 return SECFailure;
1205 }
1206 CFRelease(policy);
1207 #endif
1208
1209 poolp = signerinfo->cmsg->poolp;
1210 mark = PORT_ArenaMark(poolp);
1211
1212 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1213 if (smimeekp == NULL)
1214 goto loser;
1215
1216 /* create new signing time attribute */
1217 if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess)
1218 goto loser;
1219
1220 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1221 goto loser;
1222
1223 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1224 goto loser;
1225
1226 PORT_ArenaUnmark (poolp, mark);
1227 return SECSuccess;
1228
1229 loser:
1230 PORT_ArenaRelease (poolp, mark);
1231 return SECFailure;
1232 }
1233
1234 /*
1235 * SecCmsSignerInfoAddTimeStamp - add time stamp to the
1236 * unauthenticated (i.e. unsigned) attributes of "signerinfo".
1237 *
1238 * This will initially be used for time stamping signed applications
1239 * by using a Time Stamping Authority. It may also be included in outgoing signed
1240 * messages for email (S/MIME), and may be useful in other situations.
1241 *
1242 * This should only be added once; a second call will do nothing.
1243 *
1244 */
1245
1246 /*
1247 Countersignature attribute values have ASN.1 type Countersignature:
1248 Countersignature ::= SignerInfo
1249 Countersignature values have the same meaning as SignerInfo values
1250 for ordinary signatures, except that:
1251 1. The signedAttributes field MUST NOT contain a content-type
1252 attribute; there is no content type for countersignatures.
1253 2. The signedAttributes field MUST contain a message-digest
1254 attribute if it contains any other attributes.
1255 3. The input to the message-digesting process is the contents octets
1256 of the DER encoding of the signatureValue field of the SignerInfo
1257 value with which the attribute is associated.
1258 */
1259
1260 /*!
1261 @function
1262 @abstract Create a timestamp unsigned attribute with a TimeStampToken.
1263 */
1264
1265 OSStatus
1266 SecCmsSignerInfoAddTimeStamp(SecCmsSignerInfoRef signerinfo, CSSM_DATA *tstoken)
1267 {
1268 SecCmsAttribute *attr;
1269 PLArenaPool *poolp = signerinfo->cmsg->poolp;
1270 void *mark = PORT_ArenaMark(poolp);
1271
1272 // We have already encoded this ourselves, so last param is PR_TRUE
1273 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_TIMESTAMP_TOKEN, tstoken, PR_TRUE)) == NULL)
1274 goto loser;
1275
1276 if (SecCmsSignerInfoAddUnauthAttr(signerinfo, attr) != SECSuccess)
1277 goto loser;
1278
1279 PORT_ArenaUnmark (poolp, mark);
1280
1281 return SECSuccess;
1282
1283 loser:
1284 PORT_ArenaRelease (poolp, mark);
1285 return SECFailure;
1286 }
1287
1288 /*
1289 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1290 *
1291 * 1. digest the DER-encoded signature value of the original signerinfo
1292 * 2. create new signerinfo with correct version, sid, digestAlg
1293 * 3. add message-digest authAttr, but NO content-type
1294 * 4. sign the authAttrs
1295 * 5. DER-encode the new signerInfo
1296 * 6. add the whole thing to original signerInfo's unAuthAttrs
1297 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1298 *
1299 * XXXX give back the new signerinfo?
1300 */
1301 OSStatus
1302 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1303 SECOidTag digestalg, SecIdentityRef identity)
1304 {
1305 /* XXXX TBD XXXX */
1306 return SECFailure;
1307 }
1308
1309 /*
1310 * XXXX the following needs to be done in the S/MIME layer code
1311 * after signature of a signerinfo is verified
1312 */
1313 OSStatus
1314 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1315 {
1316 SecCertificateRef cert = NULL;
1317 CSSM_DATA_PTR profile = NULL;
1318 SecCmsAttribute *attr;
1319 CSSM_DATA_PTR utc_stime = NULL;
1320 CSSM_DATA_PTR ekp;
1321 int save_error;
1322 OSStatus rv;
1323 Boolean must_free_cert = PR_FALSE;
1324 OSStatus status;
1325 SecKeychainRef keychainOrArray;
1326
1327 status = SecKeychainCopyDefault(&keychainOrArray);
1328
1329 /* sanity check - see if verification status is ok (unverified does not count...) */
1330 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1331 return SECFailure;
1332
1333 /* find preferred encryption cert */
1334 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1335 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1336 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1337 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
1338 ekp = SecCmsAttributeGetValue(attr);
1339 if (ekp == NULL)
1340 return SECFailure;
1341
1342 /* we assume that all certs coming with the message have been imported to the */
1343 /* temporary database */
1344 cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, ekp);
1345 if (cert == NULL)
1346 return SECFailure;
1347 must_free_cert = PR_TRUE;
1348 }
1349
1350 if (cert == NULL) {
1351 /* no preferred cert found?
1352 * find the cert the signerinfo is signed with instead */
1353 CFStringRef emailAddress=NULL;
1354
1355 cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray);
1356 if (cert == NULL)
1357 return SECFailure;
1358 if (SecCertificateGetEmailAddress(cert,&emailAddress))
1359 return SECFailure;
1360 }
1361
1362 /* 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.
1363 * that's OK, we can still save the S/MIME profile. The encryption cert
1364 * should have already been saved */
1365 #ifdef notdef
1366 if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1367 if (must_free_cert)
1368 CERT_DestroyCertificate(cert);
1369 return SECFailure;
1370 }
1371 #endif
1372
1373 /* XXX store encryption cert permanently? */
1374
1375 /*
1376 * Remember the current error set because we do not care about
1377 * anything set by the functions we are about to call.
1378 */
1379 save_error = PORT_GetError();
1380
1381 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
1382 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1383 SEC_OID_PKCS9_SMIME_CAPABILITIES,
1384 PR_TRUE);
1385 profile = SecCmsAttributeGetValue(attr);
1386 attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1387 SEC_OID_PKCS9_SIGNING_TIME,
1388 PR_TRUE);
1389 utc_stime = SecCmsAttributeGetValue(attr);
1390 }
1391
1392 rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
1393 if (must_free_cert)
1394 CERT_DestroyCertificate(cert);
1395
1396 /*
1397 * Restore the saved error in case the calls above set a new
1398 * one that we do not actually care about.
1399 */
1400 PORT_SetError (save_error);
1401
1402 return rv;
1403 }
1404
1405 /*
1406 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1407 */
1408 OSStatus
1409 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1410 {
1411 if (signerinfo->cert == NULL)
1412 return SECFailure;
1413
1414 /* don't leak if we get called twice */
1415 if (signerinfo->certList != NULL) {
1416 CFRelease(signerinfo->certList);
1417 signerinfo->certList = NULL;
1418 }
1419
1420 switch (cm) {
1421 case SecCmsCMNone:
1422 signerinfo->certList = NULL;
1423 break;
1424 case SecCmsCMCertOnly:
1425 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1426 break;
1427 case SecCmsCMCertChain:
1428 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1429 break;
1430 case SecCmsCMCertChainWithRoot:
1431 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1432 break;
1433 }
1434
1435 if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1436 return SECFailure;
1437
1438 return SECSuccess;
1439 }