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