summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pk11wrap/pk11pbe.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/pk11wrap/pk11pbe.c')
-rw-r--r--security/nss/lib/pk11wrap/pk11pbe.c1486
1 files changed, 1486 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11pbe.c b/security/nss/lib/pk11wrap/pk11pbe.c
new file mode 100644
index 0000000000..dfe4dee716
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11pbe.c
@@ -0,0 +1,1486 @@
+/* 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/. */
+
+#include "plarena.h"
+
+#include "blapit.h"
+#include "seccomon.h"
+#include "secitem.h"
+#include "secport.h"
+#include "hasht.h"
+#include "pkcs11t.h"
+#include "sechash.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "secmod.h"
+#include "pk11func.h"
+#include "secpkcs5.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "keyhi.h"
+
+typedef struct SEC_PKCS5PBEParameterStr SEC_PKCS5PBEParameter;
+struct SEC_PKCS5PBEParameterStr {
+ PLArenaPool *poolp;
+ SECItem salt; /* octet string */
+ SECItem iteration; /* integer */
+ SECItem keyLength; /* PKCS5v2 only */
+ SECAlgorithmID *pPrfAlgId; /* PKCS5v2 only */
+ SECAlgorithmID prfAlgId; /* PKCS5v2 only */
+};
+
+/* PKCS5 V2 has an algorithm ID for the encryption and for
+ * the key generation. This is valid for SEC_OID_PKCS5_PBES2
+ * and SEC_OID_PKCS5_PBMAC1
+ */
+struct sec_pkcs5V2ParameterStr {
+ PLArenaPool *poolp;
+ SECAlgorithmID pbeAlgId; /* real pbe algorithms */
+ SECAlgorithmID cipherAlgId; /* encryption/mac */
+};
+
+typedef struct sec_pkcs5V2ParameterStr sec_pkcs5V2Parameter;
+
+/* template for PKCS 5 PBE Parameter. This template has been expanded
+ * based upon the additions in PKCS 12. This should eventually be moved
+ * if RSA updates PKCS 5.
+ */
+const SEC_ASN1Template SEC_PKCS5PBEParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS5PBEParameter, iteration) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_V2PKCS12PBEParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SEC_PKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER, offsetof(SEC_PKCS5PBEParameter, iteration) },
+ { 0 }
+};
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+/* SECOID_PKCS5_PBKDF2 */
+const SEC_ASN1Template SEC_PKCS5V2PBEParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ /* This is really a choice, but since we only understand this
+ * choice, just inline it */
+ { SEC_ASN1_OCTET_STRING, offsetof(SEC_PKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER, offsetof(SEC_PKCS5PBEParameter, iteration) },
+ { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS5PBEParameter, keyLength) },
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS5PBEParameter, pPrfAlgId),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { 0 }
+};
+
+/* SEC_OID_PKCS5_PBES2, SEC_OID_PKCS5_PBMAC1 */
+const SEC_ASN1Template SEC_PKCS5V2ParameterTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS5PBEParameter) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(sec_pkcs5V2Parameter, pbeAlgId),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(sec_pkcs5V2Parameter, cipherAlgId),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { 0 }
+};
+
+/*
+ * maps a PBE algorithm to a crypto algorithm. for PKCS12 and PKCS5v1
+ * for PKCS5v2 it returns SEC_OID_PKCS5_PBKDF2.
+ */
+SECOidTag
+sec_pkcs5GetCryptoFromAlgTag(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+ return SEC_OID_DES_EDE3_CBC;
+ case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+ return SEC_OID_DES_CBC;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ return SEC_OID_RC2_CBC;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ return SEC_OID_RC4;
+ case SEC_OID_PKCS5_PBKDF2:
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1:
+ return SEC_OID_PKCS5_PBKDF2;
+ default:
+ break;
+ }
+
+ return SEC_OID_UNKNOWN;
+}
+
+/*
+ * get a new PKCS5 V2 Parameter from the algorithm id.
+ * if arena is passed in, use it, otherwise create a new arena.
+ */
+sec_pkcs5V2Parameter *
+sec_pkcs5_v2_get_v2_param(PLArenaPool *arena, SECAlgorithmID *algid)
+{
+ PLArenaPool *localArena = NULL;
+ sec_pkcs5V2Parameter *pbeV2_param;
+ SECStatus rv;
+
+ if (arena == NULL) {
+ localArena = arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+ }
+ pbeV2_param = PORT_ArenaZNew(arena, sec_pkcs5V2Parameter);
+ if (pbeV2_param == NULL) {
+ goto loser;
+ }
+
+ rv = SEC_ASN1DecodeItem(arena, pbeV2_param,
+ SEC_PKCS5V2ParameterTemplate, &algid->parameters);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ pbeV2_param->poolp = arena;
+ return pbeV2_param;
+loser:
+ if (localArena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+void
+sec_pkcs5_v2_destroy_v2_param(sec_pkcs5V2Parameter *param)
+{
+ if (param && param->poolp) {
+ PORT_FreeArena(param->poolp, PR_TRUE);
+ }
+}
+
+/* maps crypto algorithm from PBE algorithm.
+ */
+SECOidTag
+SEC_PKCS5GetCryptoAlgorithm(SECAlgorithmID *algid)
+{
+
+ SECOidTag pbeAlg;
+ SECOidTag cipherAlg;
+
+ if (algid == NULL)
+ return SEC_OID_UNKNOWN;
+
+ pbeAlg = SECOID_GetAlgorithmTag(algid);
+ cipherAlg = sec_pkcs5GetCryptoFromAlgTag(pbeAlg);
+ if ((cipherAlg == SEC_OID_PKCS5_PBKDF2) &&
+ (pbeAlg != SEC_OID_PKCS5_PBKDF2)) {
+ sec_pkcs5V2Parameter *pbeV2_param;
+ cipherAlg = SEC_OID_UNKNOWN;
+
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param != NULL) {
+ cipherAlg = SECOID_GetAlgorithmTag(&pbeV2_param->cipherAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ }
+ }
+
+ return cipherAlg;
+}
+
+/* check to see if an oid is a pbe algorithm
+ */
+PRBool
+SEC_PKCS5IsAlgorithmPBEAlg(SECAlgorithmID *algid)
+{
+ return (PRBool)(SEC_PKCS5GetCryptoAlgorithm(algid) != SEC_OID_UNKNOWN);
+}
+
+PRBool
+SEC_PKCS5IsAlgorithmPBEAlgTag(SECOidTag algtag)
+{
+ return (PRBool)(sec_pkcs5GetCryptoFromAlgTag(algtag) != SEC_OID_UNKNOWN);
+}
+
+/*
+ * find the most appropriate PKCS5v2 overall oid tag from a regular
+ * cipher/hash algorithm tag.
+ */
+static SECOidTag
+sec_pkcs5v2_get_pbe(SECOidTag algTag)
+{
+ /* if it's a valid hash oid... */
+ if (HASH_GetHashOidTagByHMACOidTag(algTag) != SEC_OID_UNKNOWN) {
+ /* use the MAC tag */
+ return SEC_OID_PKCS5_PBMAC1;
+ }
+ if (HASH_GetHashTypeByOidTag(algTag) != HASH_AlgNULL) {
+ /* eliminate Hash algorithms */
+ return SEC_OID_UNKNOWN;
+ }
+ if (PK11_AlgtagToMechanism(algTag) != CKM_INVALID_MECHANISM) {
+ /* it's not a hash, if it has a PKCS #11 mechanism associated
+ * with it, assume it's a cipher. (NOTE this will generate
+ * some false positives). */
+ return SEC_OID_PKCS5_PBES2;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+/*
+ * maps PBE algorithm from crypto algorithm, assumes SHA1 hashing.
+ * input keyLen in bits.
+ */
+SECOidTag
+SEC_PKCS5GetPBEAlgorithm(SECOidTag algTag, int keyLen)
+{
+ switch (algTag) {
+ case SEC_OID_DES_EDE3_CBC:
+ switch (keyLen) {
+ case 168:
+ case 192:
+ case 0:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
+ case 128:
+ case 92:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC;
+ default:
+ break;
+ }
+ break;
+ case SEC_OID_DES_CBC:
+ return SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC;
+ case SEC_OID_RC2_CBC:
+ switch (keyLen) {
+ case 40:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ case 128:
+ case 0:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC;
+ default:
+ break;
+ }
+ break;
+ case SEC_OID_RC4:
+ switch (keyLen) {
+ case 40:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4;
+ case 128:
+ case 0:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4;
+ default:
+ break;
+ }
+ break;
+ default:
+ return sec_pkcs5v2_get_pbe(algTag);
+ }
+
+ return SEC_OID_UNKNOWN;
+}
+
+/*
+ * Some oids encode the key size in the oid, while the actual PKCS
+ * PKCS #11 mechanism does not. In those cases we can't use
+ * the PKCS #11 automated key length code to select the key size.
+ */
+static int
+sec_pkcs5v2_key_length_by_oid(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_AES_128_CBC:
+ case SEC_OID_CAMELLIA_128_CBC:
+ return AES_128_KEY_LENGTH;
+ case SEC_OID_AES_192_CBC:
+ case SEC_OID_CAMELLIA_192_CBC:
+ return AES_192_KEY_LENGTH;
+ case SEC_OID_AES_256_CBC:
+ case SEC_OID_CAMELLIA_256_CBC:
+ return AES_256_KEY_LENGTH;
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* find the keylength from the algorithm id */
+static int
+sec_pkcs5v2_default_key_length(SECOidTag algorithm)
+{
+ CK_MECHANISM_TYPE cryptoMech;
+ int key_length = sec_pkcs5v2_key_length_by_oid(algorithm);
+ if (key_length != -1) {
+ return key_length;
+ }
+ cryptoMech = PK11_AlgtagToMechanism(algorithm);
+ if (cryptoMech == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return -1;
+ }
+ return PK11_GetMaxKeyLength(cryptoMech);
+}
+
+/*
+ * get the key length in bytes from a PKCS5 PBE
+ */
+static int
+sec_pkcs5v2_key_length(SECAlgorithmID *algid, SECAlgorithmID *cipherAlgId)
+{
+ SECOidTag algorithm;
+ PLArenaPool *arena = NULL;
+ SEC_PKCS5PBEParameter p5_param;
+ SECStatus rv;
+ int length = -1;
+ SECOidTag cipherAlg = SEC_OID_UNKNOWN;
+
+ algorithm = SECOID_GetAlgorithmTag(algid);
+ /* sanity check, they should all be PBKDF2 here */
+ if (algorithm != SEC_OID_PKCS5_PBKDF2) {
+ return -1;
+ }
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ PORT_Memset(&p5_param, 0, sizeof(p5_param));
+ rv = SEC_ASN1DecodeItem(arena, &p5_param,
+ SEC_PKCS5V2PBEParameterTemplate, &algid->parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (cipherAlgId)
+ cipherAlg = SECOID_GetAlgorithmTag(cipherAlgId);
+
+ if (p5_param.keyLength.data != NULL) {
+ /* if the length is given, accept that length. This
+ * will allow us to decode old NSS encrypted data
+ * where we used the MAX keysize for the algorithm,
+ * but put an incorrect header for a different keysize.
+ */
+ length = DER_GetInteger(&p5_param.keyLength);
+ } else {
+ /* if the keylength was not specified, figure it
+ * out from the oid */
+ length = sec_pkcs5v2_default_key_length(cipherAlg);
+ }
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return length;
+}
+
+/*
+ * get the key length in bytes needed for the PBE algorithm
+ */
+int
+SEC_PKCS5GetKeyLength(SECAlgorithmID *algid)
+{
+
+ SECOidTag algorithm;
+
+ if (algid == NULL)
+ return SEC_OID_UNKNOWN;
+
+ algorithm = SECOID_GetAlgorithmTag(algid);
+
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ return 24;
+ case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+ return 8;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ return 5;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ return 16;
+ case SEC_OID_PKCS5_PBKDF2:
+ return sec_pkcs5v2_key_length(algid, NULL);
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1: {
+ sec_pkcs5V2Parameter *pbeV2_param;
+ int length = -1;
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param != NULL) {
+ length = sec_pkcs5v2_key_length(&pbeV2_param->pbeAlgId,
+ &pbeV2_param->cipherAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ }
+ return length;
+ }
+
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* the PKCS12 V2 algorithms only encode the salt, there is no iteration
+ * count so we need a check for V2 algorithm parameters.
+ */
+static PRBool
+sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ return PR_TRUE;
+ default:
+ break;
+ }
+
+ return PR_FALSE;
+}
+
+static PRBool
+sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1:
+ case SEC_OID_PKCS5_PBKDF2:
+ return PR_TRUE;
+ default:
+ break;
+ }
+
+ return PR_FALSE;
+}
+
+/* destroy a pbe parameter. it assumes that the parameter was
+ * generated using the appropriate create function and therefor
+ * contains an arena pool.
+ */
+static void
+sec_pkcs5_destroy_pbe_param(SEC_PKCS5PBEParameter *pbe_param)
+{
+ if (pbe_param != NULL)
+ PORT_FreeArena(pbe_param->poolp, PR_TRUE);
+}
+
+/* creates a PBE parameter based on the PBE algorithm. the only required
+ * parameters are algorithm and interation. the return is a PBE parameter
+ * which conforms to PKCS 5 parameter unless an extended parameter is needed.
+ * this is primarily if keyLength and a variable key length algorithm are
+ * specified.
+ * salt - if null, a salt will be generated from random bytes.
+ * iteration - number of iterations to perform hashing.
+ * keyLength - only used in variable key length algorithms. if specified,
+ * should be in bytes.
+ * once a parameter is allocated, it should be destroyed calling
+ * sec_pkcs5_destroy_pbe_parameter or SEC_PKCS5DestroyPBEParameter.
+ */
+#define DEFAULT_SALT_LENGTH 16
+static SEC_PKCS5PBEParameter *
+sec_pkcs5_create_pbe_parameter(SECOidTag algorithm,
+ SECItem *salt,
+ int iteration,
+ int keyLength,
+ SECOidTag prfAlg)
+{
+ PLArenaPool *poolp = NULL;
+ SEC_PKCS5PBEParameter *pbe_param = NULL;
+ SECStatus rv = SECSuccess;
+ void *dummy = NULL;
+
+ if (iteration < 0) {
+ return NULL;
+ }
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (poolp == NULL)
+ return NULL;
+
+ pbe_param = (SEC_PKCS5PBEParameter *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS5PBEParameter));
+ if (!pbe_param) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ return NULL;
+ }
+
+ pbe_param->poolp = poolp;
+
+ rv = SECFailure;
+ if (salt && salt->data) {
+ rv = SECITEM_CopyItem(poolp, &pbe_param->salt, salt);
+ } else {
+ /* sigh, the old interface generated salt on the fly, so we have to
+ * preserve the semantics */
+ pbe_param->salt.len = DEFAULT_SALT_LENGTH;
+ pbe_param->salt.data = PORT_ArenaZAlloc(poolp, DEFAULT_SALT_LENGTH);
+ if (pbe_param->salt.data) {
+ rv = PK11_GenerateRandom(pbe_param->salt.data, DEFAULT_SALT_LENGTH);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ return NULL;
+ }
+
+ /* encode the integer */
+ dummy = SEC_ASN1EncodeInteger(poolp, &pbe_param->iteration,
+ iteration);
+ rv = (dummy) ? SECSuccess : SECFailure;
+
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ return NULL;
+ }
+
+ /*
+ * for PKCS5 v2 Add the keylength and the prf
+ */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ dummy = SEC_ASN1EncodeInteger(poolp, &pbe_param->keyLength,
+ keyLength);
+ rv = (dummy) ? SECSuccess : SECFailure;
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ return NULL;
+ }
+ rv = SECOID_SetAlgorithmID(poolp, &pbe_param->prfAlgId, prfAlg, NULL);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_FALSE);
+ return NULL;
+ }
+ pbe_param->pPrfAlgId = &pbe_param->prfAlgId;
+ }
+
+ return pbe_param;
+}
+
+/* creates a algorithm ID containing the PBE algorithm and appropriate
+ * parameters. the required parameter is the algorithm. if salt is
+ * not specified, it is generated randomly.
+ *
+ * the returned SECAlgorithmID should be destroyed using
+ * SECOID_DestroyAlgorithmID
+ */
+SECAlgorithmID *
+sec_pkcs5CreateAlgorithmID(SECOidTag algorithm,
+ SECOidTag cipherAlgorithm,
+ SECOidTag prfAlg,
+ SECOidTag *pPbeAlgorithm,
+ int keyLength,
+ SECItem *salt,
+ int iteration)
+{
+ PLArenaPool *poolp = NULL;
+ SECAlgorithmID *algid, *ret_algid = NULL;
+ SECOidTag pbeAlgorithm = algorithm;
+ SECItem der_param;
+ void *dummy;
+ SECStatus rv = SECFailure;
+ SEC_PKCS5PBEParameter *pbe_param = NULL;
+ sec_pkcs5V2Parameter pbeV2_param;
+
+ if (iteration <= 0) {
+ return NULL;
+ }
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (!poolp) {
+ goto loser;
+ }
+
+ if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm) ||
+ sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(algorithm)) {
+ /* use PKCS 5 v2 */
+ SECItem *cipherParams;
+
+ /*
+ * if we ask for pkcs5 Algorithms directly, then the
+ * application needs to supply the cipher algorithm,
+ * otherwise we are implicitly using pkcs5 v2 and the
+ * passed in algorithm is the encryption algorithm.
+ */
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(algorithm)) {
+ if (cipherAlgorithm == SEC_OID_UNKNOWN) {
+ goto loser;
+ }
+ } else {
+ cipherAlgorithm = algorithm;
+ /* force algorithm to be chosen below */
+ algorithm = SEC_OID_PKCS5_PBKDF2;
+ }
+
+ pbeAlgorithm = SEC_OID_PKCS5_PBKDF2;
+ /*
+ * 'algorithm' is the overall algorithm oid tag used to wrap the
+ * entire algorithm ID block. For PKCS5v1 and PKCS12, this
+ * algorithm OID has encoded in it both the PBE KDF function
+ * and the encryption algorithm. For PKCS 5v2, PBE KDF and
+ * encryption/macing oids are encoded as parameters in
+ * the algorithm ID block.
+ *
+ * Thus in PKCS5 v1 and PKCS12, this algorithm maps to a pkcs #11
+ * mechanism, where as in PKCS 5v2, this algorithm tag does not map
+ * directly to a PKCS #11 mechanim, instead the 2 oids in the
+ * algorithm ID block map the the actual PKCS #11 mechanism.
+ * algorithm is). We use choose this algorithm oid based on the
+ * cipherAlgorithm to determine what this should be (MAC1 or PBES2).
+ */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ /* choose mac or pbes */
+ algorithm = sec_pkcs5v2_get_pbe(cipherAlgorithm);
+ }
+
+ /* set the PKCS5v2 specific parameters */
+ if (keyLength == 0) {
+ SECOidTag hashAlg = HASH_GetHashOidTagByHMACOidTag(cipherAlgorithm);
+ if (hashAlg != SEC_OID_UNKNOWN) {
+ keyLength = HASH_ResultLenByOidTag(hashAlg);
+ } else {
+ keyLength = sec_pkcs5v2_default_key_length(cipherAlgorithm);
+ }
+ if (keyLength <= 0) {
+ goto loser;
+ }
+ }
+ /* currently SEC_OID_HMAC_SHA1 is the default */
+ if (prfAlg == SEC_OID_UNKNOWN) {
+ prfAlg = SEC_OID_HMAC_SHA1;
+ }
+
+ /* build the PKCS5v2 cipher algorithm id */
+ cipherParams = pk11_GenerateNewParamWithKeyLen(
+ PK11_AlgtagToMechanism(cipherAlgorithm), keyLength);
+ if (!cipherParams) {
+ goto loser;
+ }
+
+ PORT_Memset(&pbeV2_param, 0, sizeof(pbeV2_param));
+
+ rv = PK11_ParamToAlgid(cipherAlgorithm, cipherParams,
+ poolp, &pbeV2_param.cipherAlgId);
+ SECITEM_FreeItem(cipherParams, PR_TRUE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ /* generate the parameter */
+ pbe_param = sec_pkcs5_create_pbe_parameter(pbeAlgorithm, salt, iteration,
+ keyLength, prfAlg);
+ if (!pbe_param) {
+ goto loser;
+ }
+
+ /* generate the algorithm id */
+ algid = (SECAlgorithmID *)PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
+ if (algid == NULL) {
+ goto loser;
+ }
+
+ der_param.data = NULL;
+ der_param.len = 0;
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(algorithm)) {
+ /* first encode the PBE algorithm ID */
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, pbe_param,
+ SEC_PKCS5V2PBEParameterTemplate);
+ if (dummy == NULL) {
+ goto loser;
+ }
+ rv = SECOID_SetAlgorithmID(poolp, &pbeV2_param.pbeAlgId,
+ pbeAlgorithm, &der_param);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* now encode the Full PKCS 5 parameter */
+ der_param.data = NULL;
+ der_param.len = 0;
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, &pbeV2_param,
+ SEC_PKCS5V2ParameterTemplate);
+ } else if (!sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(algorithm)) {
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, pbe_param,
+ SEC_PKCS5PBEParameterTemplate);
+ } else {
+ dummy = SEC_ASN1EncodeItem(poolp, &der_param, pbe_param,
+ SEC_V2PKCS12PBEParameterTemplate);
+ }
+ if (dummy == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(poolp, algid, algorithm, &der_param);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ ret_algid = (SECAlgorithmID *)PORT_ZAlloc(sizeof(SECAlgorithmID));
+ if (ret_algid == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_CopyAlgorithmID(NULL, ret_algid, algid);
+ if (rv != SECSuccess) {
+ SECOID_DestroyAlgorithmID(ret_algid, PR_TRUE);
+ ret_algid = NULL;
+ } else if (pPbeAlgorithm) {
+ *pPbeAlgorithm = pbeAlgorithm;
+ }
+
+loser:
+ if (poolp != NULL) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ algid = NULL;
+ }
+
+ if (pbe_param) {
+ sec_pkcs5_destroy_pbe_param(pbe_param);
+ }
+
+ return ret_algid;
+}
+
+SECStatus
+pbe_PK11AlgidToParam(SECAlgorithmID *algid, SECItem *mech)
+{
+ SEC_PKCS5PBEParameter p5_param;
+ SECItem *salt = NULL;
+ SECOidTag algorithm = SECOID_GetAlgorithmTag(algid);
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECFailure;
+ unsigned char *paramData = NULL;
+ unsigned char *pSalt = NULL;
+ CK_ULONG iterations;
+ int paramLen = 0;
+ int iv_len;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+
+ /*
+ * decode the algid based on the pbe type
+ */
+ PORT_Memset(&p5_param, 0, sizeof(p5_param));
+ if (sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(algorithm)) {
+ iv_len = PK11_GetIVLength(PK11_AlgtagToMechanism(algorithm));
+ rv = SEC_ASN1DecodeItem(arena, &p5_param,
+ SEC_V2PKCS12PBEParameterTemplate, &algid->parameters);
+ } else if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ iv_len = 0;
+ rv = SEC_ASN1DecodeItem(arena, &p5_param,
+ SEC_PKCS5V2PBEParameterTemplate, &algid->parameters);
+ } else {
+ iv_len = PK11_GetIVLength(PK11_AlgtagToMechanism(algorithm));
+ rv = SEC_ASN1DecodeItem(arena, &p5_param, SEC_PKCS5PBEParameterTemplate,
+ &algid->parameters);
+ }
+
+ if (iv_len < 0) {
+ goto loser;
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* get salt */
+ salt = &p5_param.salt;
+ iterations = (CK_ULONG)DER_GetInteger(&p5_param.iteration);
+
+ /* allocate and fill in the PKCS #11 parameters
+ * based on the algorithm. */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ SECOidTag prfAlgTag;
+ CK_PKCS5_PBKD2_PARAMS *pbeV2_params =
+ (CK_PKCS5_PBKD2_PARAMS *)PORT_ZAlloc(
+ sizeof(CK_PKCS5_PBKD2_PARAMS) + salt->len);
+
+ if (pbeV2_params == NULL) {
+ goto loser;
+ }
+ paramData = (unsigned char *)pbeV2_params;
+ paramLen = sizeof(CK_PKCS5_PBKD2_PARAMS);
+
+ /* set the prf */
+ prfAlgTag = SEC_OID_HMAC_SHA1;
+ if (p5_param.pPrfAlgId &&
+ p5_param.pPrfAlgId->algorithm.data != 0) {
+ prfAlgTag = SECOID_GetAlgorithmTag(p5_param.pPrfAlgId);
+ }
+ switch (prfAlgTag) {
+ case SEC_OID_HMAC_SHA1:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA1;
+ break;
+ case SEC_OID_HMAC_SHA224:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA224;
+ break;
+ case SEC_OID_HMAC_SHA256:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA256;
+ break;
+ case SEC_OID_HMAC_SHA384:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA384;
+ break;
+ case SEC_OID_HMAC_SHA512:
+ pbeV2_params->prf = CKP_PKCS5_PBKD2_HMAC_SHA512;
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ /* probably should fetch these from the prfAlgid */
+ pbeV2_params->pPrfData = NULL;
+ pbeV2_params->ulPrfDataLen = 0;
+ pbeV2_params->saltSource = CKZ_SALT_SPECIFIED;
+ pSalt = ((CK_CHAR_PTR)pbeV2_params) + sizeof(CK_PKCS5_PBKD2_PARAMS);
+ if (salt->data) {
+ PORT_Memcpy(pSalt, salt->data, salt->len);
+ }
+ pbeV2_params->pSaltSourceData = pSalt;
+ pbeV2_params->ulSaltSourceDataLen = salt->len;
+ pbeV2_params->iterations = iterations;
+ } else {
+ CK_PBE_PARAMS *pbe_params = NULL;
+ pbe_params = (CK_PBE_PARAMS *)PORT_ZAlloc(sizeof(CK_PBE_PARAMS) +
+ salt->len + iv_len);
+ if (pbe_params == NULL) {
+ goto loser;
+ }
+ paramData = (unsigned char *)pbe_params;
+ paramLen = sizeof(CK_PBE_PARAMS);
+
+ pSalt = ((CK_CHAR_PTR)pbe_params) + sizeof(CK_PBE_PARAMS);
+ pbe_params->pSalt = pSalt;
+ if (salt->data) {
+ PORT_Memcpy(pSalt, salt->data, salt->len);
+ }
+ pbe_params->ulSaltLen = salt->len;
+ if (iv_len) {
+ pbe_params->pInitVector =
+ ((CK_CHAR_PTR)pbe_params) + sizeof(CK_PBE_PARAMS) + salt->len;
+ }
+ pbe_params->ulIteration = iterations;
+ }
+
+ /* copy into the mechanism sec item */
+ mech->data = paramData;
+ mech->len = paramLen;
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ return SECSuccess;
+
+loser:
+ if (paramData) {
+ PORT_Free(paramData);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ return SECFailure;
+}
+
+/*
+ * public, deprecated, not valid for pkcs5 v2
+ *
+ * use PK11_CreatePBEV2AlgorithmID or PK11_CreatePBEAlgorithmID to create
+ * PBE algorithmID's directly.
+ */
+SECStatus
+PBE_PK11ParamToAlgid(SECOidTag algTag, SECItem *param, PLArenaPool *arena,
+ SECAlgorithmID *algId)
+{
+ CK_PBE_PARAMS *pbe_param;
+ SECItem pbeSalt;
+ SECAlgorithmID *pbeAlgID = NULL;
+ SECStatus rv;
+
+ if (!param || !algId) {
+ return SECFailure;
+ }
+
+ pbe_param = (CK_PBE_PARAMS *)param->data;
+ pbeSalt.data = (unsigned char *)pbe_param->pSalt;
+ pbeSalt.len = pbe_param->ulSaltLen;
+ pbeAlgID = sec_pkcs5CreateAlgorithmID(algTag, SEC_OID_UNKNOWN,
+ SEC_OID_UNKNOWN, NULL, 0,
+ &pbeSalt, (int)pbe_param->ulIteration);
+ if (!pbeAlgID) {
+ return SECFailure;
+ }
+
+ rv = SECOID_CopyAlgorithmID(arena, algId, pbeAlgID);
+ SECOID_DestroyAlgorithmID(pbeAlgID, PR_TRUE);
+ return rv;
+}
+
+/*
+ * public, Deprecated, This function is only for binary compatibility with
+ * older applications. Does not support PKCS5v2.
+ *
+ * Applications should use PK11_PBEKeyGen() for keys and PK11_GetPBEIV() for
+ * iv values rather than generating PBE bits directly.
+ */
+PBEBitGenContext *
+PBE_CreateContext(SECOidTag hashAlgorithm, PBEBitGenID bitGenPurpose,
+ SECItem *pwitem, SECItem *salt, unsigned int bitsNeeded,
+ unsigned int iterations)
+{
+ SECItem *context = NULL;
+ SECItem mechItem;
+ CK_PBE_PARAMS pbe_params;
+ CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM;
+ PK11SlotInfo *slot;
+ PK11SymKey *symKey = NULL;
+ unsigned char ivData[8];
+
+ /* use the purpose to select the low level keygen algorithm */
+ switch (bitGenPurpose) {
+ case pbeBitGenIntegrityKey:
+ switch (hashAlgorithm) {
+ case SEC_OID_SHA1:
+ mechanism = CKM_PBA_SHA1_WITH_SHA1_HMAC;
+ break;
+ case SEC_OID_MD2:
+ mechanism = CKM_NSS_PBE_MD2_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_MD5:
+ mechanism = CKM_NSS_PBE_MD5_HMAC_KEY_GEN;
+ break;
+ default:
+ break;
+ }
+ break;
+ case pbeBitGenCipherIV:
+ if (bitsNeeded > 64) {
+ break;
+ }
+ if (hashAlgorithm != SEC_OID_SHA1) {
+ break;
+ }
+ mechanism = CKM_PBE_SHA1_DES3_EDE_CBC;
+ break;
+ case pbeBitGenCipherKey:
+ if (hashAlgorithm != SEC_OID_SHA1) {
+ break;
+ }
+ switch (bitsNeeded) {
+ case 40:
+ mechanism = CKM_PBE_SHA1_RC4_40;
+ break;
+ case 128:
+ mechanism = CKM_PBE_SHA1_RC4_128;
+ break;
+ default:
+ break;
+ }
+ case pbeBitGenIDNull:
+ break;
+ }
+
+ if (mechanism == CKM_INVALID_MECHANISM) {
+ /* we should set an error, but this is a deprecated function, and
+ * we are keeping bug for bug compatibility;)... */
+ return NULL;
+ }
+
+ pbe_params.pInitVector = ivData;
+ pbe_params.pPassword = pwitem->data;
+ pbe_params.ulPasswordLen = pwitem->len;
+ pbe_params.pSalt = salt->data;
+ pbe_params.ulSaltLen = salt->len;
+ pbe_params.ulIteration = iterations;
+ mechItem.data = (unsigned char *)&pbe_params;
+ mechItem.len = sizeof(pbe_params);
+
+ slot = PK11_GetInternalSlot();
+ symKey = PK11_RawPBEKeyGen(slot, mechanism,
+ &mechItem, pwitem, PR_FALSE, NULL);
+ PK11_FreeSlot(slot);
+ if (symKey != NULL) {
+ if (bitGenPurpose == pbeBitGenCipherIV) {
+ /* NOTE: this assumes that bitsNeeded is a multiple of 8! */
+ SECItem ivItem;
+
+ ivItem.data = ivData;
+ ivItem.len = bitsNeeded / 8;
+ context = SECITEM_DupItem(&ivItem);
+ } else {
+ SECItem *keyData;
+ PK11_ExtractKeyValue(symKey);
+ keyData = PK11_GetKeyData(symKey);
+
+ /* assert bitsNeeded with length? */
+ if (keyData) {
+ context = SECITEM_DupItem(keyData);
+ }
+ }
+ PK11_FreeSymKey(symKey);
+ }
+
+ return (PBEBitGenContext *)context;
+}
+
+/*
+ * public, Deprecated, This function is only for binary compatibility with
+ * older applications. Does not support PKCS5v2.
+ *
+ * Applications should use PK11_PBEKeyGen() for keys and PK11_GetIV() for
+ * iv values rather than generating PBE bits directly.
+ */
+SECItem *
+PBE_GenerateBits(PBEBitGenContext *context)
+{
+ return (SECItem *)context;
+}
+
+/*
+ * public, Deprecated, This function is only for binary compatibility with
+ * older applications. Does not support PKCS5v2.
+ *
+ * Applications should use PK11_PBEKeyGen() for keys and PK11_GetPBEIV() for
+ * iv values rather than generating PBE bits directly.
+ */
+void
+PBE_DestroyContext(PBEBitGenContext *context)
+{
+ SECITEM_FreeItem((SECItem *)context, PR_TRUE);
+}
+
+/*
+ * public, deprecated. Replaced with PK11_GetPBEIV().
+ */
+SECItem *
+SEC_PKCS5GetIV(SECAlgorithmID *algid, SECItem *pwitem, PRBool faulty3DES)
+{
+ /* pbe stuff */
+ CK_MECHANISM_TYPE type;
+ SECItem *param = NULL;
+ SECItem *iv = NULL;
+ SECItem src;
+ int iv_len = 0;
+ PK11SymKey *symKey;
+ PK11SlotInfo *slot;
+ CK_PBE_PARAMS_PTR pPBEparams;
+ SECOidTag pbeAlg;
+
+ pbeAlg = SECOID_GetAlgorithmTag(algid);
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(pbeAlg)) {
+ unsigned char *ivData;
+ sec_pkcs5V2Parameter *pbeV2_param = NULL;
+
+ /* can only return the IV if the crypto Algorithm exists */
+ if (pbeAlg == SEC_OID_PKCS5_PBKDF2) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param == NULL) {
+ goto loser;
+ }
+ /* extract the IV from the cipher algid portion of our pkcs 5 v2
+ * algorithm id */
+ type = PK11_AlgtagToMechanism(
+ SECOID_GetAlgorithmTag(&pbeV2_param->cipherAlgId));
+ param = PK11_ParamFromAlgid(&pbeV2_param->cipherAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ if (!param) {
+ goto loser;
+ }
+ /* NOTE: NULL is a permissible return here */
+ ivData = PK11_IVFromParam(type, param, &iv_len);
+ src.data = ivData;
+ src.len = iv_len;
+ goto done;
+ }
+
+ type = PK11_AlgtagToMechanism(pbeAlg);
+ param = PK11_ParamFromAlgid(algid);
+ if (param == NULL) {
+ goto done;
+ }
+ slot = PK11_GetInternalSlot();
+ symKey = PK11_RawPBEKeyGen(slot, type, param, pwitem, faulty3DES, NULL);
+ PK11_FreeSlot(slot);
+ if (symKey == NULL) {
+ goto loser;
+ }
+ PK11_FreeSymKey(symKey);
+ pPBEparams = (CK_PBE_PARAMS_PTR)param->data;
+ iv_len = PK11_GetIVLength(type);
+
+ src.data = (unsigned char *)pPBEparams->pInitVector;
+ src.len = iv_len;
+
+done:
+ iv = SECITEM_DupItem(&src);
+
+loser:
+ if (param) {
+ SECITEM_ZfreeItem(param, PR_TRUE);
+ }
+ return iv;
+}
+
+/*
+ * Subs from nss 3.x that are deprecated
+ */
+PBEBitGenContext *
+__PBE_CreateContext(SECOidTag hashAlgorithm, PBEBitGenID bitGenPurpose,
+ SECItem *pwitem, SECItem *salt, unsigned int bitsNeeded,
+ unsigned int iterations)
+{
+ PORT_Assert("__PBE_CreateContext is Deprecated" == NULL);
+ return NULL;
+}
+
+SECItem *
+__PBE_GenerateBits(PBEBitGenContext *context)
+{
+ PORT_Assert("__PBE_GenerateBits is Deprecated" == NULL);
+ return NULL;
+}
+
+void
+__PBE_DestroyContext(PBEBitGenContext *context)
+{
+ PORT_Assert("__PBE_DestroyContext is Deprecated" == NULL);
+}
+
+SECStatus
+RSA_FormatBlock(SECItem *result, unsigned modulusLen,
+ int blockType, SECItem *data)
+{
+ PORT_Assert("RSA_FormatBlock is Deprecated" == NULL);
+ return SECFailure;
+}
+
+/****************************************************************************
+ *
+ * Now Do The PBE Functions Here...
+ *
+ ****************************************************************************/
+
+static void
+pk11_destroy_ck_pbe_params(CK_PBE_PARAMS *pbe_params)
+{
+ if (pbe_params) {
+ if (pbe_params->pPassword)
+ PORT_ZFree(pbe_params->pPassword, pbe_params->ulPasswordLen);
+ if (pbe_params->pSalt)
+ PORT_ZFree(pbe_params->pSalt, pbe_params->ulSaltLen);
+ PORT_ZFree(pbe_params, sizeof(CK_PBE_PARAMS));
+ }
+}
+
+/*
+ * public, deprecated. use PK11_CreatePBEAlgorithmID or
+ * PK11_CreatePBEV2AlgorithmID instead. If you needthe pkcs #11 parameters,
+ * use PK11_ParamFromAlgid from the algorithm id you created using
+ * PK11_CreatePBEAlgorithmID or PK11_CreatePBEV2AlgorithmID.
+ */
+SECItem *
+PK11_CreatePBEParams(SECItem *salt, SECItem *pwd, unsigned int iterations)
+{
+ CK_PBE_PARAMS *pbe_params = NULL;
+ SECItem *paramRV = NULL;
+
+ paramRV = SECITEM_AllocItem(NULL, NULL, sizeof(CK_PBE_PARAMS));
+ if (!paramRV) {
+ goto loser;
+ }
+ /* init paramRV->data with zeros. SECITEM_AllocItem does not do it */
+ PORT_Memset(paramRV->data, 0, sizeof(CK_PBE_PARAMS));
+
+ pbe_params = (CK_PBE_PARAMS *)paramRV->data;
+ pbe_params->pPassword = (CK_CHAR_PTR)PORT_ZAlloc(pwd->len);
+ if (!pbe_params->pPassword) {
+ goto loser;
+ }
+ if (pwd->data) {
+ PORT_Memcpy(pbe_params->pPassword, pwd->data, pwd->len);
+ }
+ pbe_params->ulPasswordLen = pwd->len;
+
+ pbe_params->pSalt = (CK_CHAR_PTR)PORT_ZAlloc(salt->len);
+ if (!pbe_params->pSalt) {
+ goto loser;
+ }
+ PORT_Memcpy(pbe_params->pSalt, salt->data, salt->len);
+ pbe_params->ulSaltLen = salt->len;
+
+ pbe_params->ulIteration = (CK_ULONG)iterations;
+ return paramRV;
+
+loser:
+ if (pbe_params)
+ pk11_destroy_ck_pbe_params(pbe_params);
+ if (paramRV)
+ PORT_ZFree(paramRV, sizeof(SECItem));
+ return NULL;
+}
+
+/*
+ * public, deprecated.
+ */
+void
+PK11_DestroyPBEParams(SECItem *pItem)
+{
+ if (pItem) {
+ CK_PBE_PARAMS *params = (CK_PBE_PARAMS *)(pItem->data);
+ if (params)
+ pk11_destroy_ck_pbe_params(params);
+ PORT_ZFree(pItem, sizeof(SECItem));
+ }
+}
+
+/*
+ * public, Partially supports PKCS5 V2 (some parameters are not controllable
+ * through this interface). Use PK11_CreatePBEV2AlgorithmID() if you need
+ * finer control these.
+ */
+SECAlgorithmID *
+PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt)
+{
+ SECAlgorithmID *algid = NULL;
+ algid = sec_pkcs5CreateAlgorithmID(algorithm,
+ SEC_OID_UNKNOWN, SEC_OID_UNKNOWN, NULL,
+ 0, salt, iteration);
+ return algid;
+}
+
+/*
+ * public, fully support pkcs5v2.
+ */
+SECAlgorithmID *
+PK11_CreatePBEV2AlgorithmID(SECOidTag pbeAlgTag, SECOidTag cipherAlgTag,
+ SECOidTag prfAlgTag, int keyLength, int iteration,
+ SECItem *salt)
+{
+ SECAlgorithmID *algid = NULL;
+ algid = sec_pkcs5CreateAlgorithmID(pbeAlgTag, cipherAlgTag, prfAlgTag,
+ NULL, keyLength, salt, iteration);
+ return algid;
+}
+
+/*
+ * private.
+ */
+PK11SymKey *
+pk11_RawPBEKeyGenWithKeyType(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *params, CK_KEY_TYPE keyType, int keyLen,
+ SECItem *pwitem, void *wincx)
+{
+ CK_ULONG pwLen;
+ /* do some sanity checks */
+ if ((params == NULL) || (params->data == NULL)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (type == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ /* set the password pointer in the parameters... */
+ if (type == CKM_PKCS5_PBKD2) {
+ CK_PKCS5_PBKD2_PARAMS *pbev2_params;
+ if (params->len < sizeof(CK_PKCS5_PBKD2_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ pbev2_params = (CK_PKCS5_PBKD2_PARAMS *)params->data;
+ pbev2_params->pPassword = pwitem->data;
+ pwLen = pwitem->len;
+ pbev2_params->ulPasswordLen = &pwLen;
+ } else {
+ CK_PBE_PARAMS *pbe_params;
+ if (params->len < sizeof(CK_PBE_PARAMS)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ pbe_params = (CK_PBE_PARAMS *)params->data;
+ pbe_params->pPassword = pwitem->data;
+ pbe_params->ulPasswordLen = pwitem->len;
+ }
+
+ /* generate the key (and sometimes the IV as a side effect...) */
+ return pk11_TokenKeyGenWithFlagsAndKeyType(slot, type, params, keyType,
+ keyLen, NULL,
+ CKF_SIGN | CKF_ENCRYPT | CKF_DECRYPT | CKF_UNWRAP | CKF_WRAP,
+ 0, wincx);
+}
+
+/*
+ * public, deprecated. use PK11_PBEKeyGen instead.
+ */
+PK11SymKey *
+PK11_RawPBEKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *mech,
+ SECItem *pwitem, PRBool faulty3DES, void *wincx)
+{
+ if (faulty3DES && (type == CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC)) {
+ type = CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC;
+ }
+ return pk11_RawPBEKeyGenWithKeyType(slot, type, mech, -1, 0, pwitem, wincx);
+}
+
+/*
+ * pubic, supports pkcs5 v2.
+ *
+ * Create symkey from a PBE key. The algid can be created with
+ * PK11_CreatePBEV2AlgorithmID and PK11_CreatePBEAlgorithmID, or by
+ * extraction of der data.
+ */
+PK11SymKey *
+PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx)
+{
+ CK_MECHANISM_TYPE type;
+ SECItem *param = NULL;
+ PK11SymKey *symKey = NULL;
+ SECOidTag pbeAlg;
+ CK_KEY_TYPE keyType = -1;
+ int keyLen = 0;
+
+ pbeAlg = SECOID_GetAlgorithmTag(algid);
+ /* if we're using PKCS5v2, extract the additional information we need
+ * (key length, key type, and pbeAlg). */
+ if (sec_pkcs5_is_algorithm_v2_pkcs5_algorithm(pbeAlg)) {
+ CK_MECHANISM_TYPE cipherMech;
+ sec_pkcs5V2Parameter *pbeV2_param;
+
+ pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
+ if (pbeV2_param == NULL) {
+ return NULL;
+ }
+ cipherMech = PK11_AlgtagToMechanism(
+ SECOID_GetAlgorithmTag(&pbeV2_param->cipherAlgId));
+ pbeAlg = SECOID_GetAlgorithmTag(&pbeV2_param->pbeAlgId);
+ param = PK11_ParamFromAlgid(&pbeV2_param->pbeAlgId);
+ sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
+ keyLen = SEC_PKCS5GetKeyLength(algid);
+ if (keyLen == -1) {
+ keyLen = 0;
+ }
+ keyType = PK11_GetKeyType(cipherMech, keyLen);
+ } else {
+ param = PK11_ParamFromAlgid(algid);
+ }
+
+ if (param == NULL) {
+ goto loser;
+ }
+
+ type = PK11_AlgtagToMechanism(pbeAlg);
+ if (type == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ if (faulty3DES && (type == CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC)) {
+ type = CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC;
+ }
+ symKey = pk11_RawPBEKeyGenWithKeyType(slot, type, param, keyType, keyLen,
+ pwitem, wincx);
+
+loser:
+ if (param) {
+ SECITEM_ZfreeItem(param, PR_TRUE);
+ }
+ return symKey;
+}
+
+/*
+ * public, supports pkcs5v2
+ */
+SECItem *
+PK11_GetPBEIV(SECAlgorithmID *algid, SECItem *pwitem)
+{
+ return SEC_PKCS5GetIV(algid, pwitem, PR_FALSE);
+}
+
+CK_MECHANISM_TYPE
+pk11_GetPBECryptoMechanism(SECAlgorithmID *algid, SECItem **param,
+ SECItem *pbe_pwd, PRBool faulty3DES)
+{
+ int keyLen = 0;
+ SECOidTag algTag = SEC_PKCS5GetCryptoAlgorithm(algid);
+ CK_MECHANISM_TYPE mech = PK11_AlgtagToMechanism(algTag);
+ CK_MECHANISM_TYPE returnedMechanism = CKM_INVALID_MECHANISM;
+ SECItem *iv = NULL;
+
+ if (mech == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ if (PK11_GetIVLength(mech)) {
+ iv = SEC_PKCS5GetIV(algid, pbe_pwd, faulty3DES);
+ if (iv == NULL) {
+ goto loser;
+ }
+ }
+
+ keyLen = SEC_PKCS5GetKeyLength(algid);
+
+ *param = pk11_ParamFromIVWithLen(mech, iv, keyLen);
+ if (*param == NULL) {
+ goto loser;
+ }
+ returnedMechanism = mech;
+
+loser:
+ if (iv) {
+ SECITEM_FreeItem(iv, PR_TRUE);
+ }
+ return returnedMechanism;
+}
+
+/*
+ * Public, supports pkcs5 v2
+ *
+ * Get the crypto mechanism directly from the pbe algorithmid.
+ *
+ * It's important to go directly from the algorithm id so that we can
+ * handle both the PKCS #5 v1, PKCS #12, and PKCS #5 v2 cases.
+ *
+ * This function returns both the mechanism and the parameter for the mechanism.
+ * The caller is responsible for freeing the parameter.
+ */
+CK_MECHANISM_TYPE
+PK11_GetPBECryptoMechanism(SECAlgorithmID *algid, SECItem **param,
+ SECItem *pbe_pwd)
+{
+ return pk11_GetPBECryptoMechanism(algid, param, pbe_pwd, PR_FALSE);
+}