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