Back to index

lightning-sunbird  0.9+nobinonly
cmssiginfo.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * CMS signerInfo methods.
00040  *
00041  * $Id: cmssiginfo.c,v 1.29.2.1 2006/04/28 03:35:31 rrelyea%redhat.com Exp $
00042  */
00043 
00044 #include "cmslocal.h"
00045 
00046 #include "cert.h"
00047 #include "key.h"
00048 #include "secasn1.h"
00049 #include "secitem.h"
00050 #include "secoid.h"
00051 #include "pk11func.h"
00052 #include "prtime.h"
00053 #include "secerr.h"
00054 #include "secder.h"
00055 #include "cryptohi.h"
00056 
00057 #include "smime.h"
00058 
00059 /* =============================================================================
00060  * SIGNERINFO
00061  */
00062 NSSCMSSignerInfo *
00063 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 
00064        CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 
00065        SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
00066 
00067 NSSCMSSignerInfo *
00068 NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID, 
00069        SECKEYPublicKey *pubKey, SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
00070 {
00071     return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag); 
00072 }
00073 
00074 NSSCMSSignerInfo *
00075 NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
00076 {
00077     return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL, NULL, NULL, digestalgtag); 
00078 }
00079 
00080 NSSCMSSignerInfo *
00081 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 
00082        CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 
00083        SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
00084 {
00085     void *mark;
00086     NSSCMSSignerInfo *signerinfo;
00087     int version;
00088     PLArenaPool *poolp;
00089 
00090     poolp = cmsg->poolp;
00091 
00092     mark = PORT_ArenaMark(poolp);
00093 
00094     signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
00095     if (signerinfo == NULL) {
00096        PORT_ArenaRelease(poolp, mark);
00097        return NULL;
00098     }
00099 
00100 
00101     signerinfo->cmsg = cmsg;
00102 
00103     switch(type) {
00104     case NSSCMSSignerID_IssuerSN:
00105         signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
00106         if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
00107            goto loser;
00108         if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
00109            goto loser;
00110         break;
00111     case NSSCMSSignerID_SubjectKeyID:
00112         signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
00113         PORT_Assert(subjKeyID);
00114         if (!subjKeyID)
00115             goto loser;
00116 
00117         signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
00118         SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
00119                          subjKeyID);
00120         signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
00121         if (!signerinfo->signingKey)
00122             goto loser;
00123         signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
00124         if (!signerinfo->pubKey)
00125             goto loser;
00126         break;
00127     default:
00128         goto loser;
00129     }
00130 
00131     /* set version right now */
00132     version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
00133     /* RFC2630 5.3 "version is the syntax version number. If the .... " */
00134     if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
00135        version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
00136     (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
00137 
00138     if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
00139        goto loser;
00140 
00141     PORT_ArenaUnmark(poolp, mark);
00142     return signerinfo;
00143 
00144 loser:
00145     PORT_ArenaRelease(poolp, mark);
00146     return NULL;
00147 }
00148 
00149 /*
00150  * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
00151  */
00152 void
00153 NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
00154 {
00155     if (si->cert != NULL)
00156        CERT_DestroyCertificate(si->cert);
00157 
00158     if (si->certList != NULL) 
00159        CERT_DestroyCertificateList(si->certList);
00160 
00161     /* XXX storage ??? */
00162 }
00163 
00164 /*
00165  * NSS_CMSSignerInfo_Sign - sign something
00166  *
00167  */
00168 SECStatus
00169 NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType)
00170 {
00171     CERTCertificate *cert;
00172     SECKEYPrivateKey *privkey = NULL;
00173     SECOidTag digestalgtag;
00174     SECOidTag pubkAlgTag;
00175     SECItem signature = { 0 };
00176     SECStatus rv;
00177     PLArenaPool *poolp, *tmppoolp;
00178     SECAlgorithmID *algID, freeAlgID;
00179     CERTSubjectPublicKeyInfo *spki;
00180 
00181     PORT_Assert (digest != NULL);
00182 
00183     poolp = signerinfo->cmsg->poolp;
00184 
00185     switch (signerinfo->signerIdentifier.identifierType) {
00186     case NSSCMSSignerID_IssuerSN:
00187         cert = signerinfo->cert;
00188 
00189         if ((privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg)) == NULL)
00190            goto loser;
00191         algID = &cert->subjectPublicKeyInfo.algorithm;
00192         break;
00193     case NSSCMSSignerID_SubjectKeyID:
00194         privkey = signerinfo->signingKey;
00195         signerinfo->signingKey = NULL;
00196         spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
00197         SECKEY_DestroyPublicKey(signerinfo->pubKey);
00198         signerinfo->pubKey = NULL;
00199         SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
00200         SECKEY_DestroySubjectPublicKeyInfo(spki); 
00201         algID = &freeAlgID;
00202         break;
00203     default:
00204         goto loser;
00205     }
00206     digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
00207     /*
00208      * XXX I think there should be a cert-level interface for this,
00209      * so that I do not have to know about subjectPublicKeyInfo...
00210      */
00211     pubkAlgTag = SECOID_GetAlgorithmTag(algID);
00212     if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) {
00213       SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
00214     }
00215 
00216     /* Fortezza MISSI have weird signature formats.  
00217      * Map them to standard DSA formats 
00218      */
00219     pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag);
00220 
00221     if (signerinfo->authAttr != NULL) {
00222        SECOidTag signAlgTag;
00223        SECItem encoded_attrs;
00224 
00225        /* find and fill in the message digest attribute. */
00226        rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 
00227                               SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
00228        if (rv != SECSuccess)
00229            goto loser;
00230 
00231        if (contentType != NULL) {
00232            /* if the caller wants us to, find and fill in the content type attribute. */
00233            rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 
00234                            SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
00235            if (rv != SECSuccess)
00236               goto loser;
00237        }
00238 
00239        if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
00240            PORT_SetError(SEC_ERROR_NO_MEMORY);
00241            goto loser;
00242        }
00243 
00244        /*
00245         * Before encoding, reorder the attributes so that when they
00246         * are encoded, they will be conforming DER, which is required
00247         * to have a specific order and that is what must be used for
00248         * the hash/signature.  We do this here, rather than building
00249         * it into EncodeAttributes, because we do not want to do
00250         * such reordering on incoming messages (which also uses
00251         * EncodeAttributes) or our old signatures (and other "broken"
00252         * implementations) will not verify.  So, we want to guarantee
00253         * that we send out good DER encodings of attributes, but not
00254         * to expect to receive them.
00255         */
00256        if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
00257            goto loser;
00258 
00259        encoded_attrs.data = NULL;
00260        encoded_attrs.len = 0;
00261        if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), 
00262                        &encoded_attrs) == NULL)
00263            goto loser;
00264 
00265        signAlgTag = NSS_CMSUtil_MakeSignatureAlgorithm(digestalgtag, pubkAlgTag);
00266        rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, 
00267                          privkey, signAlgTag);
00268        PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
00269     } else {
00270        rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
00271     }
00272     SECKEY_DestroyPrivateKey(privkey);
00273     privkey = NULL;
00274 
00275     if (rv != SECSuccess)
00276        goto loser;
00277 
00278     if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) 
00279           != SECSuccess)
00280        goto loser;
00281 
00282     SECITEM_FreeItem(&signature, PR_FALSE);
00283 
00284     if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, 
00285                               NULL) != SECSuccess)
00286        goto loser;
00287 
00288     return SECSuccess;
00289 
00290 loser:
00291     if (signature.len != 0)
00292        SECITEM_FreeItem (&signature, PR_FALSE);
00293     if (privkey)
00294        SECKEY_DestroyPrivateKey(privkey);
00295     return SECFailure;
00296 }
00297 
00298 SECStatus
00299 NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
00300                          SECCertUsage certusage)
00301 {
00302     CERTCertificate *cert;
00303     int64 stime;
00304 
00305     if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
00306        signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
00307        return SECFailure;
00308     }
00309 
00310     /*
00311      * Get and convert the signing time; if available, it will be used
00312      * both on the cert verification and for importing the sender
00313      * email profile.
00314      */
00315     if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess)
00316        stime = PR_Now(); /* not found or conversion failed, so check against now */
00317     
00318     /*
00319      * XXX  This uses the signing time, if available.  Additionally, we
00320      * might want to, if there is no signing time, get the message time
00321      * from the mail header itself, and use that.  That would require
00322      * a change to our interface though, and for S/MIME callers to pass
00323      * in a time (and for non-S/MIME callers to pass in nothing, or
00324      * maybe make them pass in the current time, always?).
00325      */
00326     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, 
00327                         signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
00328        signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
00329        return SECFailure;
00330     }
00331     return SECSuccess;
00332 }
00333 
00334 /*
00335  * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
00336  *
00337  * Just verifies the signature. The assumption is that verification of 
00338  * the certificate is done already.
00339  */
00340 SECStatus
00341 NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, 
00342                          SECItem *digest,               /* may be NULL */
00343                          SECItem *contentType)          /* may be NULL */
00344 {
00345     SECKEYPublicKey *publickey = NULL;
00346     NSSCMSAttribute *attr;
00347     SECItem encoded_attrs;
00348     CERTCertificate *cert;
00349     NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
00350     PLArenaPool *poolp;
00351     SECOidTag    digestalgtag;
00352     SECOidTag    pubkAlgTag;
00353     SECOidTag    signAlgTag;
00354 
00355     if (signerinfo == NULL)
00356        return SECFailure;
00357 
00358     /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL 
00359     ** and cert has not been verified 
00360     */
00361     cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
00362     if (cert == NULL) {
00363        vs = NSSCMSVS_SigningCertNotFound;
00364        goto loser;
00365     }
00366 
00367     if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
00368        vs = NSSCMSVS_ProcessingError;
00369        goto loser;
00370     }
00371 
00372     digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
00373 
00374     /*
00375      * XXX This may not be the right set of algorithms to check.
00376      * I'd prefer to trust that just calling VFY_Verify{Data,Digest}
00377      * would do the right thing (and set an error if it could not);
00378      * then additional algorithms could be handled by that code
00379      * and we would Just Work.  So this check should just be removed,
00380      * but not until the VFY code is better at setting errors.
00381      */
00382     pubkAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
00383     switch (pubkAlgTag) {
00384     case SEC_OID_PKCS1_RSA_ENCRYPTION:
00385     case SEC_OID_ANSIX9_DSA_SIGNATURE:
00386     case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
00387     case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
00388     case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
00389 #ifdef NSS_ECC_MORE_THAN_SUITE_B
00390     case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
00391 #endif
00392        /* ok */
00393        break;
00394     case SEC_OID_UNKNOWN:
00395        vs = NSSCMSVS_SignatureAlgorithmUnknown;
00396        goto loser;
00397     default:
00398        vs = NSSCMSVS_SignatureAlgorithmUnsupported;
00399        goto loser;
00400     }
00401 
00402     signAlgTag = NSS_CMSUtil_MakeSignatureAlgorithm(digestalgtag, pubkAlgTag);
00403 
00404     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
00405        if (contentType) {
00406            /*
00407             * Check content type
00408             *
00409             * RFC2630 sez that if there are any authenticated attributes,
00410             * then there must be one for content type which matches the
00411             * content type of the content being signed, and there must
00412             * be one for message digest which matches our message digest.
00413             * So check these things first.
00414             */
00415            attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
00416                                    SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
00417            if (attr == NULL) {
00418               vs = NSSCMSVS_MalformedSignature;
00419               goto loser;
00420            }
00421               
00422            if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
00423               vs = NSSCMSVS_MalformedSignature;
00424               goto loser;
00425            }
00426        }
00427 
00428        /*
00429         * Check digest
00430         */
00431        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 
00432                                      SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
00433        if (attr == NULL) {
00434            vs = NSSCMSVS_MalformedSignature;
00435            goto loser;
00436        }
00437        if (!digest || 
00438            NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
00439            vs = NSSCMSVS_DigestMismatch;
00440            goto loser;
00441        }
00442 
00443        if ((poolp = PORT_NewArena (1024)) == NULL) {
00444            vs = NSSCMSVS_ProcessingError;
00445            goto loser;
00446        }
00447 
00448        /*
00449         * Check signature
00450         *
00451         * The signature is based on a digest of the DER-encoded authenticated
00452         * attributes.  So, first we encode and then we digest/verify.
00453         * we trust the decoder to have the attributes in the right (sorted) 
00454         * order
00455         */
00456        encoded_attrs.data = NULL;
00457        encoded_attrs.len = 0;
00458 
00459        if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), 
00460                                         &encoded_attrs) == NULL ||
00461               encoded_attrs.data == NULL || encoded_attrs.len == 0) {
00462            vs = NSSCMSVS_ProcessingError;
00463            goto loser;
00464        }
00465 
00466        vs = (VFY_VerifyData (encoded_attrs.data, encoded_attrs.len,
00467                      publickey, &(signerinfo->encDigest), signAlgTag,
00468                      signerinfo->cmsg->pwfn_arg) != SECSuccess) 
00469                      ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
00470 
00471        PORT_FreeArena(poolp, PR_FALSE);  /* awkward memory management :-( */
00472 
00473     } else {
00474        SECItem *sig;
00475 
00476        /* No authenticated attributes. 
00477        ** The signature is based on the plain message digest. 
00478        */
00479        sig = &(signerinfo->encDigest);
00480        if (sig->len == 0)
00481            goto loser;
00482 
00483        vs = (!digest || 
00484              VFY_VerifyDigest(digest, publickey, sig, signAlgTag,
00485                      signerinfo->cmsg->pwfn_arg) != SECSuccess) 
00486                      ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
00487     }
00488 
00489     if (vs == NSSCMSVS_BadSignature) {
00490        /*
00491         * XXX Change the generic error into our specific one, because
00492         * in that case we get a better explanation out of the Security
00493         * Advisor.  This is really a bug in our error strings (the
00494         * "generic" error has a lousy/wrong message associated with it
00495         * which assumes the signature verification was done for the
00496         * purposes of checking the issuer signature on a certificate)
00497         * but this is at least an easy workaround and/or in the
00498         * Security Advisor, which specifically checks for the error
00499         * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
00500         * in that case but does not similarly check for
00501         * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
00502         * probably say the wrong thing in the case that it *was* the
00503         * certificate signature check that failed during the cert
00504         * verification done above.  Our error handling is really a mess.
00505         */
00506        if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
00507            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
00508     }
00509 
00510     if (publickey != NULL)
00511        SECKEY_DestroyPublicKey (publickey);
00512 
00513     signerinfo->verificationStatus = vs;
00514 
00515     return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
00516 
00517 loser:
00518     if (publickey != NULL)
00519        SECKEY_DestroyPublicKey (publickey);
00520 
00521     signerinfo->verificationStatus = vs;
00522 
00523     PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
00524     return SECFailure;
00525 }
00526 
00527 NSSCMSVerificationStatus
00528 NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
00529 {
00530     return signerinfo->verificationStatus;
00531 }
00532 
00533 SECOidData *
00534 NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
00535 {
00536     return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
00537 }
00538 
00539 SECOidTag
00540 NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
00541 {
00542     SECOidData *algdata;
00543 
00544     if (!signerinfo) {
00545         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00546         return SEC_OID_UNKNOWN;
00547     }
00548 
00549     algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
00550     if (algdata != NULL)
00551        return algdata->offset;
00552     else
00553        return SEC_OID_UNKNOWN;
00554 }
00555 
00556 CERTCertificateList *
00557 NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
00558 {
00559     return signerinfo->certList;
00560 }
00561 
00562 int
00563 NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
00564 {
00565     unsigned long version;
00566 
00567     /* always take apart the SECItem */
00568     if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
00569        return 0;
00570     else
00571        return (int)version;
00572 }
00573 
00574 /*
00575  * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
00576  *                                in UTCTime or GeneralizedTime format,
00577  *                                    of a CMS signerInfo.
00578  *
00579  * sinfo - signerInfo data for this signer
00580  *
00581  * Returns a pointer to XXXX (what?)
00582  * A return value of NULL is an error.
00583  */
00584 SECStatus
00585 NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
00586 {
00587     NSSCMSAttribute *attr;
00588     SECItem *value;
00589 
00590     if (sinfo == NULL)
00591        return SECFailure;
00592 
00593     if (sinfo->signingTime != 0) {
00594        *stime = sinfo->signingTime;       /* cached copy */
00595        return SECSuccess;
00596     }
00597 
00598     attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
00599     /* XXXX multi-valued attributes NIH */
00600     if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
00601        return SECFailure;
00602     if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
00603        return SECFailure;
00604     sinfo->signingTime = *stime;   /* make cached copy */
00605     return SECSuccess;
00606 }
00607 
00608 /*
00609  * Return the signing cert of a CMS signerInfo.
00610  *
00611  * the certs in the enclosing SignedData must have been imported already
00612  */
00613 CERTCertificate *
00614 NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
00615 {
00616     CERTCertificate *cert;
00617     NSSCMSSignerIdentifier *sid;
00618 
00619     if (signerinfo->cert != NULL)
00620        return signerinfo->cert;
00621 
00622     /* no certdb, and cert hasn't been set yet? */
00623     if (certdb == NULL)
00624        return NULL;
00625 
00626     /*
00627      * This cert will also need to be freed, but since we save it
00628      * in signerinfo for later, we do not want to destroy it when
00629      * we leave this function -- we let the clean-up of the entire
00630      * cinfo structure later do the destroy of this cert.
00631      */
00632     sid = &signerinfo->signerIdentifier;
00633     switch (sid->identifierType) {
00634     case NSSCMSSignerID_IssuerSN:
00635        cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
00636        break;
00637     case NSSCMSSignerID_SubjectKeyID:
00638        cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
00639        break;
00640     default:
00641        cert = NULL;
00642        break;
00643     }
00644 
00645     /* cert can be NULL at that point */
00646     signerinfo->cert = cert;       /* earmark it */
00647 
00648     return cert;
00649 }
00650 
00651 /*
00652  * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
00653  *
00654  * sinfo - signerInfo data for this signer
00655  *
00656  * Returns a pointer to allocated memory, which must be freed with PORT_Free.
00657  * A return value of NULL is an error.
00658  */
00659 char *
00660 NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
00661 {
00662     CERTCertificate *signercert;
00663 
00664     /* will fail if cert is not verified */
00665     if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
00666        return NULL;
00667 
00668     return (CERT_GetCommonName(&signercert->subject));
00669 }
00670 
00671 /*
00672  * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
00673  *
00674  * sinfo - signerInfo data for this signer
00675  *
00676  * Returns a pointer to allocated memory, which must be freed.
00677  * A return value of NULL is an error.
00678  */
00679 char *
00680 NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
00681 {
00682     CERTCertificate *signercert;
00683 
00684     if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
00685        return NULL;
00686 
00687     if (!signercert->emailAddr || !signercert->emailAddr[0])
00688        return NULL;
00689 
00690     return (PORT_Strdup(signercert->emailAddr));
00691 }
00692 
00693 /*
00694  * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
00695  * authenticated (i.e. signed) attributes of "signerinfo". 
00696  */
00697 SECStatus
00698 NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
00699 {
00700     return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
00701 }
00702 
00703 /*
00704  * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
00705  * unauthenticated attributes of "signerinfo". 
00706  */
00707 SECStatus
00708 NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
00709 {
00710     return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
00711 }
00712 
00713 /* 
00714  * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
00715  * authenticated (i.e. signed) attributes of "signerinfo". 
00716  *
00717  * This is expected to be included in outgoing signed
00718  * messages for email (S/MIME) but is likely useful in other situations.
00719  *
00720  * This should only be added once; a second call will do nothing.
00721  *
00722  * XXX This will probably just shove the current time into "signerinfo"
00723  * but it will not actually get signed until the entire item is
00724  * processed for encoding.  Is this (expected to be small) delay okay?
00725  */
00726 SECStatus
00727 NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
00728 {
00729     NSSCMSAttribute *attr;
00730     SECItem stime;
00731     void *mark;
00732     PLArenaPool *poolp;
00733 
00734     poolp = signerinfo->cmsg->poolp;
00735 
00736     mark = PORT_ArenaMark(poolp);
00737 
00738     /* create new signing time attribute */
00739     if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
00740        goto loser;
00741 
00742     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
00743        SECITEM_FreeItem (&stime, PR_FALSE);
00744        goto loser;
00745     }
00746 
00747     SECITEM_FreeItem (&stime, PR_FALSE);
00748 
00749     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
00750        goto loser;
00751 
00752     PORT_ArenaUnmark (poolp, mark);
00753 
00754     return SECSuccess;
00755 
00756 loser:
00757     PORT_ArenaRelease (poolp, mark);
00758     return SECFailure;
00759 }
00760 
00761 /* 
00762  * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
00763  * authenticated (i.e. signed) attributes of "signerinfo". 
00764  *
00765  * This is expected to be included in outgoing signed
00766  * messages for email (S/MIME).
00767  */
00768 SECStatus
00769 NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
00770 {
00771     NSSCMSAttribute *attr;
00772     SECItem *smimecaps = NULL;
00773     void *mark;
00774     PLArenaPool *poolp;
00775 
00776     poolp = signerinfo->cmsg->poolp;
00777 
00778     mark = PORT_ArenaMark(poolp);
00779 
00780     smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
00781     if (smimecaps == NULL)
00782        goto loser;
00783 
00784     /* create new signing time attribute */
00785     if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps,
00786                          PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
00787        goto loser;
00788 
00789     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
00790        goto loser;
00791 
00792     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
00793        goto loser;
00794 
00795     PORT_ArenaUnmark (poolp, mark);
00796     return SECSuccess;
00797 
00798 loser:
00799     PORT_ArenaRelease (poolp, mark);
00800     return SECFailure;
00801 }
00802 
00803 /* 
00804  * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
00805  * authenticated (i.e. signed) attributes of "signerinfo". 
00806  *
00807  * This is expected to be included in outgoing signed messages for email (S/MIME).
00808  */
00809 SECStatus
00810 NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
00811 {
00812     NSSCMSAttribute *attr;
00813     SECItem *smimeekp = NULL;
00814     void *mark;
00815     PLArenaPool *poolp;
00816 
00817     /* verify this cert for encryption */
00818     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
00819        return SECFailure;
00820     }
00821 
00822     poolp = signerinfo->cmsg->poolp;
00823     mark = PORT_ArenaMark(poolp);
00824 
00825     smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
00826     if (smimeekp == NULL)
00827        goto loser;
00828 
00829     /* create new signing time attribute */
00830     if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
00831        goto loser;
00832 
00833     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
00834        goto loser;
00835 
00836     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
00837        goto loser;
00838 
00839     PORT_ArenaUnmark (poolp, mark);
00840     return SECSuccess;
00841 
00842 loser:
00843     PORT_ArenaRelease (poolp, mark);
00844     return SECFailure;
00845 }
00846 
00847 /* 
00848  * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
00849  * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft.
00850  *
00851  * This is expected to be included in outgoing signed messages for email (S/MIME),
00852  * if compatibility with Microsoft mail clients is wanted.
00853  */
00854 SECStatus
00855 NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
00856 {
00857     NSSCMSAttribute *attr;
00858     SECItem *smimeekp = NULL;
00859     void *mark;
00860     PLArenaPool *poolp;
00861 
00862     /* verify this cert for encryption */
00863     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
00864        return SECFailure;
00865     }
00866 
00867     poolp = signerinfo->cmsg->poolp;
00868     mark = PORT_ArenaMark(poolp);
00869 
00870     smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
00871     if (smimeekp == NULL)
00872        goto loser;
00873 
00874     /* create new signing time attribute */
00875     if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
00876        goto loser;
00877 
00878     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
00879        goto loser;
00880 
00881     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
00882        goto loser;
00883 
00884     PORT_ArenaUnmark (poolp, mark);
00885     return SECSuccess;
00886 
00887 loser:
00888     PORT_ArenaRelease (poolp, mark);
00889     return SECFailure;
00890 }
00891 
00892 /* 
00893  * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
00894  *
00895  * 1. digest the DER-encoded signature value of the original signerinfo
00896  * 2. create new signerinfo with correct version, sid, digestAlg
00897  * 3. add message-digest authAttr, but NO content-type
00898  * 4. sign the authAttrs
00899  * 5. DER-encode the new signerInfo
00900  * 6. add the whole thing to original signerInfo's unAuthAttrs
00901  *    as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
00902  *
00903  * XXXX give back the new signerinfo?
00904  */
00905 SECStatus
00906 NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
00907                                 SECOidTag digestalg, CERTCertificate signingcert)
00908 {
00909     /* XXXX TBD XXXX */
00910     return SECFailure;
00911 }
00912 
00913 /*
00914  * XXXX the following needs to be done in the S/MIME layer code
00915  * after signature of a signerinfo is verified
00916  */
00917 SECStatus
00918 NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
00919 {
00920     CERTCertificate *cert = NULL;
00921     SECItem *profile = NULL;
00922     NSSCMSAttribute *attr;
00923     SECItem *stime = NULL;
00924     SECItem *ekp;
00925     CERTCertDBHandle *certdb;
00926     int save_error;
00927     SECStatus rv;
00928     PRBool must_free_cert = PR_FALSE;
00929 
00930     certdb = CERT_GetDefaultCertDB();
00931 
00932     /* sanity check - see if verification status is ok (unverified does not count...) */
00933     if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
00934        return SECFailure;
00935 
00936     /* find preferred encryption cert */
00937     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
00938        (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
00939                             SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
00940     { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
00941        ekp = NSS_CMSAttribute_GetValue(attr);
00942        if (ekp == NULL)
00943            return SECFailure;
00944 
00945        /* we assume that all certs coming with the message have been imported to the */
00946        /* temporary database */
00947        cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
00948        if (cert == NULL)
00949            return SECFailure;
00950        must_free_cert = PR_TRUE;
00951     }
00952 
00953     if (cert == NULL) {
00954        /* no preferred cert found?
00955         * find the cert the signerinfo is signed with instead */
00956        cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
00957        if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
00958            return SECFailure;
00959     }
00960 
00961     /* verify this cert for encryption (has been verified for signing so far) */
00962     /* don't verify this cert for encryption. It may just be a signing cert.
00963      * that's OK, we can still save the S/MIME profile. The encryption cert
00964      * should have already been saved */
00965 #ifdef notdef
00966     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
00967        if (must_free_cert)
00968            CERT_DestroyCertificate(cert);
00969        return SECFailure;
00970     }
00971 #endif
00972 
00973     /* XXX store encryption cert permanently? */
00974 
00975     /*
00976      * Remember the current error set because we do not care about
00977      * anything set by the functions we are about to call.
00978      */
00979     save_error = PORT_GetError();
00980 
00981     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
00982        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
00983                                    SEC_OID_PKCS9_SMIME_CAPABILITIES,
00984                                    PR_TRUE);
00985        profile = NSS_CMSAttribute_GetValue(attr);
00986        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
00987                                    SEC_OID_PKCS9_SIGNING_TIME,
00988                                    PR_TRUE);
00989        stime = NSS_CMSAttribute_GetValue(attr);
00990     }
00991 
00992     rv = CERT_SaveSMimeProfile (cert, profile, stime);
00993     if (must_free_cert)
00994        CERT_DestroyCertificate(cert);
00995 
00996     /*
00997      * Restore the saved error in case the calls above set a new
00998      * one that we do not actually care about.
00999      */
01000     PORT_SetError (save_error);
01001 
01002     return rv;
01003 }
01004 
01005 /*
01006  * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
01007  */
01008 SECStatus
01009 NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, NSSCMSCertChainMode cm, SECCertUsage usage)
01010 {
01011     if (signerinfo->cert == NULL)
01012        return SECFailure;
01013 
01014     /* don't leak if we get called twice */
01015     if (signerinfo->certList != NULL) {
01016        CERT_DestroyCertificateList(signerinfo->certList);
01017        signerinfo->certList = NULL;
01018     }
01019 
01020     switch (cm) {
01021     case NSSCMSCM_None:
01022        signerinfo->certList = NULL;
01023        break;
01024     case NSSCMSCM_CertOnly:
01025        signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
01026        break;
01027     case NSSCMSCM_CertChain:
01028        signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
01029        break;
01030     case NSSCMSCM_CertChainWithRoot:
01031        signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
01032        break;
01033     }
01034 
01035     if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
01036        return SECFailure;
01037     
01038     return SECSuccess;
01039 }