security/nss/lib/pkcs7/secmime.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  * Stuff specific to S/MIME policy and interoperability.
     7  * Depends on PKCS7, but there should be no dependency the other way around.
     8  */
    10 #include "secmime.h"
    11 #include "secoid.h"
    12 #include "pk11func.h"
    13 #include "ciferfam.h"	/* for CIPHER_FAMILY symbols */
    14 #include "secasn1.h"
    15 #include "secitem.h"
    16 #include "cert.h"
    17 #include "key.h"
    18 #include "secerr.h"
    20 typedef struct smime_cipher_map_struct {
    21     unsigned long cipher;
    22     SECOidTag algtag;
    23     SECItem *parms;
    24 } smime_cipher_map;
    26 /*
    27  * These are macros because I think some subsequent parameters,
    28  * like those for RC5, will want to use them, too, separately.
    29  */
    30 #define SMIME_DER_INTVAL_16	SEC_ASN1_INTEGER, 0x01, 0x10
    31 #define SMIME_DER_INTVAL_40	SEC_ASN1_INTEGER, 0x01, 0x28
    32 #define SMIME_DER_INTVAL_64	SEC_ASN1_INTEGER, 0x01, 0x40
    33 #define SMIME_DER_INTVAL_128	SEC_ASN1_INTEGER, 0x02, 0x00, 0x80
    35 #ifdef SMIME_DOES_RC5	/* will be needed; quiet unused warning for now */
    36 static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 };
    37 #endif
    38 static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 };
    39 static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 };
    40 static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 };
    42 static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) };
    43 static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) };
    44 static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) };
    46 static smime_cipher_map smime_cipher_maps[] = {
    47     { SMIME_RC2_CBC_40,		SEC_OID_RC2_CBC,	&smime_rc2p40 },
    48     { SMIME_RC2_CBC_64,		SEC_OID_RC2_CBC,	&smime_rc2p64 },
    49     { SMIME_RC2_CBC_128,	SEC_OID_RC2_CBC,	&smime_rc2p128 },
    50 #ifdef SMIME_DOES_RC5
    51     { SMIME_RC5PAD_64_16_40,	SEC_OID_RC5_CBC_PAD,	&smime_rc5p40 },
    52     { SMIME_RC5PAD_64_16_64,	SEC_OID_RC5_CBC_PAD,	&smime_rc5p64 },
    53     { SMIME_RC5PAD_64_16_128,	SEC_OID_RC5_CBC_PAD,	&smime_rc5p128 },
    54 #endif
    55     { SMIME_DES_CBC_56,		SEC_OID_DES_CBC,	NULL },
    56     { SMIME_DES_EDE3_168,	SEC_OID_DES_EDE3_CBC,	NULL }
    57 };
    59 /*
    60  * Note, the following value really just needs to be an upper bound
    61  * on the ciphers.
    62  */
    63 static const int smime_symmetric_count = sizeof(smime_cipher_maps)
    64 					 / sizeof(smime_cipher_map);
    66 static unsigned long *smime_prefs, *smime_newprefs;
    67 static int smime_current_pref_index = 0;
    68 static PRBool smime_prefs_complete = PR_FALSE;
    69 static PRBool smime_prefs_changed = PR_TRUE;
    71 static unsigned long smime_policy_bits = 0;
    74 static int
    75 smime_mapi_by_cipher (unsigned long cipher)
    76 {
    77     int i;
    79     for (i = 0; i < smime_symmetric_count; i++) {
    80 	if (smime_cipher_maps[i].cipher == cipher)
    81 	    break;
    82     }
    84     if (i == smime_symmetric_count)
    85 	return -1;
    87     return i;
    88 }
    91 /*
    92  * this function locally records the user's preference
    93  */
    94 SECStatus 
    95 SECMIME_EnableCipher(long which, int on)
    96 {
    97     unsigned long mask;
    99     if (smime_newprefs == NULL || smime_prefs_complete) {
   100 	/*
   101 	 * This is either the very first time, or we are starting over.
   102 	 */
   103 	smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count
   104 				      * sizeof(*smime_newprefs));
   105 	if (smime_newprefs == NULL)
   106 	    return SECFailure;
   107 	smime_current_pref_index = 0;
   108 	smime_prefs_complete = PR_FALSE;
   109     }
   111     mask = which & CIPHER_FAMILYID_MASK;
   112     if (mask == CIPHER_FAMILYID_MASK) {
   113     	/*
   114 	 * This call signifies that all preferences have been set.
   115 	 * Move "newprefs" over, after checking first whether or
   116 	 * not the new ones are different from the old ones.
   117 	 */
   118 	if (smime_prefs != NULL) {
   119 	    if (PORT_Memcmp (smime_prefs, smime_newprefs,
   120 			     smime_symmetric_count * sizeof(*smime_prefs)) == 0)
   121 		smime_prefs_changed = PR_FALSE;
   122 	    else
   123 		smime_prefs_changed = PR_TRUE;
   124 	    PORT_Free (smime_prefs);
   125 	}
   127 	smime_prefs = smime_newprefs;
   128 	smime_prefs_complete = PR_TRUE;
   129 	return SECSuccess;
   130     }
   132     PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
   133     if (mask != CIPHER_FAMILYID_SMIME) {
   134 	/* XXX set an error! */
   135     	return SECFailure;
   136     }
   138     if (on) {
   139 	PORT_Assert (smime_current_pref_index < smime_symmetric_count);
   140 	if (smime_current_pref_index >= smime_symmetric_count) {
   141 	    /* XXX set an error! */
   142 	    return SECFailure;
   143 	}
   145 	smime_newprefs[smime_current_pref_index++] = which;
   146     }
   148     return SECSuccess;
   149 }
   152 /*
   153  * this function locally records the export policy
   154  */
   155 SECStatus 
   156 SECMIME_SetPolicy(long which, int on)
   157 {
   158     unsigned long mask;
   160     PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);
   161     if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {
   162 	/* XXX set an error! */
   163     	return SECFailure;
   164     }
   166     which &= ~CIPHER_FAMILYID_MASK;
   168     PORT_Assert (which < 32);	/* bits in the long */
   169     if (which >= 32) {
   170 	/* XXX set an error! */
   171     	return SECFailure;
   172     }
   174     mask = 1UL << which;
   176     if (on) {
   177     	smime_policy_bits |= mask;
   178     } else {
   179     	smime_policy_bits &= ~mask;
   180     }
   182     return SECSuccess;
   183 }
   186 /*
   187  * Based on the given algorithm (including its parameters, in some cases!)
   188  * and the given key (may or may not be inspected, depending on the
   189  * algorithm), find the appropriate policy algorithm specification
   190  * and return it.  If no match can be made, -1 is returned.
   191  */
   192 static long
   193 smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key)
   194 {
   195     SECOidTag algtag;
   197     algtag = SECOID_GetAlgorithmTag (algid);
   198     switch (algtag) {
   199       case SEC_OID_RC2_CBC:
   200 	{
   201 	    unsigned int keylen_bits;
   203 	    keylen_bits = PK11_GetKeyStrength (key, algid);
   204 	    switch (keylen_bits) {
   205 	      case 40:
   206 		return SMIME_RC2_CBC_40;
   207 	      case 64:
   208 		return SMIME_RC2_CBC_64;
   209 	      case 128:
   210 		return SMIME_RC2_CBC_128;
   211 	      default:
   212 		break;
   213 	    }
   214 	}
   215 	break;
   216       case SEC_OID_DES_CBC:
   217 	return SMIME_DES_CBC_56;
   218       case SEC_OID_DES_EDE3_CBC:
   219 	return SMIME_DES_EDE3_168;
   220 #ifdef SMIME_DOES_RC5
   221       case SEC_OID_RC5_CBC_PAD:
   222 	PORT_Assert (0);	/* XXX need to pull out parameters and match */
   223 	break;
   224 #endif
   225       default:
   226 	break;
   227     }
   229     return -1;
   230 }
   233 static PRBool
   234 smime_cipher_allowed (unsigned long which)
   235 {
   236     unsigned long mask;
   238     which &= ~CIPHER_FAMILYID_MASK;
   239     PORT_Assert (which < 32);	/* bits per long (min) */
   240     if (which >= 32)
   241 	return PR_FALSE;
   243     mask = 1UL << which;
   244     if ((mask & smime_policy_bits) == 0)
   245 	return PR_FALSE;
   247     return PR_TRUE;
   248 }
   251 PRBool
   252 SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
   253 {
   254     long which;
   256     which = smime_policy_algorithm (algid, key);
   257     if (which < 0)
   258 	return PR_FALSE;
   260     return smime_cipher_allowed ((unsigned long)which);
   261 }
   264 /*
   265  * Does the current policy allow *any* S/MIME encryption (or decryption)?
   266  *
   267  * This tells whether or not *any* S/MIME encryption can be done,
   268  * according to policy.  Callers may use this to do nicer user interface
   269  * (say, greying out a checkbox so a user does not even try to encrypt
   270  * a message when they are not allowed to) or for any reason they want
   271  * to check whether S/MIME encryption (or decryption, for that matter)
   272  * may be done.
   273  *
   274  * It takes no arguments.  The return value is a simple boolean:
   275  *   PR_TRUE means encryption (or decryption) is *possible*
   276  *	(but may still fail due to other reasons, like because we cannot
   277  *	find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
   278  *   PR_FALSE means encryption (or decryption) is not permitted
   279  *
   280  * There are no errors from this routine.
   281  */
   282 PRBool
   283 SECMIME_EncryptionPossible (void)
   284 {
   285     if (smime_policy_bits != 0)
   286 	return PR_TRUE;
   288     return PR_FALSE;
   289 }
   292 /*
   293  * XXX Would like the "parameters" field to be a SECItem *, but the
   294  * encoder is having trouble with optional pointers to an ANY.  Maybe
   295  * once that is fixed, can change this back...
   296  */
   297 typedef struct smime_capability_struct {
   298     unsigned long cipher;	/* local; not part of encoding */
   299     SECOidTag capIDTag;		/* local; not part of encoding */
   300     SECItem capabilityID;
   301     SECItem parameters;
   302 } smime_capability;
   304 static const SEC_ASN1Template smime_capability_template[] = {
   305     { SEC_ASN1_SEQUENCE,
   306 	  0, NULL, sizeof(smime_capability) },
   307     { SEC_ASN1_OBJECT_ID,
   308 	  offsetof(smime_capability,capabilityID), },
   309     { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
   310 	  offsetof(smime_capability,parameters), },
   311     { 0, }
   312 };
   314 static const SEC_ASN1Template smime_capabilities_template[] = {
   315     { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
   316 };
   320 static void
   321 smime_fill_capability (smime_capability *cap)
   322 {
   323     unsigned long cipher;
   324     SECOidTag algtag;
   325     int i;
   327     algtag = SECOID_FindOIDTag (&(cap->capabilityID));
   329     for (i = 0; i < smime_symmetric_count; i++) {
   330 	if (smime_cipher_maps[i].algtag != algtag)
   331 	    continue;
   332 	/*
   333 	 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
   334 	 * 2 NULLs as equal and NULL and non-NULL as not equal), we could
   335 	 * use that here instead of all of the following comparison code.
   336 	 */
   337 	if (cap->parameters.data != NULL) {
   338 	    if (smime_cipher_maps[i].parms == NULL)
   339 		continue;
   340 	    if (cap->parameters.len != smime_cipher_maps[i].parms->len)
   341 		continue;
   342 	    if (PORT_Memcmp (cap->parameters.data,
   343 			     smime_cipher_maps[i].parms->data,
   344 			     cap->parameters.len) == 0)
   345 		break;
   346 	} else if (smime_cipher_maps[i].parms == NULL) {
   347 	    break;
   348 	}
   349     }
   351     if (i == smime_symmetric_count)
   352 	cipher = 0;
   353     else
   354 	cipher = smime_cipher_maps[i].cipher;
   356     cap->cipher = cipher;
   357     cap->capIDTag = algtag;
   358 }
   361 static long
   362 smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts)
   363 {
   364     PLArenaPool *poolp;
   365     long chosen_cipher;
   366     int *cipher_abilities;
   367     int *cipher_votes;
   368     int strong_mapi;
   369     int rcount, mapi, max;
   371     if (smime_policy_bits == 0) {
   372 	PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
   373 	return -1;
   374     }
   376     chosen_cipher = SMIME_RC2_CBC_40;		/* the default, LCD */
   378     poolp = PORT_NewArena (1024);		/* XXX what is right value? */
   379     if (poolp == NULL)
   380 	goto done;
   382     cipher_abilities = (int*)PORT_ArenaZAlloc (poolp,
   383 					 smime_symmetric_count * sizeof(int));
   384     if (cipher_abilities == NULL)
   385 	goto done;
   387     cipher_votes = (int*)PORT_ArenaZAlloc (poolp,
   388 				     smime_symmetric_count * sizeof(int));
   389     if (cipher_votes == NULL)
   390 	goto done;
   392     /*
   393      * XXX Should have a #define somewhere which specifies default
   394      * strong cipher.  (Or better, a way to configure.)
   395      */
   397     /* Make triple-DES the strong cipher. */
   398     strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
   400     PORT_Assert (strong_mapi >= 0);
   402     for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
   403 	SECItem *profile;
   404 	smime_capability **caps;
   405 	int capi, pref;
   406 	SECStatus dstat;
   408 	pref = smime_symmetric_count;
   409 	profile = CERT_FindSMimeProfile (rcerts[rcount]);
   410 	if (profile != NULL && profile->data != NULL && profile->len > 0) {
   411 	    caps = NULL;
   412 	    dstat = SEC_QuickDERDecodeItem (poolp, &caps,
   413 					smime_capabilities_template,
   414 					profile);
   415 	    if (dstat == SECSuccess && caps != NULL) {
   416 		for (capi = 0; caps[capi] != NULL; capi++) {
   417 		    smime_fill_capability (caps[capi]);
   418 		    mapi = smime_mapi_by_cipher (caps[capi]->cipher);
   419 		    if (mapi >= 0) {
   420 			cipher_abilities[mapi]++;
   421 			cipher_votes[mapi] += pref;
   422 			--pref;
   423 		    }
   424 		}
   425 	    }
   426 	} else {
   427 	    SECKEYPublicKey *key;
   428 	    unsigned int pklen_bits;
   430 	    /*
   431 	     * XXX This is probably only good for RSA keys.  What I would
   432 	     * really like is a function to just say;  Is the public key in
   433 	     * this cert an export-length key?  Then I would not have to
   434 	     * know things like the value 512, or the kind of key, or what
   435 	     * a subjectPublicKeyInfo is, etc.
   436 	     */
   437 	    key = CERT_ExtractPublicKey (rcerts[rcount]);
   438 	    if (key != NULL) {
   439 		pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
   440 		SECKEY_DestroyPublicKey (key);
   442 		if (pklen_bits > 512) {
   443 		    cipher_abilities[strong_mapi]++;
   444 		    cipher_votes[strong_mapi] += pref;
   445 		}
   446 	    }
   447 	}
   448 	if (profile != NULL)
   449 	    SECITEM_FreeItem (profile, PR_TRUE);
   450     }
   452     max = 0;
   453     for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
   454 	if (cipher_abilities[mapi] != rcount)
   455 	    continue;
   456 	if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher))
   457 	    continue;
   458 	if (cipher_votes[mapi] > max) {
   459 	    chosen_cipher = smime_cipher_maps[mapi].cipher;
   460 	    max = cipher_votes[mapi];
   461 	} /* XXX else if a tie, let scert break it? */
   462     }
   464 done:
   465     if (poolp != NULL)
   466 	PORT_FreeArena (poolp, PR_FALSE);
   468     return chosen_cipher;
   469 }
   472 /*
   473  * XXX This is a hack for now to satisfy our current interface.
   474  * Eventually, with more parameters needing to be specified, just
   475  * looking up the keysize is not going to be sufficient.
   476  */
   477 static int
   478 smime_keysize_by_cipher (unsigned long which)
   479 {
   480     int keysize;
   482     switch (which) {
   483       case SMIME_RC2_CBC_40:
   484 	keysize = 40;
   485 	break;
   486       case SMIME_RC2_CBC_64:
   487 	keysize = 64;
   488 	break;
   489       case SMIME_RC2_CBC_128:
   490 	keysize = 128;
   491 	break;
   492 #ifdef SMIME_DOES_RC5
   493       case SMIME_RC5PAD_64_16_40:
   494       case SMIME_RC5PAD_64_16_64:
   495       case SMIME_RC5PAD_64_16_128:
   496 	/* XXX See comment above; keysize is not enough... */
   497 	PORT_Assert (0);
   498 	PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
   499 	keysize = -1;
   500 	break;
   501 #endif
   502       case SMIME_DES_CBC_56:
   503       case SMIME_DES_EDE3_168:
   504 	/*
   505 	 * These are special; since the key size is fixed, we actually
   506 	 * want to *avoid* specifying a key size.
   507 	 */
   508 	keysize = 0;
   509 	break;
   510       default:
   511 	keysize = -1;
   512 	break;
   513     }
   515     return keysize;
   516 }
   519 /*
   520  * Start an S/MIME encrypting context.
   521  *
   522  * "scert" is the cert for the sender.  It will be checked for validity.
   523  * "rcerts" are the certs for the recipients.  They will also be checked.
   524  *
   525  * "certdb" is the cert database to use for verifying the certs.
   526  * It can be NULL if a default database is available (like in the client).
   527  *
   528  * This function already does all of the stuff specific to S/MIME protocol
   529  * and local policy; the return value just needs to be passed to
   530  * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
   531  * and finally to SEC_PKCS7DestroyContentInfo().
   532  *
   533  * An error results in a return value of NULL and an error set.
   534  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
   535  */
   536 SEC_PKCS7ContentInfo *
   537 SECMIME_CreateEncrypted(CERTCertificate *scert,
   538 			CERTCertificate **rcerts,
   539 			CERTCertDBHandle *certdb,
   540 			SECKEYGetPasswordKey pwfn,
   541 			void *pwfn_arg)
   542 {
   543     SEC_PKCS7ContentInfo *cinfo;
   544     long cipher;
   545     SECOidTag encalg;
   546     int keysize;
   547     int mapi, rci;
   549     cipher = smime_choose_cipher (scert, rcerts);
   550     if (cipher < 0)
   551 	return NULL;
   553     mapi = smime_mapi_by_cipher (cipher);
   554     if (mapi < 0)
   555 	return NULL;
   557     /*
   558      * XXX This is stretching it -- CreateEnvelopedData should probably
   559      * take a cipher itself of some sort, because we cannot know what the
   560      * future will bring in terms of parameters for each type of algorithm.
   561      * For example, just an algorithm and keysize is *not* sufficient to
   562      * fully specify the usage of RC5 (which also needs to know rounds and
   563      * block size).  Work this out into a better API!
   564      */
   565     encalg = smime_cipher_maps[mapi].algtag;
   566     keysize = smime_keysize_by_cipher (cipher);
   567     if (keysize < 0)
   568 	return NULL;
   570     cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient,
   571 					  certdb, encalg, keysize,
   572 					  pwfn, pwfn_arg);
   573     if (cinfo == NULL)
   574 	return NULL;
   576     for (rci = 0; rcerts[rci] != NULL; rci++) {
   577 	if (rcerts[rci] == scert)
   578 	    continue;
   579 	if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient,
   580 				   NULL) != SECSuccess) {
   581 	    SEC_PKCS7DestroyContentInfo (cinfo);
   582 	    return NULL;
   583 	}
   584     }
   586     return cinfo;
   587 }
   590 static smime_capability **smime_capabilities;
   591 static SECItem *smime_encoded_caps;
   594 static SECStatus
   595 smime_init_caps (void)
   596 {
   597     smime_capability *cap;
   598     smime_cipher_map *map;
   599     SECOidData *oiddata;
   600     SECStatus rv;
   601     int i;
   603     if (smime_encoded_caps != NULL && (! smime_prefs_changed))
   604 	return SECSuccess;
   606     if (smime_encoded_caps != NULL) {
   607 	SECITEM_FreeItem (smime_encoded_caps, PR_TRUE);
   608 	smime_encoded_caps = NULL;
   609     }
   611     if (smime_capabilities == NULL) {
   612 	smime_capabilities = (smime_capability**)PORT_ZAlloc (
   613 					  (smime_symmetric_count + 1)
   614 					  * sizeof(smime_capability *));
   615 	if (smime_capabilities == NULL)
   616 	    return SECFailure;
   617     }
   619     rv = SECFailure;
   621     /* 
   622        The process of creating the encoded PKCS7 cipher capability list
   623        involves two basic steps: 
   625        (a) Convert our internal representation of cipher preferences 
   626            (smime_prefs) into an array containing cipher OIDs and 
   627 	   parameter data (smime_capabilities). This step is
   628 	   performed here.
   630        (b) Encode, using ASN.1, the cipher information in 
   631            smime_capabilities, leaving the encoded result in 
   632 	   smime_encoded_caps.
   634        (In the process of performing (a), Lisa put in some optimizations
   635        which allow us to avoid needlessly re-populating elements in 
   636        smime_capabilities as we walk through smime_prefs.)
   637     */
   638     for (i = 0; i < smime_current_pref_index; i++) {
   639 	int mapi;
   641 	/* Get the next cipher preference in smime_prefs. */
   642 	mapi = smime_mapi_by_cipher (smime_prefs[i]);
   643 	if (mapi < 0)
   644 	    break;
   646 	/* Find the corresponding entry in the cipher map. */
   647 	PORT_Assert (mapi < smime_symmetric_count);
   648 	map = &(smime_cipher_maps[mapi]);
   650 	/*
   651 	 * Convert the next preference found in smime_prefs into an
   652 	 * smime_capability.
   653 	 */
   655 	cap = smime_capabilities[i];
   656 	if (cap == NULL) {
   657 	    cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability));
   658 	    if (cap == NULL)
   659 		break;
   660 	    smime_capabilities[i] = cap;
   661 	} else if (cap->cipher == smime_prefs[i]) {
   662 	    continue;		/* no change to this one */
   663 	}
   665 	cap->capIDTag = map->algtag;
   666 	oiddata = SECOID_FindOIDByTag (map->algtag);
   667 	if (oiddata == NULL)
   668 	    break;
   670 	if (cap->capabilityID.data != NULL) {
   671 	    SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
   672 	    cap->capabilityID.data = NULL;
   673 	    cap->capabilityID.len = 0;
   674 	}
   676 	rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid));
   677 	if (rv != SECSuccess)
   678 	    break;
   680 	if (map->parms == NULL) {
   681 	    cap->parameters.data = NULL;
   682 	    cap->parameters.len = 0;
   683 	} else {
   684 	    cap->parameters.data = map->parms->data;
   685 	    cap->parameters.len = map->parms->len;
   686 	}
   688 	cap->cipher = smime_prefs[i];
   689     }
   691     if (i != smime_current_pref_index)
   692 	return rv;
   694     while (i < smime_symmetric_count) {
   695 	cap = smime_capabilities[i];
   696 	if (cap != NULL) {
   697 	    SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
   698 	    PORT_Free (cap);
   699 	}
   700 	smime_capabilities[i] = NULL;
   701 	i++;
   702     }
   703     smime_capabilities[i] = NULL;
   705     smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities,
   706 					     smime_capabilities_template);
   707     if (smime_encoded_caps == NULL)
   708 	return SECFailure;
   710     return SECSuccess;
   711 }
   714 static SECStatus
   715 smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
   716 {
   717     PORT_Assert (smime_prefs_complete);
   718     if (! smime_prefs_complete)
   719 	return SECFailure;
   721     /* For that matter, if capabilities haven't been initialized yet,
   722        do so now. */
   723     if (smime_encoded_caps == NULL || smime_prefs_changed) {
   724 	SECStatus rv;
   726 	rv = smime_init_caps();
   727 	if (rv != SECSuccess)
   728 	    return rv;
   730 	PORT_Assert (smime_encoded_caps != NULL);
   731     }
   733     return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
   734 					smime_encoded_caps);
   735 }
   738 /*
   739  * Start an S/MIME signing context.
   740  *
   741  * "scert" is the cert that will be used to sign the data.  It will be
   742  * checked for validity.
   743  *
   744  * "ecert" is the signer's encryption cert.  If it is different from
   745  * scert, then it will be included in the signed message so that the
   746  * recipient can save it for future encryptions.
   747  *
   748  * "certdb" is the cert database to use for verifying the cert.
   749  * It can be NULL if a default database is available (like in the client).
   750  * 
   751  * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
   752  * XXX There should be SECMIME functions for hashing, or the hashing should
   753  * be built into this interface, which we would like because we would
   754  * support more smartcards that way, and then this argument should go away.)
   755  *
   756  * "digest" is the actual digest of the data.  It must be provided in
   757  * the case of detached data or NULL if the content will be included.
   758  *
   759  * This function already does all of the stuff specific to S/MIME protocol
   760  * and local policy; the return value just needs to be passed to
   761  * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
   762  * and finally to SEC_PKCS7DestroyContentInfo().
   763  *
   764  * An error results in a return value of NULL and an error set.
   765  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
   766  */
   768 SEC_PKCS7ContentInfo *
   769 SECMIME_CreateSigned (CERTCertificate *scert,
   770 		      CERTCertificate *ecert,
   771 		      CERTCertDBHandle *certdb,
   772 		      SECOidTag digestalg,
   773 		      SECItem *digest,
   774 		      SECKEYGetPasswordKey pwfn,
   775 		      void *pwfn_arg)
   776 {
   777     SEC_PKCS7ContentInfo *cinfo;
   778     SECStatus rv;
   780     /* See note in header comment above about digestalg. */
   781     /* Doesn't explain this. PORT_Assert (digestalg == SEC_OID_SHA1); */
   783     cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner,
   784 				       certdb, digestalg, digest,
   785 				       pwfn, pwfn_arg);
   786     if (cinfo == NULL)
   787 	return NULL;
   789     if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) {
   790 	SEC_PKCS7DestroyContentInfo (cinfo);
   791 	return NULL;
   792     }
   794     /* if the encryption cert and the signing cert differ, then include
   795      * the encryption cert too.
   796      */
   797     /* it is ok to compare the pointers since we ref count, and the same
   798      * cert will always have the same pointer
   799      */
   800     if ( ( ecert != NULL ) && ( ecert != scert ) ) {
   801 	rv = SEC_PKCS7AddCertificate(cinfo, ecert);
   802 	if ( rv != SECSuccess ) {
   803 	    SEC_PKCS7DestroyContentInfo (cinfo);
   804 	    return NULL;
   805 	}
   806     }
   807     /*
   808      * Add the signing time.  But if it fails for some reason,
   809      * may as well not give up altogether -- just assert.
   810      */
   811     rv = SEC_PKCS7AddSigningTime (cinfo);
   812     PORT_Assert (rv == SECSuccess);
   814     /*
   815      * Add the email profile.  Again, if it fails for some reason,
   816      * may as well not give up altogether -- just assert.
   817      */
   818     rv = smime_add_profile (ecert, cinfo);
   819     PORT_Assert (rv == SECSuccess);
   821     return cinfo;
   822 }

mercurial