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
87static OSStatus
88DER_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
153loser:
154    return SECFailure;
155}
156
157static OSStatus
158DER_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 */
197SecCmsSignerInfoRef
198nss_cmssignerinfo_create(SecCmsMessageRef cmsg, SecCmsSignerIDSelector type, SecCertificateRef cert, CSSM_DATA_PTR subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
199
200SecCmsSignerInfoRef
201SecCmsSignerInfoCreateWithSubjKeyID(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
206SecCmsSignerInfoRef
207SecCmsSignerInfoCreate(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
220loser:
221    if (cert)
222	CFRelease(cert);
223    if (signingKey)
224	CFRelease(signingKey);
225
226    return signerInfo;
227}
228
229SecCmsSignerInfoRef
230nss_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
296loser:
297    PORT_ArenaRelease(poolp, mark);
298    return NULL;
299}
300
301/*
302 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
303 */
304void
305SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
306{
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 */
329OSStatus
330SecCmsSignerInfoSign(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
482loser:
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
495OSStatus
496SecCmsSignerInfoVerifyCertificate(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
539static 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 */
562OSStatus
563SecCmsSignerInfoVerify(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
732loser:
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
744OSStatus
745SecCmsSignerInfoVerifyUnAuthAttrs(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);
769xit:
770    return status;
771}
772
773CSSM_DATA *
774SecCmsSignerInfoGetEncDigest(SecCmsSignerInfoRef signerinfo)
775{
776    return &signerinfo->encDigest;
777}
778
779SecCmsVerificationStatus
780SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
781{
782    return signerinfo->verificationStatus;
783}
784
785SECOidData *
786SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
787{
788    return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
789}
790
791SECOidTag
792SecCmsSignerInfoGetDigestAlgTag(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
803CFArrayRef
804SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
805{
806    dprintfRC("SecCmsSignerInfoGetCertList: certList.rc %d\n",
807	    (int)CFGetRetainCount(signerinfo->certList));
808    return signerinfo->certList;
809}
810
811CFArrayRef
812SecCmsSignerInfoGetTimestampCertList(SecCmsSignerInfoRef signerinfo)
813{
814    dprintfRC("SecCmsSignerInfoGetCertList: timestampCertList.rc %d\n",
815	    (int)CFGetRetainCount(signerinfo->timestampCertList));
816    return signerinfo->timestampCertList;
817}
818
819
820
821int
822SecCmsSignerInfoGetVersion(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 */
842OSStatus
843SecCmsSignerInfoGetSigningTime(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
866OSStatus
867SecCmsSignerInfoGetTimestampTime(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;
882xit:
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 */
891SecCertificateRef
892SecCmsSignerInfoGetSigningCertificate(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 */
947CFStringRef
948SecCmsSignerInfoGetSignerCommonName(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 */
970CFStringRef
971SecCmsSignerInfoGetSignerEmailAddress(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 */
989OSStatus
990SecCmsSignerInfoAddAuthAttr(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 */
999OSStatus
1000SecCmsSignerInfoAddUnauthAttr(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 */
1018OSStatus
1019SecCmsSignerInfoAddSigningTime(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
1048loser:
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 */
1060OSStatus
1061SecCmsSignerInfoAddSMIMECaps(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
1095loser:
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 */
1106OSStatus
1107SecCmsSignerInfoAddSMIMEEncKeyPrefs(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
1146loser:
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 */
1158OSStatus
1159SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(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
1198loser:
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/*
1216Countersignature 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
1234OSStatus
1235SecCmsSignerInfoAddTimeStamp(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
1252loser:
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 */
1270OSStatus
1271SecCmsSignerInfoAddCounterSignature(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 */
1282OSStatus
1283SecCmsSignerInfoSaveSMIMEProfile(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 */
1377OSStatus
1378SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1379{
1380    if (signerinfo->cert == NULL)
1381	return SECFailure;
1382
1383    /* don't leak if we get called twice */
1384    if (signerinfo->certList != NULL) {
1385	CFRelease(signerinfo->certList);
1386	signerinfo->certList = NULL;
1387    }
1388
1389    switch (cm) {
1390    case SecCmsCMNone:
1391	signerinfo->certList = NULL;
1392	break;
1393    case SecCmsCMCertOnly:
1394	signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1395	break;
1396    case SecCmsCMCertChain:
1397	signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1398	break;
1399    case SecCmsCMCertChainWithRoot:
1400	signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1401	break;
1402    }
1403
1404    if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1405	return SECFailure;
1406
1407    return SECSuccess;
1408}
1409