/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "plarena.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 "key.h"

typedef struct SEC_PKCS5PBEParameterStr SEC_PKCS5PBEParameter;
struct SEC_PKCS5PBEParameterStr {
    PRArenaPool *poolp;
    SECItem     salt;           /* octet string */
    SECItem     iteration;      /* integer */
};


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

/* maps crypto algorithm from PBE algorithm.
 */
SECOidTag 
SEC_PKCS5GetCryptoAlgorithm(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_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;
	default:
	    break;
    }

    return SEC_OID_UNKNOWN;
}

/* check to see if an oid is a pbe algorithm
 */ 
PRBool 
SEC_PKCS5IsAlgorithmPBEAlg(SECAlgorithmID *algid)
{
    return (PRBool)(SEC_PKCS5GetCryptoAlgorithm(algid) != SEC_OID_UNKNOWN);
}

/* maps PBE algorithm from crypto algorithm, assumes SHA1 hashing.
 */
SECOidTag 
SEC_PKCS5GetPBEAlgorithm(SECOidTag algTag, int keyLen)
{
    switch(algTag)
    {
	case SEC_OID_DES_EDE3_CBC:
	    switch(keyLen) {
		case 168:
		case 192:
		    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:
		    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:
		    return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4;
		default:
		    break;
	    }
	    break;
	default:
	    break;
    }

    return SEC_OID_UNKNOWN;
}


/* get the key length 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;
	default:
	    break;
    }
    return -1;
}


/* the 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;
}
/* 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 keyLen 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.
 *   keyLen - only used in variable key length algorithms
 *   iv - if null, the IV will be generated based on PKCS 5 when needed.
 *   params - optional, currently unsupported additional parameters.
 * 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)
{
    PRArenaPool *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;
    }

    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.  if IV is specified, it overrides
 * the PKCS 5 generation of the IV.  
 *
 * the returned SECAlgorithmID should be destroyed using 
 * SECOID_DestroyAlgorithmID
 */
SECAlgorithmID *
SEC_PKCS5CreateAlgorithmID(SECOidTag algorithm, 
			   SECItem *salt, 
			   int iteration)
{
    PRArenaPool *poolp = NULL;
    SECAlgorithmID *algid, *ret_algid;
    SECItem der_param;
    SECStatus rv = SECFailure;
    SEC_PKCS5PBEParameter *pbe_param;

    if(iteration <= 0) {
	return NULL;
    }

    der_param.data = NULL;
    der_param.len = 0;

    /* generate the parameter */
    pbe_param = sec_pkcs5_create_pbe_parameter(algorithm, salt, iteration);
    if(!pbe_param) {
	return NULL;
    }

    poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    if(!poolp) {
	sec_pkcs5_destroy_pbe_param(pbe_param);
	return NULL;
    }

    /* generate the algorithm id */
    algid = (SECAlgorithmID *)PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
    if(algid != NULL) {
	void *dummy;
	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) {
	    rv = SECOID_SetAlgorithmID(poolp, algid, algorithm, &der_param);
	}
    }

    ret_algid = NULL;
    if(algid != NULL) {
	ret_algid = (SECAlgorithmID *)PORT_ZAlloc(sizeof(SECAlgorithmID));
	if(ret_algid != NULL) {
	    rv = SECOID_CopyAlgorithmID(NULL, ret_algid, algid);
	    if(rv != SECSuccess) {
		SECOID_DestroyAlgorithmID(ret_algid, PR_TRUE);
		ret_algid = NULL;
	    }
	}
    }
	
    if(poolp != NULL) {
	PORT_FreeArena(poolp, PR_TRUE);
	algid = NULL;
    }

    sec_pkcs5_destroy_pbe_param(pbe_param);

    return ret_algid;
}

SECStatus
pbe_PK11AlgidToParam(SECAlgorithmID *algid,SECItem *mech)
{
    CK_PBE_PARAMS *pbe_params = NULL;
    SEC_PKCS5PBEParameter p5_param;
    SECItem *salt = NULL;
    SECOidTag algorithm = SECOID_GetAlgorithmTag(algid);
    PRArenaPool *arena = NULL;
    SECStatus rv = SECFailure;
    int iv_len;
    

    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    if (arena == NULL) {
	goto loser;
    }
    iv_len = PK11_GetIVLength(PK11_AlgtagToMechanism(algorithm));
    if (iv_len < 0) {
	goto loser;
    }

    if (sec_pkcs5_is_algorithm_v2_pkcs12_algorithm(algorithm)) {
        rv = SEC_ASN1DecodeItem(arena, &p5_param,
			 SEC_V2PKCS12PBEParameterTemplate, &algid->parameters);
    } else {
        rv = SEC_ASN1DecodeItem(arena,&p5_param,SEC_PKCS5PBEParameterTemplate, 
						&algid->parameters);
    }

    if (rv != SECSuccess) {
	goto loser;
    }
        
    salt = &p5_param.salt;

    pbe_params = (CK_PBE_PARAMS *)PORT_ZAlloc(sizeof(CK_PBE_PARAMS)+
						salt->len+iv_len);
    if (pbe_params == NULL) {
	goto loser;
    }

    /* get salt */
    pbe_params->pSalt = ((CK_CHAR_PTR) pbe_params)+sizeof(CK_PBE_PARAMS);
    if (iv_len) {
	pbe_params->pInitVector = ((CK_CHAR_PTR) pbe_params)+
					sizeof(CK_PBE_PARAMS)+salt->len;
    }
    PORT_Memcpy(pbe_params->pSalt, salt->data, salt->len);
    pbe_params->ulSaltLen = (CK_ULONG) salt->len;

    /* get iteration count */
    pbe_params->ulIteration = (CK_ULONG) DER_GetInteger(&p5_param.iteration);

    /* copy into the mechanism sec item */
    mech->data = (unsigned char *)pbe_params;
    mech->len = sizeof(*pbe_params);
    if (arena) {
	PORT_FreeArena(arena,PR_TRUE);
    }
    return SECSuccess;

loser:
    if (pbe_params) {
	PORT_Free(pbe_params);
    }
    if (arena) {
	PORT_FreeArena(arena,PR_TRUE);
    }
    return SECFailure;
}

SECStatus
PBE_PK11ParamToAlgid(SECOidTag algTag, SECItem *param, PRArenaPool *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, &pbeSalt, 
					  (int)pbe_param->ulIteration);
    if(!pbeAlgID) {
	return SECFailure;
    }

    rv = SECOID_CopyAlgorithmID(arena, algId, pbeAlgID);
    SECOID_DestroyAlgorithmID(pbeAlgID, PR_TRUE);
    return rv;
}

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_NETSCAPE_PBE_MD2_HMAC_KEY_GEN;
	    break;
	case SEC_OID_MD5:
	    mechanism = CKM_NETSCAPE_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 depricated 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;
}

SECItem *
PBE_GenerateBits(PBEBitGenContext *context)
{
    return (SECItem *)context;
}

void
PBE_DestroyContext(PBEBitGenContext *context)
{
    SECITEM_FreeItem((SECItem *)context,PR_TRUE);
}

SECItem *
SEC_PKCS5GetIV(SECAlgorithmID *algid, SECItem *pwitem, PRBool faulty3DES)
{
    SECItem mechItem;
    SECOidTag algorithm = SECOID_GetAlgorithmTag(algid);
    CK_PBE_PARAMS *pbe_params;
    CK_MECHANISM_TYPE mechanism;
    SECItem *iv = NULL;
    SECStatus rv;
    int iv_len;
    PK11SlotInfo *slot;
    PK11SymKey *symKey;

    rv = pbe_PK11AlgidToParam(algid,&mechItem);
    if (rv != SECSuccess) {
	return NULL;
    }

    mechanism = PK11_AlgtagToMechanism(algorithm);
    iv_len = PK11_GetIVLength(mechanism);
    pbe_params = (CK_PBE_PARAMS_PTR)mechItem.data;

    slot = PK11_GetInternalSlot();
    symKey = PK11_RawPBEKeyGen(slot,mechanism,
					&mechItem, pwitem, faulty3DES,NULL);
    PK11_FreeSlot(slot);

    if (symKey) {
	SECItem tmp;

	tmp.data = pbe_params->pInitVector;
	tmp.len = iv_len;
	iv = SECITEM_DupItem(&tmp);
        PK11_FreeSymKey(symKey);
    }

    if (mechItem.data) {
	PORT_ZFree(mechItem.data,mechItem.len);
    }

    return iv;
}

/*
 * Subs from nss 3.x that are depricated
 */
PBEBitGenContext *
__PBE_CreateContext(SECOidTag hashAlgorithm, PBEBitGenID bitGenPurpose,
	SECItem *pwitem, SECItem *salt, unsigned int bitsNeeded,
	unsigned int iterations)
{
    PORT_Assert("__PBE_CreateContext is Depricated" == NULL);
    return NULL;
}

SECItem *
__PBE_GenerateBits(PBEBitGenContext *context)
{
    PORT_Assert("__PBE_GenerateBits is Depricated" == NULL);
    return NULL;
}

void
__PBE_DestroyContext(PBEBitGenContext *context)
{
    PORT_Assert("__PBE_DestroyContext is Depricated" == NULL);
}

SECStatus
RSA_FormatBlock(SECItem *result, unsigned modulusLen,
                int blockType, SECItem *data)
{
    PORT_Assert("RSA_FormatBlock is Depricated" == 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));
    }
}

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;
    }
    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;
}

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

SECAlgorithmID *
PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt)
{
    SECAlgorithmID *algid = NULL;
    algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, iteration);
    return algid;
}

PK11SymKey *
PK11_RawPBEKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *mech,
			 SECItem *pwitem, PRBool faulty3DES, void *wincx)
{
    /* pbe stuff */
    CK_PBE_PARAMS *pbe_params;
    PK11SymKey *symKey;

    if(faulty3DES && (type == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) {
	type = CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC;
    }
    if(mech == NULL) {
	return NULL;
    }

    pbe_params = (CK_PBE_PARAMS *)mech->data;
    if (!pbe_params) {
	return NULL;
    }
    pbe_params->pPassword = (CK_CHAR_PTR)PORT_ZAlloc(pwitem->len);
    if(pbe_params->pPassword != NULL) {
	PORT_Memcpy(pbe_params->pPassword, pwitem->data, pwitem->len);
	pbe_params->ulPasswordLen = pwitem->len;
    } else {
	SECITEM_ZfreeItem(mech, PR_TRUE);
	return NULL;
    }

    symKey = PK11_TokenKeyGenWithFlags(slot, type, mech, 0, NULL,
           CKF_SIGN|CKF_ENCRYPT|CKF_DECRYPT|CKF_UNWRAP|CKF_WRAP, 0, wincx);

    PORT_ZFree(pbe_params->pPassword, pwitem->len);
    pbe_params->pPassword = NULL;
    pbe_params->ulPasswordLen = 0;
    return symKey;
}

PK11SymKey *
PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
	       					PRBool faulty3DES, void *wincx)
{
    /* pbe stuff */
    CK_MECHANISM_TYPE type;
    SECItem *mech;
    PK11SymKey *symKey;

    mech = PK11_ParamFromAlgid(algid);
    type = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm));
    if(faulty3DES && (type == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) {
	type = CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC;
    }
    if(mech == NULL) {
	return NULL;
    }
    symKey = PK11_RawPBEKeyGen(slot, type, mech, pwitem, faulty3DES, wincx);

    SECITEM_ZfreeItem(mech, PR_TRUE);
    return symKey;
}

SECItem *
PK11_GetPBEIV(SECAlgorithmID *algid, SECItem *pwitem)
{
    /* pbe stuff */
    CK_MECHANISM_TYPE type;
    SECItem *mech;
    PK11SymKey *symKey;
    PK11SlotInfo *slot = PK11_GetInternalSlot();
    int iv_len = 0;
    CK_PBE_PARAMS_PTR pPBEparams;
    SECItem src;
    SECItem *iv;


    mech = PK11_ParamFromAlgid(algid);
    type = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm));
    if(mech == NULL) {
	return NULL;
    }
    symKey = PK11_RawPBEKeyGen(slot, type, mech, pwitem, PR_FALSE, NULL);
    PK11_FreeSlot(slot);
    if (symKey == NULL) {
	SECITEM_ZfreeItem(mech, PR_TRUE);
	return NULL;
    }
    PK11_FreeSymKey(symKey);
    pPBEparams = (CK_PBE_PARAMS_PTR)mech->data;
    iv_len = PK11_GetIVLength(type);

    src.data = (unsigned char *)pPBEparams->pInitVector;
    src.len = iv_len;
    iv = SECITEM_DupItem(&src);

    SECITEM_ZfreeItem(mech, PR_TRUE);
    return iv;
}
