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 + -
显示快捷键?