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