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