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