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