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