Add generation of SMIMECapabilities
authorchrisk%netscape.com
Wed, 14 Jun 2000 23:17:52 +0000
changeset 371 1307a54c0e8e976a60b0161ca71de42f65059633
parent 370 6e3393a67db564bcf93b0bc8e8bd444b989ef8b9
child 372 b2929d0992e2c03eb2489583a2db4e2c1a1b6f6c
push idunknown
push userunknown
push dateunknown
Add generation of SMIMECapabilities
security/nss/cmd/smimetools/cmsutil.c
security/nss/lib/smime/cms.h
security/nss/lib/smime/cmssiginfo.c
security/nss/lib/smime/smime.h
security/nss/lib/smime/smimemessage.c
security/nss/lib/smime/smimeutil.c
--- a/security/nss/cmd/smimetools/cmsutil.c
+++ b/security/nss/cmd/smimetools/cmsutil.c
@@ -413,23 +413,27 @@ sign(FILE *out, FILE *infile, char *prog
     if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, options.certUsage) != SECSuccess) {
 	fprintf(stderr, "ERROR: cannot find cert chain.\n");
 	NSS_CMSMessage_Destroy(cmsg);
 	return SECFailure;
     }
 
     if (signOptions.signingTime) {
 	if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
-	    fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
+	    fprintf(stderr, "ERROR: cannot add signingTime attribute.\n");
 	    NSS_CMSMessage_Destroy(cmsg);
 	    return SECFailure;
 	}
     }
     if (signOptions.smimeProfile) {
-	/* TBD */
+	if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
+	    fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
+	    NSS_CMSMessage_Destroy(cmsg);
+	    return SECFailure;
+	}
     }
     if (signOptions.encryptionKeyPreferenceNick) {
 	/* TBD */
 	/* get the cert, add it to the message */
     }
 
     if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
 	fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
--- a/security/nss/lib/smime/cms.h
+++ b/security/nss/lib/smime/cms.h
@@ -701,16 +701,26 @@ NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSi
  *
  * XXX This will probably just shove the current time into "signerinfo"
  * but it will not actually get signed until the entire item is
  * processed for encoding.  Is this (expected to be small) delay okay?
  */
 extern SECStatus
 NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t);
 
+/*
+ * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo".
+ *
+ * This is expected to be included in outgoing signed
+ * messages for email (S/MIME).
+ */
+extern SECStatus
+NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo);
+
 /* 
  * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
  *
  * 1. digest the DER-encoded signature value of the original signerinfo
  * 2. create new signerinfo with correct version, sid, digestAlg
  * 3. add message-digest authAttr, but NO content-type
  * 4. sign the authAttrs
  * 5. add the whole thing to original signerInfo's unAuthAttrs
--- a/security/nss/lib/smime/cmssiginfo.c
+++ b/security/nss/lib/smime/cmssiginfo.c
@@ -646,16 +646,58 @@ NSS_CMSSignerInfo_AddSigningTime(NSSCMSS
     return SECSuccess;
 
 loser:
     PORT_ArenaRelease (poolp, mark);
     return SECFailure;
 }
 
 /* 
+ * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
+ * authenticated (i.e. signed) attributes of "signerinfo". 
+ *
+ * This is expected to be included in outgoing signed
+ * messages for email (S/MIME).
+ */
+SECStatus
+NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
+{
+    NSSCMSAttribute *attr;
+    SECItem *smimecaps = NULL;
+    void *mark;
+    PLArenaPool *poolp;
+
+    poolp = signerinfo->cmsg->poolp;
+
+    mark = PORT_ArenaMark(poolp);
+
+    smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
+    if (smimecaps == NULL)
+	goto loser;
+
+    /* create new signing time attribute */
+    if (NSS_SMIMEUtil_GetSMIMECapabilities(poolp, smimecaps,
+			    PK11_FortezzaHasKEA(signerinfo->cert)) != SECSuccess)
+	goto loser;
+
+    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
+	goto loser;
+
+    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
+	goto loser;
+
+    PORT_ArenaUnmark (poolp, mark);
+    return SECSuccess;
+
+loser:
+    PORT_ArenaRelease (poolp, mark);
+    return SECFailure;
+}
+
+/* 
  * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
  *
  * 1. digest the DER-encoded signature value of the original signerinfo
  * 2. create new signerinfo with correct version, sid, digestAlg
  * 3. add message-digest authAttr, but NO content-type
  * 4. sign the authAttrs
  * 5. add the whole thing to original signerInfo's unAuthAttrs
  *
--- a/security/nss/lib/smime/smime.h
+++ b/security/nss/lib/smime/smime.h
@@ -117,20 +117,18 @@ extern PRBool NSS_SMIMEUtil_DecryptionAl
  */
 extern PRBool NSS_SMIMEUtil_EncryptionPossible(void);
 
 /*
  * NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
  *
  * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
  * S/MIME capabilities attribute value.
- *
- * "cert" - sender's certificate
  */
-extern SECItem *NSS_SMIMEUtil_GetSMIMECapabilities(CERTCertificate *cert);
+extern SECStatus NSS_SMIMEUtil_GetSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers);
 
 /*
  * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
  */
 extern SECStatus
 NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize);
 
 /************************************************************************/
--- a/security/nss/lib/smime/smimemessage.c
+++ b/security/nss/lib/smime/smimemessage.c
@@ -45,34 +45,16 @@
 #include "secasn1.h"
 #include "secitem.h"
 #include "secoid.h"
 #include "pk11func.h"
 #include "prtime.h"
 #include "secerr.h"
 
 
-SECStatus
-NSS_SMIMESignerInfo_AddSMIMEProfile(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert)
-{
-    SECItem *smimecapsdata;
-    NSSCMSAttribute *smimecapsattr;
-
-    if ((smimecapsdata = NSS_SMIMEUtil_GetSMIMECapabilities(cert)) == NULL)
-	return SECFailure;
-
-    smimecapsattr = NSS_CMSAttribute_Create(signerinfo->cmsg->poolp,
-				    SEC_OID_PKCS9_SMIME_CAPABILITIES,
-				    smimecapsdata, PR_TRUE);
-    if (smimecapsattr == NULL)
-	return SECFailure;
-
-    return NSS_CMSSignerInfo_AddAuthAttr(signerinfo, smimecapsattr);
-}
-
 #if 0
 /*
  * NSS_SMIMEMessage_CreateEncrypted - start an S/MIME encrypting context.
  *
  * "scert" is the cert for the sender.  It will be checked for validity.
  * "rcerts" are the certs for the recipients.  They will also be checked.
  *
  * "certdb" is the cert database to use for verifying the certs.
--- a/security/nss/lib/smime/smimeutil.c
+++ b/security/nss/lib/smime/smimeutil.c
@@ -99,22 +99,16 @@ static smime_cipher_map_entry smime_ciph
     { SMIME_DES_CBC_56,		SEC_OID_DES_CBC,	NULL,		PR_TRUE, PR_TRUE },
     { SMIME_RC2_CBC_64,		SEC_OID_RC2_CBC,	&param_int64,	PR_TRUE, PR_TRUE },
     { SMIME_RC2_CBC_128,	SEC_OID_RC2_CBC,	&param_int128,	PR_TRUE, PR_TRUE },
     { SMIME_DES_EDE3_168,	SEC_OID_DES_EDE3_CBC,	NULL,		PR_TRUE, PR_TRUE },
     { SMIME_FORTEZZA,		SEC_OID_FORTEZZA_SKIPJACK, NULL,	PR_TRUE, PR_TRUE }
 };
 static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
 
-/* the other global variables */
-static PRBool smime_prefs_changed = PR_TRUE;
-static NSSSMIMECapability **smime_capabilities;
-static SECItem *smime_encoded_caps;
-static PRBool lastUsedFortezza;
-
 /*
  * smime_mapi_by_cipher - find index into smime_cipher_map by cipher
  */
 static int
 smime_mapi_by_cipher(unsigned long cipher)
 {
     int i;
 
@@ -147,20 +141,19 @@ NSS_SMIMEUtil_EnableCipher(unsigned long
 	return SECFailure;
 
     /* do we try to turn on a forbidden cipher? */
     if (!smime_cipher_map[mapi].allowed && on) {
 	PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
 	return SECFailure;
     }
 
-    if (smime_cipher_map[mapi].enabled != on) {
+    if (smime_cipher_map[mapi].enabled != on)
 	smime_cipher_map[mapi].enabled = on;
-	smime_prefs_changed = PR_TRUE;
-    }
+
     return SECSuccess;
 }
 
 
 /*
  * this function locally records the export policy
  */
 SECStatus 
@@ -176,20 +169,19 @@ NSS_SMIMEUtil_AllowCipher(unsigned long 
 	/* XXX set an error! */
     	return SECFailure;
 
     mapi = smime_mapi_by_cipher(which);
     if (mapi < 0)
 	/* XXX set an error */
 	return SECFailure;
 
-    if (smime_cipher_map[mapi].allowed != on) {
+    if (smime_cipher_map[mapi].allowed != on)
 	smime_cipher_map[mapi].allowed = on;
-	smime_prefs_changed = PR_TRUE;
-    }
+
     return SECSuccess;
 }
 
 /*
  * Based on the given algorithm (including its parameters, in some cases!)
  * and the given key (may or may not be inspected, depending on the
  * algorithm), find the appropriate policy algorithm specification
  * and return it.  If no match can be made, -1 is returned.
@@ -516,160 +508,87 @@ NSS_SMIMEUtil_FindBulkAlgForRecipients(C
     mapi = smime_mapi_by_cipher(cipher);
 
     *bulkalgtag = smime_cipher_map[mapi].algtag;
     *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].algtag);
 
     return SECSuccess;
 }
 
-static SECStatus
-smime_init_caps(PRBool isFortezza)
+/*
+ * NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
+ *
+ * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
+ * S/MIME capabilities attribute value.
+ *
+ * XXX Please note that, in contradiction to RFC2633 2.5.2, the capabilities only include
+ * symmetric ciphers, NO signature algorithms or key encipherment algorithms.
+ *
+ * "poolp" - arena pool to create the S/MIME capabilities data on
+ * "dest" - SECItem to put the data in
+ * "includeFortezzaCiphers" - PR_TRUE if fortezza ciphers should be included
+ */
+SECStatus
+NSS_SMIMEUtil_GetSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers)
 {
     NSSSMIMECapability *cap;
+    NSSSMIMECapability **smime_capabilities;
     smime_cipher_map_entry *map;
     SECOidData *oiddata;
-    SECStatus rv;
+    SECItem *dummy;
     int i, capIndex;
 
-    /* if we have caps, and the prefs did not change, and we are using fortezza as last time */
-    /* we're done */
-    if (smime_encoded_caps != NULL && (!smime_prefs_changed) && lastUsedFortezza == isFortezza)
-	return SECSuccess;
-
-    /* ok, we need to cook up new caps. So throw the old ones away */
-    if (smime_encoded_caps != NULL) {
-	SECITEM_FreeItem (smime_encoded_caps, PR_TRUE);
-	smime_encoded_caps = NULL;
-    }
-
     /* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
-    if (smime_capabilities == NULL) {
-	smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
-					  * sizeof(NSSSMIMECapability *));
-	if (smime_capabilities == NULL)
-	    return SECFailure;
-    }
-
-    rv = SECFailure;
-
-    /* 
-       The process of creating the encoded CMS cipher capability list
-       involves two basic steps: 
-
-       (a) Convert our internal representation of cipher preferences 
-           (smime_prefs) into an array containing cipher OIDs and 
-	   parameter data (smime_capabilities). This step is
-	   performed here.
+    /* smime_cipher_map_count + 1 is an upper bound - we might end up with less */
+    smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
+				      * sizeof(NSSSMIMECapability *));
+    if (smime_capabilities == NULL)
+	return SECFailure;
 
-       (b) Encode, using ASN.1, the cipher information in 
-           smime_capabilities, leaving the encoded result in 
-	   smime_encoded_caps.
-
-       (In the process of performing (a), Lisa put in some optimizations
-       which allow us to avoid needlessly re-populating elements in 
-       smime_capabilities as we walk through smime_prefs.)
+    capIndex = 0;
 
-       We want to use separate loop variables for smime_prefs and
-       smime_capabilities because in the case where the Skipjack cipher 
-       is turned on in the prefs, but where we don't want to include 
-       Skipjack in the encoded capabilities (presumably due to using a 
-       non-fortezza cert when sending a message), we want to avoid creating
-       an empty element in smime_capabilities. This would otherwise cause 
-       the encoding step to produce an empty set, since Skipjack happens 
-       to be the first cipher in smime_prefs, if it is turned on.
-    */
-    capIndex = 0;
-    for (i = 0; i < smime_cipher_map_count; i++) {
+    /* Add all the symmetric ciphers
+     * We walk the cipher list backwards, as it is ordered by increasing strength,
+     * we prefer the stronger cipher over a weaker one, and we have to list the
+     * preferred algorithm first */
+    for (i = smime_cipher_map_count - 1; i >= 0; i--) {
 	/* Find the corresponding entry in the cipher map. */
 	map = &(smime_cipher_map[i]);
-
 	if (!map->enabled)
 	    continue;
 
 	/* If we're using a non-Fortezza cert, only advertise non-Fortezza
 	   capabilities. (We advertise all capabilities if we have a 
 	   Fortezza cert.) */
-	if ((!isFortezza) && (map->cipher == SMIME_FORTEZZA))
+	if ((!includeFortezzaCiphers) && (map->cipher == SMIME_FORTEZZA))
 	    continue;
 
 	/* get next SMIME capability */
-	cap = smime_capabilities[capIndex];
-	if (cap == NULL) {
-	    cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
-	    if (cap == NULL)
-		break;
-	    smime_capabilities[capIndex] = cap;
-	}
-	capIndex++;
-
-	if (cap->cipher == smime_cipher_map[i].cipher)
-	    continue;		/* no change to this one */
+	cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
+	if (cap == NULL)
+	    break;
+	smime_capabilities[capIndex++] = cap;
 
 	oiddata = SECOID_FindOIDByTag(map->algtag);
 	if (oiddata == NULL)
 	    break;
 
-	if (cap->capabilityID.data != NULL) {
-	    SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
-	    cap->capabilityID.data = NULL;
-	    cap->capabilityID.len = 0;
-	}
-
-	rv = SECITEM_CopyItem(NULL, &(cap->capabilityID), &(oiddata->oid));
-	if (rv != SECSuccess)
-	    break;
-
-	if (map->parms == NULL) {
-	    cap->parameters.data = NULL;
-	    cap->parameters.len = 0;
-	} else {
-	    cap->parameters.data = map->parms->data;
-	    cap->parameters.len = map->parms->len;
-	}
-
+	cap->capabilityID.data = oiddata->oid.data;
+	cap->capabilityID.len = oiddata->oid.len;
+	cap->parameters.data = map->parms ? map->parms->data : NULL;
+	cap->parameters.len = map->parms ? map->parms->len : 0;
 	cap->cipher = smime_cipher_map[i].cipher;
     }
 
-    while (capIndex < smime_cipher_map_count) {
-	cap = smime_capabilities[capIndex];
-	if (cap != NULL) {
-	    SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE);
-	    PORT_Free(cap);
-	}
-	smime_capabilities[capIndex] = NULL;
-	capIndex++;
-    }
-    smime_capabilities[capIndex] = NULL;	/* last one */
+    /* XXX add signature algorithms */
+    /* XXX add key encipherment algorithms */
 
-    smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities,
-					     smime_capabilities_template);
-    if (smime_encoded_caps == NULL)
-	return SECFailure;
-
-    lastUsedFortezza = isFortezza;
-
-    return SECSuccess;
-}
+    smime_capabilities[capIndex] = NULL;	/* last one - now encode */
+    dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, smime_capabilities_template);
 
-/*
- * NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
- *
- * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
- * S/MIME capabilities attribute value.
- *
- * "cert" - sender's certificate
- */
-SECItem *
-NSS_SMIMEUtil_GetSMIMECapabilities(CERTCertificate *cert)
-{
+    /* now that we have the proper encoded SMIMECapabilities (or not),
+     * free the work data */
+    for (i = 0; smime_capabilities[i] != NULL; i++)
+	PORT_Free(smime_capabilities[i]);
+    PORT_Free(smime_capabilities);
 
-    PRBool isFortezza = PR_FALSE;
-
-    /* See if the sender's cert specifies Fortezza key exchange. */
-    if (cert != NULL)
-	isFortezza = PK11_FortezzaHasKEA(cert);
-
-    if (smime_init_caps(isFortezza) != SECSuccess)
-	return NULL;
-
-    return smime_encoded_caps;
+    return (dummy == NULL) ? SECFailure : SECSuccess;
 }