cmssiginfo.c
上传用户:lyxiangda
上传日期:2007-01-12
资源大小:3042k
文件大小:26k
源码类别:

CA认证

开发平台:

WINDOWS

  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.  * CMS signerInfo methods.
  35.  *
  36.  * $Id: cmssiginfo.c,v 1.4 2000/06/20 16:28:57 chrisk%netscape.com Exp $
  37.  */
  38. #include "cmslocal.h"
  39. #include "cert.h"
  40. #include "key.h"
  41. #include "secasn1.h"
  42. #include "secitem.h"
  43. #include "secoid.h"
  44. #include "pk11func.h"
  45. #include "prtime.h"
  46. #include "secerr.h"
  47. #include "cryptohi.h"
  48. #include "smime.h"
  49. /* =============================================================================
  50.  * SIGNERINFO
  51.  */
  52. NSSCMSSignerInfo *
  53. NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
  54. {
  55.     void *mark;
  56.     NSSCMSSignerInfo *signerinfo;
  57.     int version;
  58.     PLArenaPool *poolp;
  59.     poolp = cmsg->poolp;
  60.     mark = PORT_ArenaMark(poolp);
  61.     signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
  62.     if (signerinfo == NULL) {
  63. PORT_ArenaRelease(poolp, mark);
  64. return NULL;
  65.     }
  66.     if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
  67. goto loser;
  68.     signerinfo->cmsg = cmsg;
  69.     signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN; /* hardcoded for now */
  70.     if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
  71. goto loser;
  72.     /* set version right now */
  73.     version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
  74.     /* RFC2630 5.3 "version is the syntax version number. If the .... " */
  75.     if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
  76. version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
  77.     (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
  78.     if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
  79. goto loser;
  80.     PORT_ArenaUnmark(poolp, mark);
  81.     return signerinfo;
  82. loser:
  83.     PORT_ArenaRelease(poolp, mark);
  84.     return NULL;
  85. }
  86. /*
  87.  * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
  88.  */
  89. void
  90. NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
  91. {
  92.     if (si->cert != NULL)
  93. CERT_DestroyCertificate(si->cert);
  94.     /* XXX storage ??? */
  95. }
  96. /*
  97.  * NSS_CMSSignerInfo_Sign - sign something
  98.  *
  99.  */
  100. SECStatus
  101. NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType)
  102. {
  103.     CERTCertificate *cert;
  104.     SECKEYPrivateKey *privkey = NULL;
  105.     SECOidTag digestalgtag;
  106.     SECOidTag signalgtag;
  107.     SECItem signature = { 0 };
  108.     SECStatus rv;
  109.     PLArenaPool *poolp, *tmppoolp; 
  110.     PORT_Assert (digest != NULL);
  111.     poolp = signerinfo->cmsg->poolp;
  112.     cert = signerinfo->cert;
  113.     if ((privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg)) == NULL)
  114. goto loser;
  115.     digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
  116.     /*
  117.      * XXX I think there should be a cert-level interface for this,
  118.      * so that I do not have to know about subjectPublicKeyInfo...
  119.      */
  120.     signalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
  121.     /* Fortezza MISSI have weird signature formats.  Map them to standard DSA formats */
  122.     signalgtag = PK11_FortezzaMapSig(signalgtag);
  123.     if (signerinfo->authAttr != NULL) {
  124. SECItem encoded_attrs;
  125. /* find and fill in the message digest attribute. */
  126. rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
  127. if (rv != SECSuccess)
  128.     goto loser;
  129. if (contentType != NULL) {
  130.     /* if the caller wants us to, find and fill in the content type attribute. */
  131.     rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
  132.     if (rv != SECSuccess)
  133. goto loser;
  134. }
  135. if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
  136.     PORT_SetError(SEC_ERROR_NO_MEMORY);
  137.     goto loser;
  138. }
  139. /*
  140.  * Before encoding, reorder the attributes so that when they
  141.  * are encoded, they will be conforming DER, which is required
  142.  * to have a specific order and that is what must be used for
  143.  * the hash/signature.  We do this here, rather than building
  144.  * it into EncodeAttributes, because we do not want to do
  145.  * such reordering on incoming messages (which also uses
  146.  * EncodeAttributes) or our old signatures (and other "broken"
  147.  * implementations) will not verify.  So, we want to guarantee
  148.  * that we send out good DER encodings of attributes, but not
  149.  * to expect to receive them.
  150.  */
  151. if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
  152.     goto loser;
  153. encoded_attrs.data = NULL;
  154. encoded_attrs.len = 0;
  155. if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), &encoded_attrs) == NULL)
  156.     goto loser;
  157. rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, privkey,
  158.    NSS_CMSUtil_MakeSignatureAlgorithm(digestalgtag, signalgtag));
  159. PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
  160.     } else {
  161. rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
  162.     }
  163.     SECKEY_DestroyPrivateKey(privkey);
  164.     privkey = NULL;
  165.     if (rv != SECSuccess)
  166. goto loser;
  167.     if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
  168. goto loser;
  169.     SECITEM_FreeItem(&signature, PR_FALSE);
  170.     if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), signalgtag, NULL) != SECSuccess)
  171. goto loser;
  172.     return SECSuccess;
  173. loser:
  174.     if (signature.len != 0)
  175. SECITEM_FreeItem (&signature, PR_FALSE);
  176.     if (privkey)
  177. SECKEY_DestroyPrivateKey(privkey);
  178.     return SECFailure;
  179. }
  180. SECStatus
  181. NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
  182.     SECCertUsage certusage)
  183. {
  184.     CERTCertificate *cert;
  185.     int64 stime;
  186.     if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
  187. signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
  188. return SECFailure;
  189.     }
  190.     /*
  191.      * Get and convert the signing time; if available, it will be used
  192.      * both on the cert verification and for importing the sender
  193.      * email profile.
  194.      */
  195.     if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess)
  196. stime = PR_Now(); /* not found or conversion failed, so check against now */
  197.     
  198.     /*
  199.      * XXX  This uses the signing time, if available.  Additionally, we
  200.      * might want to, if there is no signing time, get the message time
  201.      * from the mail header itself, and use that.  That would require
  202.      * a change to our interface though, and for S/MIME callers to pass
  203.      * in a time (and for non-S/MIME callers to pass in nothing, or
  204.      * maybe make them pass in the current time, always?).
  205.      */
  206.     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
  207. signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
  208. return SECFailure;
  209.     }
  210.     return SECSuccess;
  211. }
  212. /*
  213.  * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
  214.  *
  215.  * Just verifies the signature. The assumption is that verification of the certificate
  216.  * is done already.
  217.  */
  218. SECStatus
  219. NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType)
  220. {
  221.     SECKEYPublicKey *publickey = NULL;
  222.     NSSCMSAttribute *attr;
  223.     SECItem encoded_attrs;
  224.     CERTCertificate *cert;
  225.     NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
  226.     PLArenaPool *poolp;
  227.     if (signerinfo == NULL)
  228. return SECFailure;
  229.     /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL and */
  230.     /* cert has not been verified */
  231.     if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL)) == NULL) {
  232. vs = NSSCMSVS_SigningCertNotFound;
  233. goto loser;
  234.     }
  235.     if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
  236. vs = NSSCMSVS_ProcessingError;
  237. goto loser;
  238.     }
  239.     /*
  240.      * XXX This may not be the right set of algorithms to check.
  241.      * I'd prefer to trust that just calling VFY_Verify{Data,Digest}
  242.      * would do the right thing (and set an error if it could not);
  243.      * then additional algorithms could be handled by that code
  244.      * and we would Just Work.  So this check should just be removed,
  245.      * but not until the VFY code is better at setting errors.
  246.      */
  247.     switch (SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg))) {
  248.     case SEC_OID_PKCS1_RSA_ENCRYPTION:
  249.     case SEC_OID_ANSIX9_DSA_SIGNATURE:
  250.     case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
  251. /* ok */
  252. break;
  253.     case SEC_OID_UNKNOWN:
  254. vs = NSSCMSVS_SignatureAlgorithmUnknown;
  255. goto loser;
  256.     default:
  257. vs = NSSCMSVS_SignatureAlgorithmUnsupported;
  258. goto loser;
  259.     }
  260.     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
  261. if (contentType) {
  262.     /*
  263.      * Check content type
  264.      *
  265.      * RFC2630 sez that if there are any authenticated attributes,
  266.      * then there must be one for content type which matches the
  267.      * content type of the content being signed, and there must
  268.      * be one for message digest which matches our message digest.
  269.      * So check these things first.
  270.      */
  271.     if ((attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
  272. SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
  273.     {
  274. vs = NSSCMSVS_MalformedSignature;
  275. goto loser;
  276.     }
  277.     if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
  278. vs = NSSCMSVS_MalformedSignature;
  279. goto loser;
  280.     }
  281. }
  282. /*
  283.  * Check digest
  284.  */
  285. if ((attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
  286. {
  287.     vs = NSSCMSVS_MalformedSignature;
  288.     goto loser;
  289. }
  290. if (NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
  291.     vs = NSSCMSVS_DigestMismatch;
  292.     goto loser;
  293. }
  294. if ((poolp = PORT_NewArena (1024)) == NULL) {
  295.     vs = NSSCMSVS_ProcessingError;
  296.     goto loser;
  297. }
  298. /*
  299.  * Check signature
  300.  *
  301.  * The signature is based on a digest of the DER-encoded authenticated
  302.  * attributes.  So, first we encode and then we digest/verify.
  303.  * we trust the decoder to have the attributes in the right (sorted) order
  304.  */
  305. encoded_attrs.data = NULL;
  306. encoded_attrs.len = 0;
  307. if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
  308. encoded_attrs.data == NULL || encoded_attrs.len == 0)
  309. {
  310.     vs = NSSCMSVS_ProcessingError;
  311.     goto loser;
  312. }
  313. vs = (VFY_VerifyData (encoded_attrs.data, encoded_attrs.len,
  314. publickey, &(signerinfo->encDigest),
  315. SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
  316. signerinfo->cmsg->pwfn_arg) != SECSuccess) ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
  317. PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
  318.     } else {
  319. SECItem *sig;
  320. /* No authenticated attributes. The signature is based on the plain message digest. */
  321. sig = &(signerinfo->encDigest);
  322. if (sig->len == 0)
  323.     goto loser;
  324. vs = (VFY_VerifyDigest(digest, publickey, sig,
  325. SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
  326. signerinfo->cmsg->pwfn_arg) != SECSuccess) ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
  327.     }
  328.     if (vs == NSSCMSVS_BadSignature) {
  329. /*
  330.  * XXX Change the generic error into our specific one, because
  331.  * in that case we get a better explanation out of the Security
  332.  * Advisor.  This is really a bug in our error strings (the
  333.  * "generic" error has a lousy/wrong message associated with it
  334.  * which assumes the signature verification was done for the
  335.  * purposes of checking the issuer signature on a certificate)
  336.  * but this is at least an easy workaround and/or in the
  337.  * Security Advisor, which specifically checks for the error
  338.  * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
  339.  * in that case but does not similarly check for
  340.  * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
  341.  * probably say the wrong thing in the case that it *was* the
  342.  * certificate signature check that failed during the cert
  343.  * verification done above.  Our error handling is really a mess.
  344.  */
  345. if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
  346.     PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
  347.     }
  348.     if (publickey != NULL)
  349. SECKEY_DestroyPublicKey (publickey);
  350.     signerinfo->verificationStatus = vs;
  351.     return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
  352. loser:
  353.     if (publickey != NULL)
  354. SECKEY_DestroyPublicKey (publickey);
  355.     signerinfo->verificationStatus = vs;
  356.     PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
  357.     return SECFailure;
  358. }
  359. NSSCMSVerificationStatus
  360. NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
  361. {
  362.     return signerinfo->verificationStatus;
  363. }
  364. SECOidData *
  365. NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
  366. {
  367.     return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
  368. }
  369. SECOidTag
  370. NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
  371. {
  372.     SECOidData *algdata;
  373.     algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
  374.     if (algdata != NULL)
  375. return algdata->offset;
  376.     else
  377. return SEC_OID_UNKNOWN;
  378. }
  379. CERTCertificateList *
  380. NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
  381. {
  382.     return signerinfo->certList;
  383. }
  384. int
  385. NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
  386. {
  387.     unsigned long version;
  388.     /* always take apart the SECItem */
  389.     if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
  390. return 0;
  391.     else
  392. return (int)version;
  393. }
  394. /*
  395.  * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
  396.  *       in UTCTime format, of a CMS signerInfo.
  397.  *
  398.  * sinfo - signerInfo data for this signer
  399.  *
  400.  * Returns a pointer to XXXX (what?)
  401.  * A return value of NULL is an error.
  402.  */
  403. SECStatus
  404. NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
  405. {
  406.     NSSCMSAttribute *attr;
  407.     SECItem *value;
  408.     if (sinfo == NULL)
  409. return SECFailure;
  410.     if (sinfo->signingTime != 0) {
  411. *stime = sinfo->signingTime; /* cached copy */
  412. return SECSuccess;
  413.     }
  414.     attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
  415.     /* XXXX multi-valued attributes NIH */
  416.     if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
  417. return SECFailure;
  418.     if (DER_UTCTimeToTime(stime, value) != SECSuccess)
  419. return SECFailure;
  420.     sinfo->signingTime = *stime; /* make cached copy */
  421.     return SECSuccess;
  422. }
  423. /*
  424.  * Return the signing cert of a CMS signerInfo.
  425.  *
  426.  * the certs in the enclosing SignedData must have been imported already
  427.  */
  428. CERTCertificate *
  429. NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
  430. {
  431.     CERTCertificate *cert;
  432.     if (signerinfo->cert != NULL)
  433. return signerinfo->cert;
  434.     /* no certdb, and cert hasn't been set yet? */
  435.     if (certdb == NULL)
  436. return NULL;
  437.     /*
  438.      * This cert will also need to be freed, but since we save it
  439.      * in signerinfo for later, we do not want to destroy it when
  440.      * we leave this function -- we let the clean-up of the entire
  441.      * cinfo structure later do the destroy of this cert.
  442.      */
  443.     switch (signerinfo->signerIdentifier.identifierType) {
  444.     case NSSCMSSignerID_IssuerSN:
  445. cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->signerIdentifier.id.issuerAndSN);
  446. break;
  447.     case NSSCMSSignerID_SubjectKeyID:
  448. #if 0 /* not yet implemented */
  449. cert = CERT_FindCertBySubjectKeyID(certdb, signerinfo->signerIdentifier.id.subjectKeyID);
  450. #else
  451. cert = NULL;
  452. #endif
  453. break;
  454.     default:
  455. cert = NULL;
  456. break;
  457.     }
  458.     /* cert can be NULL at that point */
  459.     signerinfo->cert = cert; /* earmark it */
  460.     return cert;
  461. }
  462. /*
  463.  * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
  464.  *
  465.  * sinfo - signerInfo data for this signer
  466.  *
  467.  * Returns a pointer to allocated memory, which must be freed.
  468.  * A return value of NULL is an error.
  469.  */
  470. char *
  471. NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
  472. {
  473.     CERTCertificate *signercert;
  474.     /* will fail if cert is not verified */
  475.     if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
  476. return NULL;
  477.     return (CERT_GetCommonName(&signercert->subject));
  478. }
  479. /*
  480.  * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
  481.  *
  482.  * sinfo - signerInfo data for this signer
  483.  *
  484.  * Returns a pointer to allocated memory, which must be freed.
  485.  * A return value of NULL is an error.
  486.  */
  487. char *
  488. NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
  489. {
  490.     CERTCertificate *signercert;
  491.     if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
  492. return NULL;
  493.     if (signercert->emailAddr == NULL)
  494. return NULL;
  495.     return (PORT_Strdup(signercert->emailAddr));
  496. }
  497. /*
  498.  * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
  499.  * authenticated (i.e. signed) attributes of "signerinfo". 
  500.  */
  501. SECStatus
  502. NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
  503. {
  504.     return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
  505. }
  506. /*
  507.  * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
  508.  * unauthenticated attributes of "signerinfo". 
  509.  */
  510. SECStatus
  511. NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
  512. {
  513.     return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
  514. }
  515. /* 
  516.  * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
  517.  * authenticated (i.e. signed) attributes of "signerinfo". 
  518.  *
  519.  * This is expected to be included in outgoing signed
  520.  * messages for email (S/MIME) but is likely useful in other situations.
  521.  *
  522.  * This should only be added once; a second call will do nothing.
  523.  *
  524.  * XXX This will probably just shove the current time into "signerinfo"
  525.  * but it will not actually get signed until the entire item is
  526.  * processed for encoding.  Is this (expected to be small) delay okay?
  527.  */
  528. SECStatus
  529. NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
  530. {
  531.     NSSCMSAttribute *attr;
  532.     SECItem stime;
  533.     void *mark;
  534.     PLArenaPool *poolp;
  535.     poolp = signerinfo->cmsg->poolp;
  536.     mark = PORT_ArenaMark(poolp);
  537.     /* create new signing time attribute */
  538.     if (DER_TimeToUTCTime(&stime, t) != SECSuccess)
  539. goto loser;
  540.     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
  541. SECITEM_FreeItem (&stime, PR_FALSE);
  542. goto loser;
  543.     }
  544.     SECITEM_FreeItem (&stime, PR_FALSE);
  545.     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
  546. goto loser;
  547.     PORT_ArenaUnmark (poolp, mark);
  548.     return SECSuccess;
  549. loser:
  550.     PORT_ArenaRelease (poolp, mark);
  551.     return SECFailure;
  552. }
  553. /* 
  554.  * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
  555.  * authenticated (i.e. signed) attributes of "signerinfo". 
  556.  *
  557.  * This is expected to be included in outgoing signed
  558.  * messages for email (S/MIME).
  559.  */
  560. SECStatus
  561. NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
  562. {
  563.     NSSCMSAttribute *attr;
  564.     SECItem *smimecaps = NULL;
  565.     void *mark;
  566.     PLArenaPool *poolp;
  567.     poolp = signerinfo->cmsg->poolp;
  568.     mark = PORT_ArenaMark(poolp);
  569.     smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
  570.     if (smimecaps == NULL)
  571. goto loser;
  572.     /* create new signing time attribute */
  573.     if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps,
  574.     PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
  575. goto loser;
  576.     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
  577. goto loser;
  578.     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
  579. goto loser;
  580.     PORT_ArenaUnmark (poolp, mark);
  581.     return SECSuccess;
  582. loser:
  583.     PORT_ArenaRelease (poolp, mark);
  584.     return SECFailure;
  585. }
  586. /* 
  587.  * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
  588.  * authenticated (i.e. signed) attributes of "signerinfo". 
  589.  *
  590.  * This is expected to be included in outgoing signed messages for email (S/MIME).
  591.  */
  592. SECStatus
  593. NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
  594. {
  595.     NSSCMSAttribute *attr;
  596.     SECItem *smimeekp = NULL;
  597.     void *mark;
  598.     PLArenaPool *poolp;
  599.     /* verify this cert for encryption */
  600.     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
  601. return SECFailure;
  602.     }
  603.     poolp = signerinfo->cmsg->poolp;
  604.     mark = PORT_ArenaMark(poolp);
  605.     smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
  606.     if (smimeekp == NULL)
  607. goto loser;
  608.     /* create new signing time attribute */
  609.     if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
  610. goto loser;
  611.     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
  612. goto loser;
  613.     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
  614. goto loser;
  615.     PORT_ArenaUnmark (poolp, mark);
  616.     return SECSuccess;
  617. loser:
  618.     PORT_ArenaRelease (poolp, mark);
  619.     return SECFailure;
  620. }
  621. /* 
  622.  * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
  623.  *
  624.  * 1. digest the DER-encoded signature value of the original signerinfo
  625.  * 2. create new signerinfo with correct version, sid, digestAlg
  626.  * 3. add message-digest authAttr, but NO content-type
  627.  * 4. sign the authAttrs
  628.  * 5. DER-encode the new signerInfo
  629.  * 6. add the whole thing to original signerInfo's unAuthAttrs
  630.  *    as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
  631.  *
  632.  * XXXX give back the new signerinfo?
  633.  */
  634. SECStatus
  635. NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
  636.     SECOidTag digestalg, CERTCertificate signingcert)
  637. {
  638.     /* XXXX TBD XXXX */
  639.     return SECFailure;
  640. }
  641. /*
  642.  * XXXX the following needs to be done in the S/MIME layer code
  643.  * after signature of a signerinfo is verified
  644.  */
  645. SECStatus
  646. NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
  647. {
  648.     CERTCertificate *cert = NULL;
  649.     SECItem *profile = NULL;
  650.     NSSCMSAttribute *attr;
  651.     SECItem *utc_stime = NULL;
  652.     SECItem *ekp;
  653.     CERTCertDBHandle *certdb;
  654.     int save_error;
  655.     SECStatus rv;
  656.     certdb = CERT_GetDefaultCertDB();
  657.     /* sanity check - see if verification status is ok (unverified does not count...) */
  658.     if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
  659. return SECFailure;
  660.     /* find preferred encryption cert */
  661.     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
  662. (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
  663.        SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
  664.     { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
  665. ekp = NSS_CMSAttribute_GetValue(attr);
  666. if (ekp == NULL)
  667.     return SECFailure;
  668. /* we assume that all certs coming with the message have been imported to the */
  669. /* temporary database */
  670. cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
  671. if (cert == NULL)
  672.     return SECFailure;
  673.     }
  674.     if (cert == NULL) {
  675. /* no preferred cert found?
  676.  * find the cert the signerinfo is signed with instead */
  677. cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
  678. if (cert == NULL || cert->emailAddr == NULL)
  679.     return SECFailure;
  680.     }
  681.     /* verify this cert for encryption (has been verified for signing so far) */
  682.     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
  683. return SECFailure;
  684.     }
  685.     /* XXX store encryption cert permanently? */
  686.     /*
  687.      * Remember the current error set because we do not care about
  688.      * anything set by the functions we are about to call.
  689.      */
  690.     save_error = PORT_GetError();
  691.     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
  692. attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
  693.        SEC_OID_PKCS9_SMIME_CAPABILITIES,
  694.        PR_TRUE);
  695. profile = NSS_CMSAttribute_GetValue(attr);
  696. attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
  697.        SEC_OID_PKCS9_SIGNING_TIME,
  698.        PR_TRUE);
  699. utc_stime = NSS_CMSAttribute_GetValue(attr);
  700.     }
  701.     rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
  702.     /*
  703.      * Restore the saved error in case the calls above set a new
  704.      * one that we do not actually care about.
  705.      */
  706.     PORT_SetError (save_error);
  707.     return rv;
  708. }
  709. /*
  710.  * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
  711.  */
  712. SECStatus
  713. NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, NSSCMSCertChainMode cm, SECCertUsage usage)
  714. {
  715.     if (signerinfo->cert == NULL)
  716. return SECFailure;
  717.     switch (cm) {
  718.     case NSSCMSCM_None:
  719. signerinfo->certList = NULL;
  720. break;
  721.     case NSSCMSCM_CertOnly:
  722. signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
  723. break;
  724.     case NSSCMSCM_CertChain:
  725. signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
  726. break;
  727.     case NSSCMSCM_CertChainWithRoot:
  728. signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
  729. break;
  730.     }
  731.     if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
  732. return SECFailure;
  733.     
  734.     return SECSuccess;
  735. }