secmime.c

来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 902 行 · 第 1/2 页

C
902
字号
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ *  * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. *  * The Original Code is the Netscape security libraries. *  * The Initial Developer of the Original Code is Netscape * Communications Corporation.  Portions created by Netscape are  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All * Rights Reserved. *  * Contributor(s): *  * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable  * instead of those above.  If you wish to allow use of your  * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL.  If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. *//* * Stuff specific to S/MIME policy and interoperability. * Depends on PKCS7, but there should be no dependency the other way around. * * $Id: secmime.c,v 1.1 2000/03/31 19:16:08 relyea%netscape.com Exp $ */#include "secmime.h"#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"typedef struct smime_cipher_map_struct {    unsigned long cipher;    SECOidTag algtag;    SECItem *parms;} smime_cipher_map;/* * These are macros because I think some subsequent parameters, * like those for RC5, will want to use them, too, separately. */#define SMIME_DER_INTVAL_16	SEC_ASN1_INTEGER, 0x01, 0x10#define SMIME_DER_INTVAL_40	SEC_ASN1_INTEGER, 0x01, 0x28#define SMIME_DER_INTVAL_64	SEC_ASN1_INTEGER, 0x01, 0x40#define SMIME_DER_INTVAL_128	SEC_ASN1_INTEGER, 0x02, 0x00, 0x80#ifdef SMIME_DOES_RC5	/* will be needed; quiet unused warning for now */static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 };#endifstatic unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 };static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 };static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 };static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) };static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) };static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) };static smime_cipher_map smime_cipher_maps[] = {    { SMIME_RC2_CBC_40,		SEC_OID_RC2_CBC,	&smime_rc2p40 },    { SMIME_RC2_CBC_64,		SEC_OID_RC2_CBC,	&smime_rc2p64 },    { SMIME_RC2_CBC_128,	SEC_OID_RC2_CBC,	&smime_rc2p128 },#ifdef SMIME_DOES_RC5    { SMIME_RC5PAD_64_16_40,	SEC_OID_RC5_CBC_PAD,	&smime_rc5p40 },    { SMIME_RC5PAD_64_16_64,	SEC_OID_RC5_CBC_PAD,	&smime_rc5p64 },    { SMIME_RC5PAD_64_16_128,	SEC_OID_RC5_CBC_PAD,	&smime_rc5p128 },#endif    { SMIME_DES_CBC_56,		SEC_OID_DES_CBC,	NULL },    { SMIME_DES_EDE3_168,	SEC_OID_DES_EDE3_CBC,	NULL },    { SMIME_FORTEZZA,		SEC_OID_FORTEZZA_SKIPJACK, NULL}};/* * Note, the following value really just needs to be an upper bound * on the ciphers. */static const int smime_symmetric_count = sizeof(smime_cipher_maps)					 / sizeof(smime_cipher_map);static unsigned long *smime_prefs, *smime_newprefs;static int smime_current_pref_index = 0;static PRBool smime_prefs_complete = PR_FALSE;static PRBool smime_prefs_changed = PR_TRUE;static unsigned long smime_policy_bits = 0;static intsmime_mapi_by_cipher (unsigned long cipher){    int i;    for (i = 0; i < smime_symmetric_count; i++) {	if (smime_cipher_maps[i].cipher == cipher)	    break;    }    if (i == smime_symmetric_count)	return -1;    return i;}/* * this function locally records the user's preference */SECStatus SECMIME_EnableCipher(long which, int on){    unsigned long mask;    if (smime_newprefs == NULL || smime_prefs_complete) {	/*	 * This is either the very first time, or we are starting over.	 */	smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count				      * sizeof(*smime_newprefs));	if (smime_newprefs == NULL)	    return SECFailure;	smime_current_pref_index = 0;	smime_prefs_complete = PR_FALSE;    }    mask = which & CIPHER_FAMILYID_MASK;    if (mask == CIPHER_FAMILYID_MASK) {    	/*	 * This call signifies that all preferences have been set.	 * Move "newprefs" over, after checking first whether or	 * not the new ones are different from the old ones.	 */	if (smime_prefs != NULL) {	    if (PORT_Memcmp (smime_prefs, smime_newprefs,			     smime_symmetric_count * sizeof(*smime_prefs)) == 0)		smime_prefs_changed = PR_FALSE;	    else		smime_prefs_changed = PR_TRUE;	    PORT_Free (smime_prefs);	}	smime_prefs = smime_newprefs;	smime_prefs_complete = PR_TRUE;	return SECSuccess;    }    PORT_Assert (mask == CIPHER_FAMILYID_SMIME);    if (mask != CIPHER_FAMILYID_SMIME) {	/* XXX set an error! */    	return SECFailure;    }    if (on) {	PORT_Assert (smime_current_pref_index < smime_symmetric_count);	if (smime_current_pref_index >= smime_symmetric_count) {	    /* XXX set an error! */	    return SECFailure;	}	smime_newprefs[smime_current_pref_index++] = which;    }    return SECSuccess;}/* * this function locally records the export policy */SECStatus SECMIME_SetPolicy(long which, int on){    unsigned long mask;    PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);    if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {	/* XXX set an error! */    	return SECFailure;    }    which &= ~CIPHER_FAMILYID_MASK;    PORT_Assert (which < 32);	/* bits in the long */    if (which >= 32) {	/* XXX set an error! */    	return SECFailure;    }    mask = 1UL << which;    if (on) {    	smime_policy_bits |= mask;    } else {    	smime_policy_bits &= ~mask;    }    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. */static longsmime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key){    SECOidTag algtag;    algtag = SECOID_GetAlgorithmTag (algid);    switch (algtag) {      case SEC_OID_RC2_CBC:	{	    unsigned int keylen_bits;	    keylen_bits = PK11_GetKeyStrength (key, algid);	    switch (keylen_bits) {	      case 40:		return SMIME_RC2_CBC_40;	      case 64:		return SMIME_RC2_CBC_64;	      case 128:		return SMIME_RC2_CBC_128;	      default:		break;	    }	}	break;      case SEC_OID_DES_CBC:	return SMIME_DES_CBC_56;      case SEC_OID_DES_EDE3_CBC:	return SMIME_DES_EDE3_168;      case SEC_OID_FORTEZZA_SKIPJACK:	return SMIME_FORTEZZA;#ifdef SMIME_DOES_RC5      case SEC_OID_RC5_CBC_PAD:	PORT_Assert (0);	/* XXX need to pull out parameters and match */	break;#endif      default:	break;    }    return -1;}static PRBoolsmime_cipher_allowed (unsigned long which){    unsigned long mask;    which &= ~CIPHER_FAMILYID_MASK;    PORT_Assert (which < 32);	/* bits per long (min) */    if (which >= 32)	return PR_FALSE;    mask = 1UL << which;    if ((mask & smime_policy_bits) == 0)	return PR_FALSE;    return PR_TRUE;}PRBoolSECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key){    long which;    which = smime_policy_algorithm (algid, key);    if (which < 0)	return PR_FALSE;    return smime_cipher_allowed ((unsigned long)which);}/* * Does the current policy allow *any* S/MIME encryption (or decryption)? * * This tells whether or not *any* S/MIME encryption can be done, * according to policy.  Callers may use this to do nicer user interface * (say, greying out a checkbox so a user does not even try to encrypt * a message when they are not allowed to) or for any reason they want * to check whether S/MIME encryption (or decryption, for that matter) * may be done. * * It takes no arguments.  The return value is a simple boolean: *   PR_TRUE means encryption (or decryption) is *possible* *	(but may still fail due to other reasons, like because we cannot *	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. */PRBoolSECMIME_EncryptionPossible (void){    if (smime_policy_bits != 0)	return PR_TRUE;    return PR_FALSE;}/* * XXX Would like the "parameters" field to be a SECItem *, but the * encoder is having trouble with optional pointers to an ANY.  Maybe * once that is fixed, can change this back... */typedef struct smime_capability_struct {    unsigned long cipher;	/* local; not part of encoding */    SECOidTag capIDTag;		/* local; not part of encoding */    SECItem capabilityID;    SECItem parameters;} smime_capability;static const SEC_ASN1Template smime_capability_template[] = {    { SEC_ASN1_SEQUENCE,	  0, NULL, sizeof(smime_capability) },    { SEC_ASN1_OBJECT_ID,	  offsetof(smime_capability,capabilityID), },    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,	  offsetof(smime_capability,parameters), },    { 0, }};static const SEC_ASN1Template smime_capabilities_template[] = {    { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }};static voidsmime_fill_capability (smime_capability *cap){    unsigned long cipher;    SECOidTag algtag;    int i;    algtag = SECOID_FindOIDTag (&(cap->capabilityID));    for (i = 0; i < smime_symmetric_count; i++) {	if (smime_cipher_maps[i].algtag != algtag)	    continue;	/*	 * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing	 * 2 NULLs as equal and NULL and non-NULL as not equal), we could	 * use that here instead of all of the following comparison code.	 */	if (cap->parameters.data != NULL) {	    if (smime_cipher_maps[i].parms == NULL)		continue;	    if (cap->parameters.len != smime_cipher_maps[i].parms->len)		continue;	    if (PORT_Memcmp (cap->parameters.data,			     smime_cipher_maps[i].parms->data,			     cap->parameters.len) == 0)		break;	} else if (smime_cipher_maps[i].parms == NULL) {	    break;	}    }    if (i == smime_symmetric_count)	cipher = 0;    else	cipher = smime_cipher_maps[i].cipher;    cap->cipher = cipher;    cap->capIDTag = algtag;}static longsmime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts){    PRArenaPool *poolp;    long chosen_cipher;    int *cipher_abilities;    int *cipher_votes;    int strong_mapi;    int rcount, mapi, max, i;	PRBool isFortezza = PK11_FortezzaHasKEA(scert);    if (smime_policy_bits == 0) {	PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);	return -1;    }    chosen_cipher = SMIME_RC2_CBC_40;		/* the default, LCD */    poolp = PORT_NewArena (1024);		/* XXX what is right value? */    if (poolp == NULL)	goto done;    cipher_abilities = (int*)PORT_ArenaZAlloc (poolp,					 smime_symmetric_count * sizeof(int));    if (cipher_abilities == NULL)	goto done;    cipher_votes = (int*)PORT_ArenaZAlloc (poolp,				     smime_symmetric_count * sizeof(int));    if (cipher_votes == NULL)	goto done;    /*     * XXX Should have a #define somewhere which specifies default     * strong cipher.  (Or better, a way to configure, which would     * take Fortezza into account as well.)     */    /* If the user has the Fortezza preference turned on, make     *  that the strong cipher. Otherwise, use triple-DES. */    strong_mapi = -1;    if (isFortezza) {	for(i=0;i < smime_current_pref_index && strong_mapi < 0;i++)	{	    if (smime_prefs[i] == SMIME_FORTEZZA)		strong_mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);	}    }    if (strong_mapi == -1)	strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);    PORT_Assert (strong_mapi >= 0);    for (rcount = 0; rcerts[rcount] != NULL; rcount++) {	SECItem *profile;	smime_capability **caps;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?