1/*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation.  Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above.  If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL.  If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34/*
35 * CMS signerInfo methods.
36 */
37
38#include <Security/SecCmsSignerInfo.h>
39#include "SecSMIMEPriv.h"
40
41#include "cmslocal.h"
42
43#include "cert.h"
44#include "SecAsn1Item.h"
45#include "secoid.h"
46#include "cryptohi.h"
47
48#include <security_asn1/secasn1.h>
49#include <security_asn1/secerr.h>
50#include <security_asn1/secport.h>
51
52#if USE_CDSA_CRYPTO
53#include <Security/SecKeychain.h>
54#endif
55
56#include <Security/SecIdentity.h>
57#include <Security/SecCertificateInternal.h>
58#include <Security/SecInternal.h>
59#include <Security/SecKeyPriv.h>
60#include <utilities/SecCFWrappers.h>
61#include <CoreFoundation/CFTimeZone.h>
62
63
64#define HIDIGIT(v) (((v) / 10) + '0')
65#define LODIGIT(v) (((v) % 10) + '0')
66
67#define ISDIGIT(dig) (((dig) >= '0') && ((dig) <= '9'))
68#define CAPTURE(var,p,label)                              \
69{                                                         \
70    if (!ISDIGIT((p)[0]) || !ISDIGIT((p)[1])) goto label; \
71    (var) = ((p)[0] - '0') * 10 + ((p)[1] - '0');         \
72}
73
74
75static OSStatus
76DER_UTCTimeToCFDate(const SecAsn1Item * utcTime, CFAbsoluteTime *date)
77{
78    char *string = (char *)utcTime->Data;
79    int year, month, mday, hour, minute, second, hourOff, minOff;
80
81    /* Verify time is formatted properly and capture information */
82    second = 0;
83    hourOff = 0;
84    minOff = 0;
85    CAPTURE(year,string+0,loser);
86    if (year < 50) {
87        /* ASSUME that year # is in the 2000's, not the 1900's */
88        year += 2000;
89    } else {
90        year += 1900;
91    }
92    CAPTURE(month,string+2,loser);
93    if ((month == 0) || (month > 12)) goto loser;
94    CAPTURE(mday,string+4,loser);
95    if ((mday == 0) || (mday > 31)) goto loser;
96    CAPTURE(hour,string+6,loser);
97    if (hour > 23) goto loser;
98    CAPTURE(minute,string+8,loser);
99    if (minute > 59) goto loser;
100    if (ISDIGIT(string[10])) {
101        CAPTURE(second,string+10,loser);
102        if (second > 59) goto loser;
103        string += 2;
104    }
105    if (string[10] == '+') {
106        CAPTURE(hourOff,string+11,loser);
107        if (hourOff > 23) goto loser;
108        CAPTURE(minOff,string+13,loser);
109        if (minOff > 59) goto loser;
110    } else if (string[10] == '-') {
111        CAPTURE(hourOff,string+11,loser);
112        if (hourOff > 23) goto loser;
113        hourOff = -hourOff;
114        CAPTURE(minOff,string+13,loser);
115        if (minOff > 59) goto loser;
116        minOff = -minOff;
117    } else if (string[10] != 'Z') {
118        goto loser;
119    }
120
121    if (hourOff == 0 && minOff == 0) {
122        *date = CFAbsoluteTimeForGregorianZuluMoment(year, month, mday, hour, minute, second);
123    } else {
124        CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, (hourOff * 60 + minOff) * 60);
125        *date = CFAbsoluteTimeForGregorianMoment(tz, year, month, mday, hour, minute, second);
126        CFReleaseSafe(tz);
127    }
128
129    return SECSuccess;
130
131loser:
132    return SECFailure;
133}
134
135static OSStatus
136DER_CFDateToUTCTime(CFAbsoluteTime date, SecAsn1Item * utcTime)
137{
138    unsigned char *d;
139
140    utcTime->Length = 13;
141    utcTime->Data = d = PORT_Alloc(13);
142    if (!utcTime->Data)
143	return SECFailure;
144
145    int year;
146    int month;
147    int day;
148    int hour;
149    int minute;
150    int second;
151
152    if (!CFCalendarDecomposeAbsoluteTime(SecCFCalendarGetZulu(), date, "yMdHms", &year, &month, &day, &hour, &minute, &second))
153        return SECFailure;
154
155
156    /* UTC time does not handle the years before 1950 */
157    if (year < 1950)
158        return SECFailure;
159
160    /* remove the century since it's added to the year by the
161       CFAbsoluteTimeGetGregorianDate routine, but is not needed for UTC time */
162    year %= 100;
163
164    d[0] = HIDIGIT(year);
165    d[1] = LODIGIT(year);
166    d[2] = HIDIGIT(month);
167    d[3] = LODIGIT(month);
168    d[4] = HIDIGIT(day);
169    d[5] = LODIGIT(day);
170    d[6] = HIDIGIT(hour);
171    d[7] = LODIGIT(hour);
172    d[8] = HIDIGIT(minute);
173    d[9] = LODIGIT(minute);
174    d[10] = HIDIGIT(second);
175    d[11] = LODIGIT(second);
176    d[12] = 'Z';
177    return SECSuccess;
178}
179
180/* =============================================================================
181 * SIGNERINFO
182 */
183SecCmsSignerInfoRef
184nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag);
185
186SecCmsSignerInfoRef
187SecCmsSignerInfoCreateWithSubjKeyID(SecCmsSignedDataRef sigd, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
188{
189    return nss_cmssignerinfo_create(sigd, SecCmsSignerIDSubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag);
190}
191
192SecCmsSignerInfoRef
193SecCmsSignerInfoCreate(SecCmsSignedDataRef sigd, SecIdentityRef identity, SECOidTag digestalgtag)
194{
195    SecCmsSignerInfoRef signerInfo = NULL;
196    SecCertificateRef cert = NULL;
197    SecPrivateKeyRef signingKey = NULL;
198
199    if (SecIdentityCopyCertificate(identity, &cert))
200	goto loser;
201    if (SecIdentityCopyPrivateKey(identity, &signingKey))
202	goto loser;
203
204    signerInfo = nss_cmssignerinfo_create(sigd, SecCmsSignerIDIssuerSN, cert, NULL, NULL, signingKey, digestalgtag);
205
206loser:
207    if (cert)
208	CFRelease(cert);
209    if (signingKey)
210	CFRelease(signingKey);
211
212    return signerInfo;
213}
214
215SecCmsSignerInfoRef
216nss_cmssignerinfo_create(SecCmsSignedDataRef sigd, SecCmsSignerIDSelector type, SecCertificateRef cert, const SecAsn1Item *subjKeyID, SecPublicKeyRef pubKey, SecPrivateKeyRef signingKey, SECOidTag digestalgtag)
217{
218    void *mark;
219    SecCmsSignerInfoRef signerinfo;
220    int version;
221    PLArenaPool *poolp;
222
223    poolp = sigd->contentInfo.cmsg->poolp;
224
225    mark = PORT_ArenaMark(poolp);
226
227    signerinfo = (SecCmsSignerInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsSignerInfo));
228    if (signerinfo == NULL) {
229	PORT_ArenaRelease(poolp, mark);
230	return NULL;
231    }
232
233
234    signerinfo->signedData = sigd;
235
236    switch(type) {
237    case SecCmsSignerIDIssuerSN:
238        signerinfo->signerIdentifier.identifierType = SecCmsSignerIDIssuerSN;
239        if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
240	    goto loser;
241        if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
242	    goto loser;
243        break;
244    case SecCmsSignerIDSubjectKeyID:
245        signerinfo->signerIdentifier.identifierType = SecCmsSignerIDSubjectKeyID;
246        PORT_Assert(subjKeyID);
247        if (!subjKeyID)
248            goto loser;
249        signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
250        SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
251                         subjKeyID);
252        signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
253        if (!signerinfo->pubKey)
254            goto loser;
255        break;
256    default:
257        goto loser;
258    }
259
260    if (!signingKey)
261	goto loser;
262
263    signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
264    if (!signerinfo->signingKey)
265	goto loser;
266
267    /* set version right now */
268    version = SEC_CMS_SIGNER_INFO_VERSION_ISSUERSN;
269    /* RFC2630 5.3 "version is the syntax version number. If the .... " */
270    if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID)
271	version = SEC_CMS_SIGNER_INFO_VERSION_SUBJKEY;
272    (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
273
274    if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
275	goto loser;
276
277    if (SecCmsSignedDataAddSignerInfo(sigd, signerinfo))
278	goto loser;
279
280    PORT_ArenaUnmark(poolp, mark);
281    return signerinfo;
282
283loser:
284    PORT_ArenaRelease(poolp, mark);
285    return NULL;
286}
287
288/*
289 * SecCmsSignerInfoDestroy - destroy a SignerInfo data structure
290 */
291void
292SecCmsSignerInfoDestroy(SecCmsSignerInfoRef si)
293{
294    if (si->cert != NULL)
295	CERT_DestroyCertificate(si->cert);
296
297    if (si->certList != NULL)
298	CFRelease(si->certList);
299
300    /* XXX storage ??? */
301}
302
303static SecAsn1AlgId SecCertificateGetPublicKeyAlgorithmID(SecCertificateRef cert)
304{
305    const DERAlgorithmId *length_data_swapped = SecCertificateGetPublicKeyAlgorithm(cert);
306    SecAsn1AlgId temp = {
307        { length_data_swapped->oid.length, length_data_swapped->oid.data },
308        { length_data_swapped->params.length, length_data_swapped->params.data } };
309
310    return temp;
311}
312
313/*
314 * SecCmsSignerInfoSign - sign something
315 *
316 */
317OSStatus
318SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
319{
320    SecCertificateRef cert;
321    SecPrivateKeyRef privkey = NULL;
322    SECOidTag digestalgtag;
323    SECOidTag pubkAlgTag;
324    SecAsn1Item signature = { 0 };
325    OSStatus rv;
326    PLArenaPool *poolp, *tmppoolp;
327    const SECAlgorithmID *algID = NULL;
328    //CERTSubjectPublicKeyInfo *spki;
329
330    PORT_Assert (digest != NULL);
331
332    poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
333
334    switch (signerinfo->signerIdentifier.identifierType) {
335    case SecCmsSignerIDIssuerSN:
336        privkey = signerinfo->signingKey;
337        signerinfo->signingKey = NULL;
338        cert = signerinfo->cert;
339#if USE_CDSA_CRYPTO
340	if (SecCertificateGetAlgorithmID(cert,&algID)) {
341	    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
342	    goto loser;
343        }
344#else
345        SecAsn1AlgId _algID = SecCertificateGetPublicKeyAlgorithmID(cert);
346        algID = &_algID;
347#endif
348        break;
349    case SecCmsSignerIDSubjectKeyID:
350        privkey = signerinfo->signingKey;
351        signerinfo->signingKey = NULL;
352#if 0
353        spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
354        SECKEY_DestroyPublicKey(signerinfo->pubKey);
355        signerinfo->pubKey = NULL;
356        SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
357        SECKEY_DestroySubjectPublicKeyInfo(spki);
358        algID = &freeAlgID;
359#else
360#if USE_CDSA_CRYPTO
361	if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) {
362	    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
363	    goto loser;
364        }
365#endif
366#endif
367	CFRelease(signerinfo->pubKey);
368        signerinfo->pubKey = NULL;
369        break;
370    default:
371        PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
372        goto loser;
373    }
374    digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo);
375    pubkAlgTag = SECOID_GetAlgorithmTag(algID);
376#if USE_CDSA_CRYPTO
377    if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) {
378      SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
379    }
380#endif
381#if 0
382    // @@@ Not yet
383    /* Fortezza MISSI have weird signature formats.
384     * Map them to standard DSA formats
385     */
386    pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
387#endif
388
389    if (signerinfo->authAttr != NULL) {
390	SecAsn1Item encoded_attrs;
391
392	/* find and fill in the message digest attribute. */
393	rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
394	                       SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
395	if (rv != SECSuccess)
396	    goto loser;
397
398	if (contentType != NULL) {
399	    /* if the caller wants us to, find and fill in the content type attribute. */
400	    rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr),
401	                    SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
402	    if (rv != SECSuccess)
403		goto loser;
404	}
405
406	if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
407	    PORT_SetError(SEC_ERROR_NO_MEMORY);
408	    goto loser;
409	}
410
411	/*
412	 * Before encoding, reorder the attributes so that when they
413	 * are encoded, they will be conforming DER, which is required
414	 * to have a specific order and that is what must be used for
415	 * the hash/signature.  We do this here, rather than building
416	 * it into EncodeAttributes, because we do not want to do
417	 * such reordering on incoming messages (which also uses
418	 * EncodeAttributes) or our old signatures (and other "broken"
419	 * implementations) will not verify.  So, we want to guarantee
420	 * that we send out good DER encodings of attributes, but not
421	 * to expect to receive them.
422	 */
423	if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess)
424	    goto loser;
425
426	encoded_attrs.Data = NULL;
427	encoded_attrs.Length = 0;
428	if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr),
429	                &encoded_attrs) == NULL)
430	    goto loser;
431
432#if USE_CDSA_CRYPTO
433	rv = SEC_SignData(&signature, encoded_attrs.Data, encoded_attrs.Length,
434	                  privkey, digestalgtag, pubkAlgTag);
435#else
436        signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
437        signature.Data = PORT_ZAlloc(signature.Length);
438        if (!signature.Data) {
439            signature.Length = 0;
440            goto loser;
441        }
442        rv = SecKeyDigestAndSign(privkey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signature.Data, &signature.Length);
443        if (rv) {
444            PORT_ZFree(signature.Data, signature.Length);
445            signature.Length = 0;
446        }
447#endif
448
449	PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
450    } else {
451        signature.Length = SecKeyGetSize(privkey, kSecKeySignatureSize);
452        signature.Data = PORT_ZAlloc(signature.Length);
453        if (!signature.Data) {
454            signature.Length = 0;
455            goto loser;
456        }
457        rv = SecKeySignDigest(privkey, &signerinfo->digestAlg, digest->Data, digest->Length,
458                              signature.Data, &signature.Length);
459        if (rv) {
460            PORT_ZFree(signature.Data, signature.Length);
461            signature.Length = 0;
462        }
463    }
464    SECKEY_DestroyPrivateKey(privkey);
465    privkey = NULL;
466
467    if (rv != SECSuccess)
468	goto loser;
469
470    if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature)
471          != SECSuccess)
472	goto loser;
473
474    SECITEM_FreeItem(&signature, PR_FALSE);
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    return SECFailure;
488}
489
490#if !USE_CDSA_CRYPTO
491static CFArrayRef
492SecCmsSignerInfoCopySigningCertificates(SecCmsSignerInfoRef signerinfo)
493{
494    CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
495    SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
496    SecAsn1Item *cert_data;
497    if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
498        SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
499        if (cert) {
500            switch (signerinfo->signerIdentifier.identifierType) {
501            case SecCmsSignerIDIssuerSN:
502                if (CERT_CheckIssuerAndSerial(cert,
503                    &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
504                    &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
505                        CFArrayInsertValueAtIndex(certs, 0, cert);
506                else
507                    CFArrayAppendValue(certs, cert);
508                break;
509            case SecCmsSignerIDSubjectKeyID:
510                {
511                    CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
512                    SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
513                    if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
514                        !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
515                            CFArrayInsertValueAtIndex(certs, 0, cert);
516                    else
517                        CFArrayAppendValue(certs, cert);
518                    break;
519                }
520            }
521            CFReleaseNull(cert);
522        }
523        cert_datas++;
524    }
525
526    if ((CFArrayGetCount(certs) == 0) &&
527        (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN))
528    {
529        SecCertificateRef cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
530        if (cert) {
531            CFArrayAppendValue(certs, cert);
532            CFRelease(cert);
533        }
534    }
535    return certs;
536}
537#endif
538
539OSStatus
540SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray,
541				  CFTypeRef policies, SecTrustRef *trustRef)
542{
543    CFAbsoluteTime stime;
544    OSStatus rv;
545
546#if USE_CDSA_CRYPTO
547    SecCertificateRef cert;
548
549    if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) {
550#else
551    CFArrayRef certs;
552
553    if ((certs = SecCmsSignerInfoCopySigningCertificates(signerinfo)) == NULL) {
554#endif
555	signerinfo->verificationStatus = SecCmsVSSigningCertNotFound;
556	return SECFailure;
557    }
558    /*
559     * Get and convert the signing time; if available, it will be used
560     * both on the cert verification and for importing the sender
561     * email profile.
562     */
563    if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess)
564	stime = CFAbsoluteTimeGetCurrent();
565
566#if USE_CDSA_CRYPTO
567    rv = CERT_VerifyCert(keychainOrArray, cert, policies, stime, trustRef);
568#else
569    rv = CERT_VerifyCert(keychainOrArray, certs, policies, stime, trustRef);
570    CFRelease(certs);
571#endif
572    if (rv || !trustRef)
573    {
574	if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT)
575	{
576	    /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */
577	    if (signerinfo->verificationStatus == SecCmsVSGoodSignature)
578		signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted;
579	}
580    }
581
582    return rv;
583}
584
585/*
586 * SecCmsSignerInfoVerify - verify the signature of a single SignerInfo
587 *
588 * Just verifies the signature. The assumption is that verification of the certificate
589 * is done already.
590 */
591OSStatus
592SecCmsSignerInfoVerify(SecCmsSignerInfoRef signerinfo, SecAsn1Item * digest, SecAsn1Item * contentType)
593{
594    SecPublicKeyRef publickey = NULL;
595    SecCmsAttribute *attr;
596    SecAsn1Item encoded_attrs;
597    SecCertificateRef cert;
598    SecCmsVerificationStatus vs = SecCmsVSUnverified;
599    PLArenaPool *poolp;
600    SECOidTag digestAlgTag, digestEncAlgTag;
601
602    if (signerinfo == NULL)
603	return SECFailure;
604
605    /* SecCmsSignerInfoGetSigningCertificate will fail if 2nd parm is NULL and */
606    /* cert has not been verified */
607    if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, NULL)) == NULL) {
608	vs = SecCmsVSSigningCertNotFound;
609	goto loser;
610    }
611
612#if USE_CDSA_CRYPTO
613    if (SecCertificateCopyPublicKey(cert, &publickey)) {
614	vs = SecCmsVSProcessingError;
615	goto loser;
616    }
617#else
618    publickey = SecCertificateCopyPublicKey(cert);
619    if (publickey == NULL)
620        goto loser;
621#endif
622
623    digestAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg));
624    digestEncAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
625    if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) {
626	if (contentType) {
627	    /*
628	     * Check content type
629	     *
630	     * RFC2630 sez that if there are any authenticated attributes,
631	     * then there must be one for content type which matches the
632	     * content type of the content being signed, and there must
633	     * be one for message digest which matches our message digest.
634	     * So check these things first.
635	     */
636	    if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr,
637					SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
638	    {
639		vs = SecCmsVSMalformedSignature;
640		goto loser;
641	    }
642
643	    if (SecCmsAttributeCompareValue(attr, contentType) == PR_FALSE) {
644		vs = SecCmsVSMalformedSignature;
645		goto loser;
646	    }
647	}
648
649	/*
650	 * Check digest
651	 */
652	if ((attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
653	{
654	    vs = SecCmsVSMalformedSignature;
655	    goto loser;
656	}
657	if (SecCmsAttributeCompareValue(attr, digest) == PR_FALSE) {
658	    vs = SecCmsVSDigestMismatch;
659	    goto loser;
660	}
661
662	if ((poolp = PORT_NewArena (1024)) == NULL) {
663	    vs = SecCmsVSProcessingError;
664	    goto loser;
665	}
666
667	/*
668	 * Check signature
669	 *
670	 * The signature is based on a digest of the DER-encoded authenticated
671	 * attributes.  So, first we encode and then we digest/verify.
672	 * we trust the decoder to have the attributes in the right (sorted) order
673	 */
674	encoded_attrs.Data = NULL;
675	encoded_attrs.Length = 0;
676
677	if (SecCmsAttributeArrayEncode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
678		encoded_attrs.Data == NULL || encoded_attrs.Length == 0)
679	{
680	    vs = SecCmsVSProcessingError;
681	    goto loser;
682	}
683        if (errSecSuccess == SecKeyDigestAndVerify(publickey, &signerinfo->digestAlg, encoded_attrs.Data, encoded_attrs.Length, signerinfo->encDigest.Data, signerinfo->encDigest.Length))
684            vs = SecCmsVSGoodSignature;
685        else
686            vs = SecCmsVSBadSignature;
687
688	PORT_FreeArena(poolp, PR_FALSE);	/* awkward memory management :-( */
689
690    } else {
691	SecAsn1Item * sig;
692
693	/* No authenticated attributes. The signature is based on the plain message digest. */
694	sig = &(signerinfo->encDigest);
695	if (sig->Length == 0)
696	    goto loser;
697
698        if (SecKeyVerifyDigest(publickey, &signerinfo->digestAlg, digest->Data, digest->Length, sig->Data, sig->Length))
699            vs = SecCmsVSBadSignature;
700        else
701            vs = SecCmsVSGoodSignature;
702    }
703
704    if (vs == SecCmsVSBadSignature) {
705	/*
706	 * XXX Change the generic error into our specific one, because
707	 * in that case we get a better explanation out of the Security
708	 * Advisor.  This is really a bug in our error strings (the
709	 * "generic" error has a lousy/wrong message associated with it
710	 * which assumes the signature verification was done for the
711	 * purposes of checking the issuer signature on a certificate)
712	 * but this is at least an easy workaround and/or in the
713	 * Security Advisor, which specifically checks for the error
714	 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
715	 * in that case but does not similarly check for
716	 * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
717	 * probably say the wrong thing in the case that it *was* the
718	 * certificate signature check that failed during the cert
719	 * verification done above.  Our error handling is really a mess.
720	 */
721	if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
722	    PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
723    }
724
725    if (publickey != NULL)
726	CFRelease(publickey);
727
728    signerinfo->verificationStatus = vs;
729
730    return (vs == SecCmsVSGoodSignature) ? SECSuccess : SECFailure;
731
732loser:
733    if (publickey != NULL)
734	SECKEY_DestroyPublicKey (publickey);
735
736    signerinfo->verificationStatus = vs;
737
738    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
739    return SECFailure;
740}
741
742SecCmsVerificationStatus
743SecCmsSignerInfoGetVerificationStatus(SecCmsSignerInfoRef signerinfo)
744{
745    return signerinfo->verificationStatus;
746}
747
748SECOidData *
749SecCmsSignerInfoGetDigestAlg(SecCmsSignerInfoRef signerinfo)
750{
751    return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
752}
753
754SECOidTag
755SecCmsSignerInfoGetDigestAlgTag(SecCmsSignerInfoRef signerinfo)
756{
757    SECOidData *algdata;
758
759    algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
760    if (algdata != NULL)
761	return algdata->offset;
762    else
763	return SEC_OID_UNKNOWN;
764}
765
766CFArrayRef
767SecCmsSignerInfoGetCertList(SecCmsSignerInfoRef signerinfo)
768{
769    return signerinfo->certList;
770}
771
772int
773SecCmsSignerInfoGetVersion(SecCmsSignerInfoRef signerinfo)
774{
775    unsigned long version;
776
777    /* always take apart the SecAsn1Item */
778    if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
779	return 0;
780    else
781	return (int)version;
782}
783
784/*
785 * SecCmsSignerInfoGetSigningTime - return the signing time,
786 *				      in UTCTime format, of a CMS signerInfo.
787 *
788 * sinfo - signerInfo data for this signer
789 *
790 * Returns a pointer to XXXX (what?)
791 * A return value of NULL is an error.
792 */
793OSStatus
794SecCmsSignerInfoGetSigningTime(SecCmsSignerInfoRef sinfo, CFAbsoluteTime *stime)
795{
796    SecCmsAttribute *attr;
797    SecAsn1Item * value;
798
799    if (sinfo == NULL)
800	return SECFailure;
801
802    if (sinfo->signingTime != 0) {
803	*stime = sinfo->signingTime;	/* cached copy */
804	return SECSuccess;
805    }
806
807    attr = SecCmsAttributeArrayFindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
808    /* XXXX multi-valued attributes NIH */
809    if (attr == NULL || (value = SecCmsAttributeGetValue(attr)) == NULL)
810	return SECFailure;
811    if (DER_UTCTimeToCFDate(value, stime) != SECSuccess)
812	return SECFailure;
813    sinfo->signingTime = *stime;	/* make cached copy */
814    return SECSuccess;
815}
816
817/*
818 * Return the signing cert of a CMS signerInfo.
819 *
820 * the certs in the enclosing SignedData must have been imported already
821 */
822SecCertificateRef
823SecCmsSignerInfoGetSigningCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray)
824{
825    SecCertificateRef cert = NULL;
826
827    if (signerinfo->cert != NULL)
828	return signerinfo->cert;
829
830    /* @@@ Make sure we search though all the certs in the cms message itself as well, it's silly
831       to require them to be added to a keychain first. */
832
833#if USE_CDSA_CRYPTO
834    SecCmsSignerIdentifier *sid;
835
836    /*
837     * This cert will also need to be freed, but since we save it
838     * in signerinfo for later, we do not want to destroy it when
839     * we leave this function -- we let the clean-up of the entire
840     * cinfo structure later do the destroy of this cert.
841     */
842    sid = &signerinfo->signerIdentifier;
843    switch (sid->identifierType) {
844    case SecCmsSignerIDIssuerSN:
845	cert = CERT_FindCertByIssuerAndSN(keychainOrArray, sid->id.issuerAndSN);
846	break;
847    case SecCmsSignerIDSubjectKeyID:
848	cert = CERT_FindCertBySubjectKeyID(keychainOrArray, sid->id.subjectKeyID);
849	break;
850    default:
851	cert = NULL;
852	break;
853    }
854
855    /* cert can be NULL at that point */
856    signerinfo->cert = cert;	/* earmark it */
857#else
858    SecAsn1Item **cert_datas = signerinfo->signedData->rawCerts;
859    SecAsn1Item *cert_data;
860    if (cert_datas) while ((cert_data = *cert_datas) != NULL) {
861        cert = SecCertificateCreateWithBytes(NULL, cert_data->Data, cert_data->Length);
862        if (cert) {
863            switch (signerinfo->signerIdentifier.identifierType) {
864            case SecCmsSignerIDIssuerSN:
865                if (CERT_CheckIssuerAndSerial(cert,
866                    &(signerinfo->signerIdentifier.id.issuerAndSN->derIssuer),
867                    &(signerinfo->signerIdentifier.id.issuerAndSN->serialNumber)))
868                        signerinfo->cert = cert;
869                    break;
870            case SecCmsSignerIDSubjectKeyID: {
871                CFDataRef cert_keyid = SecCertificateGetSubjectKeyID(cert);
872                SecAsn1Item *tbf_keyid = signerinfo->signerIdentifier.id.subjectKeyID;
873                if (tbf_keyid->Length == (size_t)CFDataGetLength(cert_keyid) &&
874                    !memcmp(tbf_keyid->Data, CFDataGetBytePtr(cert_keyid), tbf_keyid->Length))
875                        signerinfo->cert = cert;
876                    }
877            }
878            if (signerinfo->cert)
879                break;
880            CFReleaseNull(cert);
881        }
882        cert_datas++;
883    }
884
885    if (!signerinfo->cert && (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDIssuerSN)) {
886        cert = CERT_FindCertificateByIssuerAndSN(signerinfo->signedData->certs, signerinfo->signerIdentifier.id.issuerAndSN);
887        signerinfo->cert = cert;
888    }
889#endif
890
891    return cert;
892}
893
894
895/*
896 * SecCmsSignerInfoGetSignerCommonName - return the common name of the signer
897 *
898 * sinfo - signerInfo data for this signer
899 *
900 * Returns a CFStringRef containing the common name of the signer.
901 * A return value of NULL is an error.
902 */
903CFStringRef
904SecCmsSignerInfoGetSignerCommonName(SecCmsSignerInfoRef sinfo)
905{
906    SecCertificateRef signercert;
907    CFStringRef commonName = NULL;
908
909    /* will fail if cert is not verified */
910    if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
911	return NULL;
912
913#if USE_CDSA_CRYPTO
914    SecCertificateGetCommonName(signercert, &commonName);
915#else
916    CFArrayRef commonNames = SecCertificateCopyCommonNames(signercert);
917    if (commonNames) {
918        /* SecCertificateCopyCommonNames doesn't return empty arrays */
919        commonName = (CFStringRef)CFArrayGetValueAtIndex(commonNames, CFArrayGetCount(commonNames) - 1);
920        CFRetain(commonName);
921        CFRelease(commonNames);
922    }
923#endif
924
925    return commonName;
926}
927
928/*
929 * SecCmsSignerInfoGetSignerEmailAddress - return the email address of the signer
930 *
931 * sinfo - signerInfo data for this signer
932 *
933 * Returns a CFStringRef containing the name of the signer.
934 * A return value of NULL is an error.
935 */
936CFStringRef
937SecCmsSignerInfoGetSignerEmailAddress(SecCmsSignerInfoRef sinfo)
938{
939    SecCertificateRef signercert;
940    CFStringRef emailAddress = NULL;
941
942    if ((signercert = SecCmsSignerInfoGetSigningCertificate(sinfo, NULL)) == NULL)
943	return NULL;
944
945#if USE_CDSA_CRYPTO
946    SecCertificateGetEmailAddress(signercert, &emailAddress);
947#else
948    CFArrayRef names = SecCertificateCopyRFC822Names(signercert);
949    if (names) {
950        if (CFArrayGetCount(names) > 0)
951            emailAddress = (CFStringRef)CFArrayGetValueAtIndex(names, 0);
952        if (emailAddress)
953            CFRetain(emailAddress);
954        CFRelease(names);
955    }
956#endif
957    return emailAddress;
958}
959
960
961/*
962 * SecCmsSignerInfoAddAuthAttr - add an attribute to the
963 * authenticated (i.e. signed) attributes of "signerinfo".
964 */
965OSStatus
966SecCmsSignerInfoAddAuthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
967{
968    return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->authAttr), attr);
969}
970
971/*
972 * SecCmsSignerInfoAddUnauthAttr - add an attribute to the
973 * unauthenticated attributes of "signerinfo".
974 */
975OSStatus
976SecCmsSignerInfoAddUnauthAttr(SecCmsSignerInfoRef signerinfo, SecCmsAttribute *attr)
977{
978    return SecCmsAttributeArrayAddAttr(signerinfo->signedData->contentInfo.cmsg->poolp, &(signerinfo->unAuthAttr), attr);
979}
980
981/*
982 * SecCmsSignerInfoAddSigningTime - add the signing time to the
983 * authenticated (i.e. signed) attributes of "signerinfo".
984 *
985 * This is expected to be included in outgoing signed
986 * messages for email (S/MIME) but is likely useful in other situations.
987 *
988 * This should only be added once; a second call will do nothing.
989 *
990 * XXX This will probably just shove the current time into "signerinfo"
991 * but it will not actually get signed until the entire item is
992 * processed for encoding.  Is this (expected to be small) delay okay?
993 */
994OSStatus
995SecCmsSignerInfoAddSigningTime(SecCmsSignerInfoRef signerinfo, CFAbsoluteTime t)
996{
997    SecCmsAttribute *attr;
998    SecAsn1Item stime;
999    void *mark;
1000    PLArenaPool *poolp;
1001
1002    poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1003
1004    mark = PORT_ArenaMark(poolp);
1005
1006    /* create new signing time attribute */
1007    if (DER_CFDateToUTCTime(t, &stime) != SECSuccess)
1008	goto loser;
1009
1010    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
1011	SECITEM_FreeItem (&stime, PR_FALSE);
1012	goto loser;
1013    }
1014
1015    SECITEM_FreeItem (&stime, PR_FALSE);
1016
1017    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1018	goto loser;
1019
1020    PORT_ArenaUnmark (poolp, mark);
1021
1022    return SECSuccess;
1023
1024loser:
1025    PORT_ArenaRelease (poolp, mark);
1026    return SECFailure;
1027}
1028
1029/*
1030 * SecCmsSignerInfoAddSMIMECaps - add a SMIMECapabilities attribute to the
1031 * authenticated (i.e. signed) attributes of "signerinfo".
1032 *
1033 * This is expected to be included in outgoing signed
1034 * messages for email (S/MIME).
1035 */
1036OSStatus
1037SecCmsSignerInfoAddSMIMECaps(SecCmsSignerInfoRef signerinfo)
1038{
1039    SecCmsAttribute *attr;
1040    SecAsn1Item * smimecaps = NULL;
1041    void *mark;
1042    PLArenaPool *poolp;
1043
1044    poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1045
1046    mark = PORT_ArenaMark(poolp);
1047
1048    smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
1049    if (smimecaps == NULL)
1050	goto loser;
1051
1052    /* create new signing time attribute */
1053#if 1
1054    // @@@ We don't do Fortezza yet.
1055    if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps, PR_FALSE) != SECSuccess)
1056#else
1057    if (SecSMIMECreateSMIMECapabilities(poolp, smimecaps,
1058			    PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
1059#endif
1060	goto loser;
1061
1062    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
1063	goto loser;
1064
1065    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1066	goto loser;
1067
1068    PORT_ArenaUnmark (poolp, mark);
1069    return SECSuccess;
1070
1071loser:
1072    PORT_ArenaRelease (poolp, mark);
1073    return SECFailure;
1074}
1075
1076/*
1077 * SecCmsSignerInfoAddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1078 * authenticated (i.e. signed) attributes of "signerinfo".
1079 *
1080 * This is expected to be included in outgoing signed messages for email (S/MIME).
1081 */
1082OSStatus
1083SecCmsSignerInfoAddSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1084{
1085    SecCmsAttribute *attr;
1086    SecAsn1Item * smimeekp = NULL;
1087    void *mark;
1088    PLArenaPool *poolp;
1089
1090#if 0
1091    CFTypeRef policy;
1092
1093    /* verify this cert for encryption */
1094    policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1095    if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1096	CFRelease(policy);
1097	return SECFailure;
1098    }
1099    CFRelease(policy);
1100#endif
1101
1102    poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1103    mark = PORT_ArenaMark(poolp);
1104
1105    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1106    if (smimeekp == NULL)
1107	goto loser;
1108
1109    /* create new signing time attribute */
1110    if (SecSMIMECreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
1111	goto loser;
1112
1113    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1114	goto loser;
1115
1116    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1117	goto loser;
1118
1119    PORT_ArenaUnmark (poolp, mark);
1120    return SECSuccess;
1121
1122loser:
1123    PORT_ArenaRelease (poolp, mark);
1124    return SECFailure;
1125}
1126
1127/*
1128 * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
1129 * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
1130 *
1131 * This is expected to be included in outgoing signed messages for email (S/MIME),
1132 * if compatibility with Microsoft mail clients is wanted.
1133 */
1134OSStatus
1135SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray)
1136{
1137    SecCmsAttribute *attr;
1138    SecAsn1Item * smimeekp = NULL;
1139    void *mark;
1140    PLArenaPool *poolp;
1141
1142#if 0
1143    CFTypeRef policy;
1144
1145    /* verify this cert for encryption */
1146    policy = CERT_PolicyForCertUsage(certUsageEmailRecipient);
1147    if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) {
1148	CFRelease(policy);
1149	return SECFailure;
1150    }
1151    CFRelease(policy);
1152#endif
1153
1154    poolp = signerinfo->signedData->contentInfo.cmsg->poolp;
1155    mark = PORT_ArenaMark(poolp);
1156
1157    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
1158    if (smimeekp == NULL)
1159	goto loser;
1160
1161    /* create new signing time attribute */
1162    if (SecSMIMECreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
1163	goto loser;
1164
1165    if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
1166	goto loser;
1167
1168    if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess)
1169	goto loser;
1170
1171    PORT_ArenaUnmark (poolp, mark);
1172    return SECSuccess;
1173
1174loser:
1175    PORT_ArenaRelease (poolp, mark);
1176    return SECFailure;
1177}
1178
1179/*
1180 * SecCmsSignerInfoAddCounterSignature - countersign a signerinfo
1181 *
1182 * 1. digest the DER-encoded signature value of the original signerinfo
1183 * 2. create new signerinfo with correct version, sid, digestAlg
1184 * 3. add message-digest authAttr, but NO content-type
1185 * 4. sign the authAttrs
1186 * 5. DER-encode the new signerInfo
1187 * 6. add the whole thing to original signerInfo's unAuthAttrs
1188 *    as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
1189 *
1190 * XXXX give back the new signerinfo?
1191 */
1192OSStatus
1193SecCmsSignerInfoAddCounterSignature(SecCmsSignerInfoRef signerinfo,
1194				    SECOidTag digestalg, SecIdentityRef identity)
1195{
1196    /* XXXX TBD XXXX */
1197    return SECFailure;
1198}
1199
1200/*
1201 * XXXX the following needs to be done in the S/MIME layer code
1202 * after signature of a signerinfo is verified
1203 */
1204OSStatus
1205SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo)
1206{
1207    return -4 /*unImp*/;
1208}
1209
1210/*
1211 * SecCmsSignerInfoIncludeCerts - set cert chain inclusion mode for this signer
1212 */
1213OSStatus
1214SecCmsSignerInfoIncludeCerts(SecCmsSignerInfoRef signerinfo, SecCmsCertChainMode cm, SECCertUsage usage)
1215{
1216    if (signerinfo->cert == NULL)
1217	return SECFailure;
1218
1219    /* don't leak if we get called twice */
1220    if (signerinfo->certList != NULL) {
1221	CFRelease(signerinfo->certList);
1222	signerinfo->certList = NULL;
1223    }
1224
1225    switch (cm) {
1226    case SecCmsCMNone:
1227	signerinfo->certList = NULL;
1228	break;
1229    case SecCmsCMCertOnly:
1230	signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1231	break;
1232    case SecCmsCMCertChain:
1233	signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
1234	break;
1235    case SecCmsCMCertChainWithRoot:
1236	signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
1237	break;
1238    }
1239
1240    if (cm != SecCmsCMNone && signerinfo->certList == NULL)
1241	return SECFailure;
1242
1243    return SECSuccess;
1244}
1245