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