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