/* 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.
*/
/* * 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 {
SECItem capabilityID;
SECItem parameters; long cipher; /* optimization */
} NSSSMIMECapability;
/* * 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.
*/ typedefenum {
NSSSMIMEEncryptionKeyPref_IssuerSN,
NSSSMIMEEncryptionKeyPref_RKeyID,
NSSSMIMEEncryptionKeyPref_SubjectKeyID
} NSSSMIMEEncryptionKeyPrefSelector;
staticint
smime_legacy_pref(SECOidTag algtag)
{ int i;
for (i = 0; i < smime_legacy_map_count; i++) { if (smime_legacy_map[i].policytag == algtag) return i;
} return -1;
}
/* * smime_legacy_to policy - find policy algtag from a legacy input
*/ static SECOidTag
smime_legacy_to_policy(unsignedlong which)
{ int i;
for (i = 0; i < smime_legacy_map_count; i++) { if (smime_legacy_map[i].cipher == which) return smime_legacy_map[i].policytag;
} return SEC_OID_UNKNOWN;
}
/* map the old legacy values to modern oids. If the value isn't a recognized * legacy value, assume it's a SECOidTag and continue. This allows us to use
* the old query and set interfaces with modern oids. */
SECOidTag
smime_legacy_to_oid(unsignedlong which)
{ unsignedlong mask;
/* NOTE: all the legacy values and a CIPHER_FAMILYID of 0x00010000, * (CIPHER_FAMILYID_MASK is 0xffff0000). SECOidTags start at 0 and * increase monotonically, so as long as there is less than 16K of * tags, we can distinguish between values intended to be SMIME ciphers
* and values intended to be SECOidTags */
mask = which & CIPHER_FAMILYID_MASK; if (mask == CIPHER_FAMILYID_SMIME) { return smime_legacy_to_policy(which);
} return (SECOidTag)which;
}
/* SEC_OID_RC2_CBC is actually 3 ciphers with different key lengths. All modern * symmetric ciphers include the key length with the oid. To handle policy for * the different keylengths, we include fake oids that let us map the policy based
* on key length */ static SECOidTag
smime_get_policy_tag_from_key_length(SECOidTag algtag, unsignedlong keybits)
{ if (algtag == SEC_OID_RC2_CBC) { switch (keybits) { case 40: return SEC_OID_RC2_40_CBC; case 64: return SEC_OID_RC2_64_CBC; case 128: return SEC_OID_RC2_128_CBC; default: break;
} return SEC_OID_UNKNOWN;
} return algtag;
}
/* some S/MIME algs map to the same underlying KEA mechanism,
* collaps them here */ if ((neededPolicy & (NSS_USE_ALG_IN_SMIME_KX | NSS_USE_ALG_IN_SMIME_KX_LEGACY)) != 0) {
CK_MECHANISM_TYPE mechType = PK11_AlgtagToMechanism(algtag); switch (mechType) { case CKM_ECDH1_DERIVE: case CKM_ECDH1_COFACTOR_DERIVE:
algtag = SEC_OID_ECDH_KEA; break;
}
}
/* * We'll need this for the fake policy oids for RC2, but the * rest of these should be moved to pk11wrap for generic * algtag to key size values. We already need this for * sec_pkcs5v2_key_length_by oid.
*/ staticint
smime_keysize_by_cipher(SECOidTag algtag)
{ int keysize;
switch (algtag) { case SEC_OID_RC2_40_CBC:
keysize = 40; break; case SEC_OID_RC2_64_CBC:
keysize = 64; break; case SEC_OID_RC2_128_CBC: case SEC_OID_AES_128_CBC: case SEC_OID_CAMELLIA_128_CBC:
keysize = 128; break; case SEC_OID_AES_192_CBC: case SEC_OID_CAMELLIA_192_CBC:
keysize = 192; break; case SEC_OID_AES_256_CBC: case SEC_OID_CAMELLIA_256_CBC:
keysize = 256; break; default:
keysize = 0; break;
}
return keysize;
}
staticint
smime_max_keysize_by_cipher(SECOidTag algtag)
{ int keysize = smime_keysize_by_cipher(algtag);
/* return the number of algorithms in the list */
size_t
smime_list_length(const SMIMEList *list)
{ if ((list == NULL) || (list->tags == NULL)) { return 0;
} return list->array_len;
}
/* find the index of the algtag in the list. If the algtag isn't on the list,
* return the size of the list */
size_t
smime_list_index_find(const SMIMEList *list, SECOidTag algtag)
{ int i; if ((list == NULL) || (list->tags == NULL)) { return 0;
} for (i = 0; i < list->array_len; i++) { if (algtag == list->tags[i]) { return i;
}
} return list->array_len;
}
#define SMIME_CHUNK_COUNT 10 /* initialize and grow the list if necessary */ static SECStatus
smime_list_grow(SMIMEList **list)
{ /* first make sure the inital list is created */ if (*list == NULL) {
*list = PORT_ZNew(SMIMEList); if (*list == NULL) { return SECFailure;
}
} /* make sure the tag array is intialized */ if ((*list)->tags == NULL) {
(*list)->tags = PORT_ZNewArray(SECOidTag, SMIME_CHUNK_COUNT); if ((*list)->tags == NULL) { return SECFailure;
}
(*list)->space_len = SMIME_CHUNK_COUNT;
} /* grow the tag array if necessary */ if ((*list)->array_len == (*list)->space_len) {
SECOidTag *new_space;
size_t new_len = (*list)->space_len + SMIME_CHUNK_COUNT;
new_space = (SECOidTag *)PORT_Realloc((*list)->tags,
new_len * sizeof(SECOidTag)); if (new_space) { return SECFailure;
}
(*list)->tags = new_space;
(*list)->space_len = new_len;
} return SECSuccess;
}
/* add a new algtag to the list. if the algtag is already on the list,
* do nothing */ static SECStatus
smime_list_add(SMIMEList **list, SECOidTag algtag)
{
SECStatus rv;
size_t array_len = smime_list_length(*list);
size_t c_index = smime_list_index_find(*list, algtag);
if (array_len != c_index) { /* already on the list */ return SECSuccess;
}
/* go the list if necessary */
rv = smime_list_grow(list); if (rv != SECSuccess) { return rv;
}
(*list)->tags[(*list)->array_len++] = algtag; return SECSuccess;
}
if (c_index >= cipher_count) { return SEC_OID_UNKNOWN;
} /* we know this is safe because list cipher_count is non-zero (if it were
* any value of c_index will cause the above if to trigger */ return list->tags[c_index];
}
staticvoid
smime_free_list(SMIMEList **list)
{ if (*list) { if ((*list)->tags) {
PORT_Free((*list)->tags);
}
PORT_Free(*list);
}
*list = NULL;
}
/* At initialization time, we need to set up the defaults. We first * look to see if the system or application has set up certain algorithms * by policy. If they have set up values by policy we'll only allow those * algorithms. We'll then look to see if any algorithms are enabled by
* the application. */
rv = NSS_GetAlgorithmPolicyAll(NSS_USE_ALG_IN_SMIME_LEGACY,
NSS_USE_ALG_IN_SMIME_LEGACY,
&tags, &tagCount); if (tags) {
PORT_Free(tags);
tags = NULL;
} if ((rv != SECSuccess) || (tagCount == 0)) { /* No algorithms have been enabled by policy (either by the system * or by the application, we then will use the traditional default
* algorithms from the policy map */ for (i = smime_legacy_map_count - 1; i >= 0; i--) {
SECOidTag policytag = smime_legacy_map[i].policytag; /* this enables the algorithm by policy. We need this or
* the policy code will reject attempts to use it */
NSS_SetAlgorithmPolicy(policytag, NSS_USE_ALG_IN_SMIME, 0); /* We also need to enable the algorithm. This is usually unde * application control once the defaults are set up, so the * application can turn off a policy that is already on, but
* not turn on a policy that is already off */
smime_list_add(&smime_algorithm_list, policytag);
} return PR_SUCCESS;
} /* We have a system supplied policy, do we also have * system supplied defaults? If we do we will only actually
* turn on the algorithms that have been specified. */
rv = NSS_GetAlgorithmPolicyAll(NSS_USE_DEFAULT_NOT_VALID |
NSS_USE_DEFAULT_SMIME_ENABLE,
NSS_USE_DEFAULT_SMIME_ENABLE,
&tags, &tagCount); /* if none found, enable the default algorithms */ if ((rv != SECSuccess) || (tagCount == 0)) { if (tags) {
PORT_Free(tags);
tags = NULL;
} for (i = smime_legacy_map_count - 1; i >= 0; i--) {
SECOidTag policytag = smime_legacy_map[i].policytag; /* we only enable the default algorithm, we don't change * it's policy, which the system has already set. NOTE: * what 'enable' means in the S/MIME sense is we advertise
* that we can do the given algorithm in our smime capabilities. */
smime_list_add(&smime_algorithm_list, policytag);
} return PR_SUCCESS;
}
/* Sort tags by key strength here */
lengths = PORT_ZNewArray(int, tagCount); if (lengths == NULL) {
*error = PORT_GetError(); goto loser;
}
legacy_prefs = PORT_ZNewArray(int, tagCount); if (lengths == NULL) {
*error = PORT_GetError(); goto loser;
} /* Sort the tags array, highest preference at index 0 */ for (i = 0; i < tagCount; i++) { int len = smime_max_keysize_by_cipher(tags[i]); int lpref = smime_legacy_pref(tags[i]);
SECOidTag current = tags[i];
PRBool shift = PR_FALSE; int j; /* Determine best position for tags[i]. * For each position j, check if tags [i] has a higher preference. * If yes, store tags[i] at position j, and move all following * entries one position to the back of the array.
*/ for (j = 0; j < i; j++) { int tlen = lengths[j]; int tpref = legacy_prefs[j];
SECOidTag ttag = tags[j]; /* we prefer ciphers with bigger keysizes, then * we prefer ciphers in our historical list, * then we prefer ciphers that show up first
* from the oid table */ if (shift || (len > tlen) || ((len == tlen) && (lpref > tpref))) {
tags[j] = current;
lengths[j] = len;
legacy_prefs[j] = lpref;
current = ttag;
len = tlen;
lpref = tpref;
shift = PR_TRUE;
}
}
tags[i] = current;
lengths[i] = len;
legacy_prefs[i] = lpref;
}
/* put them in the enable list */ for (i = 0; i < tagCount; i++) {
smime_list_add(&smime_algorithm_list, tags[i]);
}
PORT_Free(lengths);
PORT_Free(legacy_prefs);
PORT_Free(tags); return PR_SUCCESS;
loser: if (lengths)
PORT_Free(lengths); if (legacy_prefs)
PORT_Free(legacy_prefs); if (tags)
PORT_Free(tags); return PR_FAILURE;
}
/* has NSS been initialized? */ if (!NSS_IsInitialized()) {
PORT_SetError(SEC_ERROR_NOT_INITIALIZED); return SECFailure;
} if (smime_policy_initted) { return SECSuccess;
}
nrv = PR_CallOnceWithArg(&smime_init_arg, smime_init_once, &error); if (nrv == PR_SUCCESS) {
smime_policy_initted = PR_TRUE; return SECSuccess;
}
PORT_SetError(error); return SECFailure;
}
/* * NSS_SMIME_EnableCipher - this function locally records the user's preference
*/
SECStatus
NSS_SMIMEUtil_EnableCipher(unsignedlong which, PRBool on)
{
SECOidTag algtag;
/* * this function locally records the export policy
*/
SECStatus
NSS_SMIMEUtil_AllowCipher(unsignedlong which, PRBool on)
{
SECOidTag algtag = smime_legacy_to_oid(which);
PRUint32 set = on ? NSS_USE_ALG_IN_SMIME : 0;
PRUint32 clear = on ? 0 : NSS_USE_ALG_IN_SMIME; /* make sure we are inited before setting, so
* the defaults are correct */
SECStatus rv = smime_init(); if (rv != SECSuccess) { return SECFailure;
}
PRBool
NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
{
SECOidTag algtag; /* make sure we are inited before checking policy, so
* the defaults are correct */
SECStatus rv = smime_init(); if (rv != SECSuccess) { return SECFailure;
}
PRBool
NSS_SMIMEUtil_EncryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
{
SECOidTag algtag; /* make sure we are inited before checking policy, so
* the defaults are correct */
SECStatus rv = smime_init(); if (rv != SECSuccess) { return SECFailure;
}
PRBool
NSS_SMIMEUtil_SigningAllowed(SECAlgorithmID *algid)
{
SECOidTag algtag; /* we don't adjust SIGNATURE policy based on defaults, so no need
* to call smime_init() */
if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) != SECFailure) { if (optFlags & NSS_KEY_SIZE_POLICY_SMIME_FLAG) { return PR_TRUE;
}
} return PR_FALSE;
}
PRBool
NSS_SMIMEUtil_KeyEncodingAllowed(SECAlgorithmID *algid, CERTCertificate *cert,
SECKEYPublicKey *key)
{
SECOidTag algtag; /* we don't adjust KEA policy based on defaults, so no need
* to call smime_init() */
/* if required, make sure the key lengths are enforced */ if (nss_smime_enforce_key_size()) {
SECStatus rv;
PRBool freeKey = PR_FALSE;
if (!key) { /* either the public key or the cert must be supplied. If the
* key wasn't supplied, get it from the certificate */ if (!cert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return PR_FALSE;
}
key = CERT_ExtractPublicKey(cert);
freeKey = PR_TRUE;
}
rv = SECKEY_EnforceKeySize(key->keyType,
SECKEY_PublicKeyStrengthInBits(key),
SEC_ERROR_BAD_EXPORT_ALGORITHM); if (freeKey) {
SECKEY_DestroyPublicKey(key);
} if (rv != SECSuccess) { return PR_FALSE;
}
}
algtag = SECOID_GetAlgorithmTag(algid); return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME_KX);
}
PRBool
NSS_SMIMEUtil_KeyDecodingAllowed(SECAlgorithmID *algid, SECKEYPrivateKey *key)
{
SECOidTag algtag; /* we don't adjust KEA policy based on defaults, so no need
* to call smime_init() */
/* if required, make sure the key lengths are enforced */ if (nss_smime_enforce_key_size()) {
SECStatus rv;
rv = SECKEY_EnforceKeySize(key->keyType,
SECKEY_PrivateKeyStrengthInBits(key),
SEC_ERROR_BAD_EXPORT_ALGORITHM); if (rv != SECSuccess) { return PR_FALSE;
}
}
algtag = SECOID_GetAlgorithmTag(algid); return smime_allowed_by_policy(algtag, NSS_USE_ALG_IN_SMIME_KX_LEGACY);
}
/* * NSS_SMIME_EncryptionPossible - check if any encryption is allowed * * 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
NSS_SMIMEUtil_EncryptionPossible(void)
{
SECStatus rv = smime_init();
size_t len; if (rv != SECSuccess) { return SECFailure;
}
smime_lock_algorithm_list();
len = smime_list_length(smime_algorithm_list);
smime_unlock_algorithm_list(); return len != 0 ? PR_TRUE : PR_FALSE;
}
/* we need the OIDTag here */
capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
/* RC2 used a generic oid and encoded the key length in the
* parameters */ if (capIDTag == SEC_OID_RC2_CBC) {
SECStatus rv; unsignedlong key_bits;
SECItem keyItem = { siBuffer, NULL, 0 };
/* walk all the recipient's certs */ for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
SECItem *profile;
NSSSMIMECapability **caps; int pref;
/* the first cipher that matches in the user's SMIME profile gets * "cipher_count" votes; the next one gets "cipher_count" - 1
* and so on. If every cipher matches, the last one gets 1 (one) vote */
pref = cipher_count;
if (profile != NULL && profile->data != NULL && profile->len > 0) { /* we have a profile (still DER-encoded) */
caps = NULL; /* decode it */ if (SEC_QuickDERDecodeItem(poolp, &caps,
NSSSMIMECapabilitiesTemplate, profile) == SECSuccess &&
caps != NULL) { int i; /* walk the SMIME capabilities for this recipient */ for (i = 0; caps[i] != NULL; i++) {
cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
c_index = smime_list_index_find(smime_algorithm_list, cipher); if (c_index < cipher_count) { /* found the cipher */
cipher_abilities[c_index]++;
cipher_votes[c_index] += pref;
--pref;
}
}
}
} else { /* no profile found - so we can only assume that the user can do * the mandatory algorithms which are RC2-40 (weak crypto) and * 3DES (strong crypto), unless the user has an elliptic curve * key. For elliptic curve keys, RFC 5753 mandates support
* for AES 128 CBC. */
SECKEYPublicKey *key; unsignedint pklen_bits;
KeyType key_type;
/* * if recipient's public key length is > 512, vote for a strong cipher * please not that the side effect of this is that if only one recipient * has an export-level public key, the strong cipher is disabled. * * 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]);
pklen_bits = 0;
key_type = nullKey; if (key != NULL) {
pklen_bits = SECKEY_PublicKeyStrengthInBits(key);
key_type = SECKEY_GetPublicKeyType(key);
SECKEY_DestroyPublicKey(key);
key = NULL;
}
if (key_type == ecKey) { /* While RFC 5753 mandates support for AES-128 CBC, should use * AES 256 if user's key provides more than 128 bits of
* security strength so that symmetric key is not weak link. */
/* RC2-40 is not compatible with elliptic curve keys. */ if (chosen_cipher == SEC_OID_RC2_40_CBC) {
chosen_cipher = SEC_OID_AES_128_CBC;
} if (pklen_bits > 256) {
cipher_abilities[aes256_index]++;
cipher_votes[aes256_index] += pref;
pref--;
}
cipher_abilities[aes128_index]++;
cipher_votes[aes128_index] += pref;
pref--;
cipher_abilities[strong_index]++;
cipher_votes[strong_index] += pref;
pref--;
} else { if (pklen_bits > 3072) { /* While support for AES 256 is a SHOULD+ in RFC 5751 * rather than a MUST, RSA and DSA keys longer than 3072 * bits provide more than 128 bits of security strength. * So, AES 256 should be used to provide comparable
* security. */
cipher_abilities[aes256_index]++;
cipher_votes[aes256_index] += pref;
pref--;
} if (pklen_bits > 1023) { /* RFC 5751 mandates support for AES 128, but also says * that RSA and DSA signature keys SHOULD NOT be less than * 1024 bits. So, cast vote for AES 128 if key length
* is at least 1024 bits. */
cipher_abilities[aes128_index]++;
cipher_votes[aes128_index] += pref;
pref--;
} if (pklen_bits > 512) { /* cast votes for the strong algorithm */
cipher_abilities[strong_index]++;
cipher_votes[strong_index] += pref;
pref--;
}
/* always cast (possibly less) votes for the weak algorithm */
cipher_abilities[weak_index]++;
cipher_votes[weak_index] += pref;
}
} if (profile != NULL)
SECITEM_FreeItem(profile, PR_TRUE);
}
/* find cipher that is agreeable by all recipients and that has the most votes */
max = 0; for (c_index = 0; c_index < cipher_count; c_index++) { /* if not all of the recipients can do this, forget it */ if (cipher_abilities[c_index] != rcount) continue;
cipher = smime_list_fetch_by_index(smime_algorithm_list, c_index); /* if cipher is allowed by policy, forget it */ if (!smime_allowed_by_policy(cipher, NSS_USE_ALG_IN_SMIME)) { continue;
} /* now see if this one has more votes than the last best one */ if (cipher_votes[c_index] >= max) { /* if equal number of votes, prefer the ones further down in the list */ /* with the expectation that these are higher rated ciphers */
chosen_cipher = cipher;
max = cipher_votes[c_index];
}
} /* if no common cipher was found, chosen_cipher stays at the default */
done:
smime_unlock_algorithm_list(); if (poolp != NULL)
PORT_FreeArena(poolp, PR_FALSE);
return chosen_cipher;
}
/* * NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients * * it would be great for UI purposes if there would be a way to find out which recipients * prevented a strong cipher from being used...
*/
SECStatus
NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts,
SECOidTag *bulkalgtag, int *keysize)
{
SECOidTag cipher;
/* * Create a new Capability from an oid tag
*/ static NSSSMIMECapability *
smime_create_capability(SECOidTag cipher)
{
NSSSMIMECapability *cap = NULL;
SECOidData *oiddata = NULL;
SECItem *dummy = NULL;
cap = PORT_ZNew(NSSSMIMECapability); if (cap == NULL) { return NULL;
}
cap->capabilityID.data = oiddata->oid.data;
cap->capabilityID.len = oiddata->oid.len; if (cipher == SEC_OID_RC2_CBC) {
SECItem keyItem = { siBuffer, NULL, 0 }; unsignedlong keybits = smime_get_alg_from_policy(cipher);
dummy = SEC_ASN1EncodeInteger(NULL, &keyItem, keybits); if (dummy == NULL) {
PORT_Free(cap); return NULL;
}
dummy = SEC_ASN1EncodeItem(NULL, &cap->parameters,
&keyItem, SEC_ASN1_GET(SEC_IntegerTemplate));
SECITEM_FreeItem(&keyItem, PR_FALSE); if (dummy == NULL) {
PORT_Free(cap); return NULL;
}
} else {
cap->parameters.data = NULL;
cap->parameters.len = 0;
} return cap;
} /* * 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
*/
SECStatus
NSS_SMIMEUtil_CreateSMIMECapabilities(PLArenaPool *poolp, SECItem *dest)
{
NSSSMIMECapability *cap = NULL;
NSSSMIMECapability **smime_capabilities = NULL;
SECItem *dummy = NULL; int i, capIndex; int cap_count; int cipher_count; int hash_count;
SECStatus rv = smime_init(); if (rv != SECSuccess) { return SECFailure;
} /* First get the hash count */ for (i = HASH_AlgNULL + 1;; i++) { if (HASH_GetHashOidTagByHashType(i) == SEC_OID_UNKNOWN) { break;
}
}
hash_count = i - 1;
smime_lock_algorithm_list(); /* now get the cipher count */
cipher_count = smime_list_length(smime_algorithm_list); if (cipher_count == 0) {
smime_unlock_algorithm_list();
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return SECFailure;
}
/* cipher_count + 1 is an upper bound - we might end up with less */
smime_capabilities = PORT_ZNewArray(NSSSMIMECapability *, cap_count + 1); if (smime_capabilities == NULL) {
smime_unlock_algorithm_list(); return SECFailure;
}
capIndex = 0;
/* Add all the symmetric ciphers * We walk the cipher list, as it is ordered by decreasing strength, * we prefer the stronger cipher over a weaker one, and we have to list the
* preferred algorithm first */ for (i = 0; i < cipher_count; i++) {
SECOidTag cipher = smime_list_fetch_by_index(smime_algorithm_list, i);
/* is it allowed by policy? */ if (!smime_allowed_by_policy(cipher, NSS_USE_ALG_IN_SMIME)) { continue;
}
cipher = smime_get_alg_from_policy(cipher);
cap = smime_create_capability(cipher); if (cap == NULL) break;
smime_capabilities[capIndex++] = cap;
} /* add signature algorithms = hash algs. * probably also need to figure how what * actual signatures we support in secvfy * as well. We currently don't look a these * when choosing hash and signature (hash is * chosen by the application and signature
* type is chosen by the signing cert/key) */
smime_unlock_algorithm_list(); for (i = HASH_AlgNULL + 1; i < hash_count + 1; i++) {
SECOidTag hash_alg = HASH_GetHashOidTagByHashType(i);
if (!smime_allowed_by_policy(hash_alg,
NSS_USE_ALG_IN_SMIME_SIGNATURE | NSS_USE_ALG_IN_SIGNATURE)) { continue;
}
cap = smime_create_capability(hash_alg); /* get next SMIME capability */ if (cap == NULL) break;
smime_capabilities[capIndex++] = cap;
}
/* add key encipherment algorithms . These are static * to the s/mime library, so we can just use the table. * new kea algs should be implemented. We don't use these * because the senders key pretty much selects what time
* of kea we are going to implement */ for (i = 0; i < implemented_key_encipherment_len; i++) {
SECOidTag kea_alg = implemented_key_encipherment[i];
if (!smime_allowed_by_policy(kea_alg, NSS_USE_ALG_IN_SMIME_KX)) { continue;
}
cap = smime_create_capability(kea_alg); /* get next SMIME capability */ if (cap == NULL) break;
smime_capabilities[capIndex++] = cap;
}
smime_capabilities[capIndex] = NULL; /* last one - now encode */
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++) { if (smime_capabilities[i]->parameters.data) {
PORT_Free(smime_capabilities[i]->parameters.data);
}
PORT_Free(smime_capabilities[i]);
}
PORT_Free(smime_capabilities);
/* * 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 = NULL;
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;
/* * NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs - create S/MIME encryption key preferences attr value using MS oid * * "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_CreateMSSMIMEEncKeyPrefs(PLArenaPool *poolp, SECItem *dest, CERTCertificate *cert)
{
SECItem *dummy = NULL;
PLArenaPool *tmppoolp = NULL;
CERTIssuerAndSN *isn;
if (cert == NULL) goto loser;
tmppoolp = PORT_NewArena(1024); if (tmppoolp == NULL) goto loser;
isn = CERT_GetCertIssuerAndSN(tmppoolp, cert); if (isn == NULL) goto loser;
/* * 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;
/* 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;
}
externconstchar __nss_smime_version[];
PRBool
NSSSMIME_VersionCheck(constchar *importedVersion)
{ #define NSS_VERSION_VARIABLE __nss_smime_version #include"verref.h" /* * This is the secret handshake algorithm. * * This release has a simple version compatibility * check algorithm. This release is not backward * compatible with previous major releases. It is * not compatible with future major, minor, or * patch releases.
*/ return NSS_VersionCheck(importedVersion);
}
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.