]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmssiginfo.c
Security-59306.11.20.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 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
613 #if USE_CDSA_CRYPTO
614 rv = CERT_VerifyCert(keychainOrArray, cert, policies, stime, trustRef);
615 #else
616 rv = CERT_VerifyCert(keychainOrArray, certs, policies, stime, trustRef);
617 CFRelease(certs);
618 #endif
619 if (rv || !trustRef)
620 {
621 if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
622 {
623 /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
624 #if 0
625 #warning DEBUG - SecCmsSignerInfoVerifyCertificate trusts everything!
626 if (signerinfo->verificationStatus == SecCmsVSGoodSignature) {
627 syslog(LOG_ERR, "SecCmsSignerInfoVerifyCertificate ignoring SEC_ERROR_UNTRUSTED_CERT");
628 rv = SECSuccess;
629 }
630 #else
631 if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
632 signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
633 #endif
634 }
635 }
636
637 return rv;
638 }
639
640 /*
641 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
642 *
643 * Just verifies the signature. The assumption is that verification of the certificate
644 * is done already.
645 */
646 OSStatus
647 SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
648 {
649 SecPublicKeyRef publickey = NULL;
650 SecCmsAttribute *attr;
651 SecAsn1Item encoded_attrs;
652 SecCertificateRef cert;
653 SecCmsVerificationStatus vs = SecCmsVSUnverified;
654 PLArenaPool *poolp;
655
656 if (signerinfo == NULL)
657 return SECFailure;
658
659 /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
660 /* cert has not been verified */
661 if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
662 vs = SecCmsVSSigningCertNotFound;
663 goto loser;
664 }
665
666 publickey = SecCertificateCopyKey(cert);
667 if (publickey == NULL)
668 goto loser;
669
670 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
671 if (contentType) {
672 /*
673 * Check content type
674 *
675 * RFC2630 sez that if there are any authenticated attributes,
676 * then there must be one for content type which matches the
677 * content type of the content being signed, and there must
678 * be one for message digest which matches our message digest.
679 * So check these things first.
680 */
681 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
682 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
683 {
684 vs = SecCmsVSMalformedSignature;
685 goto loser;
686 }
687
688 if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
689 vs = SecCmsVSMalformedSignature;
690 goto loser;
691 }
692 }
693
694 /*
695 * Check digest
696 */
697 if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
698 {
699 vs = SecCmsVSMalformedSignature;
700 goto loser;
701 }
702 if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
703 vs = SecCmsVSDigestMismatch;
704 goto loser;
705 }
706
707 if ((poolp = PORT_NewArena (1024)) == NULL) {
708 vs = SecCmsVSProcessingError;
709 goto loser;
710 }
711
712 /*
713 * Check signature
714 *
715 * The signature is based on a digest of the DER-encoded authenticated
716 * attributes. So, first we encode and then we digest/verify.
717 * we trust the decoder to have the attributes in the right (sorted) order
718 */
719 encoded_attrs.Data = NULL;
720 encoded_attrs.Length = 0;
721
722 if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
723 encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
724 {
725 vs = SecCmsVSProcessingError;
726 goto loser;
727 }
728 if (errSecSuccess == SecKeyDigestAndVerify(publickey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signerinfo->encDigest.Data, signerinfo->encDigest.Length))
729 vs = SecCmsVSGoodSignature;
730 else
731 vs = SecCmsVSBadSignature;
732
733 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
734
735 } else {
736 SecAsn1Item * sig;
737
738 /* No authenticated attributes. The signature is based on the plain message digest. */
739 sig = &(signerinfo->encDigest);
740 if (sig->Length == 0)
741 goto loser;
742
743 if (SecKeyVerifyDigest(publickey, &signerinfo->digestAlg, digest->Data, digest->Length, sig->Data, sig->Length))
744 vs = SecCmsVSBadSignature;
745 else
746 vs = SecCmsVSGoodSignature;
747 }
748
749 if (vs == SecCmsVSBadSignature) {
750 /*
751 * XXX Change the generic error into our specific one, because
752 * in that case we get a better explanation out of the Security
753 * Advisor. This is really a bug in our error strings (the
754 * "generic" error has a lousy/wrong message associated with it
755 * which assumes the signature verification was done for the
756 * purposes of checking the issuer signature on a certificate)
757 * but this is at least an easy workaround and/or in the
758 * Security Advisor, which specifically checks for the error
759 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
760 * in that case but does not similarly check for
761 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
762 * probably say the wrong thing in the case that it *was* the
763 * certificate signature check that failed during the cert
764 * verification done above. Our error handling is really a mess.
765 */
766 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
767 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
768 }
769
770 if (publickey != NULL)
771 CFRelease(publickey);
772
773 signerinfo->verificationStatus = vs;
774
775 return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
776
777 loser:
778 if (publickey != NULL)
779 SECKEY_DestroyPublicKey (publickey);
780
781 signerinfo->verificationStatus = vs;
782
783 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
784 return SECFailure;
785 }
786
787 SecCmsVerificationStatus
788 SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
789 {
790 return signerinfo->verificationStatus;
791 }
792
793 SECOidData *
794 SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
795 {
796 return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
797 }
798
799 SECOidTag
800 SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
801 {
802 SECOidData *algdata;
803
804 algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
805 if (algdata != NULL)
806 return algdata->offset;
807 else
808 return SEC_OID_UNKNOWN;
809 }
810
811 CFArrayRef
812 SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
813 {
814 return signerinfo->certList;
815 }
816
817 int
818 SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
819 {
820 unsigned long version;
821
822 /* always take apart the SecAsn1Item */
823 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
824 return 0;
825 else
826 return (int)version;
827 }
828
829 /*
830 * SecCmsSignerInfoGetSigningTime - return the signing time,
831 * in UTCTime format, of a CMS signerInfo.
832 *
833 * sinfo - signerInfo data for this signer
834 *
835 * Returns a pointer to XXXX (what?)
836 * A return value of NULL is an error.
837 */
838 OSStatus
839 SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
840 {
841 SecCmsAttribute *attr;
842 SecAsn1Item * value;
843
844 if (sinfo == NULL)
845 return SECFailure;
846
847 if (sinfo->signingTime != 0) {
848 *stime = sinfo->signingTime; /* cached copy */
849 return SECSuccess;
850 }
851
852 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
853 /* XXXX multi-valued attributes NIH */
854 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
855 return SECFailure;
856 if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
857 return SECFailure;
858 sinfo->signingTime = *stime; /* make cached copy */
859 return SECSuccess;
860 }
861
862 /*!
863 @function
864 @abstract Return the data in the signed Codesigning Hash Agility attribute.
865 @param sinfo SignerInfo data for this signer, pointer to a CFDataRef for attribute value
866 @discussion Returns a CFDataRef containing the value of the attribute
867 @result A return value of errSecInternal is an error trying to look up the oid.
868 A status value of success with null result data indicates the attribute was not present.
869 */
870 OSStatus
871 SecCmsSignerInfoGetAppleCodesigningHashAgility(SecCmsSignerInfoRef sinfo, CFDataRef *sdata)
872 {
873 SecCmsAttribute *attr;
874 SecAsn1Item *value;
875
876 if (sinfo == NULL || sdata == NULL)
877 return errSecParam;
878
879 *sdata = NULL;
880
881 if (sinfo->hashAgilityAttrValue != NULL) {
882 *sdata = sinfo->hashAgilityAttrValue; /* cached copy */
883 return SECSuccess;
884 }
885
886 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY, PR_TRUE);
887
888 /* attribute not found */
889 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
890 return SECSuccess;
891
892 sinfo->hashAgilityAttrValue = CFDataCreate(NULL, value->Data, value->Length); /* make cached copy */
893 if (sinfo->hashAgilityAttrValue) {
894 *sdata = sinfo->hashAgilityAttrValue;
895 return SECSuccess;
896 }
897 return errSecAllocate;
898 }
899
900 /* AgileHash ::= SEQUENCE {
901 hashType OBJECT IDENTIFIER,
902 hashValues OCTET STRING }
903 */
904 typedef struct {
905 SecAsn1Item digestOID;
906 SecAsn1Item digestValue;
907 } CMSAppleAgileHash;
908
909 static const SecAsn1Template CMSAppleAgileHashTemplate[] = {
910 { SEC_ASN1_SEQUENCE,
911 0, NULL, sizeof(CMSAppleAgileHash) },
912 { SEC_ASN1_OBJECT_ID,
913 offsetof(CMSAppleAgileHash, digestOID), },
914 { SEC_ASN1_OCTET_STRING,
915 offsetof(CMSAppleAgileHash, digestValue), },
916 { 0, }
917 };
918
919 static OSStatus CMSAddAgileHashToDictionary(CFMutableDictionaryRef dictionary, SecAsn1Item *DERAgileHash) {
920 PLArenaPool *tmppoolp = NULL;
921 OSStatus status = errSecSuccess;
922 CMSAppleAgileHash agileHash;
923 CFDataRef digestValue = NULL;
924 CFNumberRef digestTag = NULL;
925
926 tmppoolp = PORT_NewArena(1024);
927 if (tmppoolp == NULL) {
928 return errSecAllocate;
929 }
930
931 if ((status = SEC_ASN1DecodeItem(tmppoolp, &agileHash, CMSAppleAgileHashTemplate, DERAgileHash)) != errSecSuccess) {
932 goto loser;
933 }
934
935 int64_t tag = SECOID_FindOIDTag(&agileHash.digestOID);
936 digestTag = CFNumberCreate(NULL, kCFNumberSInt64Type, &tag);
937 digestValue = CFDataCreate(NULL, agileHash.digestValue.Data, agileHash.digestValue.Length);
938 CFDictionaryAddValue(dictionary, digestTag, digestValue);
939
940 loser:
941 CFReleaseNull(digestValue);
942 CFReleaseNull(digestTag);
943 if (tmppoolp) {
944 PORT_FreeArena(tmppoolp, PR_FALSE);
945 }
946 return status;
947 }
948
949 /*!
950 @function
951 @abstract Return the data in the signed Codesigning Hash Agility V2 attribute.
952 @param sinfo SignerInfo data for this signer, pointer to a CFDictionaryRef for attribute values
953 @discussion Returns a CFDictionaryRef containing the values of the attribute
954 @result A return value of errSecInternal is an error trying to look up the oid.
955 A status value of success with null result data indicates the attribute was not present.
956 */
957 OSStatus
958 SecCmsSignerInfoGetAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef sinfo, CFDictionaryRef *sdict)
959 {
960 SecCmsAttribute *attr;
961
962 if (sinfo == NULL || sdict == NULL) {
963 return errSecParam;
964 }
965
966 *sdict = NULL;
967
968 if (sinfo->hashAgilityV2AttrValues != NULL) {
969 *sdict = sinfo->hashAgilityV2AttrValues; /* cached copy */
970 return SECSuccess;
971 }
972
973 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_HASH_AGILITY_V2, PR_TRUE);
974
975 /* attribute not found */
976 if (attr == NULL) {
977 return SECSuccess;
978 }
979
980 /* attrValues SET OF AttributeValue
981 * AttributeValue ::= ANY
982 */
983 SecAsn1Item **values = attr->values;
984 if (values == NULL) { /* There must be values */
985 return errSecDecode;
986 }
987
988 CFMutableDictionaryRef agileHashValues = CFDictionaryCreateMutable(NULL, SecCmsArrayCount((void **)values),
989 &kCFTypeDictionaryKeyCallBacks,
990 &kCFTypeDictionaryValueCallBacks);
991 while (*values != NULL) {
992 (void)CMSAddAgileHashToDictionary(agileHashValues, *values++);
993 }
994 if (CFDictionaryGetCount(agileHashValues) != SecCmsArrayCount((void **)attr->values)) {
995 CFReleaseNull(agileHashValues);
996 return errSecDecode;
997 }
998
999 sinfo->hashAgilityV2AttrValues = agileHashValues; /* make cached copy */
1000 if (sinfo->hashAgilityV2AttrValues) {
1001 *sdict = sinfo->hashAgilityV2AttrValues;
1002 return SECSuccess;
1003 }
1004 return errSecAllocate;
1005 }
1006
1007 /*
1008 * SecCmsSignerInfoGetAppleExpirationTime - return the expiration time,
1009 * in UTCTime format, of a CMS signerInfo.
1010 *
1011 * sinfo - signerInfo data for this signer
1012 *
1013 * Returns a pointer to XXXX (what?)
1014 * A return value of NULL is an error.
1015 */
1016 OSStatus
1017 SecCmsSignerInfoGetAppleExpirationTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *etime)
1018 {
1019 SecCmsAttribute *attr = NULL;
1020 SecAsn1Item * value = NULL;
1021
1022 if (sinfo == NULL || etime == NULL) {
1023 return SECFailure;
1024 }
1025
1026 if (sinfo->expirationTime != 0) {
1027 *etime = sinfo->expirationTime; /* cached copy */
1028 return SECSuccess;
1029 }
1030
1031 attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_APPLE_EXPIRATION_TIME, PR_TRUE);
1032 if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL) {
1033 return SECFailure;
1034 }
1035 if (DER_UTCTimeToCFDate(value, etime) != SECSuccess) {
1036 return SECFailure;
1037 }
1038 sinfo->expirationTime = *etime; /* make cached copy */
1039 return SECSuccess;
1040 }
1041
1042 /*
1043 * Return the signing cert of a CMS signerInfo.
1044 *
1045 * the certs in the enclosing SignedData must have been imported already
1046 */
1047 SecCertificateRef
1048 SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
1049 {
1050 SecCertificateRef cert = NULL;
1051
1052 if (signerinfo->cert != NULL)
1053 return signerinfo->cert;
1054
1055 /* @@@ Make sure we search though all the certs in the cms message itself as well, it's silly
1056 to require them to be added to a keychain first. */
1057
1058 #if USE_CDSA_CRYPTO
1059 SecCmsSignerIdentifier *sid;
1060
1061 /*
1062 * This cert will also need to be freed, but since we save it
1063 * in signerinfo for later, we do not want to destroy it when
1064 * we leave this function -- we let the clean-up of the entire
1065 * cinfo structure later do the destroy of this cert.
1066 */
1067 sid = &signerinfo->signerIdentifier;
1068 switch (sid->identifierType) {
1069 case SecCmsSignerIDIssuerSN:
1070 cert = CERT_FindCertByIssuerAndSN(keychainOrArray, sid->id.issuerAndSN);
1071 break;
1072 case SecCmsSignerIDSubjectKeyID:
1073 cert = CERT_FindCertBySubjectKeyID(keychainOrArray, sid->id.subjectKeyID);
1074 break;
1075 default:
1076 cert = NULL;
1077 break;
1078 }
1079
1080 /* cert can be NULL at that point */
1081 signerinfo->cert = cert; /* earmark it */
1082 #else
1083 SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
1084 SecAsn1Item *cert_data;
1085 if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
1086 cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
1087 if (cert) {
1088 switch (signerinfo->signerIdentifier.identifierType) {
1089 case SecCmsSignerIDIssuerSN:
1090 if (CERT_CheckIssuerAndSerial(cert,
1091 &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
1092 &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
1093 signerinfo->cert = cert;
1094 break;
1095 case SecCmsSignerIDSubjectKeyID: {
1096 CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
1097 SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
1098 if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
1099 !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
1100 signerinfo->cert = cert;
1101 }
1102 }
1103 if (signerinfo->cert)
1104 break;
1105 CFReleaseNull(cert);
1106 }
1107 cert_datas++;
1108 }
1109
1110 if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN)) {
1111 cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
1112 signerinfo->cert = cert;
1113 }
1114 if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)) {
1115 cert = CERT_FindCertificateBySubjectKeyID(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.subjectKeyID);
1116 signerinfo->cert = cert;
1117 }
1118 #endif
1119
1120 return cert;
1121 }
1122
1123
1124 /*
1125 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
1126 *
1127 * sinfo - signerInfo data for this signer
1128 *
1129 * Returns a CFStringRef containing the common name of the signer.
1130 * A return value of NULL is an error.
1131 */
1132 CFStringRef
1133 SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
1134 {
1135 SecCertificateRef signercert;
1136 CFStringRef commonName = NULL;
1137
1138 /* will fail if cert is not verified */
1139 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1140 return NULL;
1141
1142 #if USE_CDSA_CRYPTO
1143 SecCertificateGetCommonName(signercert, &commonName);
1144 #else
1145 CFArrayRef commonNames = SecCertificateCopyCommonNames(signercert);
1146 if (commonNames) {
1147 /* SecCertificateCopyCommonNames doesn't return empty arrays */
1148 commonName = (CFStringRef)CFArrayGetValueAtIndex(commonNames, CFArrayGetCount(commonNames) - 1);
1149 CFRetain(commonName);
1150 CFRelease(commonNames);
1151 }
1152 #endif
1153
1154 return commonName;
1155 }
1156
1157 /*
1158 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
1159 *
1160 * sinfo - signerInfo data for this signer
1161 *
1162 * Returns a CFStringRef containing the name of the signer.
1163 * A return value of NULL is an error.
1164 */
1165 CFStringRef
1166 SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
1167 {
1168 SecCertificateRef signercert;
1169 CFStringRef emailAddress = NULL;
1170
1171 if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
1172 return NULL;
1173
1174 #if USE_CDSA_CRYPTO
1175 SecCertificateGetEmailAddress(signercert, &emailAddress);
1176 #else
1177 CFArrayRef names = SecCertificateCopyRFC822Names(signercert);
1178 if (names) {
1179 if (CFArrayGetCount(names) > 0)
1180 emailAddress = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
1181 if (emailAddress)
1182 CFRetain(emailAddress);
1183 CFRelease(names);
1184 }
1185 #endif
1186 return emailAddress;
1187 }
1188
1189
1190 /*
1191 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
1192 * authenticated (i.e. signed) attributes of "signerinfo".
1193 */
1194 OSStatus
1195 SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1196 {
1197 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->authAttr), attr);
1198 }
1199
1200 /*
1201 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
1202 * unauthenticated attributes of "signerinfo".
1203 */
1204 OSStatus
1205 SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
1206 {
1207 return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->unAuthAttr), attr);
1208 }
1209
1210 /*
1211 * SecCmsSignerInfoAddSigningTime - add the signing time to the
1212 * authenticated (i.e. signed) attributes of "signerinfo".
1213 *
1214 * This is expected to be included in outgoing signed
1215 * messages for email (S/MIME) but is likely useful in other situations.
1216 *
1217 * This should only be added once; a second call will do nothing.
1218 *
1219 * XXX This will probably just shove the current time into "signerinfo"
1220 * but it will not actually get signed until the entire item is
1221 * processed for encoding. Is this (expected to be small) delay okay?
1222 */
1223 OSStatus
1224 SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1225 {
1226 SecCmsAttribute *attr;
1227 SecAsn1Item stime;
1228 void *mark;
1229 PLArenaPool *poolp;
1230
1231 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1232
1233 mark = PORT_ArenaMark(poolp);
1234
1235 /* create new signing time attribute */
1236 if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1237 goto loser;
1238
1239 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1240 SECITEM_FreeItem (&stime, PR_FALSE);
1241 goto loser;
1242 }
1243
1244 SECITEM_FreeItem (&stime, PR_FALSE);
1245
1246 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1247 goto loser;
1248
1249 PORT_ArenaUnmark (poolp, mark);
1250
1251 return SECSuccess;
1252
1253 loser:
1254 PORT_ArenaRelease (poolp, mark);
1255 return SECFailure;
1256 }
1257
1258 /*
1259 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1260 * authenticated (i.e. signed) attributes of "signerinfo".
1261 *
1262 * This is expected to be included in outgoing signed
1263 * messages for email (S/MIME).
1264 */
1265 OSStatus
1266 SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1267 {
1268 SecCmsAttribute *attr;
1269 SecAsn1Item * smimecaps = NULL;
1270 void *mark;
1271 PLArenaPool *poolp;
1272
1273 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1274
1275 mark = PORT_ArenaMark(poolp);
1276
1277 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1278 if (smimecaps == NULL)
1279 goto loser;
1280
1281 /* create new signing time attribute */
1282 #if 1
1283 // @@@ We don't do Fortezza yet.
1284 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, PR_FALSE) != SECSuccess)
1285 #else
1286 if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1287 PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1288 #endif
1289 goto loser;
1290
1291 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1292 goto loser;
1293
1294 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1295 goto loser;
1296
1297 PORT_ArenaUnmark (poolp, mark);
1298 return SECSuccess;
1299
1300 loser:
1301 PORT_ArenaRelease (poolp, mark);
1302 return SECFailure;
1303 }
1304
1305 /*
1306 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1307 * authenticated (i.e. signed) attributes of "signerinfo".
1308 *
1309 * This is expected to be included in outgoing signed messages for email (S/MIME).
1310 */
1311 OSStatus
1312 SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1313 {
1314 SecCmsAttribute *attr;
1315 SecAsn1Item * smimeekp = NULL;
1316 void *mark;
1317 PLArenaPool *poolp;
1318
1319 #if 0
1320 CFTypeRef policy;
1321
1322 /* verify this cert for encryption */
1323 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1324 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1325 CFRelease(policy);
1326 return SECFailure;
1327 }
1328 CFRelease(policy);
1329 #endif
1330
1331 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1332 mark = PORT_ArenaMark(poolp);
1333
1334 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1335 if (smimeekp == NULL)
1336 goto loser;
1337
1338 /* create new signing time attribute */
1339 if (SecSMIMECreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
1340 goto loser;
1341
1342 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1343 goto loser;
1344
1345 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1346 goto loser;
1347
1348 PORT_ArenaUnmark (poolp, mark);
1349 return SECSuccess;
1350
1351 loser:
1352 PORT_ArenaRelease (poolp, mark);
1353 return SECFailure;
1354 }
1355
1356 /*
1357 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1358 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
1359 *
1360 * This is expected to be included in outgoing signed messages for email (S/MIME),
1361 * if compatibility with Microsoft mail clients is wanted.
1362 */
1363 OSStatus
1364 SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1365 {
1366 SecCmsAttribute *attr;
1367 SecAsn1Item * smimeekp = NULL;
1368 void *mark;
1369 PLArenaPool *poolp;
1370
1371 #if 0
1372 CFTypeRef policy;
1373
1374 /* verify this cert for encryption */
1375 policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1376 if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1377 CFRelease(policy);
1378 return SECFailure;
1379 }
1380 CFRelease(policy);
1381 #endif
1382
1383 poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1384 mark = PORT_ArenaMark(poolp);
1385
1386 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1387 if (smimeekp == NULL)
1388 goto loser;
1389
1390 /* create new signing time attribute */
1391 if (SecSMIMECreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
1392 goto loser;
1393
1394 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1395 goto loser;
1396
1397 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1398 goto loser;
1399
1400 PORT_ArenaUnmark (poolp, mark);
1401 return SECSuccess;
1402
1403 loser:
1404 PORT_ArenaRelease (poolp, mark);
1405 return SECFailure;
1406 }
1407
1408 /*
1409 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1410 *
1411 * 1. digest the DER-encoded signature value of the original signerinfo
1412 * 2. create new signerinfo with correct version, sid, digestAlg
1413 * 3. add message-digest authAttr, but NO content-type
1414 * 4. sign the authAttrs
1415 * 5. DER-encode the new signerInfo
1416 * 6. add the whole thing to original signerInfo's unAuthAttrs
1417 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1418 *
1419 * XXXX give back the new signerinfo?
1420 */
1421 OSStatus
1422 SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1423 SECOidTag digestalg, SecIdentityRef identity)
1424 {
1425 /* XXXX TBD XXXX */
1426 return SECFailure;
1427 }
1428
1429 /*!
1430 @function
1431 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1432 @discussion This is expected to be included in outgoing Apple code signatures.
1433 */
1434 OSStatus
1435 SecCmsSignerInfoAddAppleCodesigningHashAgility(SecCmsSignerInfoRef signerinfo, CFDataRef attrValue)
1436 {
1437 SecCmsAttribute *attr;
1438 PLArenaPool *poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1439 void *mark = PORT_ArenaMark(poolp);
1440 OSStatus status = SECFailure;
1441
1442 /* The value is required for this attribute. */
1443 if (!attrValue) {
1444 status = errSecParam;
1445 goto loser;
1446 }
1447
1448 /*
1449 * SecCmsAttributeCreate makes a copy of the data in value, so
1450 * we don't need to copy into the CSSM_DATA struct.
1451 */
1452 SecAsn1Item value;
1453 value.Length = CFDataGetLength(attrValue);
1454 value.Data = (uint8_t *)CFDataGetBytePtr(attrValue);
1455
1456 if ((attr = SecCmsAttributeCreate(poolp,
1457 SEC_OID_APPLE_HASH_AGILITY,
1458 &value,
1459 PR_FALSE)) == NULL) {
1460 status = errSecAllocate;
1461 goto loser;
1462 }
1463
1464 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1465 status = errSecInternal;
1466 goto loser;
1467 }
1468
1469 PORT_ArenaUnmark(poolp, mark);
1470 return SECSuccess;
1471
1472 loser:
1473 PORT_ArenaRelease(poolp, mark);
1474 return status;
1475 }
1476
1477 static OSStatus CMSAddAgileHashToAttribute(PLArenaPool *poolp, SecCmsAttribute *attr, CFNumberRef cftag, CFDataRef value) {
1478 PLArenaPool *tmppoolp = NULL;
1479 int64_t tag;
1480 SECOidData *digestOid = NULL;
1481 CMSAppleAgileHash agileHash;
1482 SecAsn1Item attrValue = { .Data = NULL, .Length = 0 };
1483 OSStatus status = errSecSuccess;
1484
1485 memset(&agileHash, 0, sizeof(agileHash));
1486
1487 if(!CFNumberGetValue(cftag, kCFNumberSInt64Type, &tag)) {
1488 return errSecParam;
1489 }
1490 digestOid = SECOID_FindOIDByTag((SECOidTag)tag);
1491
1492 agileHash.digestValue.Data = (uint8_t *)CFDataGetBytePtr(value);
1493 agileHash.digestValue.Length = CFDataGetLength(value);
1494 agileHash.digestOID.Data = digestOid->oid.Data;
1495 agileHash.digestOID.Length = digestOid->oid.Length;
1496
1497 tmppoolp = PORT_NewArena(1024);
1498 if (tmppoolp == NULL) {
1499 return errSecAllocate;
1500 }
1501
1502 if (SEC_ASN1EncodeItem(tmppoolp, &attrValue, &agileHash, CMSAppleAgileHashTemplate) == NULL) {
1503 status = errSecParam;
1504 goto loser;
1505 }
1506
1507 status = SecCmsAttributeAddValue(poolp, attr, &attrValue);
1508
1509 loser:
1510 if (tmppoolp) {
1511 PORT_FreeArena(tmppoolp, PR_FALSE);
1512 }
1513 return status;
1514 }
1515
1516 /*!
1517 @function
1518 @abstract Add the Apple Codesigning Hash Agility attribute to the authenticated (i.e. signed) attributes of "signerinfo".
1519 @discussion This is expected to be included in outgoing Apple code signatures.
1520 */
1521 OSStatus
1522 SecCmsSignerInfoAddAppleCodesigningHashAgilityV2(SecCmsSignerInfoRef signerinfo, CFDictionaryRef attrValues)
1523 {
1524 __block SecCmsAttribute *attr;
1525 __block PLArenaPool *poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1526 void *mark = PORT_ArenaMark(poolp);
1527 OSStatus status = SECFailure;
1528
1529 /* The value is required for this attribute. */
1530 if (!attrValues) {
1531 status = errSecParam;
1532 goto loser;
1533 }
1534
1535 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_HASH_AGILITY_V2,
1536 NULL, PR_TRUE)) == NULL) {
1537 status = errSecAllocate;
1538 goto loser;
1539 }
1540
1541 CFDictionaryForEach(attrValues, ^(const void *key, const void *value) {
1542 if (!isNumber(key) || !isData(value)) {
1543 return;
1544 }
1545 (void)CMSAddAgileHashToAttribute(poolp, attr, (CFNumberRef)key, (CFDataRef)value);
1546 });
1547
1548 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1549 status = errSecInternal;
1550 goto loser;
1551 }
1552
1553 PORT_ArenaUnmark(poolp, mark);
1554 return SECSuccess;
1555
1556 loser:
1557 PORT_ArenaRelease(poolp, mark);
1558 return status;
1559 }
1560
1561 /*
1562 * SecCmsSignerInfoAddAppleExpirationTime - add the expiration time to the
1563 * authenticated (i.e. signed) attributes of "signerinfo".
1564 *
1565 * This is expected to be included in outgoing signed
1566 * messages for Asset Receipts but is likely useful in other situations.
1567 *
1568 * This should only be added once; a second call will do nothing.
1569 */
1570 OSStatus
1571 SecCmsSignerInfoAddAppleExpirationTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
1572 {
1573 SecCmsAttribute *attr = NULL;
1574 PLArenaPool *poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1575 void *mark = PORT_ArenaMark(poolp);
1576
1577 /* create new expiration time attribute */
1578 SecAsn1Item etime;
1579 if (DER_CFDateToUTCTime(t, &etime) != SECSuccess) {
1580 goto loser;
1581 }
1582
1583 if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_APPLE_EXPIRATION_TIME, &etime, PR_FALSE)) == NULL) {
1584 SECITEM_FreeItem (&etime, PR_FALSE);
1585 goto loser;
1586 }
1587
1588 SECITEM_FreeItem(&etime, PR_FALSE);
1589
1590 if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) {
1591 goto loser;
1592 }
1593
1594 PORT_ArenaUnmark(poolp, mark);
1595 return SECSuccess;
1596
1597 loser:
1598 PORT_ArenaRelease(poolp, mark);
1599 return SECFailure;
1600 }
1601
1602 SecCertificateRef SecCmsSignerInfoCopyCertFromEncryptionKeyPreference(SecCmsSignerInfoRef signerinfo) {
1603 SecCertificateRef cert = NULL;
1604 SecCmsAttribute *attr;
1605 SecAsn1Item *ekp;
1606
1607 /* sanity check - see if verification status is ok (unverified does not count...) */
1608 if (signerinfo->verificationStatus != SecCmsVSGoodSignature)
1609 return NULL;
1610
1611 /* Prep the rawCerts */
1612 SecAsn1Item **rawCerts = NULL;
1613 if (signerinfo->signedData) {
1614 rawCerts = signerinfo->signedData->rawCerts;
1615 }
1616
1617 /* find preferred encryption cert */
1618 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1619 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1620 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1621 { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1622 ekp = SecCmsAttributeGetValue(attr);
1623 if (ekp == NULL)
1624 return NULL;
1625 cert = SecSMIMEGetCertFromEncryptionKeyPreference(rawCerts, ekp);
1626 }
1627 if (cert) return cert;
1628
1629 if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) &&
1630 (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
1631 SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
1632 { /* we have a MS_SMIME_ENCRYPTION_KEY_PREFERENCE attribute! Find the cert. */
1633 ekp = SecCmsAttributeGetValue(attr);
1634 if (ekp == NULL)
1635 return NULL;
1636 cert = SecSMIMEGetCertFromEncryptionKeyPreference(rawCerts, ekp);
1637 }
1638 return cert;
1639 }
1640
1641 /*
1642 * XXXX the following needs to be done in the S/MIME layer code
1643 * after signature of a signerinfo is verified
1644 */
1645 OSStatus
1646 SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1647 {
1648 return -4 /*unImp*/;
1649 }
1650
1651 /*
1652 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1653 */
1654 OSStatus
1655 SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1656 {
1657 if (signerinfo->cert == NULL) {
1658 return SECFailure;
1659 }
1660
1661 /* don't leak if we get called twice */
1662 if (signerinfo->certList != NULL) {
1663 CFRelease(signerinfo->certList);
1664 signerinfo->certList = NULL;
1665 }
1666
1667 switch (cm) {
1668 case SecCmsCMNone:
1669 signerinfo->certList = NULL;
1670 break;
1671 case SecCmsCMCertOnly:
1672 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1673 break;
1674 case SecCmsCMCertChain:
1675 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE, PR_FALSE);
1676 break;
1677 case SecCmsCMCertChainWithRoot:
1678 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE, PR_FALSE);
1679 break;
1680 case SecCmsCMCertChainWithRootOrFail:
1681 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE, PR_TRUE);
1682 break;
1683 }
1684
1685 if (cm != SecCmsCMNone && signerinfo->certList == NULL) {
1686 return SECFailure;
1687 }
1688
1689 return SECSuccess;
1690 }