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