/* 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); }