Add code for generation of SMIMEProfile and SMIMEKeyEncryptionPreference
authorchrisk%netscape.com
Tue, 20 Jun 2000 16:28:59 +0000
changeset 385 3dd1a1ac0b8356f759cc687250934c5da44983e0
parent 384 370021482121f6eda2c685342bf0fab64ff2bc5d
child 386 afe3bc4ebfae21710de77c3e91297c770d6cd600
push idunknown
push userunknown
push dateunknown
Add code for generation of SMIMEProfile and SMIMEKeyEncryptionPreference
security/nss/cmd/smimetools/cmsutil.c
security/nss/lib/smime/cms.h
security/nss/lib/smime/cmsasn1.c
security/nss/lib/smime/cmsattr.c
security/nss/lib/smime/cmssiginfo.c
security/nss/lib/smime/smime.h
security/nss/lib/smime/smimeutil.c
security/nss/lib/util/secoid.c
security/nss/lib/util/secoidt.h
--- a/security/nss/cmd/smimetools/cmsutil.c
+++ b/security/nss/cmd/smimetools/cmsutil.c
@@ -351,17 +351,17 @@ sign(FILE *out, FILE *infile, char *prog
     NSSCMSMessage *cmsg;
     NSSCMSContentInfo *cinfo;
     NSSCMSSignedData *sigd;
     NSSCMSSignerInfo *signerinfo;
     int nb;
     char ibuf[4096];
     PK11PasswordFunc pwcb;
     void *pwcb_arg;
-    CERTCertificate *cert;
+    CERTCertificate *cert, *ekpcert;
 
     if (signOptions.nickname == NULL) {
 	fprintf(stderr, "ERROR: please indicate the nickname of a certificate to sign with.\n");
 	return SECFailure;
     }
 	
     if ((cert = CERT_FindCertByNickname(options.certHandle, signOptions.nickname)) == NULL) {
 	SECU_PrintError(progName, "the corresponding cert for key \"%s\" does not exist",
@@ -426,18 +426,41 @@ sign(FILE *out, FILE *infile, char *prog
     if (signOptions.smimeProfile) {
 	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 ((ekpcert = CERT_FindCertByNickname(options.certHandle, signOptions.encryptionKeyPreferenceNick)) == NULL) {
+	    SECU_PrintError(progName, "the corresponding cert for key \"%s\" does not exist",
+				signOptions.encryptionKeyPreferenceNick);
+	    NSS_CMSMessage_Destroy(cmsg);
+	    return SECFailure;
+	}
+	if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, options.certHandle) != SECSuccess) {
+	    fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
+	    NSS_CMSMessage_Destroy(cmsg);
+	    return SECFailure;
+	}
+	if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
+	    fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
+	    NSS_CMSMessage_Destroy(cmsg);
+	    return SECFailure;
+	}
+    } else {
+	/* check signing cert for fitness as encryption cert */
+	/* if yes, add signing cert as EncryptionKeyPreference */
+	if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert, options.certHandle) != SECSuccess) {
+	    fprintf(stderr, "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n");
+	    NSS_CMSMessage_Destroy(cmsg);
+	    return SECFailure;
+	}
     }
 
     if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
 	fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
 	NSS_CMSMessage_Destroy(cmsg);
 	return SECFailure;
     }
 
--- a/security/nss/lib/smime/cms.h
+++ b/security/nss/lib/smime/cms.h
@@ -711,26 +711,27 @@ NSS_CMSSignerInfo_AddSigningTime(NSSCMSS
  * 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_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences 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_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb);
+
 /* 
  * 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
- *
- * XXXX give back the new signerinfo?
  */
 extern SECStatus
 NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
 				    SECOidTag digestalg, CERTCertificate signingcert);
 
 /*
  * XXXX the following needs to be done in the S/MIME layer code
  * after signature of a signerinfo is verified
--- a/security/nss/lib/smime/cmsasn1.c
+++ b/security/nss/lib/smime/cmsasn1.c
@@ -300,17 +300,17 @@ static const SEC_ASN1Template NSSCMSOrig
 	  NSSCMSOriginatorIDOrKey_SubjectKeyID },
     { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
 	  offsetof(NSSCMSOriginatorIdentifierOrKey,id.originatorPublicKey),
 	  NSSCMSOriginatorPublicKeyTemplate,
 	  NSSCMSOriginatorIDOrKey_OriginatorPublicKey },
     { 0 }
 };
 
-static const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[] = {
+const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[] = {
     { SEC_ASN1_SEQUENCE,
 	  0, NULL, sizeof(NSSCMSRecipientKeyIdentifier) },
     { SEC_ASN1_OCTET_STRING,
 	  offsetof(NSSCMSRecipientKeyIdentifier,subjectKeyIdentifier) },
     { SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
 	  offsetof(NSSCMSRecipientKeyIdentifier,date) },
     { SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
 	  offsetof(NSSCMSRecipientKeyIdentifier,other) },
--- a/security/nss/lib/smime/cmsattr.c
+++ b/security/nss/lib/smime/cmsattr.c
@@ -229,16 +229,19 @@ cms_attr_choose_attr_value_template(void
     }
 
     if (oiddata == NULL) {
 	/* still no OID tag? OID is unknown then. en/decode value as ANY. */
 	encoded = PR_TRUE;
 	theTemplate = SEC_AnyTemplate;
     } else {
 	switch (oiddata->offset) {
+	SEC_OID_PKCS9_SMIME_CAPABILITIES:
+	SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE:
+	    /* these guys need to stay DER-encoded */
 	default:
 	    /* same goes for OIDs that are not handled here */
 	    encoded = PR_TRUE;
 	    theTemplate = SEC_AnyTemplate;
 	    break;
 	    /* otherwise choose proper template */
 	case SEC_OID_PKCS9_EMAIL_ADDRESS:
 	case SEC_OID_RFC1274_MAIL:
--- a/security/nss/lib/smime/cmssiginfo.c
+++ b/security/nss/lib/smime/cmssiginfo.c
@@ -44,16 +44,18 @@
 #include "secasn1.h"
 #include "secitem.h"
 #include "secoid.h"
 #include "pk11func.h"
 #include "prtime.h"
 #include "secerr.h"
 #include "cryptohi.h"
 
+#include "smime.h"
+
 /* =============================================================================
  * SIGNERINFO
  */
 
 NSSCMSSignerInfo *
 NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
 {
     void *mark;
@@ -669,17 +671,17 @@ NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSig
 
     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,
+    if (NSS_SMIMEUtil_CreateSMIMECapabilities(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;
@@ -688,23 +690,69 @@ NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSig
     return SECSuccess;
 
 loser:
     PORT_ArenaRelease (poolp, mark);
     return SECFailure;
 }
 
 /* 
+ * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences 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_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
+{
+    NSSCMSAttribute *attr;
+    SECItem *smimeekp = NULL;
+    void *mark;
+    PLArenaPool *poolp;
+
+    /* verify this cert for encryption */
+    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
+	return SECFailure;
+    }
+
+    poolp = signerinfo->cmsg->poolp;
+    mark = PORT_ArenaMark(poolp);
+
+    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
+    if (smimeekp == NULL)
+	goto loser;
+
+    /* create new signing time attribute */
+    if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
+	goto loser;
+
+    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, 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
+ * 5. DER-encode the new signerInfo
+ * 6. add the whole thing to original signerInfo's unAuthAttrs
+ *    as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
  *
  * XXXX give back the new signerinfo?
  */
 SECStatus
 NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
 				    SECOidTag digestalg, CERTCertificate signingcert)
 {
     /* XXXX TBD XXXX */
@@ -713,32 +761,61 @@ NSS_CMSSignerInfo_AddCounterSignature(NS
 
 /*
  * XXXX the following needs to be done in the S/MIME layer code
  * after signature of a signerinfo is verified
  */
 SECStatus
 NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
 {
-    CERTCertificate *cert;
+    CERTCertificate *cert = NULL;
     SECItem *profile = NULL;
     NSSCMSAttribute *attr;
-    SECItem *utc_stime;
+    SECItem *utc_stime = NULL;
+    SECItem *ekp;
+    CERTCertDBHandle *certdb;
     int save_error;
     SECStatus rv;
 
-    /* XXX sanity check - see if verification status is ok */
-    /* XXX 0 = unverified */
+    certdb = CERT_GetDefaultCertDB();
+
+    /* sanity check - see if verification status is ok (unverified does not count...) */
+    if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
+	return SECFailure;
 
-    /* XXX find preferred encyption cert */
+    /* find preferred encryption cert */
+    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
+	(attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
+			       SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL)
+    { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
+	ekp = NSS_CMSAttribute_GetValue(attr);
+	if (ekp == NULL)
+	    return SECFailure;
 
-    /* find the cert the signerinfo is signed with */
-    cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
-    if (cert == NULL || cert->emailAddr == NULL)
+	/* we assume that all certs coming with the message have been imported to the */
+	/* temporary database */
+	cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
+	if (cert == NULL)
+	    return SECFailure;
+    }
+
+    if (cert == NULL) {
+	/* no preferred cert found?
+	 * find the cert the signerinfo is signed with instead */
+	cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
+	if (cert == NULL || cert->emailAddr == NULL)
+	    return SECFailure;
+    }
+
+    /* verify this cert for encryption (has been verified for signing so far) */
+    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
 	return SECFailure;
+    }
+
+    /* XXX store encryption cert permanently? */
 
     /*
      * Remember the current error set because we do not care about
      * anything set by the functions we are about to call.
      */
     save_error = PORT_GetError();
 
     if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
--- a/security/nss/lib/smime/smime.h
+++ b/security/nss/lib/smime/smime.h
@@ -113,22 +113,33 @@ extern PRBool NSS_SMIMEUtil_DecryptionAl
  *	find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
  *   PR_FALSE means encryption (or decryption) is not permitted
  *
  * There are no errors from this routine.
  */
 extern PRBool NSS_SMIMEUtil_EncryptionPossible(void);
 
 /*
- * NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
+ * NSS_SMIMEUtil_CreateSMIMECapabilities - get S/MIME capabilities attr value
  *
  * scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
  * S/MIME capabilities attribute value.
  */
-extern SECStatus NSS_SMIMEUtil_GetSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers);
+extern SECStatus NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers);
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
+ */
+extern SECStatus NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert);
+
+/*
+ * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference - find cert marked by EncryptionKeyPreference
+ *          attribute
+ */
+extern CERTCertificate *NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp);
 
 /*
  * 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/smimeutil.c
+++ b/security/nss/lib/smime/smimeutil.c
@@ -41,16 +41,17 @@
 #include "secoid.h"
 #include "pk11func.h"
 #include "ciferfam.h"	/* for CIPHER_FAMILY symbols */
 #include "secasn1.h"
 #include "secitem.h"
 #include "cert.h"
 #include "key.h"
 #include "secerr.h"
+#include "cms.h"
 
 /* various integer's ASN.1 encoding */
 static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
 static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
 static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
 
 /* RC2 algorithm parameters (used in smime_cipher_map) */
 static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
@@ -63,28 +64,68 @@ static SECItem param_int128 = { siBuffer
  * once that is fixed, can change this back...
  */
 typedef struct {
     SECItem capabilityID;
     SECItem parameters;
     long cipher;		/* optimization */
 } NSSSMIMECapability;
 
-static const SEC_ASN1Template smime_capability_template[] = {
+static const SEC_ASN1Template NSSSMIMECapabilityTemplate[] = {
     { SEC_ASN1_SEQUENCE,
 	  0, NULL, sizeof(NSSSMIMECapability) },
     { SEC_ASN1_OBJECT_ID,
 	  offsetof(NSSSMIMECapability,capabilityID), },
     { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
 	  offsetof(NSSSMIMECapability,parameters), },
     { 0, }
 };
 
-static const SEC_ASN1Template smime_capabilities_template[] = {
-    { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
+static const SEC_ASN1Template NSSSMIMECapabilitiesTemplate[] = {
+    { SEC_ASN1_SEQUENCE_OF, 0, NSSSMIMECapabilityTemplate }
+};
+
+/*
+ * NSSSMIMEEncryptionKeyPreference - if we find one of these, it needs to prompt us
+ *  to store this and only this certificate permanently for the sender email address.
+ */
+typedef enum {
+    NSSSMIMEEncryptionKeyPref_IssuerSN,
+    NSSSMIMEEncryptionKeyPref_RKeyID,
+    NSSSMIMEEncryptionKeyPref_SubjectKeyID
+} NSSSMIMEEncryptionKeyPrefSelector;
+
+typedef struct {
+    NSSSMIMEEncryptionKeyPrefSelector selector;
+    union {
+	CERTIssuerAndSN			*issuerAndSN;
+	NSSCMSRecipientKeyIdentifier	*recipientKeyID;
+	SECItem				*subjectKeyID;
+    } id;
+} NSSSMIMEEncryptionKeyPreference;
+
+extern const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[];
+
+static const SEC_ASN1Template smime_encryptionkeypref_template[] = {
+    { SEC_ASN1_CHOICE,
+	  offsetof(NSSSMIMEEncryptionKeyPreference,selector), NULL,
+	  sizeof(NSSSMIMEEncryptionKeyPreference) },
+    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+	  offsetof(NSSSMIMEEncryptionKeyPreference,id.issuerAndSN),
+	  CERT_IssuerAndSNTemplate,
+	  NSSSMIMEEncryptionKeyPref_IssuerSN },
+    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+	  offsetof(NSSSMIMEEncryptionKeyPreference,id.recipientKeyID),
+	  NSSCMSRecipientKeyIdentifierTemplate,
+	  NSSSMIMEEncryptionKeyPref_IssuerSN },
+    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | 2,
+	  offsetof(NSSSMIMEEncryptionKeyPreference,id.subjectKeyID),
+	  SEC_OctetStringTemplate,
+	  NSSSMIMEEncryptionKeyPref_SubjectKeyID },
+    { 0, }
 };
 
 /* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
 typedef struct {
     unsigned long cipher;
     SECOidTag algtag;
     SECItem *parms;
     PRBool enabled;	/* in the user's preferences */
@@ -364,20 +405,20 @@ smime_choose_cipher(CERTCertificate *sce
 	 * "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
 	 * and so on. If every cipher matches, the last one gets 1 (one) vote */
 	pref = smime_cipher_map_count;
 
 	/* find recipient's SMIME profile */
 	profile = CERT_FindSMimeProfile(rcerts[rcount]);
 
 	if (profile != NULL && profile->data != NULL && profile->len > 0) {
-	    /* we have a profile */
+	    /* we have a profile (still DER-encoded) */
 	    caps = NULL;
 	    /* decode it */
-	    if (SEC_ASN1DecodeItem(poolp, &caps, smime_capabilities_template, profile) == SECSuccess &&
+	    if (SEC_ASN1DecodeItem(poolp, &caps, NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
 		    caps != NULL)
 	    {
 		/* walk the SMIME capabilities for this recipient */
 		for (i = 0; caps[i] != NULL; i++) {
 		    cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
 		    mapi = smime_mapi_by_cipher(cipher);
 		    if (mapi >= 0) {
 			/* found the cipher */
@@ -509,30 +550,30 @@ NSS_SMIMEUtil_FindBulkAlgForRecipients(C
 
     *bulkalgtag = smime_cipher_map[mapi].algtag;
     *keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].algtag);
 
     return SECSuccess;
 }
 
 /*
- * NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
+ * NSS_SMIMEUtil_CreateSMIMECapabilities - 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)
+NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest, PRBool includeFortezzaCiphers)
 {
     NSSSMIMECapability *cap;
     NSSSMIMECapability **smime_capabilities;
     smime_cipher_map_entry *map;
     SECOidData *oiddata;
     SECItem *dummy;
     int i, capIndex;
 
@@ -577,18 +618,97 @@ NSS_SMIMEUtil_GetSMIMECapabilities(PLAre
 	cap->parameters.len = map->parms ? map->parms->len : 0;
 	cap->cipher = smime_cipher_map[i].cipher;
     }
 
     /* XXX add signature algorithms */
     /* XXX add key encipherment algorithms */
 
     smime_capabilities[capIndex] = NULL;	/* last one - now encode */
-    dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, smime_capabilities_template);
+    dummy = SEC_ASN1EncodeItem(poolp, dest, &smime_capabilities, NSSSMIMECapabilitiesTemplate);
 
     /* 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);
 
     return (dummy == NULL) ? SECFailure : SECSuccess;
 }
+
+/*
+ * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value
+ *
+ * "poolp" - arena pool to create the attr value on
+ * "dest" - SECItem to put the data in
+ * "cert" - certificate that should be marked as preferred encryption key
+ *          cert is expected to have been verified for EmailRecipient usage.
+ */
+SECStatus
+NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
+{
+    NSSSMIMEEncryptionKeyPreference ekp;
+    SECItem *dummy = NULL;
+    PLArenaPool *tmppoolp;
+
+    if (cert == NULL)
+	goto loser;
+
+    tmppoolp = PORT_NewArena(1024);
+    if (tmppoolp == NULL)
+	goto loser;
+
+    /* XXX hardcoded IssuerSN choice for now */
+    ekp.selector = NSSSMIMEEncryptionKeyPref_IssuerSN;
+    ekp.id.issuerAndSN = CERT_GetCertIssuerAndSN(tmppoolp, cert);
+    if (ekp.id.issuerAndSN == NULL)
+	goto loser;
+
+    dummy = SEC_ASN1EncodeItem(poolp, dest, &ekp, smime_encryptionkeypref_template);
+
+loser:
+    if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
+
+    return (dummy == NULL) ? SECFailure : SECSuccess;
+}
+
+/*
+ * NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference -
+ *				find cert marked by EncryptionKeyPreference attribute
+ *
+ * "certdb" - handle for the cert database to look in
+ * "DERekp" - DER-encoded value of S/MIME Encryption Key Preference attribute
+ *
+ * if certificate is supposed to be found among the message's included certificates,
+ * they are assumed to have been imported already.
+ */
+CERTCertificate *
+NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(CERTCertDBHandle *certdb, SECItem *DERekp)
+{
+    PLArenaPool *tmppoolp = NULL;
+    CERTCertificate *cert = NULL;
+    NSSSMIMEEncryptionKeyPreference ekp;
+
+    tmppoolp = PORT_NewArena(1024);
+    if (tmppoolp == NULL)
+	return NULL;
+
+    /* decode DERekp */
+    if (SEC_ASN1DecodeItem(tmppoolp, &ekp, smime_encryptionkeypref_template, DERekp) != SECSuccess)
+	goto loser;
+
+    /* find cert */
+    switch (ekp.selector) {
+    case NSSSMIMEEncryptionKeyPref_IssuerSN:
+	cert = CERT_FindCertByIssuerAndSN(certdb, ekp.id.issuerAndSN);
+	break;
+    case NSSSMIMEEncryptionKeyPref_RKeyID:
+    case NSSSMIMEEncryptionKeyPref_SubjectKeyID:
+	/* XXX not supported yet - we need to be able to look up certs by SubjectKeyID */
+	break;
+    default:
+	PORT_Assert(0);
+    }
+loser:
+    if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE);
+
+    return cert;
+}
--- a/security/nss/lib/util/secoid.c
+++ b/security/nss/lib/util/secoid.c
@@ -122,16 +122,17 @@
 #define PKCS12_OIDS		PKCS12, 0x05
 #define PKCS12_PBE_IDS		PKCS12_OIDS, 0x01
 #define PKCS12_ENVELOPING_IDS	PKCS12_OIDS, 0x02
 #define PKCS12_SIGNATURE_IDS	PKCS12_OIDS, 0x03
 #define PKCS12_V2_PBE_IDS	PKCS12, 0x01
 #define PKCS9_CERT_TYPES	PKCS9, 0x16
 #define PKCS9_CRL_TYPES		PKCS9, 0x17
 #define PKCS9_SMIME_IDS		PKCS9, 0x10
+#define PKCS9_SMIME_ATTRS	PKCS9_SMIME_IDS, 2
 #define PKCS9_SMIME_ALGS	PKCS9_SMIME_IDS, 3
 #define PKCS12_VERSION1		PKCS12, 0x0a
 #define PKCS12_V1_BAG_IDS	PKCS12_VERSION1, 1
 
 /* for DSA algorithm */
 /* { iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) } */
 #define ANSI_X9_ALGORITHM  0x2a, 0x86, 0x48, 0xce, 0x38, 0x4
 
@@ -386,16 +387,19 @@ static unsigned char netscapeNickname[] 
 static unsigned char netscapeRecoveryRequest[] = 
                                         { NETSCAPE_CERT_SERVER_CRMF, 0x01 };
 
 /* RFC2630 (CMS) OIDs */
 static unsigned char cmsESDH[] = { PKCS9_SMIME_ALGS, 5 };
 static unsigned char cms3DESwrap[] = { PKCS9_SMIME_ALGS, 6 };
 static unsigned char cmsRC2wrap[] = { PKCS9_SMIME_ALGS, 7 };
 
+/* RFC2633 SMIME message attributes */
+static unsigned char smimeEncryptionKeyPreference[] = { PKCS9_SMIME_ATTRS, 11 };
+
 /*
  * NOTE: the order of these entries must mach the SECOidTag enum in secoidt.h!
  */
 static SECOidData oids[] = {
     { { siDEROID, NULL, 0 },
 	  SEC_OID_UNKNOWN,
 	  "Unknown OID", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION },
     { { siDEROID, md2, sizeof(md2) },
@@ -1200,16 +1204,22 @@ static SECOidData oids[] = {
         SEC_OID_CMS_3DES_KEY_WRAP,
         "CMS 3DES Key Wrap", CKM_INVALID_MECHANISM /* XXX */,
         INVALID_CERT_EXTENSION },
     { { siDEROID, cmsRC2wrap,
         sizeof(cmsRC2wrap) },
         SEC_OID_CMS_RC2_KEY_WRAP,
         "CMS RC2 Key Wrap", CKM_INVALID_MECHANISM /* XXX */,
         INVALID_CERT_EXTENSION },
+    { { siDEROID, smimeEncryptionKeyPreference,
+	sizeof(smimeEncryptionKeyPreference) },
+	SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE,
+	"S/MIME Encryption Key Preference", CKM_INVALID_MECHANISM,
+	INVALID_CERT_EXTENSION },
+
 };
 
 /*
  * now the dynamic table. The dynamic table gets build at init time.
  *  and gets modified if the user loads new crypto modules.
  */
 
 static DB *oid_d_hash = 0;
--- a/security/nss/lib/util/secoidt.h
+++ b/security/nss/lib/util/secoidt.h
@@ -276,16 +276,19 @@ typedef enum {
     SEC_OID_CERT_RENEWAL_LOCATOR,
     SEC_OID_NS_CERT_EXT_SCOPE_OF_USE,
     
     /* CMS (RFC2630) OIDs */
     SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN,
     SEC_OID_CMS_3DES_KEY_WRAP,
     SEC_OID_CMS_RC2_KEY_WRAP,
 
+    /* SMIME attributes */
+    SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE,
+
     SEC_OID_TOTAL
 } SECOidTag;
 
 /* fake OID for DSS sign/verify */
 #define SEC_OID_SHA SEC_OID_MISS_DSS
 
 typedef enum {
     INVALID_CERT_EXTENSION,