/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
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_topolicy-findpolicyalgtagfromalegacyinput
*/ 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 *legacyvalue,assumeit'saSECOidTagandcontinue.Thisallowsustouse
* 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_MASKis0xffff0000).SECOidTagsstartat0and *increasemonotonically,soaslongasthereislessthan16Kof *tags,wecandistinguishbetweenvaluesintendedtobeSMIMEciphers
* 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 *symmetricciphersincludethekeylengthwiththeoid.Tohandlepolicyfor *thedifferentkeylengths,weincludefakeoidsthatletusmapthepolicybased
* on key length */ static SECOidTag
smime_get_policy_tag_from_key_length(SECOidTag algtag, unsignedlong keybits)
{ if (algtag == SEC_OID_RC2_CBC) { switch (keybits) { case40: return SEC_OID_RC2_40_CBC; case64: return SEC_OID_RC2_64_CBC; case128: 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;
}
}
/* return the number of algorithms in the list */
size_t
smime_list_length(const SMIMEList *list)
{ if ((list == NULL) || (list->tags == NULL)) { return0;
} 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)) { return0;
} 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 == NULL) { 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 *looktoseeifthesystemorapplicationhassetupcertainalgorithms *bypolicy.Iftheyhavesetupvaluesbypolicywe'llonlyallowthose *algorithms.We'llthenlooktoseeifanyalgorithmsareenabledby
* 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 *orbytheapplication,wethenwillusethetraditionaldefault
* 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 *applicationcontroloncethedefaultsaresetup,sothe *applicationcanturnoffapolicythatisalreadyon,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 *systemsupplieddefaults?Ifwedowewillonlyactually
* 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'spolicy,whichthesystemhasalreadyset.NOTE: *what'enable'meansintheS/MIMEsenseisweadvertise
* 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 (legacy_prefs == 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]. *Foreachpositionj,checkiftags[i]hasahigherpreference. *Ifyes,storetags[i]atpositionj,andmoveallfollowing *entriesonepositiontothebackofthearray.
*/ 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 *wepreferciphersinourhistoricallist, *thenwepreferciphersthatshowupfirst
* 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;
}
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);
}
/* 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;thenextonegets"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 *themandatoryalgorithmswhichareRC2-40(weakcrypto)and *3DES(strongcrypto),unlesstheuserhasanellipticcurve *key.Forellipticcurvekeys,RFC5753mandatessupport
* for AES 128 CBC. */
SECKEYPublicKey *key; unsignedint pklen_bits;
KeyType key_type;
if (key_type == ecKey) { /* While RFC 5753 mandates support for AES-128 CBC, should use *AES256ifuser'skeyprovidesmorethan128bitsof
* 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 *ratherthanaMUST,RSAandDSAkeyslongerthan3072 *bitsprovidemorethan128bitsofsecuritystrength. *So,AES256shouldbeusedtoprovidecomparable
* 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 *thatRSAandDSAsignaturekeysSHOULDNOTbelessthan *1024bits.So,castvoteforAES128ifkeylength
* 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);
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 *Wewalkthecipherlist,asitisorderedbydecreasingstrength, *wepreferthestrongercipheroveraweakerone,andwehavetolistthe
* 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. *probablyalsoneedtofigurehowwhat *actualsignatureswesupportinsecvfy *aswell.Wecurrentlydon'tlookathese *whenchoosinghashandsignature(hashis *chosenbytheapplicationandsignature
* 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 *tothes/mimelibrary,sowecanjustusethetable. *newkeaalgsshouldbeimplemented.Wedon'tusethese *becausethesenderskeyprettymuchselectswhattime
* 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);
/* 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);
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.