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