security/nss/lib/smime/cmssiginfo.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /*
     6  * CMS signerInfo methods.
     7  */
     9 #include "cmslocal.h"
    11 #include "cert.h"
    12 #include "key.h"
    13 #include "secasn1.h"
    14 #include "secitem.h"
    15 #include "secoid.h"
    16 #include "pk11func.h"
    17 #include "prtime.h"
    18 #include "secerr.h"
    19 #include "secder.h"
    20 #include "cryptohi.h"
    22 #include "smime.h"
    24 /* =============================================================================
    25  * SIGNERINFO
    26  */
    27 NSSCMSSignerInfo *
    28 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 
    29 	CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 
    30 	SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
    32 NSSCMSSignerInfo *
    33 NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID, 
    34 	SECKEYPublicKey *pubKey, SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
    35 {
    36     return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag); 
    37 }
    39 NSSCMSSignerInfo *
    40 NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
    41 {
    42     return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL, NULL, NULL, digestalgtag); 
    43 }
    45 NSSCMSSignerInfo *
    46 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 
    47 	CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 
    48 	SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
    49 {
    50     void *mark;
    51     NSSCMSSignerInfo *signerinfo;
    52     int version;
    53     PLArenaPool *poolp;
    55     poolp = cmsg->poolp;
    57     mark = PORT_ArenaMark(poolp);
    59     signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
    60     if (signerinfo == NULL) {
    61 	PORT_ArenaRelease(poolp, mark);
    62 	return NULL;
    63     }
    66     signerinfo->cmsg = cmsg;
    68     switch(type) {
    69     case NSSCMSSignerID_IssuerSN:
    70         signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
    71         if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
    72 	    goto loser;
    73         if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
    74 	    goto loser;
    75         break;
    76     case NSSCMSSignerID_SubjectKeyID:
    77         signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
    78         PORT_Assert(subjKeyID);
    79         if (!subjKeyID)
    80             goto loser;
    82         signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
    83         SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
    84                          subjKeyID);
    85         signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
    86         if (!signerinfo->signingKey)
    87             goto loser;
    88         signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
    89         if (!signerinfo->pubKey)
    90             goto loser;
    91         break;
    92     default:
    93         goto loser;
    94     }
    96     /* set version right now */
    97     version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
    98     /* RFC2630 5.3 "version is the syntax version number. If the .... " */
    99     if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
   100 	version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
   101     (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
   103     if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
   104 	goto loser;
   106     PORT_ArenaUnmark(poolp, mark);
   107     return signerinfo;
   109 loser:
   110     PORT_ArenaRelease(poolp, mark);
   111     return NULL;
   112 }
   114 /*
   115  * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
   116  */
   117 void
   118 NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
   119 {
   120     if (si->cert != NULL)
   121 	CERT_DestroyCertificate(si->cert);
   123     if (si->certList != NULL) 
   124 	CERT_DestroyCertificateList(si->certList);
   126     /* XXX storage ??? */
   127 }
   129 /*
   130  * NSS_CMSSignerInfo_Sign - sign something
   131  *
   132  */
   133 SECStatus
   134 NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, 
   135                        SECItem *contentType)
   136 {
   137     CERTCertificate *cert;
   138     SECKEYPrivateKey *privkey = NULL;
   139     SECOidTag digestalgtag;
   140     SECOidTag pubkAlgTag;
   141     SECItem signature = { 0 };
   142     SECStatus rv;
   143     PLArenaPool *poolp, *tmppoolp = NULL;
   144     SECAlgorithmID *algID, freeAlgID;
   145     CERTSubjectPublicKeyInfo *spki;
   147     PORT_Assert (digest != NULL);
   149     poolp = signerinfo->cmsg->poolp;
   151     switch (signerinfo->signerIdentifier.identifierType) {
   152     case NSSCMSSignerID_IssuerSN:
   153         cert = signerinfo->cert;
   155         privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
   156         if (privkey == NULL)
   157 	    goto loser;
   158         algID = &cert->subjectPublicKeyInfo.algorithm;
   159         break;
   160     case NSSCMSSignerID_SubjectKeyID:
   161         privkey = signerinfo->signingKey;
   162         signerinfo->signingKey = NULL;
   163         spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
   164         SECKEY_DestroyPublicKey(signerinfo->pubKey);
   165         signerinfo->pubKey = NULL;
   166         SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
   167         SECKEY_DestroySubjectPublicKeyInfo(spki); 
   168         algID = &freeAlgID;
   169         break;
   170     default:
   171         goto loser;
   172     }
   173     digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
   174     /*
   175      * XXX I think there should be a cert-level interface for this,
   176      * so that I do not have to know about subjectPublicKeyInfo...
   177      */
   178     pubkAlgTag = SECOID_GetAlgorithmTag(algID);
   179     if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) {
   180       SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
   181     }
   183     if (signerinfo->authAttr != NULL) {
   184 	SECOidTag signAlgTag;
   185 	SECItem encoded_attrs;
   187 	/* find and fill in the message digest attribute. */
   188 	rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 
   189 	                       SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
   190 	if (rv != SECSuccess)
   191 	    goto loser;
   193 	if (contentType != NULL) {
   194 	    /* if the caller wants us to, find and fill in the content type attribute. */
   195 	    rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 
   196 	                    SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
   197 	    if (rv != SECSuccess)
   198 		goto loser;
   199 	}
   201 	if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
   202 	    PORT_SetError(SEC_ERROR_NO_MEMORY);
   203 	    goto loser;
   204 	}
   206 	/*
   207 	 * Before encoding, reorder the attributes so that when they
   208 	 * are encoded, they will be conforming DER, which is required
   209 	 * to have a specific order and that is what must be used for
   210 	 * the hash/signature.  We do this here, rather than building
   211 	 * it into EncodeAttributes, because we do not want to do
   212 	 * such reordering on incoming messages (which also uses
   213 	 * EncodeAttributes) or our old signatures (and other "broken"
   214 	 * implementations) will not verify.  So, we want to guarantee
   215 	 * that we send out good DER encodings of attributes, but not
   216 	 * to expect to receive them.
   217 	 */
   218 	if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
   219 	    goto loser;
   221 	encoded_attrs.data = NULL;
   222 	encoded_attrs.len = 0;
   223 	if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), 
   224 	                &encoded_attrs) == NULL)
   225 	    goto loser;
   227 	signAlgTag = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, 
   228                                                      digestalgtag);
   229 	if (signAlgTag == SEC_OID_UNKNOWN) {
   230 	    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   231 	    goto loser;
   232 	}
   234 	rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, 
   235 	                  privkey, signAlgTag);
   236 	PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
   237 	tmppoolp = 0;
   238     } else {
   239 	rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
   240     }
   241     SECKEY_DestroyPrivateKey(privkey);
   242     privkey = NULL;
   244     if (rv != SECSuccess)
   245 	goto loser;
   247     if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) 
   248           != SECSuccess)
   249 	goto loser;
   251     SECITEM_FreeItem(&signature, PR_FALSE);
   253     if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, 
   254                               NULL) != SECSuccess)
   255 	goto loser;
   257     return SECSuccess;
   259 loser:
   260     if (signature.len != 0)
   261 	SECITEM_FreeItem (&signature, PR_FALSE);
   262     if (privkey)
   263 	SECKEY_DestroyPrivateKey(privkey);
   264     if (tmppoolp)
   265 	PORT_FreeArena(tmppoolp, PR_FALSE);
   266     return SECFailure;
   267 }
   269 SECStatus
   270 NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
   271 			    SECCertUsage certusage)
   272 {
   273     CERTCertificate *cert;
   274     PRTime stime;
   276     if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
   277 	signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
   278 	return SECFailure;
   279     }
   281     /*
   282      * Get and convert the signing time; if available, it will be used
   283      * both on the cert verification and for importing the sender
   284      * email profile.
   285      */
   286     if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess)
   287 	stime = PR_Now(); /* not found or conversion failed, so check against now */
   289     /*
   290      * XXX  This uses the signing time, if available.  Additionally, we
   291      * might want to, if there is no signing time, get the message time
   292      * from the mail header itself, and use that.  That would require
   293      * a change to our interface though, and for S/MIME callers to pass
   294      * in a time (and for non-S/MIME callers to pass in nothing, or
   295      * maybe make them pass in the current time, always?).
   296      */
   297     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, 
   298                         signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
   299 	signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
   300 	return SECFailure;
   301     }
   302     return SECSuccess;
   303 }
   305 /*
   306  * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
   307  *
   308  * Just verifies the signature. The assumption is that verification of 
   309  * the certificate is done already.
   310  */
   311 SECStatus
   312 NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, 
   313                          SECItem *digest,               /* may be NULL */
   314                          SECItem *contentType)          /* may be NULL */
   315 {
   316     SECKEYPublicKey *publickey = NULL;
   317     NSSCMSAttribute *attr;
   318     SECItem encoded_attrs;
   319     CERTCertificate *cert;
   320     NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
   321     PLArenaPool *poolp;
   322     SECOidTag    digestalgtag;
   323     SECOidTag    pubkAlgTag;
   325     if (signerinfo == NULL)
   326 	return SECFailure;
   328     /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL 
   329     ** and cert has not been verified 
   330     */
   331     cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
   332     if (cert == NULL) {
   333 	vs = NSSCMSVS_SigningCertNotFound;
   334 	goto loser;
   335     }
   337     if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
   338 	vs = NSSCMSVS_ProcessingError;
   339 	goto loser;
   340     }
   342     digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
   343     pubkAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
   344     if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN)) {
   345 	vs = NSSCMSVS_SignatureAlgorithmUnknown;
   346 	goto loser;
   347     }
   349     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
   350 	if (contentType) {
   351 	    /*
   352 	     * Check content type
   353 	     *
   354 	     * RFC2630 sez that if there are any authenticated attributes,
   355 	     * then there must be one for content type which matches the
   356 	     * content type of the content being signed, and there must
   357 	     * be one for message digest which matches our message digest.
   358 	     * So check these things first.
   359 	     */
   360 	    attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
   361 					SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
   362 	    if (attr == NULL) {
   363 		vs = NSSCMSVS_MalformedSignature;
   364 		goto loser;
   365 	    }
   367 	    if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
   368 		vs = NSSCMSVS_MalformedSignature;
   369 		goto loser;
   370 	    }
   371 	}
   373 	/*
   374 	 * Check digest
   375 	 */
   376 	attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 
   377 	                              SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
   378 	if (attr == NULL) {
   379 	    vs = NSSCMSVS_MalformedSignature;
   380 	    goto loser;
   381 	}
   382 	if (!digest || 
   383 	    NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
   384 	    vs = NSSCMSVS_DigestMismatch;
   385 	    goto loser;
   386 	}
   388 	if ((poolp = PORT_NewArena (1024)) == NULL) {
   389 	    vs = NSSCMSVS_ProcessingError;
   390 	    goto loser;
   391 	}
   393 	/*
   394 	 * Check signature
   395 	 *
   396 	 * The signature is based on a digest of the DER-encoded authenticated
   397 	 * attributes.  So, first we encode and then we digest/verify.
   398 	 * we trust the decoder to have the attributes in the right (sorted) 
   399 	 * order
   400 	 */
   401 	encoded_attrs.data = NULL;
   402 	encoded_attrs.len = 0;
   404 	if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), 
   405 	                                 &encoded_attrs) == NULL ||
   406 		encoded_attrs.data == NULL || encoded_attrs.len == 0) {
   407 	    vs = NSSCMSVS_ProcessingError;
   408 	    goto loser;
   409 	}
   411 	vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len,
   412 		publickey, &(signerinfo->encDigest), pubkAlgTag,
   413 		digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess) 
   414 		? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
   416 	PORT_FreeArena(poolp, PR_FALSE);  /* awkward memory management :-( */
   418     } else {
   419 	SECItem *sig;
   421 	/* No authenticated attributes. 
   422 	** The signature is based on the plain message digest. 
   423 	*/
   424 	sig = &(signerinfo->encDigest);
   425 	if (sig->len == 0)
   426 	    goto loser;
   428 	vs = (!digest || 
   429 	      VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag,
   430 		digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess) 
   431 		? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
   432     }
   434     if (vs == NSSCMSVS_BadSignature) {
   435 	int error = PORT_GetError();
   436 	/*
   437 	 * XXX Change the generic error into our specific one, because
   438 	 * in that case we get a better explanation out of the Security
   439 	 * Advisor.  This is really a bug in the PSM error strings (the
   440 	 * "generic" error has a lousy/wrong message associated with it
   441 	 * which assumes the signature verification was done for the
   442 	 * purposes of checking the issuer signature on a certificate)
   443 	 * but this is at least an easy workaround and/or in the
   444 	 * Security Advisor, which specifically checks for the error
   445 	 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
   446 	 * in that case but does not similarly check for
   447 	 * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
   448 	 * probably say the wrong thing in the case that it *was* the
   449 	 * certificate signature check that failed during the cert
   450 	 * verification done above.  Our error handling is really a mess.
   451 	 */
   452 	if (error == SEC_ERROR_BAD_SIGNATURE)
   453 	    PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   454 	/*
   455 	 * map algorithm failures to NSSCMSVS values 
   456 	 */
   457 	if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) ||
   458 	    (error == SEC_ERROR_INVALID_ALGORITHM)) {
   459 	    /* keep the same error code as 3.11 and before */
   460 	    PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
   461 	    vs = NSSCMSVS_SignatureAlgorithmUnsupported;
   462 	}
   463     }
   465     if (publickey != NULL)
   466 	SECKEY_DestroyPublicKey (publickey);
   468     signerinfo->verificationStatus = vs;
   470     return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
   472 loser:
   473     if (publickey != NULL)
   474 	SECKEY_DestroyPublicKey (publickey);
   476     signerinfo->verificationStatus = vs;
   478     PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
   479     return SECFailure;
   480 }
   482 NSSCMSVerificationStatus
   483 NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
   484 {
   485     return signerinfo->verificationStatus;
   486 }
   488 SECOidData *
   489 NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
   490 {
   491     SECOidData *algdata;
   492     SECOidTag   algtag;
   494     algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
   495     if (algdata == NULL) {
   496 	return algdata;
   497     }
   498     /* Windows may have given us a signer algorithm oid instead of a digest 
   499      * algorithm oid. This call will map to a signer oid to a digest one, 
   500      * otherwise it leaves the oid alone and let the chips fall as they may
   501      * if it's not a digest oid.
   502      */
   503     algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset);
   504     if (algtag != algdata->offset) {
   505 	/* if the tags don't match, then we must have received a signer 
   506 	 * algorithID. Now we need to get the oid data for the digest
   507 	 * oid, which the rest of the code is expecting */
   508 	algdata = SECOID_FindOIDByTag(algtag);
   509     }
   511     return algdata;
   513 }
   515 SECOidTag
   516 NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
   517 {
   518     SECOidData *algdata;
   520     if (!signerinfo) {
   521         PORT_SetError(SEC_ERROR_INVALID_ARGS);
   522         return SEC_OID_UNKNOWN;
   523     }
   525     algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
   526     if (algdata != NULL)
   527 	return algdata->offset;
   528     else
   529 	return SEC_OID_UNKNOWN;
   530 }
   532 CERTCertificateList *
   533 NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
   534 {
   535     return signerinfo->certList;
   536 }
   538 int
   539 NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
   540 {
   541     unsigned long version;
   543     /* always take apart the SECItem */
   544     if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
   545 	return 0;
   546     else
   547 	return (int)version;
   548 }
   550 /*
   551  * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
   552  *				      in UTCTime or GeneralizedTime format,
   553  *                                    of a CMS signerInfo.
   554  *
   555  * sinfo - signerInfo data for this signer
   556  *
   557  * Returns a pointer to XXXX (what?)
   558  * A return value of NULL is an error.
   559  */
   560 SECStatus
   561 NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
   562 {
   563     NSSCMSAttribute *attr;
   564     SECItem *value;
   566     if (sinfo == NULL)
   567 	return SECFailure;
   569     if (sinfo->signingTime != 0) {
   570 	*stime = sinfo->signingTime;	/* cached copy */
   571 	return SECSuccess;
   572     }
   574     attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
   575     /* XXXX multi-valued attributes NIH */
   576     if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
   577 	return SECFailure;
   578     if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
   579 	return SECFailure;
   580     sinfo->signingTime = *stime;	/* make cached copy */
   581     return SECSuccess;
   582 }
   584 /*
   585  * Return the signing cert of a CMS signerInfo.
   586  *
   587  * the certs in the enclosing SignedData must have been imported already
   588  */
   589 CERTCertificate *
   590 NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
   591 {
   592     CERTCertificate *cert;
   593     NSSCMSSignerIdentifier *sid;
   595     if (signerinfo->cert != NULL)
   596 	return signerinfo->cert;
   598     /* no certdb, and cert hasn't been set yet? */
   599     if (certdb == NULL)
   600 	return NULL;
   602     /*
   603      * This cert will also need to be freed, but since we save it
   604      * in signerinfo for later, we do not want to destroy it when
   605      * we leave this function -- we let the clean-up of the entire
   606      * cinfo structure later do the destroy of this cert.
   607      */
   608     sid = &signerinfo->signerIdentifier;
   609     switch (sid->identifierType) {
   610     case NSSCMSSignerID_IssuerSN:
   611 	cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
   612 	break;
   613     case NSSCMSSignerID_SubjectKeyID:
   614 	cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
   615 	break;
   616     default:
   617 	cert = NULL;
   618 	break;
   619     }
   621     /* cert can be NULL at that point */
   622     signerinfo->cert = cert;	/* earmark it */
   624     return cert;
   625 }
   627 /*
   628  * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
   629  *
   630  * sinfo - signerInfo data for this signer
   631  *
   632  * Returns a pointer to allocated memory, which must be freed with PORT_Free.
   633  * A return value of NULL is an error.
   634  */
   635 char *
   636 NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
   637 {
   638     CERTCertificate *signercert;
   640     /* will fail if cert is not verified */
   641     if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
   642 	return NULL;
   644     return (CERT_GetCommonName(&signercert->subject));
   645 }
   647 /*
   648  * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
   649  *
   650  * sinfo - signerInfo data for this signer
   651  *
   652  * Returns a pointer to allocated memory, which must be freed.
   653  * A return value of NULL is an error.
   654  */
   655 char *
   656 NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
   657 {
   658     CERTCertificate *signercert;
   660     if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
   661 	return NULL;
   663     if (!signercert->emailAddr || !signercert->emailAddr[0])
   664 	return NULL;
   666     return (PORT_Strdup(signercert->emailAddr));
   667 }
   669 /*
   670  * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
   671  * authenticated (i.e. signed) attributes of "signerinfo". 
   672  */
   673 SECStatus
   674 NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
   675 {
   676     return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
   677 }
   679 /*
   680  * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
   681  * unauthenticated attributes of "signerinfo". 
   682  */
   683 SECStatus
   684 NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
   685 {
   686     return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
   687 }
   689 /* 
   690  * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
   691  * authenticated (i.e. signed) attributes of "signerinfo". 
   692  *
   693  * This is expected to be included in outgoing signed
   694  * messages for email (S/MIME) but is likely useful in other situations.
   695  *
   696  * This should only be added once; a second call will do nothing.
   697  *
   698  * XXX This will probably just shove the current time into "signerinfo"
   699  * but it will not actually get signed until the entire item is
   700  * processed for encoding.  Is this (expected to be small) delay okay?
   701  */
   702 SECStatus
   703 NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
   704 {
   705     NSSCMSAttribute *attr;
   706     SECItem stime;
   707     void *mark;
   708     PLArenaPool *poolp;
   710     poolp = signerinfo->cmsg->poolp;
   712     mark = PORT_ArenaMark(poolp);
   714     /* create new signing time attribute */
   715     if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
   716 	goto loser;
   718     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
   719 	SECITEM_FreeItem (&stime, PR_FALSE);
   720 	goto loser;
   721     }
   723     SECITEM_FreeItem (&stime, PR_FALSE);
   725     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
   726 	goto loser;
   728     PORT_ArenaUnmark (poolp, mark);
   730     return SECSuccess;
   732 loser:
   733     PORT_ArenaRelease (poolp, mark);
   734     return SECFailure;
   735 }
   737 /* 
   738  * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
   739  * authenticated (i.e. signed) attributes of "signerinfo". 
   740  *
   741  * This is expected to be included in outgoing signed
   742  * messages for email (S/MIME).
   743  */
   744 SECStatus
   745 NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
   746 {
   747     NSSCMSAttribute *attr;
   748     SECItem *smimecaps = NULL;
   749     void *mark;
   750     PLArenaPool *poolp;
   752     poolp = signerinfo->cmsg->poolp;
   754     mark = PORT_ArenaMark(poolp);
   756     smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
   757     if (smimecaps == NULL)
   758 	goto loser;
   760     /* create new signing time attribute */
   761     if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess)
   762 	goto loser;
   764     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
   765 	goto loser;
   767     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
   768 	goto loser;
   770     PORT_ArenaUnmark (poolp, mark);
   771     return SECSuccess;
   773 loser:
   774     PORT_ArenaRelease (poolp, mark);
   775     return SECFailure;
   776 }
   778 /* 
   779  * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
   780  * authenticated (i.e. signed) attributes of "signerinfo". 
   781  *
   782  * This is expected to be included in outgoing signed messages for email (S/MIME).
   783  */
   784 SECStatus
   785 NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
   786 {
   787     NSSCMSAttribute *attr;
   788     SECItem *smimeekp = NULL;
   789     void *mark;
   790     PLArenaPool *poolp;
   792     /* verify this cert for encryption */
   793     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
   794 	return SECFailure;
   795     }
   797     poolp = signerinfo->cmsg->poolp;
   798     mark = PORT_ArenaMark(poolp);
   800     smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
   801     if (smimeekp == NULL)
   802 	goto loser;
   804     /* create new signing time attribute */
   805     if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
   806 	goto loser;
   808     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
   809 	goto loser;
   811     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
   812 	goto loser;
   814     PORT_ArenaUnmark (poolp, mark);
   815     return SECSuccess;
   817 loser:
   818     PORT_ArenaRelease (poolp, mark);
   819     return SECFailure;
   820 }
   822 /* 
   823  * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
   824  * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
   825  *
   826  * This is expected to be included in outgoing signed messages for email (S/MIME),
   827  * if compatibility with Microsoft mail clients is wanted.
   828  */
   829 SECStatus
   830 NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
   831 {
   832     NSSCMSAttribute *attr;
   833     SECItem *smimeekp = NULL;
   834     void *mark;
   835     PLArenaPool *poolp;
   837     /* verify this cert for encryption */
   838     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
   839 	return SECFailure;
   840     }
   842     poolp = signerinfo->cmsg->poolp;
   843     mark = PORT_ArenaMark(poolp);
   845     smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
   846     if (smimeekp == NULL)
   847 	goto loser;
   849     /* create new signing time attribute */
   850     if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
   851 	goto loser;
   853     if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
   854 	goto loser;
   856     if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
   857 	goto loser;
   859     PORT_ArenaUnmark (poolp, mark);
   860     return SECSuccess;
   862 loser:
   863     PORT_ArenaRelease (poolp, mark);
   864     return SECFailure;
   865 }
   867 /* 
   868  * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
   869  *
   870  * 1. digest the DER-encoded signature value of the original signerinfo
   871  * 2. create new signerinfo with correct version, sid, digestAlg
   872  * 3. add message-digest authAttr, but NO content-type
   873  * 4. sign the authAttrs
   874  * 5. DER-encode the new signerInfo
   875  * 6. add the whole thing to original signerInfo's unAuthAttrs
   876  *    as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
   877  *
   878  * XXXX give back the new signerinfo?
   879  */
   880 SECStatus
   881 NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
   882 				    SECOidTag digestalg, CERTCertificate signingcert)
   883 {
   884     /* XXXX TBD XXXX */
   885     return SECFailure;
   886 }
   888 /*
   889  * XXXX the following needs to be done in the S/MIME layer code
   890  * after signature of a signerinfo is verified
   891  */
   892 SECStatus
   893 NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
   894 {
   895     CERTCertificate *cert = NULL;
   896     SECItem *profile = NULL;
   897     NSSCMSAttribute *attr;
   898     SECItem *stime = NULL;
   899     SECItem *ekp;
   900     CERTCertDBHandle *certdb;
   901     int save_error;
   902     SECStatus rv;
   903     PRBool must_free_cert = PR_FALSE;
   905     certdb = CERT_GetDefaultCertDB();
   907     /* sanity check - see if verification status is ok (unverified does not count...) */
   908     if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
   909 	return SECFailure;
   911     /* find preferred encryption cert */
   912     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
   913 	(attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
   914 			       SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
   915     { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
   916 	ekp = NSS_CMSAttribute_GetValue(attr);
   917 	if (ekp == NULL)
   918 	    return SECFailure;
   920 	/* we assume that all certs coming with the message have been imported to the */
   921 	/* temporary database */
   922 	cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
   923 	if (cert == NULL)
   924 	    return SECFailure;
   925 	must_free_cert = PR_TRUE;
   926     }
   928     if (cert == NULL) {
   929 	/* no preferred cert found?
   930 	 * find the cert the signerinfo is signed with instead */
   931 	cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
   932 	if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
   933 	    return SECFailure;
   934     }
   936     /* verify this cert for encryption (has been verified for signing so far) */
   937     /* don't verify this cert for encryption. It may just be a signing cert.
   938      * that's OK, we can still save the S/MIME profile. The encryption cert
   939      * should have already been saved */
   940 #ifdef notdef
   941     if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
   942 	if (must_free_cert)
   943 	    CERT_DestroyCertificate(cert);
   944 	return SECFailure;
   945     }
   946 #endif
   948     /* XXX store encryption cert permanently? */
   950     /*
   951      * Remember the current error set because we do not care about
   952      * anything set by the functions we are about to call.
   953      */
   954     save_error = PORT_GetError();
   956     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
   957 	attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
   958 				       SEC_OID_PKCS9_SMIME_CAPABILITIES,
   959 				       PR_TRUE);
   960 	profile = NSS_CMSAttribute_GetValue(attr);
   961 	attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
   962 				       SEC_OID_PKCS9_SIGNING_TIME,
   963 				       PR_TRUE);
   964 	stime = NSS_CMSAttribute_GetValue(attr);
   965     }
   967     rv = CERT_SaveSMimeProfile (cert, profile, stime);
   968     if (must_free_cert)
   969 	CERT_DestroyCertificate(cert);
   971     /*
   972      * Restore the saved error in case the calls above set a new
   973      * one that we do not actually care about.
   974      */
   975     PORT_SetError (save_error);
   977     return rv;
   978 }
   980 /*
   981  * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
   982  */
   983 SECStatus
   984 NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, NSSCMSCertChainMode cm, SECCertUsage usage)
   985 {
   986     if (signerinfo->cert == NULL)
   987 	return SECFailure;
   989     /* don't leak if we get called twice */
   990     if (signerinfo->certList != NULL) {
   991 	CERT_DestroyCertificateList(signerinfo->certList);
   992 	signerinfo->certList = NULL;
   993     }
   995     switch (cm) {
   996     case NSSCMSCM_None:
   997 	signerinfo->certList = NULL;
   998 	break;
   999     case NSSCMSCM_CertOnly:
  1000 	signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
  1001 	break;
  1002     case NSSCMSCM_CertChain:
  1003 	signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
  1004 	break;
  1005     case NSSCMSCM_CertChainWithRoot:
  1006 	signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
  1007 	break;
  1010     if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
  1011 	return SECFailure;
  1013     return SECSuccess;

mercurial