/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * Stuff specific to S/MIME policy and interoperability. * Depends on PKCS7, but there should be no dependency the other way around.
*/
/* * 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 */ staticunsignedchar smime_int16[] = { SMIME_DER_INTVAL_16 }; #endif staticunsignedchar smime_int40[] = { SMIME_DER_INTVAL_40 }; staticunsignedchar smime_int64[] = { SMIME_DER_INTVAL_64 }; staticunsignedchar smime_int128[] = { SMIME_DER_INTVAL_128 };
/* * Note, the following value really just needs to be an upper bound * on the ciphers.
*/ staticconstint smime_symmetric_count = sizeof(smime_cipher_maps) / sizeof(smime_cipher_map);
staticint
smime_mapi_by_cipher(unsignedlong 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)
{ unsignedlong mask;
if (smime_newprefs == NULL || smime_prefs_complete) { /* * This is either the very first time, or we are starting over.
*/
smime_newprefs = (unsignedlong *)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);
}
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;
}
/* * 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.
*/ staticlong
smime_policy_algorithm(SECAlgorithmID *algid, PK11SymKey *key)
{
SECOidTag algtag;
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; #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;
}
/* * 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.
*/
PRBool
SECMIME_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...
*/ typedefstruct smime_capability_struct { unsignedlong cipher; /* local; not part of encoding */
SECOidTag capIDTag; /* local; not part of encoding */
SECItem capabilityID;
SECItem parameters;
} smime_capability;
staticvoid
smime_fill_capability(smime_capability *cap)
{ unsignedlong 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;
} elseif (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;
}
staticlong
smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
{
PLArenaPool *poolp; long chosen_cipher; int *cipher_abilities; int *cipher_votes; int strong_mapi; int rcount, mapi, max;
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;
/* * XXX This is probably only good for RSA keys. What I would * really like is a function to just say; Is the public key in * this cert an export-length key? Then I would not have to * know things like the value 512, or the kind of key, or what * a subjectPublicKeyInfo is, etc.
*/
key = CERT_ExtractPublicKey(rcerts[rcount]); if (key != NULL) {
pklen_bits = SECKEY_PublicKeyStrength(key) * 8;
SECKEY_DestroyPublicKey(key);
if (pklen_bits > 512) {
cipher_abilities[strong_mapi]++;
cipher_votes[strong_mapi] += pref;
}
}
} if (profile != NULL)
SECITEM_FreeItem(profile, PR_TRUE);
}
max = 0; for (mapi = 0; mapi < smime_symmetric_count; mapi++) { if (cipher_abilities[mapi] != rcount) continue; if (!smime_cipher_allowed(smime_cipher_maps[mapi].cipher)) continue; if (cipher_votes[mapi] > max) {
chosen_cipher = smime_cipher_maps[mapi].cipher;
max = cipher_votes[mapi];
} /* XXX else if a tie, let scert break it? */
}
done: if (poolp != NULL)
PORT_FreeArena(poolp, PR_FALSE);
return chosen_cipher;
}
/* * XXX This is a hack for now to satisfy our current interface. * Eventually, with more parameters needing to be specified, just * looking up the keysize is not going to be sufficient.
*/ staticint
smime_keysize_by_cipher(unsignedlong which)
{ int keysize;
switch (which) { case SMIME_RC2_CBC_40:
keysize = 40; break; case SMIME_RC2_CBC_64:
keysize = 64; break; case SMIME_RC2_CBC_128:
keysize = 128; break; #ifdef SMIME_DOES_RC5 case SMIME_RC5PAD_64_16_40: case SMIME_RC5PAD_64_16_64: case SMIME_RC5PAD_64_16_128: /* XXX See comment above; keysize is not enough... */
PORT_Assert(0);
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
keysize = -1; break; #endif case SMIME_DES_CBC_56: case SMIME_DES_EDE3_168: /* * These are special; since the key size is fixed, we actually * want to *avoid* specifying a key size.
*/
keysize = 0; break; default:
keysize = -1; break;
}
return keysize;
}
/* * 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. * It can be NULL if a default database is available (like in the client). * * This function already does all of the stuff specific to S/MIME protocol * and local policy; the return value just needs to be passed to * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, * and finally to SEC_PKCS7DestroyContentInfo(). * * An error results in a return value of NULL and an error set. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
*/
SEC_PKCS7ContentInfo *
SECMIME_CreateEncrypted(CERTCertificate *scert,
CERTCertificate **rcerts,
CERTCertDBHandle *certdb,
SECKEYGetPasswordKey pwfn, void *pwfn_arg)
{
SEC_PKCS7ContentInfo *cinfo; long cipher;
SECOidTag encalg; int keysize; int mapi, rci;
cipher = smime_choose_cipher(scert, rcerts); if (cipher < 0) return NULL;
mapi = smime_mapi_by_cipher(cipher); if (mapi < 0) return NULL;
/* * XXX This is stretching it -- CreateEnvelopedData should probably * take a cipher itself of some sort, because we cannot know what the * future will bring in terms of parameters for each type of algorithm. * For example, just an algorithm and keysize is *not* sufficient to * fully specify the usage of RC5 (which also needs to know rounds and * block size). Work this out into a better API!
*/
encalg = smime_cipher_maps[mapi].algtag;
keysize = smime_keysize_by_cipher(cipher); if (keysize < 0) return NULL;
/* The process of creating the encoded PKCS7 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.
(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.)
*/ for (i = 0; i < smime_current_pref_index; i++) { int mapi;
/* Get the next cipher preference in smime_prefs. */
mapi = smime_mapi_by_cipher(smime_prefs[i]); if (mapi < 0) break;
/* Find the corresponding entry in the cipher map. */
PORT_Assert(mapi < smime_symmetric_count);
map = &(smime_cipher_maps[mapi]);
/* * Convert the next preference found in smime_prefs into an * smime_capability.
*/
cap = smime_capabilities[i]; if (cap == NULL) {
cap = (smime_capability *)PORT_ZAlloc(sizeof(smime_capability)); if (cap == NULL) break;
smime_capabilities[i] = cap;
} elseif (cap->cipher == smime_prefs[i]) { continue; /* no change to this one */
}
/* * Start an S/MIME signing context. * * "scert" is the cert that will be used to sign the data. It will be * checked for validity. * * "ecert" is the signer's encryption cert. If it is different from * scert, then it will be included in the signed message so that the * recipient can save it for future encryptions. * * "certdb" is the cert database to use for verifying the cert. * It can be NULL if a default database is available (like in the client). * * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1). * XXX There should be SECMIME functions for hashing, or the hashing should * be built into this interface, which we would like because we would * support more smartcards that way, and then this argument should go away.) * * "digest" is the actual digest of the data. It must be provided in * the case of detached data or NULL if the content will be included. * * This function already does all of the stuff specific to S/MIME protocol * and local policy; the return value just needs to be passed to * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data, * and finally to SEC_PKCS7DestroyContentInfo(). * * An error results in a return value of NULL and an error set. * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
*/
if (SEC_PKCS7IncludeCertChain(cinfo, NULL) != SECSuccess) {
SEC_PKCS7DestroyContentInfo(cinfo); return NULL;
}
/* if the encryption cert and the signing cert differ, then include * the encryption cert too.
*/ /* it is ok to compare the pointers since we ref count, and the same * cert will always have the same pointer
*/ if ((ecert != NULL) && (ecert != scert)) {
rv = SEC_PKCS7AddCertificate(cinfo, ecert); if (rv != SECSuccess) {
SEC_PKCS7DestroyContentInfo(cinfo); return NULL;
}
} /* * Add the signing time. But if it fails for some reason, * may as well not give up altogether -- just assert.
*/
rv = SEC_PKCS7AddSigningTime(cinfo);
PORT_Assert(rv == SECSuccess);
/* * Add the email profile. Again, if it fails for some reason, * may as well not give up altogether -- just assert.
*/
rv = smime_add_profile(ecert, cinfo);
PORT_Assert(rv == SECSuccess);
return cinfo;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.