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