diff options
Diffstat (limited to 'security/nss/lib/pk11wrap/pk11pbe.c')
-rw-r--r-- | security/nss/lib/pk11wrap/pk11pbe.c | 1486 |
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); +} |