Milán Major

netscape-revival

1 branch
Code

security/lib/pkcs11/pk11skey.c

/*
 * This file implements the Symkey wrapper and the PKCS context
 * Interfaces.
 */
#include "seccomon.h"
#include "secmod.h"
#include "prlock.h"
#include "secmodi.h"
#include "pkcs11.h"
#include "pk11func.h"
#include "secitem.h"
#include "key.h"
#include "rsa.h"
#include "secpkcs5.h"
#include "secasn1.h"
#include "sechash.h"

extern int SEC_ERROR_INVALID_KEY;
extern int SEC_ERROR_BAD_KEY;
extern int SEC_ERROR_NO_MODULE;
extern int SEC_ERROR_NO_MEMORY;
extern int SEC_ERROR_LIBRARY_FAILURE;

#define PAIRWISE_SECITEM_TYPE			siBuffer
#define PAIRWISE_DIGEST_LENGTH			SHA1_LENGTH /* 160-bits */
#define PAIRWISE_MESSAGE_LENGTH			20          /* 160-bits */

/*
 * strip leading zero's from key material
 */
static void
pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib) {
    char *ptr = (char *)attrib->pValue;
    unsigned long len = attrib->ulValueLen;

    while (len && (*ptr == 0)) {
	len--;
	ptr++;
    }
    attrib->pValue = ptr;
    attrib->ulValueLen = len;
}

/*
 * get a new session on a slot. If we run out of session, use the slot's
 * 'exclusive' session. In this case owner becomes false.
 */
static CK_SESSION_HANDLE
pk11_GetNewSession(PK11SlotInfo *slot,PRBool *owner)
{
    CK_SESSION_HANDLE session;
    *owner =  PR_TRUE;
    if ( PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION, 
			slot,pk11_notify,&session) != CKR_OK) {
	*owner = PR_FALSE;
	session = slot->session;
    }

    return session;
}

SECStatus
PK11_CreateNewObject(PK11SlotInfo *slot,CK_ATTRIBUTE *template, int count,
						CK_OBJECT_HANDLE *objectID)
{
	CK_SESSION_HANDLE rwsession;
	CK_RV crv;
	SECStatus rv = SECSuccess;

	rwsession = PK11_GetRWSession(slot);
	crv = PK11_GETTAB(slot)->C_CreateObject(rwsession, template,
							count,objectID);
	if(crv != CKR_OK) {
	    PORT_SetError( PK11_MapError(crv) );
	    rv = SECFailure;
	}
	PK11_RestoreROSession(slot, rwsession);
	return rv;
}

/*
 * create a symetric key:
 *      Slot is the slot to create the key in.
 *      type is the mechainism type 
 */
PK11SymKey *
PK11_CreateSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, void *wincx)
{

    PK11SymKey *symKey = (PK11SymKey *)PORT_Alloc(sizeof(PK11SymKey));
    if (symKey == NULL) {
	return NULL;
    }
#ifdef PKCS11_USE_THREADS
    symKey->refLock = PR_NewLock(0);
    if (symKey->refLock == NULL) {
	PORT_Free(symKey);
	return NULL;
    }
#else
    symKey->refLock = NULL;
#endif
    symKey->type = type;
    symKey->data.data = NULL;
    symKey->data.len = 0;
    symKey->objectID = CK_INVALID_KEY;
    symKey->slot = slot;
    symKey->cx = wincx;
    symKey->size = 0;
    symKey->refCount = 1;
    PK11_ReferenceSlot(slot);
    return symKey;
}

/*
 * destroy a symetric key
 */
void
PK11_FreeSymKey(PK11SymKey *symKey)
{
    PRBool destroy = PR_FALSE;

    PK11_USE_THREADS(PR_Lock(symKey->refLock);)
    if (symKey->refCount-- == 1) {
	destroy= PR_TRUE;
    }
    PK11_USE_THREADS(PR_Unlock(symKey->refLock);)
    if (destroy) {
	if (symKey->objectID != CK_INVALID_KEY) {
	    (void) PK11_GETTAB(symKey->slot)->
		C_DestroyObject(symKey->slot->session, symKey->objectID);
	}
	if (symKey->data.data) {
	    PORT_Memset(symKey->data.data, 0, symKey->data.len);
	    PORT_Free(symKey->data.data);
	}
	PK11_USE_THREADS(PR_DestroyLock(symKey->refLock);)
	PK11_FreeSlot(symKey->slot);
	PORT_Free(symKey);
    }
}

PK11SymKey *
PK11_ReferenceSymKey(PK11SymKey *symKey)
{
    PK11_USE_THREADS(PR_Lock(symKey->refLock);)
    symKey->refCount++;
    PK11_USE_THREADS(PR_Unlock(symKey->refLock);)
    return symKey;
}

/*
 * turn key bits into an appropriate key object
 */
PK11SymKey *
PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                     CK_ATTRIBUTE_TYPE operation, SECItem *key,void *wincx)
{
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_BBOOL ckfalse = FALSE; /* sigh */
    CK_ATTRIBUTE keyTemplate[] = {
	{ CKA_CLASS, NULL, 0},
	{ CKA_KEY_TYPE,NULL, 0},
	{ CKA_SENSITIVE,NULL, 0},
	{ 0, NULL, 0},
	{ CKA_VALUE,NULL, 0},
    };
    int keyTemplateCount = sizeof(keyTemplate)/sizeof(keyTemplate[0]);
    PK11SymKey *symKey;
    CK_ATTRIBUTE *attrs = keyTemplate;
    CK_RV crv;

    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++;
    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++;
    PK11_SETATTRS(attrs, CKA_SENSITIVE, &ckfalse, sizeof(CK_BBOOL) ); attrs++;
    PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++;
    PK11_SETATTRS(attrs, CKA_VALUE, key->data, key->len);

    symKey = PK11_CreateSymKey(slot,type,wincx);
    if (symKey == NULL) {
	return NULL;
    }

    symKey->size = key->len;

    if (SECITEM_CopyItem(NULL,&symKey->data,key) != SECSuccess) {
	PK11_FreeSymKey(symKey);
	return NULL;
    }

    keyType = PK11_GetKeyType(type,key->len);

    /* import the keys */
    crv = PK11_GETTAB(slot)->C_CreateObject(slot->session, keyTemplate,
			keyTemplateCount,&symKey->objectID) ;
    if ( crv != CKR_OK) {
	PK11_FreeSymKey(symKey);
	PORT_SetError( PK11_MapError(crv));
	return NULL;
    }

    return symKey;
}

/*
 * import a public key into the desired slot
 */
CK_OBJECT_HANDLE
pk11_ImportTokenPublicKey(PK11SlotInfo *slot, 
				SECKEYPublicKey *pubKey, PRBool isToken)
{
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_BBOOL ckfalse = FALSE; /* sigh */
    CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_OBJECT_HANDLE objectID;
    CK_ATTRIBUTE *template = NULL;
    CK_ATTRIBUTE *signedattr = NULL;
    int signedcount = 0;
    int templateCount = 0;
    CK_RV crv;

    /* if we already have an object in the desired slot, use it */
    if (!isToken && pubKey->pkcs11Slot == slot) {
	return pubKey->pkcs11ID;
    }

    /* free the existing key */
    if (pubKey->pkcs11Slot != NULL) {
	PK11SlotInfo *oSlot = pubKey->pkcs11Slot;
	(void) PK11_GETTAB(oSlot)->C_DestroyObject(oSlot->session,
							pubKey->pkcs11ID);
	PK11_FreeSlot(oSlot);
	pubKey->pkcs11Slot = NULL;
    }

    /* now import the key */
    {
	CK_ATTRIBUTE rsaTemplate[] = {
	    { CKA_CLASS, NULL, 0 },
	    { CKA_KEY_TYPE, NULL, 0 },
	    { CKA_TOKEN,  NULL, 0 },
	    { CKA_WRAP,  NULL, 0 },
	    { CKA_ENCRYPT, NULL, 0 },
	    { CKA_VERIFY, NULL, 0 },
	    { CKA_MODULUS, NULL, 0 },
	    { CKA_PUBLIC_EXPONENT, NULL, 0 },
	};
        CK_ATTRIBUTE *attrs;

	CK_ATTRIBUTE dsaTemplate[] = {
	    { CKA_CLASS, NULL, 0},
	    { CKA_KEY_TYPE, NULL, 0},
	    { CKA_TOKEN,  NULL, 0 },
	    { CKA_VERIFY, NULL, 0},
	    { CKA_PRIME, NULL, 0},
	    { CKA_SUBPRIME, NULL, 0},
	    { CKA_BASE, NULL, 0},
	    { CKA_VALUE, NULL, 0},
	};
#ifdef notdef
	CK_ATTRIBUTE dhTemplate[] = {
	    { CKA_CLASS, &keyClass, sizeof(keyClass) },
	    { CKA_KEY_TYPE, &keyType, sizeof(keyType) },
	    { CKA_DERIVE, &cktrue, sizeof(CK_BBOOL) },
	    { CKA_PRIME,    pubKey->u.dh.p.data,pubKey->u.dh.p.len},
	    { CKA_BASE,     pubKey->u.dh.g.data,pubKey->u.dh.g.len},
	    { CKA_VALUE,    pubKey->u.dh.key.data, pubKey->u.dh.key.len}
	};
#endif

        switch (pubKey->keyType) {
        case rsaKey:
	    attrs = template = rsaTemplate;
	    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, 
						sizeof(keyClass) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, 
						sizeof(keyType) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_TOKEN, isToken ? &cktrue : &ckfalse,
						 sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_WRAP, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue, 
						sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); attrs++;
 	    signedattr = attrs;
 	    signedcount = 2;
	    PK11_SETATTRS(attrs, CKA_MODULUS, pubKey->u.rsa.modulus.data,
					 pubKey->u.rsa.modulus.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, 
	     	pubKey->u.rsa.publicExponent.data,
					 pubKey->u.rsa.publicExponent.len);
	    templateCount = sizeof(rsaTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_RSA;
	    break;
        case dsaKey:
	    attrs = template = dsaTemplate;
	    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, 
						sizeof(keyClass)); attrs++;
	    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, 
						sizeof(keyType)); attrs++;
	    PK11_SETATTRS(attrs, CKA_TOKEN, isToken ? &cktrue : &ckfalse,
						 sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); attrs++;
 	    signedattr = attrs;
 	    signedcount = 4;
	    PK11_SETATTRS(attrs, CKA_PRIME,    pubKey->u.dsa.params.prime.data,
				pubKey->u.dsa.params.prime.len); attrs++;
	    PK11_SETATTRS(attrs,CKA_SUBPRIME,pubKey->u.dsa.params.subPrime.data,
				pubKey->u.dsa.params.subPrime.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_BASE,  pubKey->u.dsa.params.base.data,
					pubKey->u.dsa.params.base.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_VALUE,    pubKey->u.dsa.publicValue.data, 
					pubKey->u.dsa.publicValue.len);
	    templateCount = sizeof(dsaTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_DSA;
	    break;
#ifdef notdef
        case dhKey:
	    template = dhTemplate;
	    templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_DH;
 	    signedattr = attrs;
 	    signedcount = 4;
	    break;
#endif
	/* what about fortezza??? */
	default:
	    PORT_SetError( SEC_ERROR_BAD_KEY );
	    return CK_INVALID_KEY;
	}

	/* not all PKCS #11 implementations like unsigned values */
	/* Hack until internal PKCS #11 can handle unsigned input */
	if (isToken) {
	    CK_ATTRIBUTE *ap;
	    for (ap=signedattr; signedcount; ap++, signedcount--) {
		pk11_SignedToUnsigned(ap);
	    }
	} 
	crv = PK11_GETTAB(slot)->C_CreateObject(slot->session, template,
						templateCount,&objectID);
	if ( crv != CKR_OK) {
	    PORT_SetError (PK11_MapError(crv));
	    return CK_INVALID_KEY;
	}
    }

    pubKey->pkcs11ID = objectID;
    pubKey->pkcs11Slot = PK11_ReferenceSlot(slot);

    return objectID;
}

CK_OBJECT_HANDLE
PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey)
{
    return pk11_ImportTokenPublicKey(slot, pubKey, PR_FALSE);
}

/*
 * extract a symetric key value. NOTE: if the key is sensitive, we will
 * not be able to do this operation. This function is used to move
 * keys from one token to another */
SECStatus
PK11_ExtractKeyValue(PK11SymKey *symKey)
{
    int len;
    CK_RV crv;
    CK_ATTRIBUTE keyTemplate[] = {
	{ CKA_VALUE, NULL, 0 },
    };

    if (symKey->data.data != NULL) return SECSuccess;

    if (symKey->slot == NULL) {
	PORT_SetError( SEC_ERROR_INVALID_KEY );
	return SECFailure;
    }

    crv = PK11_GETTAB(symKey->slot)->C_GetAttributeValue(symKey->slot->session,
		symKey->objectID,keyTemplate,1) ;
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }

    symKey->data.data = PORT_Alloc(keyTemplate[0].ulValueLen);
    if (symKey->data.data == NULL) return SECFailure;

    keyTemplate[0].pValue = symKey->data.data;
    len = keyTemplate[0].ulValueLen;
    if (symKey->size == 0) symKey->size = len;
    crv = PK11_GETTAB(symKey->slot)->C_GetAttributeValue(symKey->slot->session,
		symKey->objectID,keyTemplate,1) ;
    if (crv != CKR_OK) {
	/* the data is not valid, make sure no one tries to use it*/
	PORT_Free(symKey->data.data);
	symKey->data.data = NULL;
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }

    /* Make sure the module is consistant */
    PORT_Assert(len >= (int)keyTemplate[0].ulValueLen);
    symKey->data.len = keyTemplate[0].ulValueLen;
    return SECSuccess;
}

SECItem *
PK11_GetKeyData(PK11SymKey *symKey)
{
    return &symKey->data;
}

/*
 * extract a public key from a slot and id
 */
SECKEYPublicKey *
PK11_ExtractPublicKey(PK11SlotInfo *slot,KeyType keyType,CK_OBJECT_HANDLE id)
{
    CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
    CK_KEY_TYPE pk11KeyType = CKK_GENERIC_SECRET;
    PRArenaPool *arena;
    SECKEYPublicKey *pubKey;
    int templateCount = 0;
    CK_RV crv;

    /* if we didn't know the key type, get it */
    if (keyType== nullKey) {
	CK_ATTRIBUTE initTemplate[1];
	int initCount = sizeof(initTemplate)/sizeof(initTemplate[0]);
	PK11_SETATTRS(initTemplate, CKA_KEY_TYPE, &pk11KeyType, 
							sizeof(pk11KeyType));

	crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,
						id,initTemplate,initCount);
	if (crv != CKR_OK) {
	    PORT_SetError( PK11_MapError(crv) );
	    return NULL;
	}
	switch (pk11KeyType) {
	case CKK_RSA:
	    keyType = rsaKey;
	    break;
	case CKK_DSA:
	    keyType = dsaKey;
	    break;
	default:
	    PORT_SetError( SEC_ERROR_BAD_KEY );
	    return NULL;
	}
    }


    /* now we need to create space for the public key */
    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) return NULL;


    pubKey = (SECKEYPublicKey *) 
			PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
    if (pubKey == NULL) {
	PORT_FreeArena (arena, PR_FALSE);
	return NULL;
    }

    pubKey->arena = arena;
    pubKey->keyType = keyType;
    pubKey->pkcs11Slot = PK11_ReferenceSlot(slot);
    pubKey->pkcs11ID = id;

    switch (pubKey->keyType) {
    case rsaKey:
	{
	    CK_ATTRIBUTE rsaTemplate[] = {
		{ CKA_CLASS, NULL, 0 },
		{ CKA_KEY_TYPE, NULL, 0 },
		{ CKA_MODULUS, NULL, 0},
		{ CKA_PUBLIC_EXPONENT, NULL, 0},
	    };
	    CK_ATTRIBUTE *attrs = rsaTemplate;
	    unsigned char *dataPtr;

	    templateCount = sizeof(rsaTemplate)/sizeof(CK_ATTRIBUTE);
	    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, 
						sizeof(keyClass)); attrs++;
	    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11KeyType, 
						sizeof(pk11KeyType) ); attrs++;
	    crv = PK11_GETTAB(slot)->C_GetAttributeValue
			(slot->session, id, rsaTemplate, templateCount);
	    if (crv != CKR_OK) break;

	    if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_RSA)) {
		crv = CKR_OBJECT_HANDLE_INVALID;
		break;
	    } 
	    pubKey->u.rsa.modulus.len = rsaTemplate[2].ulValueLen;
	    dataPtr = PORT_ArenaAlloc(arena,pubKey->u.rsa.modulus.len+1);
	    if ( dataPtr == NULL) {
		crv = CKR_HOST_MEMORY;
		break;
	    } 
	    *dataPtr = 0;
	    pubKey->u.rsa.modulus.data = dataPtr+1;
	    rsaTemplate[2].pValue = pubKey->u.rsa.modulus.data;
	    pubKey->u.rsa.publicExponent.len = rsaTemplate[3].ulValueLen;
	    dataPtr = PORT_ArenaAlloc(arena,pubKey->u.rsa.publicExponent.len+1);
	    if ( dataPtr == NULL) {
		crv = CKR_HOST_MEMORY;
		break;
	    } 
	    *dataPtr = 0;
	    pubKey->u.rsa.publicExponent.data = dataPtr+1;
	    rsaTemplate[3].pValue = pubKey->u.rsa.publicExponent.data;
	    crv = PK11_GETTAB(slot)->C_GetAttributeValue
			(slot->session, id, rsaTemplate, templateCount);

	    if (pubKey->u.rsa.modulus.data[0] & 0x80) {
		pubKey->u.rsa.modulus.data = pubKey->u.rsa.modulus.data-1;
		pubKey->u.rsa.modulus.len++;
	    }
	    if (pubKey->u.rsa.publicExponent.data[0] & 0x80) {
		pubKey->u.rsa.publicExponent.data = 
				pubKey->u.rsa.publicExponent.data-1;
		pubKey->u.rsa.publicExponent.len++;
	    }
	}
	break;
    case dsaKey:
	{
	    CK_ATTRIBUTE dsaTemplate[] = {
		{ CKA_CLASS, NULL, 0},
		{ CKA_KEY_TYPE, NULL, 0},
		{ CKA_PRIME, NULL, 0 },
		{ CKA_SUBPRIME, NULL, 0 },
		{ CKA_BASE, NULL, 0 },
		{ CKA_VALUE, NULL, 0 },
	    };
	    CK_ATTRIBUTE *attrs = dsaTemplate;
	    unsigned char *dataPtr;

	    templateCount = sizeof(dsaTemplate)/sizeof(CK_ATTRIBUTE);
	    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, 
						sizeof(keyClass)); attrs++;
	    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11KeyType, 
						sizeof(pk11KeyType) ); attrs++;
	    crv = PK11_GETTAB(slot)->C_GetAttributeValue
			(slot->session, id, dsaTemplate, templateCount);
	    if (crv != CKR_OK) break;

	    if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) {
		crv = CKR_OBJECT_HANDLE_INVALID;
		break;
	    } 
	    pubKey->u.dsa.params.prime.len = dsaTemplate[2].ulValueLen;
	    dataPtr = PORT_ArenaAlloc(arena,pubKey->u.dsa.params.prime.len+1);
	    if ( dataPtr == NULL) {
		crv = CKR_HOST_MEMORY;
		break;
	    } 
	    *dataPtr = 0;
	    pubKey->u.dsa.params.prime.data = dataPtr+1;
	    dsaTemplate[2].pValue = pubKey->u.dsa.params.prime.data;

	    pubKey->u.dsa.params.subPrime.len = dsaTemplate[3].ulValueLen;
	    dataPtr=PORT_ArenaAlloc(arena,pubKey->u.dsa.params.subPrime.len+1);
	    if ( dataPtr == NULL) {
		crv = CKR_HOST_MEMORY;
		break;
	    } 
	    *dataPtr = 0;
	    pubKey->u.dsa.params.subPrime.data = dataPtr+1;
	    dsaTemplate[3].pValue = pubKey->u.dsa.params.subPrime.data;

	    pubKey->u.dsa.params.base.len = dsaTemplate[4].ulValueLen;
	    dataPtr = PORT_ArenaAlloc(arena,pubKey->u.dsa.params.base.len+1);
	    if ( dataPtr == NULL) {
		crv = CKR_HOST_MEMORY;
		break;
	    } 
	    *dataPtr = 0;
	    pubKey->u.dsa.params.base.data = dataPtr+1;
	    dsaTemplate[4].pValue = pubKey->u.dsa.params.base.data;

	    pubKey->u.dsa.publicValue.len = dsaTemplate[5].ulValueLen;
	    dataPtr = PORT_ArenaAlloc(arena,pubKey->u.dsa.publicValue.len+1);
	    if ( dataPtr == NULL) {
		crv = CKR_HOST_MEMORY;
		break;
	    } 
	    *dataPtr = 0;
	    pubKey->u.dsa.publicValue.data = dataPtr+1;
	    dsaTemplate[5].pValue = pubKey->u.dsa.publicValue.data;

	    crv = PK11_GETTAB(slot)->C_GetAttributeValue
			(slot->session, id, dsaTemplate, templateCount);

	    if (pubKey->u.dsa.params.prime.data[0] & 0x80) {
		pubKey->u.dsa.params.prime.data = 
				pubKey->u.dsa.params.prime.data-1;
		pubKey->u.dsa.params.prime.len++;
	    }
	    if (pubKey->u.dsa.params.subPrime.data[0] & 0x80) {
		pubKey->u.dsa.params.subPrime.data = 
				pubKey->u.dsa.params.subPrime.data-1;
		pubKey->u.dsa.params.subPrime.len++;
	    }
	    if (pubKey->u.dsa.params.base.data[0] & 0x80) {
		pubKey->u.dsa.params.base.data = 
				pubKey->u.dsa.params.base.data-1;
		pubKey->u.dsa.params.base.len++;
	    }
	    if (pubKey->u.dsa.publicValue.data[0] & 0x80) {
		pubKey->u.dsa.publicValue.data = 
				pubKey->u.dsa.publicValue.data-1;
		pubKey->u.dsa.publicValue.len++;
	    }
	}
	break;
#ifdef notdef
    case dhKey:

	CK_ATTRIBUTE dhTemplate[] = {
	    { CKA_CLASS, &keyClass, sizeof(keyClass) },
	    { CKA_KEY_TYPE, &keyType, sizeof(keyType) },
	    { CKA_PRIME,    pubKey->u.dh.p.data,pubKey->u.dh.p.len},
	    { CKA_BASE,     pubKey->u.dh.g.data,pubKey->u.dh.g.len},
	    { CKA_VALUE,    pubKey->u.dh.key.data, pubKey->u.dh.key.len}
	};
	    template = dhTemplate;
	    templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_DH;
	    break;
#else
    case dhKey:
    case fortezzaKey:
    case nullKey:
#endif
    default:
	crv = CKR_OBJECT_HANDLE_INVALID;
	break;
    }

    if (crv != CKR_OK) {
	PORT_FreeArena(arena,PR_FALSE);
	PK11_FreeSlot(slot);
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }

    return pubKey;
}

/*
 * Build a Private Key structure from raw PKCS #11 information.
 */
SECKEYPrivateKey *
PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType, 
			PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx)
{
    PRArenaPool *arena;
    SECKEYPrivateKey *privKey;

    /* don't know? look it up */
    if (keyType == nullKey) {
	CK_KEY_TYPE pk11Type = CKK_RSA;
	CK_BBOOL isToken = PR_TRUE;
	CK_ATTRIBUTE dattrs[] = {
	   { CKA_KEY_TYPE, NULL, 0},
	   { CKA_TOKEN,  NULL, 0},
	};
	CK_ATTRIBUTE *attrs = dattrs;
	PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11Type,sizeof(pk11Type));attrs++;
	PK11_SETATTRS(attrs, CKA_TOKEN, &isToken, sizeof(isToken));
	PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,privID,dattrs,2);
	isTemp = !isToken;
	switch (pk11Type) {
	case CKK_RSA: keyType = rsaKey; break;
	case CKK_DSA: keyType = dsaKey; break;
	case CKK_DH: keyType = dhKey; break;
	case CKK_KEA: keyType = fortezzaKey; break;
	default:
		break;
	}
    }

    /* now we need to create space for the private key */
    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) return NULL;

    privKey = (SECKEYPrivateKey *) 
			PORT_ArenaZAlloc(arena, sizeof(SECKEYPrivateKey));
    if (privKey == NULL) {
	PORT_FreeArena(arena, PR_FALSE);
	return NULL;
    }

    privKey->arena = arena;
    privKey->keyType = keyType;
    privKey->pkcs11Slot = PK11_ReferenceSlot(slot);
    privKey->pkcs11ID = privID;
    privKey->pkcs11IsTemp = isTemp;
    privKey->wincx = wincx;

    return privKey;
}

/* return the keylength if possible.  '0' if not */
unsigned int
PK11_GetKeyLength(PK11SymKey *key)
{
   if (key->size != 0) return key->size ;
   if (key->data.data == NULL) {
	PK11_ExtractKeyValue(key);
   }
   /* key is probably secret. Look up it's type and length */
   /*  this is PKCS #11 version 2.0 functionality. */
   if (key->size == 0) {
	CK_ULONG keyLength = 0;
	CK_RV crv;
	CK_ATTRIBUTE key_attrs = {CKA_VALUE_LEN, NULL, 0};
	PK11_SETATTRS(&key_attrs, CKA_VALUE_LEN, 
						&keyLength, sizeof(keyLength));
	crv = PK11_GETTAB(key->slot)->C_GetAttributeValue(key->slot->session,
			key->objectID,&key_attrs, 1);
	/* doesn't have a length field, check the known PKCS #11 key types,
	 * which don't have this field */
	if (crv != CKR_OK) {
	    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
	    PK11_SETATTRS(&key_attrs, CKA_KEY_TYPE,&keyType, sizeof(keyType));
	    switch (keyType) {
	    case CKK_DES: key->size = 8; break;
	    case CKK_DES2: key->size = 16; break;
	    case CKK_DES3: key->size = 24; break;
	    case CKK_SKIPJACK: key->size = 10; break;
	    case CKK_BATON: key->size = 20; break;
	    case CKK_JUNIPER: key->size = 20; break;
	    default: break;
	    }
	} else {
	    key->size = (unsigned int)keyLength;
	}
    }
	
   return key->size;
}

/* return the strenght of a key. This is different from length in that
 * 1) it returns the size in bits, and 2) it returns only the secret portions
 * of the key minus any checksums or parity.
 */
unsigned int
PK11_GetKeyStrength(PK11SymKey *key) 
{
     int size=0;
     switch (PK11_GetKeyType(key->type,0)) {
     case CKK_DES:
	return 56;
     case CKK_DES3:
     case CKK_DES2:
	size = PK11_GetKeyLength(key);
	if (size == 16) {
	   /* double des */
	   return 112; /* 16*7 */
	}
	return 168;
    default:
	break;
    }
    return PK11_GetKeyLength(key) * 8;
}


/*
 * get the length of a signature object based on the key
 */
int
PK11_SignatureLen(SECKEYPrivateKey *key)
{
    CK_ATTRIBUTE template = { CKA_MODULUS, NULL, 0 };
    PK11SlotInfo *slot = key->pkcs11Slot;
    int val;

    switch (key->keyType) {
    case rsaKey:
	val = PK11_GetPrivateModulusLen(key);
	if (val == -1) {
	    return MAX_RSA_MODULUS_LEN; /* failed, assume the longest len */
	}
	return (unsigned long) val;
	
    case fortezzaKey:
    case dsaKey:
	return 40;

    default:
	break;
    }
    PORT_SetError( SEC_ERROR_INVALID_KEY );
    return 0;
}

/*
 * Get the modulus length for raw parsing
 */
int
PK11_GetPrivateModulusLen(SECKEYPrivateKey *key)
{
    CK_ATTRIBUTE template = { CKA_MODULUS, NULL, 0 };
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_RV crv;
    int length;

    switch (key->keyType) {
    case rsaKey:
	crv = PK11_GetAttributes(NULL, slot, key->pkcs11ID, &template, 1);
	if (crv != CKR_OK) {
	    PORT_SetError( PK11_MapError(crv) );
	    return -1;
	}
	length = template.ulValueLen;
	if ( *(unsigned char *)template.pValue == 0) {
	    length--;
	}
	if (template.pValue != NULL)
	    PORT_Free(template.pValue);
	return (int) length;
	
    case fortezzaKey:
    case dsaKey:
    default:
	break;
    }
    if (template.pValue != NULL)
	PORT_Free(template.pValue);
    PORT_SetError( SEC_ERROR_INVALID_KEY );
    return -1;
}


/* Make a Key type to an appropriate signing/verification mechanism */
static CK_MECHANISM_TYPE
pk11_mapSignKeyType(KeyType keyType)
{
    switch (keyType) {
    case rsaKey:
	return CKM_RSA_PKCS;
    case fortezzaKey:
    case dsaKey:
	return CKM_DSA;
    default:
	break;
    }
    return CKM_INVALID_MECHANISM;
}

static CK_MECHANISM_TYPE
pk11_mapWrapKeyType(KeyType keyType)
{
    switch (keyType) {
    case rsaKey:
	return CKM_RSA_PKCS;
    /* Add fortezza?? */
    default:
	break;
    }
    return CKM_INVALID_MECHANISM;
}

/*
 * copy a key (or any other object) on a token
 */
CK_OBJECT_HANDLE
PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject)
{
    CK_OBJECT_HANDLE destObject;
    CK_RV crv;

    crv = PK11_GETTAB(slot)->C_CopyObject(slot->session,srcObject,NULL,0,
				&destObject);
    if (crv == CKR_OK) return destObject;
    PORT_SetError( PK11_MapError(crv) );
    return CK_INVALID_KEY;
}

/*
 * The next two utilities are to deal with the fact that a given operation
 * may be a multi-slot affair. This creates a new key object that is copied
 * into the new slot.
 */
static PK11SymKey *
pk11_CopyToSlot(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
		 	CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey)
{
    SECStatus rv;

    /* Extract the raw key data if possible */
    if (symKey->data.data == NULL) {
	rv = PK11_ExtractKeyValue(symKey);
	/* KEY is sensitive, we're hosed... */
	if (rv != SECSuccess) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return NULL;
	}
    }
    return PK11_ImportSymKey(slot, type, operation, &symKey->data, symKey->cx);
}

/*
 * Make sure the slot we are in the correct slot for the operation
 */
static PK11SymKey *
pk11_ForceSlot(PK11SymKey *symKey,CK_MECHANISM_TYPE type,
						CK_ATTRIBUTE_TYPE operation)
{
    PK11SlotInfo *slot = symKey->slot;
    PK11SymKey *newKey = NULL;

    if ((slot== NULL) || !PK11_DoesMechanism(slot,type)) {
	slot = PK11_GetBestSlot(type,symKey->cx);
	if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return NULL;
	}
	newKey = pk11_CopyToSlot(slot, type, operation, symKey);
	PK11_FreeSlot(slot);
    }
    return newKey;
}

/*
 * Use the token to Generate a key. keySize must be 'zero' for fixed key
 * length algorithms. NOTE: this means we can never generate a DES2 key
 * from this interface!
 */
PK11SymKey *
PK11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,int keySize,void *wincx)
{
    CK_ULONG key_size = 0;
    /* we have to use these native types because when we call PKCS 11 modules
     * we have to make sure that we are using the correct sizes for all the
     * parameters. */
    CK_BBOOL ckfalse = FALSE; /* Sigh */
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_ATTRIBUTE genTemplate[] = {
	{ CKA_SENSITIVE, NULL, 0},
	{ CKA_ENCRYPT, NULL, 0},
	/* the next entry must be the last one */
	{ CKA_VALUE_LEN, NULL, 0},
    };
    int count = sizeof(genTemplate)/sizeof(genTemplate[0]);
    CK_MECHANISM mechanism;
    CK_MECHANISM_TYPE key_gen_mechanism;
    PK11SymKey *symKey;
    CK_RV crv;
    CK_ATTRIBUTE *attrs = genTemplate;

    PK11_SETATTRS(attrs, CKA_SENSITIVE, &ckfalse, sizeof(CK_BBOOL)); attrs++;
    PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue, sizeof(CK_BBOOL)); attrs++;
    /* the next entry must be the last one */
    PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); attrs++;

    /* find a slot to generate the key into */
    if ((slot == NULL) || (!PK11_DoesMechanism(slot,type))) {
        slot = PK11_GetBestSlot(type,wincx);
        if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return NULL;
	}
    } else {
	PK11_ReferenceSlot(slot);
    }

    /* Initialize the Key Gen Mechanism */
    key_gen_mechanism = PK11_GetKeyGen(type);
    if (key_gen_mechanism == CKM_FAKE_RANDOM) {
	PK11_FreeSlot(slot);
	PORT_SetError( SEC_ERROR_NO_MODULE );
	return NULL;
    }
    mechanism.mechanism = key_gen_mechanism;
    mechanism.pParameter = NULL;
    mechanism.ulParameterLen = 0;

    key_size = keySize;

    /* if key_size was not specified, the mechanism must know it's own size */
    if (key_size == 0) {
	count--;
    }

    /* get our key Structure */
    symKey = PK11_CreateSymKey(slot,type,wincx);
    PK11_FreeSlot(slot);
    if (symKey == NULL) {
	return NULL;
    }
    symKey->size = keySize;

    crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(symKey->slot->session,
			 &mechanism, genTemplate, count, &symKey->objectID);
    if (crv != CKR_OK) {
	PK11_FreeSymKey(symKey);
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }
    return symKey;
}

CK_BBOOL
PK11_HasAttributeSet( PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
				                      CK_ATTRIBUTE_TYPE type )
{
    CK_BBOOL ckvalue = FALSE;
    CK_ATTRIBUTE template;
    CK_RV crv;

    /* Prepare to retrieve the attribute. */
    PK11_SETATTRS( &template, type, &ckvalue, sizeof( CK_BBOOL ) );

    /* Retrieve attribute value. */
    crv = PK11_GETTAB( slot )->C_GetAttributeValue( slot->session, id,
                                                    &template, 1 );
    if( crv != CKR_OK ) {
        PORT_SetError( PK11_MapError( crv ) );
        return FALSE;
    }

    return ckvalue;
}

/*
 * PKCS #11 pairwise consistency check utilized to validate key pair.
 */
static SECStatus
pk11_PairwiseConsistencyCheck(SECKEYPublicKey *pubKey, 
	SECKEYPrivateKey *privKey, CK_MECHANISM *mech, void* wincx )
{
    /* Variables used for Encrypt/Decrypt functions. */
    unsigned char *known_message = (unsigned char *)"Known Crypto Message";
    CK_BBOOL isEncryptable = FALSE;
    unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH];
    CK_ULONG bytes_decrypted;
    PK11SlotInfo *slot;
    CK_OBJECT_HANDLE id;
    unsigned char *ciphertext;
    unsigned char *text_compared;
    CK_ULONG max_bytes_encrypted;
    CK_ULONG bytes_encrypted;
    CK_ULONG bytes_compared;
    CK_RV crv;

    /* Variables used for Signature/Verification functions. */
    unsigned char *known_digest = (unsigned char *)"Mozilla Rules World!";
    SECItem  signature;
    SECItem  digest;    /* always uses SHA-1 digest */
    int signature_length;
    SECStatus rv;

    /**************************************************/
    /* Pairwise Consistency Check of Encrypt/Decrypt. */
    /**************************************************/

    isEncryptable = PK11_HasAttributeSet( privKey->pkcs11Slot, 
					privKey->pkcs11ID, CKA_DECRYPT );

    /* If the encryption attribute is set; attempt to encrypt */
    /* with the public key and decrypt with the private key.  */
    if( isEncryptable ) {
	/* Find a module to encrypt against */
	slot = PK11_GetBestSlot(pk11_mapWrapKeyType(privKey->keyType),wincx);
	if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return SECFailure;
	}

	id = PK11_ImportPublicKey(slot,pubKey);
	if (id == CK_INVALID_KEY) {
	    PK11_FreeSlot(slot);
	    return SECFailure;
	}
	/* Prepare for encryption using the public key. */
	crv = PK11_GETTAB( slot )->C_EncryptInit( slot->session,
						  mech, id );
        if( crv != CKR_OK ) {
	    PORT_SetError( PK11_MapError( crv ) );
	    PK11_FreeSlot(slot);
	    return SECFailure;
	}

	/* Compute max bytes encrypted from modulus length of private key. */
	max_bytes_encrypted = PK11_GetPrivateModulusLen( privKey );

	/* Allocate space for ciphertext. */
	ciphertext = (unsigned char *) PORT_Alloc( max_bytes_encrypted );
	if( ciphertext == NULL ) {
	    PORT_SetError( SEC_ERROR_NO_MEMORY );
	    PK11_FreeSlot(slot);
	    return SECFailure;
	}

	/* Initialize bytes encrypted to max bytes encrypted. */
	bytes_encrypted = max_bytes_encrypted;

	/* Encrypt using the public key. */
	crv = PK11_GETTAB( slot )->C_Encrypt( slot->session,
					      known_message,
					      PAIRWISE_MESSAGE_LENGTH,
					      ciphertext,
					      &bytes_encrypted );
	PK11_FreeSlot(slot);
	if( crv != CKR_OK ) {
	    PORT_SetError( PK11_MapError( crv ) );
	    PORT_Free( ciphertext );
	    return SECFailure;
	}

	/* Always use the smaller of these two values . . . */
	bytes_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH )
			 ? PAIRWISE_MESSAGE_LENGTH
			 : bytes_encrypted;

	/* If there was a failure, the plaintext */
	/* goes at the end, therefore . . .      */
	text_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH )
			? (ciphertext + bytes_encrypted -
			  PAIRWISE_MESSAGE_LENGTH )
			: ciphertext;

	/* Check to ensure that ciphertext does */
	/* NOT EQUAL known input message text   */
	/* per FIPS PUB 140-1 directive.        */
	if( ( bytes_encrypted != max_bytes_encrypted ) ||
	    ( PORT_Memcmp( text_compared, known_message,
			   bytes_compared ) == 0 ) ) {
	    /* Set error to Invalid PRIVATE Key. */
	    PORT_SetError( SEC_ERROR_INVALID_KEY );
	    PORT_Free( ciphertext );
	    return SECFailure;
	}

	slot = privKey->pkcs11Slot;
	/* Prepare for decryption using the private key. */
	crv = PK11_GETTAB( slot )->C_DecryptInit( slot->session,
						  mech,
						  privKey->pkcs11ID );
	if( crv != CKR_OK ) {
	    PORT_SetError( PK11_MapError(crv) );
	    PORT_Free( ciphertext );
	    PK11_FreeSlot(slot);
	    return SECFailure;
	}

	/* Initialize bytes decrypted to be the */
	/* expected PAIRWISE_MESSAGE_LENGTH.    */
	bytes_decrypted = PAIRWISE_MESSAGE_LENGTH;

	/* Decrypt using the private key.   */
	/* NOTE:  No need to reset the      */
	/*        value of bytes_encrypted. */
	crv = PK11_GETTAB( slot )->C_Decrypt( slot->session,
					      ciphertext,
					      bytes_encrypted,
					      plaintext,
					      &bytes_decrypted );

	/* Finished with ciphertext; free it. */
	PORT_Free( ciphertext );

	if( crv != CKR_OK ) {
	   PORT_SetError( PK11_MapError(crv) );
	    PK11_FreeSlot(slot);
	   return SECFailure;
	}

	/* Check to ensure that the output plaintext */
	/* does EQUAL known input message text.      */
	if( ( bytes_decrypted != PAIRWISE_MESSAGE_LENGTH ) ||
	    ( PORT_Memcmp( plaintext, known_message,
			   PAIRWISE_MESSAGE_LENGTH ) != 0 ) ) {
	    /* Set error to Bad PUBLIC Key. */
	    PORT_SetError( SEC_ERROR_BAD_KEY );
	    PK11_FreeSlot(slot);
	    return SECFailure;
	}
    }

    /**********************************************/
    /* Pairwise Consistency Check of Sign/Verify. */
    /**********************************************/

    /* Initialize signature and digest data. */
    signature.data = NULL;
    digest.data = NULL;

    /* Determine length of signature. */
    signature_length = PK11_SignatureLen( privKey );
    if( signature_length == 0 )
	goto failure;

    /* Allocate space for signature data. */
    signature.data = (unsigned char *) PORT_Alloc( signature_length );
    if( signature.data == NULL ) {
	PORT_SetError( SEC_ERROR_NO_MEMORY );
	goto failure;
    }

    /* Allocate space for known digest data. */
    digest.data = (unsigned char *) PORT_Alloc( PAIRWISE_DIGEST_LENGTH );
    if( digest.data == NULL ) {
	PORT_SetError( SEC_ERROR_NO_MEMORY );
	goto failure;
    }

    /* "Fill" signature type and length. */
    signature.type = PAIRWISE_SECITEM_TYPE;
    signature.len  = signature_length;

    /* "Fill" digest with known SHA-1 digest parameters. */
    digest.type = PAIRWISE_SECITEM_TYPE;
    PORT_Memcpy( digest.data, known_digest, PAIRWISE_DIGEST_LENGTH );
    digest.len = PAIRWISE_DIGEST_LENGTH;

    /* Sign the known hash using the private key. */
    rv = PK11_Sign( privKey, &signature, &digest );
    if( rv != SECSuccess )
	goto failure;

    /* Verify the known hash using the public key. */
    rv = PK11_Verify( pubKey, &signature, &digest, wincx );
    if( rv != SECSuccess )
	goto failure;

    /* Free signature and digest data. */
    PORT_Free( signature.data );
    PORT_Free( digest.data );

    return SECSuccess;

failure:
    if( signature.data != NULL )
	PORT_Free( signature.data );
    if( digest.data != NULL )
	PORT_Free( digest.data );

    return SECFailure;
}



/*
 * take a private key in one pkcs11 module and load it into another:
 *  NOTE: the source private key is a rare animal... it can't be sensitive.
 *  This is used to do a key gen using one pkcs11 module and storing the
 *  result into another.
 */
SECKEYPrivateKey *
pk11_loadPrivKey(PK11SlotInfo *slot,SECKEYPrivateKey *privKey, 
		SECKEYPublicKey *pubKey, PRBool token, PRBool sensitive) 
{
    CK_ATTRIBUTE privTemplate[] = {
        /* class must be first */
	{ CKA_CLASS, NULL, 0 },
	{ CKA_KEY_TYPE, NULL, 0 },
	/* these three must be next */
	{ CKA_TOKEN, NULL, 0 },
	{ CKA_PRIVATE, NULL, 0 },
	{ CKA_SENSITIVE, NULL, 0 },
	{ CKA_ID, NULL, 0 },
#ifdef notdef
	{ CKA_LABEL, NULL, 0 },
	{ CKA_SUBJECT, NULL, 0 },
#endif
	/* RSA */
	{ CKA_MODULUS, NULL, 0 },
	{ CKA_PRIVATE_EXPONENT, NULL, 0 },
	{ CKA_PUBLIC_EXPONENT, NULL, 0 },
	{ CKA_PRIME_1, NULL, 0 },
	{ CKA_PRIME_2, NULL, 0 },
	{ CKA_EXPONENT_1, NULL, 0 },
	{ CKA_EXPONENT_2, NULL, 0 },
	{ CKA_COEFFICIENT, NULL, 0 },
    };
    CK_ATTRIBUTE *attrs = NULL, *ap;
    int templateSize = sizeof(privTemplate)/sizeof(privTemplate[0]);
    PRArenaPool *arena;
    CK_OBJECT_HANDLE objectID;
    int i, count = 0;
    int extra_count = 0;
    CK_RV crv;
    SECStatus rv;

    for (i=0; i < templateSize; i++) {
	if (privTemplate[i].type == CKA_MODULUS) {
	    attrs= &privTemplate[i];
	    count = i;
	    break;
	}
    }
    PORT_Assert(attrs != NULL);
    if (attrs == NULL) {
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	return NULL;
    }

    switch (privKey->keyType) {
    case rsaKey:
	count = templateSize;
	extra_count = templateSize - (attrs - privTemplate);
	break;
    case dsaKey:
	ap->type = CKA_PRIME; ap++; count++; extra_count++;
	ap->type = CKA_SUBPRIME; ap++; count++; extra_count++;
	ap->type = CKA_BASE; ap++; count++; extra_count++;
	ap->type = CKA_VALUE; ap++; count++; extra_count++;
	break;
    case dhKey:
	ap->type = CKA_PRIME; ap++; count++; extra_count++;
	ap->type = CKA_BASE; ap++; count++; extra_count++;
	ap->type = CKA_VALUE; ap++; count++; extra_count++;
	break;
     default:
	count = 0;
	extra_count = 0;
	break;
     }

     if (count == 0) {
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	return NULL;
     }

     arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
     if (arena == NULL) return NULL;
     /*
      * read out the old attributes.
      */
     crv = PK11_GetAttributes(arena, privKey->pkcs11Slot, privKey->pkcs11ID,
		privTemplate,count);
     if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	PORT_FreeArena(arena, PR_TRUE);
	return NULL;
     }

     /* Reset sensitive, token, and private */
     *(CK_BBOOL *)(privTemplate[2].pValue) = token ? TRUE : FALSE;
     *(CK_BBOOL *)(privTemplate[3].pValue) = token ? TRUE : FALSE;
     *(CK_BBOOL *)(privTemplate[4].pValue) = sensitive ? TRUE : FALSE;

     /* Sigh, not everyone can handle zero padded key values, give
      * them the raw data as unsigned */
     for (ap=attrs; extra_count; ap++, extra_count--) {
	pk11_SignedToUnsigned(ap);
     }

     /* now Store the puppies */
     rv = PK11_CreateNewObject(slot,privTemplate, count, &objectID);
     PORT_FreeArena(arena, PR_TRUE);
     if (rv != SECSuccess) {
	return NULL;
     }

     /* try loading the public key as a token object */
     pk11_ImportTokenPublicKey(slot, pubKey, PR_TRUE);
     if (pubKey->pkcs11Slot) {
	PK11_FreeSlot(pubKey->pkcs11Slot);
	pubKey->pkcs11Slot = NULL;
	pubKey->pkcs11ID = CK_INVALID_KEY;
     }

     /* build new key structure */
     return PK11_MakePrivKey(slot, privKey->keyType, !token, 
						objectID, privKey->wincx);
}


/*
 * Use the token to Generate a key. keySize must be 'zero' for fixed key
 * length algorithms. NOTE: this means we can never generate a DES2 key
 * from this interface!
 */
SECKEYPrivateKey *
PK11_GenerateKeyPair(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, 
   void *param, SECKEYPublicKey **pubKey, PRBool token, 
					PRBool sensitive, void *wincx)
{
    /* we have to use these native types because when we call PKCS 11 modules
     * we have to make sure that we are using the correct sizes for all the
     * parameters. */
    CK_BBOOL ckfalse = FALSE; /* Sigh */
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_ULONG modulusBits;
    CK_BYTE publicExponent[4];
    CK_ATTRIBUTE privTemplate[] = {
	{ CKA_SENSITIVE, NULL, 0},
	{ CKA_TOKEN,  NULL, 0},
	{ CKA_PRIVATE,  NULL, 0},
    };
    CK_ATTRIBUTE rsaPubTemplate[] = {
	{ CKA_MODULUS_BITS, NULL, 0},
	{ CKA_PUBLIC_EXPONENT, NULL, 0},
    };
    CK_ATTRIBUTE dsaPubTemplate[] = {
	{ CKA_PRIME, NULL, 0 },
	{ CKA_SUBPRIME, NULL, 0 },
	{ CKA_BASE, NULL, 0 },
    };
    int dsaPubCount = sizeof(dsaPubTemplate)/sizeof(dsaPubTemplate[0]);
    /*CK_ULONG key_size = 0;*/
    CK_ATTRIBUTE *pubTemplate;
    int privCount = sizeof(privTemplate)/sizeof(privTemplate[0]);
    int rsaPubCount = sizeof(rsaPubTemplate)/sizeof(rsaPubTemplate[0]);
    int pubCount = 0;
    PK11RSAGenParams *rsaParams;
    PQGParams *dsaParams;
    CK_MECHANISM mechanism;
    CK_MECHANISM test_mech;
    CK_SESSION_HANDLE session_handle;
    CK_RV crv;
    CK_OBJECT_HANDLE privID,pubID;
    SECKEYPrivateKey *privKey;
    KeyType keyType;
    PRBool restore;
    int peCount,i;
    CK_ATTRIBUTE *attrs;
    SECItem *pubKeyIndex;
    CK_ATTRIBUTE setTemplate;
    SECStatus rv;

    PORT_Assert(slot != NULL);
    if (slot == NULL) {
	PORT_SetError( SEC_ERROR_NO_MODULE);
	return NULL;
    }

    /* if our slot really doesn't do this mechanism, Generate the key
     * in our internal token and write it out */
    if (!PK11_DoesMechanism(slot,type)) {
	PK11SlotInfo *int_slot = PK11_GetBestSlot(type,wincx);
	if (slot == int_slot) {
	    PK11_FreeSlot(int_slot);
	    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	    return NULL;
	}
	if (int_slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return NULL;
	}
	privKey = PK11_GenerateKeyPair(int_slot,type, param, pubKey, PR_FALSE, 
							PR_FALSE, wincx);
	PK11_FreeSlot(int_slot);
	if (privKey != NULL) {
	    SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey,
						*pubKey,token,sensitive);
	    SECKEY_DestroyPrivateKey(privKey);
	    if (newPrivKey == NULL) {
		SECKEY_DestroyPublicKey(*pubKey);
		*pubKey = NULL;
	    }
	    return newPrivKey;
	}
	return NULL;
   }


    mechanism.mechanism = type;
    mechanism.pParameter = NULL;
    mechanism.ulParameterLen = 0;
    test_mech.pParameter = NULL;
    test_mech.ulParameterLen = 0;
    attrs = privTemplate;
    PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse, 
						sizeof(CK_BBOOL)); attrs++;
    PK11_SETATTRS(attrs, CKA_TOKEN, token ? &cktrue : &ckfalse,
						 sizeof(CK_BBOOL)); attrs++;
    PK11_SETATTRS(attrs, CKA_PRIVATE, token ? &cktrue : &ckfalse,
							 sizeof(CK_BBOOL));

    switch (type) {
    case CKM_RSA_PKCS_KEY_PAIR_GEN:
	rsaParams = (PK11RSAGenParams *)param;
	modulusBits = rsaParams->keySizeInBits;
	peCount = 0;
	for (i=0; i < 4; i++) {
	    if (peCount || (rsaParams->pe & 
				((unsigned long)0xff000000L >> (i*8)))) {
		publicExponent[peCount] = 
				(CK_BYTE)((rsaParams->pe >> (3-i)*8) & 0xff);
		peCount++;
	    }
	}
	PORT_Assert(peCount != 0);
	attrs = rsaPubTemplate;
	PK11_SETATTRS(attrs, CKA_MODULUS_BITS, 
				&modulusBits, sizeof(modulusBits)); attrs++;
	PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, publicExponent, peCount);
	pubTemplate = rsaPubTemplate;
	pubCount = rsaPubCount;
	keyType = rsaKey;
	test_mech.mechanism = CKM_RSA_PKCS;
	break;
    case CKM_DSA_KEY_PAIR_GEN:
	dsaParams = (PQGParams *)param;
	attrs = dsaPubTemplate;
	PK11_SETATTRS(attrs, CKA_PRIME, dsaParams->prime.data,
				dsaParams->prime.len); attrs++;
	PK11_SETATTRS(attrs, CKA_SUBPRIME, dsaParams->subPrime.data,
					dsaParams->subPrime.len); attrs++;
	PK11_SETATTRS(attrs, CKA_BASE, dsaParams->base.data,
						dsaParams->base.len); 
	pubTemplate = dsaPubTemplate;
	pubCount = dsaPubCount;
	keyType = dsaKey;
	test_mech.mechanism = CKM_DSA;
	break;
    default:
	PORT_SetError( SEC_ERROR_BAD_KEY );
	return NULL;
    }

    if (token) {
	session_handle = PK11_GetRWSession(slot);
	restore = PR_TRUE;
    } else {
	session_handle = slot->session;
	restore = PR_FALSE;
    }

    crv = PK11_GETTAB(slot)->C_GenerateKeyPair(session_handle, &mechanism,
	pubTemplate,pubCount,privTemplate,privCount,&privID,&pubID);


    if (crv != CKR_OK) {
	if (restore)  PK11_RestoreROSession(slot,session_handle);
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }

    *pubKey = PK11_ExtractPublicKey(slot, keyType, pubID);
    if (*pubKey == NULL) {
	if (restore)  PK11_RestoreROSession(slot,session_handle);
	PK11_DestroyObject(slot,pubID);
	PK11_DestroyObject(slot,privID);
	return NULL;
    }

    /* set the ID to the public key so we can find it again */
    pubKeyIndex =  NULL;
    switch (type) {
    case CKM_RSA_PKCS_KEY_PAIR_GEN:
	pubKeyIndex = &(*pubKey)->u.rsa.modulus;
	break;
    case CKM_DSA_KEY_PAIR_GEN:
	pubKeyIndex = &(*pubKey)->u.dsa.publicValue;
	break;
    }
    PORT_Assert(pubKeyIndex != NULL);

    PK11_SETATTRS(&setTemplate, CKA_ID, pubKeyIndex->data, pubKeyIndex->len);
    crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, privID,
		&setTemplate, 1);
    if (crv == CKR_OK && PK11_HasAttributeSet( slot, pubID, CKA_TOKEN)) {
    	crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, pubID,
		&setTemplate, 1);
    }

    if (restore) {
	PK11_RestoreROSession(slot,session_handle);
    }

    if (crv != CKR_OK) {
	PK11_DestroyObject(slot,pubID);
	PK11_DestroyObject(slot,privID);
	PORT_SetError( PK11_MapError(crv) );
	*pubKey = NULL;
	return NULL;
    }

    privKey = PK11_MakePrivKey(slot,keyType,!token,privID,wincx);
    if (privKey == NULL) {
	SECKEY_DestroyPublicKey(*pubKey);
	PK11_DestroyObject(slot,privID);
	*pubKey = NULL;
	return NULL;  /* due to pairwise consistency check */
    }

    /*
     * despite our request not to create a token version of the
     * public key, one was created anyway (sigh)... free it.
     */
    if (PK11_HasAttributeSet( slot, pubID, CKA_TOKEN)) {
	PK11_FreeSlot((*pubKey)->pkcs11Slot);
	(*pubKey)->pkcs11Slot = NULL;
	(*pubKey)->pkcs11ID = CK_INVALID_KEY;
    }

    /* Perform PKCS #11 pairwise consistency check. */
    rv = pk11_PairwiseConsistencyCheck( *pubKey, privKey, &test_mech, wincx );
    if( rv != SECSuccess ) {
	SECKEY_DestroyPublicKey( *pubKey );
	SECKEY_DestroyPrivateKey( privKey );
	*pubKey = NULL;
	privKey = NULL;
	return NULL;
    }

    return privKey;
}

/*
 * This function does a straight public key wrap (which only RSA can do).
 * Use PK11_PubGenKey and PK11_WrapSymKey to implement the FORTEZZA and
 * Diffie-Hellman Ciphers. */
SECStatus
PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
				PK11SymKey *symKey, SECItem *wrappedKey)
{
    PK11SlotInfo *slot;
    CK_ULONG len =  wrappedKey->len;
    PK11SymKey *newKey = NULL;
    CK_OBJECT_HANDLE id;
    CK_MECHANISM mechanism;
    CK_RV crv;

    /* if this slot doesn't support the mechanism, go to a slot that does */
    newKey = pk11_ForceSlot(symKey,type,CKA_ENCRYPT);
    if (newKey != NULL) {
	symKey = newKey;
    }

    if ((symKey == NULL) || (symKey->slot == NULL)) {
	PORT_SetError( SEC_ERROR_NO_MODULE );
	return SECFailure;
    }

    slot = symKey->slot;
    mechanism.mechanism = pk11_mapWrapKeyType(pubKey->keyType);
    mechanism.pParameter = NULL;
    mechanism.ulParameterLen = 0;

    id = PK11_ImportPublicKey(slot,pubKey);

    crv = PK11_GETTAB(slot)->C_WrapKey(slot->session,&mechanism,
		id,symKey->objectID,wrappedKey->data,&len);
    if (newKey) {
	PK11_FreeSymKey(newKey);
    }

    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    wrappedKey->len = len;
    return SECSuccess;
} 

/*
 * this little function uses the Encrypt function to wrap a key, just in
 * case someone neglected to implement wrap!!
 */
static SECStatus
pk11_HandWrap(PK11SymKey *wrappingKey, SECItem *param, CK_MECHANISM_TYPE type,
			 SECItem *inKey, SECItem *outKey)
{
    PK11SlotInfo *slot;
    CK_ULONG len;
    SECItem *data;
    CK_MECHANISM mech;
    CK_RV crv;

    slot = wrappingKey->slot;
    /* use NULL IV's for wrapping */
    mech.mechanism = type;
    if (param) {
	mech.pParameter = param->data;
	mech.ulParameterLen = param->len;
    } else {
	mech.pParameter = NULL;
	mech.ulParameterLen = 0;
    }
    crv = PK11_GETTAB(slot)->C_EncryptInit(slot->session,&mech,
							wrappingKey->objectID);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }

    /* keys are almost always aligned, but if we get this far,
     * we've gone above and beyond anyway... */
    data = PK11_BlockData(inKey,PK11_GetBlockSize(type,param));
    if (data == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return SECFailure;
    }
    len = outKey->len;
    crv = PK11_GETTAB(slot)->C_Encrypt(slot->session,data->data,data->len,
							   outKey->data, &len);
    SECITEM_FreeItem(data,PR_TRUE);
    outKey->len = len;
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

/*
 * This function does a TEK based wrap. Weird things can happen if
 * Some time we should probably look into changing it to do handle FORTEZZA
 * and Diffie-Hellman, which would require a parameter pointer and a pointer
 * to the encrypter's private key... of course we're in deep problems if the
 * encrypter's private key is in a different slot from the symKey (sigh).
 */
SECStatus
PK11_WrapSymKey(CK_MECHANISM_TYPE type, PK11SymKey *wrappingKey,
				PK11SymKey *symKey, SECItem *wrappedKey)
{
    PK11SlotInfo *slot;
    CK_ULONG len;
    PK11SymKey *newKey = NULL;
    SECItem *param;
    CK_MECHANISM mechanism;
    CK_RV crv;
    SECStatus rv;

    /* if this slot doesn't support the mechanism, go to a slot that does */
    /* Force symKey and wrappingKey into the same slot */
    if ((wrappingKey->slot == NULL) || (symKey->slot != wrappingKey->slot)) {
	/* first try copying the wrapping Key to the symKey slot */
	if (symKey->slot && PK11_DoesMechanism(symKey->slot,type)) {
	    newKey = pk11_CopyToSlot(symKey->slot,type,CKA_WRAP,wrappingKey);
	}
	/* Nope, try it the other way */
	if (newKey == NULL) {
	    if (wrappingKey->slot) {
	        newKey = pk11_CopyToSlot(wrappingKey->slot,
					symKey->type, CKA_ENCRYPT, symKey);
	    }
	    /* just not playing... one last thing, can we get symKey's data?
	     * If it's possible, we it should already be in the 
	     * symKey->data.data pointer because pk11_CopyToSlot would have
	     * tried to put it there. */
	    if (newKey == NULL) {
		/* Can't get symKey's data: Game Over */
		if (symKey->data.data == NULL) {
		    PORT_SetError( SEC_ERROR_NO_MODULE );
		    return SECFailure;
		}
		param = PK11_ParamFromIV(type,NULL);
		rv = pk11_HandWrap(wrappingKey, param, type,
						&symKey->data,wrappedKey);
		if (param) SECITEM_FreeItem(param,PR_TRUE);
		return rv;
	    }
	    /* we successfully moved the sym Key */
	    symKey = newKey;
	} else {
	    /* we successfully moved the wrapping Key */
	    wrappingKey = newKey;
	}
    }

    /* at this point both keys are in the same token */
    slot = wrappingKey->slot;
    mechanism.mechanism = type;
    /* use NULL IV's for wrapping */
    param = PK11_ParamFromIV(type,NULL);
    if (param) {
	mechanism.pParameter = param->data;
	mechanism.ulParameterLen = param->len;
    } else {
	mechanism.pParameter = NULL;
	mechanism.ulParameterLen = 0;
    }

    crv = PK11_GETTAB(slot)->C_WrapKey(slot->session, &mechanism,
		 wrappingKey->objectID, symKey->objectID, 
						wrappedKey->data, &len);
    wrappedKey->len = len;
    rv = SECSuccess;
    if (crv != CKR_OK) {
	/* can't wrap it? try hand wrapping it... */
	do {
	    if (symKey->data.data == NULL) {
		rv = PK11_ExtractKeyValue(symKey);
		if (rv != SECSuccess) break;
	    }
	    rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data,
								 wrappedKey);
	} while (PR_FALSE);
    }
    if (newKey) PK11_FreeSymKey(newKey);
    if (param) SECITEM_FreeItem(param,PR_TRUE);
    return rv;
} 


/*
 * This Generates a wrapping key based on a privateKey, publicKey, and two
 * random numbers. For Mail usage RandomB should be NULL. In the Sender's
 * case RandomA is generate, outherwize it is passed.
 */
PK11SymKey *
PK11_DeriveSymKey( SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey, 
   PRBool isSender, SECItem *randomA, SECItem *randomB,
    CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
						int keySize,void *wincx)
{
    PK11SlotInfo *slot = privKey->pkcs11Slot;
    /*CK_MECHANISM mechanism; */
    PK11SymKey *symKey;

    if (privKey->keyType != pubKey->keyType) {
	PORT_SetError(SEC_ERROR_BAD_KEY);
	return NULL;
    }

    /* get our key Structure */
    symKey = PK11_CreateSymKey(slot,target,wincx);
    if (symKey == NULL) {
	return NULL;
    }

    switch (privKey->keyType) {
    case rsaKey:
    case dsaKey:
    case nullKey:
    case fortezzaKey:
    case dhKey:
	PORT_SetError(SEC_ERROR_BAD_KEY);
	break;
#ifdef notdef
    case fortezzaKey:
	{
	    CK_FORTEZZA_EXCHANGE_PARAM param;
	    param.isSender = (CK_BOOL) isSender;
	    if (isSender) {
	    	param.usRandomLen = FORTEZZA_RANDOM_LENGTH;
	    	param.pRandomA = PORT_Alloc(FORTEZZA_RANDOM_LENGTH);
	    } else {
		param.usRandomA = randomA.data;
		param.usRandomLen = randomA.len;
	    }
	    if (param.pRandomA == NULL) {
		PORT_SetError( SEC_ERROR_NO_MEMORY ):
		break;
	    }
	    param.pRandomB = NULL;
	    if (RandomB)
		 param.pRandomB = RandomB.data;
	    param.usPublicDataLen = pubKey->u.fortezza.KEAKey.len
	    param.PPublicData = pubKey->u.fortezza.KEAKey.data;

	    mechanism.mechanism = derive;
	    mechanism.pParameter = &param;
	    mechanism.usParameterSize = sizeof(param);

	    /* get a new symKey structure */
	    crv=PK11_GETTAB(slot)->C_DeriveKey(slot->session, &mechanism, NULL,
						0, &symKey->objectID);
	    if (crv == CKR_OK) return symKey;
	    PORT_SetError( PK11_MapError(crv) );
	}
	break;
    case dhKey:
	{
	    CK_BBOOL ckfalse = FALSE; /* Sigh */
	    CK_BBOOL cktrue = TRUE; /* sigh */
	    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
	    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
	    CK_BBOOL cktrue = TRUE; /* sigh */
	    CK_ULONG key_size = 0;
	    CK_ATTRIBUTE keyTemplate[] = {
		{ CKA_CLASS, &keyClass, sizeof(keyClass) },
		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
		{ CKA_SENSITIVE, &ckfalse, sizeof(CK_BBOOL) },
		{ CKA_WRAP, &cktrue, 1},
		{ CKA_UNWRAP, &cktrue, 1},
		{ CKA_VALUE_LEN, &key_size, sizeof(key_size) },
	    };
	    int templateCount = sizeof(keyTemplate)/sizeof(keyTemplate[0]);

	    keyType = PK11_GetKeyType(target,keySize);
	    key_size = keySize;
	    symKey->keySize = keySize;
	    if (key_size == 0) templateCount--;

	    mechanism.mechanism = derive;
	    mechanism.pParameter = pubKey->u.dh.key.data; 
	    mechanism.usParameterSize = pubKey->u.dh.key.len;
	    crv = PK11_GETTAB(slot)->C_DeriveKey(slot->session, &mechanism,
				 keyTempate, templateCount, &symKey->objectID);
	    if (crv == CKR_OK) return symKey;
	    PORT_SetError( PK11_MapError(crv) );
	}
	break;
#endif
   }

   PK11_FreeSymKey(symKey);
   return NULL;
}

/*
 * this little function uses the Decrypt function to unwrap a key, just in
 * case someone neglected to implement unwrap!! NOTE: The key size may
 * not be preserved properly for some algorithms!
 */
static PK11SymKey *
pk11_HandUnwrap(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey,
 		CK_MECHANISM *mech, SECItem *inKey, CK_MECHANISM_TYPE target,
						int key_size, void * wincx)
{
    CK_ULONG len;
    SECItem outKey;
    PK11SymKey *symKey;
    CK_RV crv;

    /* use NULL IV's for wrapping */
    crv = PK11_GETTAB(slot)->C_DecryptInit(slot->session,mech,wrappingKey);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }

    /* keys are almost always aligned, but if we get this far,
     * we've gone above and beyond anyway... */
    outKey.data = PORT_Alloc(inKey->len);
    if (outKey.data == NULL) {
	PORT_SetError( SEC_ERROR_NO_MEMORY );
	return NULL;
    }
    len = inKey->len;
    crv = PK11_GETTAB(slot)->C_Decrypt(slot->session,inKey->data,inKey->len,
							   outKey.data, &len);
    outKey.len = (key_size == 0) ? len : key_size;
    if (crv != CKR_OK) {
	PORT_Free(outKey.data);
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }

    if (PK11_DoesMechanism(slot,target)) {
    	symKey = PK11_ImportSymKey(slot, target, CKA_DECRYPT, &outKey, wincx);
    } else {
	slot = PK11_GetBestSlot(target,wincx);
	if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    PORT_Free(outKey.data);
	    return NULL;
	}
	symKey = PK11_ImportSymKey(slot, target, CKA_DECRYPT, &outKey, wincx);
	PK11_FreeSlot(slot);
    }
    PORT_Free(outKey.data);
    return symKey;
}

/*
 * The wrap/unwrap function is pretty much the same for private and
 * public keys. It's just getting the Object ID and slot right. This is
 * the combined unwrap function.
 */
static PK11SymKey *
pk11_AnyUnwrapKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey,
     CK_MECHANISM_TYPE wrapType, SECItem *wrappedKey,
			CK_MECHANISM_TYPE target, int keySize, void *wincx)
{
    CK_BBOOL ckfalse = FALSE; /* Sigh */
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_ULONG key_size = 0;
    CK_ATTRIBUTE keyTemplate[] = {
	{ CKA_CLASS, NULL, 0},
	{ CKA_KEY_TYPE, NULL, 0},
	{ CKA_SENSITIVE, NULL, 0},
	{ CKA_DECRYPT, NULL, 0},
	{ CKA_VALUE_LEN, NULL, 0},
    };
    int templateCount = sizeof(keyTemplate)/sizeof(keyTemplate[0]);
    PK11SymKey *symKey;
    CK_MECHANISM mechanism;
    SECItem *param;
    CK_RV crv;
    CK_ATTRIBUTE *attrs = keyTemplate;

    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); attrs++;
    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); attrs++;
    PK11_SETATTRS(attrs, CKA_SENSITIVE, &ckfalse, sizeof(CK_BBOOL)); attrs++;
    PK11_SETATTRS(attrs, CKA_DECRYPT, &cktrue, 1); attrs++;
    PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); attrs++;


    /* get our key Structure */
    symKey = PK11_CreateSymKey(slot,target,wincx);
    if (symKey == NULL) {
	return NULL;
    }

    keyType = PK11_GetKeyType(target,keySize);
    key_size = keySize;
    symKey->size = keySize;
    if (key_size == 0) templateCount--;

    mechanism.mechanism = wrapType;
    /* use NULL IV's for wrapping */
    param = PK11_ParamFromIV(wrapType,NULL);
    if (param) {
	mechanism.pParameter = param->data;
	mechanism.ulParameterLen = param->len;
    } else {
	mechanism.pParameter = NULL;
	mechanism.ulParameterLen = 0;
    }
    crv = PK11_GETTAB(slot)->C_UnwrapKey(slot->session,&mechanism,wrappingKey,
		wrappedKey->data, wrappedKey->len, keyTemplate, templateCount, 
							  &symKey->objectID);
    SECITEM_FreeItem(param,PR_TRUE);
    if (crv != CKR_OK) {
	/* try hand Unwrapping */
	PK11_FreeSymKey(symKey);
	symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey,
						target, keySize, wincx);
   }

   return symKey;
}

/* use a symetric key to unwrap another symetric key */
PK11SymKey *
PK11_UnwrapSymKey( PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
		SECItem *wrappedKey, CK_MECHANISM_TYPE target, int keySize)
{
    return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
		wrapType,  wrappedKey, target, keySize, wrappingKey->cx);
}

/* unwrap a key symetric key with a private key. */
PK11SymKey *
PK11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey,
	 			  CK_MECHANISM_TYPE target, int keySize)
{
    CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType);
    PK11_HandlePasswordCheck(wrappingKey->pkcs11Slot,wrappingKey->wincx);
    
    return pk11_AnyUnwrapKey(wrappingKey->pkcs11Slot, wrappingKey->pkcs11ID,
	wrapType, wrappedKey, target, keySize, wrappingKey->wincx);

}

/*
 * Recover the Signed data. We need this because !#$#$#!# Verify can't
 * figure out which hash algorithm to use until we decrypt this!. (sigh)
 */
SECStatus
PK11_VerifyRecover(SECKEYPublicKey *key,
			 	SECItem *sig, SECItem *dsig, void *wincx)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_OBJECT_HANDLE id = key->pkcs11ID;
    CK_MECHANISM mech = {0, NULL, 0 };
    CK_ULONG len;
    CK_RV crv;

    mech.mechanism = pk11_mapSignKeyType(key->keyType);

    if (slot == NULL) {
	slot = PK11_GetBestSlot(mech.mechanism,wincx);
	if (slot == NULL) {
	    	PORT_SetError( SEC_ERROR_NO_MODULE );
		return SECFailure;
	}
	id = PK11_ImportPublicKey(slot,key);
    }

    crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(slot->session,&mech,id);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    len = dsig->len;
    crv = PK11_GETTAB(slot)->C_VerifyRecover(slot->session,sig->data,
						sig->len, dsig->data, &len);
    dsig->len = len;
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

/*
 * verify a signature from its hash.
 */
SECStatus
PK11_Verify(SECKEYPublicKey *key, SECItem *sig, SECItem *hash, void *wincx)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_OBJECT_HANDLE id = key->pkcs11ID;
    CK_MECHANISM mech = {0, NULL, 0 };
    CK_RV crv;

    mech.mechanism = pk11_mapSignKeyType(key->keyType);

    if (slot == NULL) {
	slot = PK11_GetBestSlot(mech.mechanism,wincx);
	if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return SECFailure;
	}
	id = PK11_ImportPublicKey(slot,key);
    }

    crv = PK11_GETTAB(slot)->C_VerifyInit(slot->session,&mech,id);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_Verify(slot->session,hash->data,
					hash->len, sig->data, sig->len);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

/*
 * sign a hash. The algorithm is determined by the key.
 */
SECStatus
PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, SECItem *hash)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_MECHANISM mech = {0, NULL, 0 };
    CK_ULONG len;
    CK_RV crv;

    mech.mechanism = pk11_mapSignKeyType(key->keyType);

    PK11_HandlePasswordCheck(slot, key->wincx);
    crv = PK11_GETTAB(slot)->C_SignInit(slot->session,&mech,key->pkcs11ID);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    len = sig->len;
    crv = PK11_GETTAB(slot)->C_Sign(slot->session,hash->data,
					hash->len, sig->data, &len);
    sig->len = len;
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

/*
 * Now SSL 2.0 uses raw RSA stuff. These next to functions *must* use
 * RSA keys, or they'll fail. We do the checks up front. If anyone comes
 * up with a meaning for rawdecrypt for any other public key operation,
 * then we need to move this check into some of PK11_PubDecrypt callser,
 * (namely SSL 2.0).
 */
SECStatus
PK11_PubDecryptRaw(SECKEYPrivateKey *key, unsigned char *data, 
	unsigned *outLen, unsigned int maxLen, unsigned char *enc,
							 unsigned encLen)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 };
    CK_ULONG out = maxLen;
    CK_RV crv;

    if (key->keyType != rsaKey) {
	PORT_SetError( SEC_ERROR_INVALID_KEY );
	return SECFailure;
    }

    /* REALLY? do we want to do a PK11_handle check here? for simple
     * decryption? Well it's probably mute anyway since only servers
     * wind up using this function */
    PK11_HandlePasswordCheck(slot, key->wincx);
    crv = PK11_GETTAB(slot)->C_DecryptInit(slot->session,&mech,key->pkcs11ID);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_Decrypt(slot->session,enc, encLen,
								data, &out);
    *outLen = out;
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

/* The encrypt version of the above function */
SECStatus
PK11_PubEncryptRaw(SECKEYPublicKey *key, unsigned char *enc,
		unsigned char *data, unsigned dataLen, void *wincx)
{
    PK11SlotInfo *slot;
    CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 };
    CK_OBJECT_HANDLE id;
    CK_ULONG out = dataLen;
    CK_RV crv;

    if (key->keyType != rsaKey) {
	PORT_SetError( SEC_ERROR_BAD_KEY );
	return SECFailure;
    }

    slot = PK11_GetBestSlot(mech.mechanism, wincx);
    if (slot == NULL) {
	PORT_SetError( SEC_ERROR_NO_MODULE );
	return SECFailure;
    }

    id = PK11_ImportPublicKey(slot,key);

    crv = PK11_GETTAB(slot)->C_EncryptInit(slot->session,&mech,id);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_Encrypt(slot->session,data,dataLen,enc,&out);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

	
/**********************************************************************
 *
 *                   Now Deal with Crypto Contexts
 *
 **********************************************************************/

/*
 * Free up a Cipher Context
 */
void
PK11_DestroyContext(PK11Context *context, PRBool freeit)
{
    if (context->ownSession) {
        (void) PK11_GETTAB(context->slot)->C_CloseSession(context->session);
    }
    /* initialize the critical fields of the context */
    if (context->savedData != NULL ) PORT_Free(context->savedData);
    if (context->key) PK11_FreeSymKey(context->key);
    if (context->param) SECITEM_FreeItem(context->param, PR_TRUE);
    PK11_FreeSlot(context->slot);
    if (freeit) PORT_Free(context);
}

/*
 * save the current context. Allocate Space if necessary.
 */
void *
pk11_saveContext(PK11Context *context,void *space, unsigned long *savedLength)
{
    CK_ULONG length;
    CK_RV crv;

    if (space == NULL) {
	crv =PK11_GETTAB(context->slot)->C_GetOperationState(context->session,
				NULL,&length);
	if (crv != CKR_OK) {
	    PORT_SetError( PK11_MapError(crv) );
	    return NULL;
	}
	context->savedData = PORT_Alloc(length);
	if (context->savedData == NULL) return NULL;
	*savedLength = length;
    }
    crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session,
					space,savedLength);
    if (crv != CKR_OK) {
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }
    return space;
}

/*
 * restore the current context
 */
SECStatus
pk11_restoreContext(PK11Context *context,void *space, unsigned long savedLength)
{
    CK_RV crv;
    CK_OBJECT_HANDLE objectID = (context->key) ? context->key->objectID:
			CK_INVALID_KEY;

    PORT_Assert(space != NULL);
    if (space == NULL) {
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	return SECFailure;
    }
    crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session,
        space, savedLength, objectID, 0);
    if (crv != CKR_OK) {
       PORT_SetError( PK11_MapError(crv));
       return SECFailure;
   }
   return SECSuccess;
}

/*
 * Context initialization. Used by all flavors of CreateContext
 */
static SECStatus 
pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info)
{
    CK_RV crv;
    PK11SymKey *symKey = context->key;

    switch (context->operation) {
    case CKA_ENCRYPT:
	crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session,
				mech_info, symKey->objectID);
	break;
    case CKA_DECRYPT:
	crv=PK11_GETTAB(context->slot)->C_DecryptInit(context->session,
				mech_info, symKey->objectID);
	break;
    case CKA_SIGN:
	crv=PK11_GETTAB(context->slot)->C_SignInit(context->session,
				mech_info, symKey->objectID);
	break;
    case CKA_DIGEST:
	crv=PK11_GETTAB(context->slot)->C_DigestInit(context->session,
				mech_info);
	break;
    default:
	crv = CKR_OPERATION_NOT_INITIALIZED;
	break;
    }

    if (crv != CKR_OK) {
        PORT_SetError( PK11_MapError(crv) );
        return SECFailure;
    }

    /*
     * handle session starvation case.. use our last session to multiplex
     */
    if (!context->ownSession) {
	context->savedData = pk11_saveContext(context,context->savedData,
				&context->savedLength);
	if (context->savedData == NULL) return SECFailure;
	/* clear out out session for others to use */
	return PK11_Finalize(context);
    }
    return SECSuccess;
}


/*
 * Common Helper Function do come up with a new context.
 */
static PK11Context *pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type,
     PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey,
							     SECItem *param)
{
    CK_MECHANISM mech_info;
    PK11Context *context;
    SECStatus rv;
	
    context = (PK11Context *) PORT_Alloc(sizeof(PK11Context));
    if (context == NULL) {
	return NULL;
    }

    /* initialize the critical fields of the context */
    context->operation = operation;
    context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL;
    context->slot = PK11_ReferenceSlot(slot);
    context->session = pk11_GetNewSession(slot,&context->ownSession);
    context->cx = symKey ? symKey->cx : NULL;
    /* get our session */
    context->savedData = NULL;

    /* save the parameters so that some digesting stuff can do multiple
     * begins on a single context */
    context->type = type;
    context->param = SECITEM_DupItem(param);
    context->init = PR_FALSE;
    if (context->param == NULL) {
	PK11_DestroyContext(context,PR_TRUE);
	return NULL;
    }

    mech_info.mechanism = type;
    mech_info.pParameter = param->data;
    mech_info.ulParameterLen = param->len;
    rv = pk11_context_init(context,&mech_info);

    if (rv != SECSuccess) {
	PK11_DestroyContext(context,PR_TRUE);
	return NULL;
    }
    context->init = PR_TRUE;
    return context;
}


/*
 * put together the various PK11_Create_Context calls used by different
 * parts of libsec.
 */
PK11Context *
PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
     CK_ATTRIBUTE_TYPE operation, SECItem *key, SECItem *param, void *wincx)
{
    PK11SymKey *symKey;
    PK11Context *context;

    /* first get a slot */
    if (slot == NULL) {
	slot = PK11_GetBestSlot(type,wincx);
	if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return NULL;
	}
    } else {
	PK11_ReferenceSlot(slot);
    }

    /* now import the key */
    symKey = PK11_ImportSymKey(slot, type, operation,  key, wincx);
    if (symKey == NULL) return NULL;

    context = PK11_CreateContextBySymKey(type, operation, symKey, param);

    PK11_FreeSymKey(symKey);
    PK11_FreeSlot(slot);

    return context;
}


/*
 * Create a context from a key. We really should make sure we aren't using
 * the same key in multiple session!
 */
PK11Context *
PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,CK_ATTRIBUTE_TYPE operation,
			PK11SymKey *symKey, SECItem *param)
{
    PK11SymKey *newKey;
    PK11Context *context;

    /* if this slot doesn't support the mechanism, go to a slot that does */
    newKey = pk11_ForceSlot(symKey,type,operation);
    if (newKey == NULL) {
	PK11_ReferenceSymKey(symKey);
    } else {
	symKey = newKey;
    }

    /* Context Adopts the symKey.... */
    context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey,
							     param);
    PK11_FreeSymKey(symKey);
    return context;
}

/*
 * Digest contexts don't need keys, but the do need to find a slot.
 * Macing should use PK11_CreateContextBySymKey.
 */
PK11Context *
PK11_CreateDigestContext(SECOidTag hashAlg)
{
    /* digesting has to work without authentication to the slot */
    CK_MECHANISM_TYPE type;
    PK11SlotInfo *slot;
    PK11Context *context;
    SECItem param;

    type = PK11_AlgtagToMechanism(hashAlg);
    slot = PK11_GetBestSlot(type, NULL);
    if (slot == NULL) {
	PORT_SetError( SEC_ERROR_NO_MODULE );
	return NULL;
    }

    /* maybe should really be PK11_GenerateNewParam?? */
    param.data = NULL;
    param.len = 0;

    context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, &param);
    PK11_FreeSlot(slot);
    return context;
}

/*
 * create a new context which is the clone of the state of old context.
 */
PK11Context * PK11_CloneContext(PK11Context *old)
{
     PK11Context *new;
     PRBool needFree = PR_FALSE;
     SECStatus rv = SECSuccess;
     void *data;
     unsigned long len;

     new = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation,
						old->key, old->param);
     if (new == NULL) return NULL;

     /* now clone the save state. First we need to find the save state
      * of the old session. If the old context owns it's session,
      * the state needs to be saved, otherwise the state is in saveData. */
     if (old->ownSession) {
	data=pk11_saveContext(old,NULL,&len);
	needFree = PR_TRUE;
     } else {
	data = old->savedData;
	len = old->savedLength;
     }

     if (data == NULL) {
	PK11_DestroyContext(new,PR_TRUE);
	return NULL;
     }

     /* now copy that state into our new context. Again we have different
      * work if the new context owns it's own session. If it does, we
      * restore the state gathered above. If it doesn't, we copy the
      * saveData pointer... */
     if (new->ownSession) {
	rv = pk11_restoreContext(new,data,len);
     } else {
	XP_ASSERT(new->savedData != NULL);
	if ((new->savedData == NULL) || (new->savedLength < len)) {
	    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
	    rv = SECFailure;
	} else {
	    PORT_Memcpy(new->savedData,data,len);
	    new->savedLength = len;
	}
    }

    if (needFree) PORT_Free(data);

    if (rv != SECSuccess) {
	PK11_DestroyContext(new,PR_TRUE);
	return NULL;
    }
    return new;
}

/*
 * This is a hack to get FIPS compliance until we can convert
 * libjar and ssl to use PK11_ hashing functions. It returns PR_FALSE
 * if we can't get a PK11 Context.
 */
PRBool
PK11_HashOK(SECOidTag algID) {
    PK11Context *cx;

    cx = PK11_CreateDigestContext(algID);
    if (cx == NULL) return PR_FALSE;
    PK11_DestroyContext(cx, PR_TRUE);
    return PR_TRUE;
}



/*
 * start a new digesting or Mac'ing operation on this context
 */
SECStatus PK11_DigestBegin(PK11Context *cx)
{
    CK_MECHANISM mech_info;
    SECStatus rv;

    if (cx->init == PR_TRUE) {
	return SECSuccess;
    }

    mech_info.mechanism = cx->type;
    mech_info.pParameter = cx->param->data;
    mech_info.ulParameterLen = cx->param->len;
    rv = pk11_context_init(cx,&mech_info);

    if (rv != SECSuccess) {
	return SECFailure;
    }
    cx->init = PR_TRUE;
    return SECSuccess;
}

SECStatus
PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in, 
								int32 len) {
    PK11Context *context;
    unsigned int max_length;
    unsigned int out_length;
    SECStatus rv;

    context = PK11_CreateDigestContext(hashAlg);
    if (context == NULL) return SECFailure;

    rv = PK11_DigestBegin(context);
    if (rv != SECSuccess) {
	PK11_DestroyContext(context, PR_TRUE);
	return rv;
    }

    rv = PK11_DigestOp(context, in, len);
    if (rv != SECSuccess) {
	PK11_DestroyContext(context, PR_TRUE);
	return rv;
    }

    /* Sigh, we need the output length */
    switch (hashAlg) {
    case  SEC_OID_SHA1: max_length = SHA1_LENGTH; break;
    case  SEC_OID_MD2: max_length = MD2_LENGTH; break;
    case  SEC_OID_MD5: max_length = MD5_LENGTH; break;
    default: max_length = 16; break;
    }

    rv = PK11_DigestFinal(context,out,&out_length,max_length);
    PK11_DestroyContext(context, PR_TRUE);
    return rv;
}


/*
 * execute a bulk encryption operation
 */
SECStatus
PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen, 
				int maxout, unsigned char *in, int inlen)
{
    CK_RV crv;
    CK_ULONG length = maxout;
    PK11SymKey *symKey = context->key;
    SECStatus rv;

    /* if we ran out of session, we need to restore our previously stored
     * state.
     */
    if (!context->ownSession) {
        rv = pk11_restoreContext(context,context->savedData,
							context->savedLength);
	if (rv != SECSuccess) return rv;
    }

    switch (context->operation) {
    case CKA_ENCRYPT:
	length = maxout;
	crv=PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session,
						in, inlen, out, &length);
	break;
    case CKA_DECRYPT:
	length = maxout;
	crv=PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session,
						in, inlen, out, &length);
	break;
    default:
	crv = CKR_OPERATION_NOT_INITIALIZED;
	break;
    }

    if (crv != CKR_OK) {
        PORT_SetError( PK11_MapError(crv) );
        return SECFailure;
    }
    *outlen = length;

    /*
     * handle session starvation case.. use our last session to multiplex
     */
    if (!context->ownSession) {
	context->savedData = pk11_saveContext(context,context->savedData,
				&context->savedLength);
	if (context->savedData == NULL) return SECFailure;
	
	/* clear out out session for others to use */
	return PK11_Finalize(context);
    }
    return SECSuccess;
}

/*
 * execute a bulk encryption operation
 */
SECStatus
PK11_DigestOp(PK11Context *context, unsigned char * in, unsigned inLen) 
{
    CK_RV crv;
    SECStatus rv;

    /* if we ran out of session, we need to restore our previously stored
     * state.
     */
    if (!context->ownSession) {
        rv = pk11_restoreContext(context,context->savedData,
							context->savedLength);
	if (rv != SECSuccess) return rv;
    }

    switch (context->operation) {
    /* also for MAC'ing */
    case CKA_SIGN:
	crv=PK11_GETTAB(context->slot)->C_SignUpdate(context->session,
						in, inLen);
	break;
    case CKA_DIGEST:
	crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session,
						in, inLen);
	break;
    default:
	crv = CKR_OPERATION_NOT_INITIALIZED;
	break;
    }

    if (crv != CKR_OK) {
        PORT_SetError( PK11_MapError(crv) );
        return SECFailure;
    }

    /*
     * handle session starvation case.. use our last session to multiplex
     */
    if (!context->ownSession) {
	context->savedData = pk11_saveContext(context,context->savedData,
				&context->savedLength);
	if (context->savedData == NULL) return SECFailure;
	
	/* clear out out session for others to use */
	return PK11_Finalize(context);
    }
    return SECSuccess;
}


/*
 * clean up a cipher operation, so the session can be used by
 * someone new.
 */
SECStatus
PK11_Finalize(PK11Context *context)
{
    CK_ULONG count;
    CK_RV crv;

    if (!context->ownSession) {
	return SECSuccess;
    }

    switch (context->operation) {
    case CKA_ENCRYPT:
	crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
				NULL,&count);
	break;
    case CKA_DECRYPT:
	crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session,
				NULL,&count);
	break;
    case CKA_SIGN:
	crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session,
				NULL,&count);
	break;
    case CKA_DIGEST:
	crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session,
				NULL,&count);
	break;
    default:
	crv = CKR_OPERATION_NOT_INITIALIZED;
	break;
    }

    if (crv != CKR_OK) {
        PORT_SetError( PK11_MapError(crv) );
        return SECFailure;
    }
    return SECSuccess;
}

/*
 *  Return the final digested or signed data...
 *  this routine can either take pre initialized data, or allocate data
 *  either out of an arena or out of the standard heap.
 */
SECStatus
PK11_DigestFinal(PK11Context *context,unsigned char *data, 
			unsigned int *outLen, unsigned int length)
{
    CK_ULONG len;
    CK_RV crv;
    SECStatus rv;


    /* if we ran out of session, we need to restore our previously stored
     * state.
     */
    if (!context->ownSession) {
        rv = pk11_restoreContext(context,context->savedData,
							context->savedLength);
	if (rv != SECSuccess) return rv;
    }

    len = length;
    switch (context->operation) {
    case CKA_SIGN:
	crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session,
				data,&len);
	break;
    case CKA_DIGEST:
	crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session,
				data,&len);
	break;
    case CKA_ENCRYPT:
	crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
				data, &len);
	break;
    case CKA_DECRYPT:
	crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session,
				data, &len);
	break;
    default:
	crv = CKR_OPERATION_NOT_INITIALIZED;
	break;
    }

    *outLen = (unsigned int) len;

    context->init = PR_FALSE; /* allow Begin to start up again */

    if (crv != CKR_OK) {
        PORT_SetError( PK11_MapError(crv) );
	return SECFailure;
    }
    return SECSuccess;
}

SECAlgorithmID *
PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt,
			  int keyLength, SECItem *iv, SECItem *op_param)
{
    SECAlgorithmID *algid;

    algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, iteration, keyLength,
    				       				iv, op_param);
    return algid;
}

PK11SymKey *
PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
	       							void *wincx)
{
    CK_ULONG key_size = 0;
    /* we have to use these native types because when we call PKCS 11 modules
     * we have to make sure that we are using the correct sizes for all the
     * parameters. */
    CK_BBOOL ckfalse = FALSE; /* Sigh */
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_ATTRIBUTE genTemplate[] = {
	{ CKA_SENSITIVE, NULL, 0},
	{ CKA_ENCRYPT, NULL, 0},
	/* the next entry must be the last one */
	{ CKA_VALUE_LEN, NULL, 0},
    };
    int count = sizeof(genTemplate)/sizeof(genTemplate[0]);
    CK_MECHANISM mechanism;
    CK_MECHANISM_TYPE key_gen_mechanism;
    PK11SymKey *symKey;
    CK_RV crv;
    CK_ATTRIBUTE *attrs = genTemplate;

    /* pbe stuff */
    CK_NETSCAPE_PBE_PARAMS *pbe_params;
    CK_MECHANISM_TYPE type;
    SECItem *mech;

    mech = PK11_ParamFromAlgid(algid);
    type = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm));
    if(mech == NULL) {
	return NULL;
    } else {
	pbe_params = (CK_NETSCAPE_PBE_PARAMS *)mech->data;
	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;
	}
    }

    PK11_SETATTRS(attrs, CKA_SENSITIVE, &ckfalse, sizeof(CK_BBOOL)); attrs++;
    PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue, sizeof(CK_BBOOL)); attrs++;
    /* the next entry must be the last one */
    PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); attrs++;

    /* find a slot to generate the key into */
    if ((slot == NULL) || (!PK11_DoesMechanism(slot,type))) {
        slot = PK11_GetBestSlot(type,wincx);
        if (slot == NULL) {
	    PORT_SetError( SEC_ERROR_NO_MODULE );
	    return NULL;
	}
    } else {
	PK11_ReferenceSlot(slot);
    }

    /* Initialize the Key Gen Mechanism */
    key_gen_mechanism = PK11_GetKeyGen(type);
    if (key_gen_mechanism == CKM_FAKE_RANDOM) {
	PK11_FreeSlot(slot);
	PORT_SetError( SEC_ERROR_NO_MODULE );
	return NULL;
    }
    mechanism.mechanism = type;
    mechanism.pParameter = mech->data;
    mechanism.ulParameterLen = mech->len;

    /* if key_size was not specified, the mechanism must know it's own size */
    if (key_size == 0) {
	count--;
    }

    /* get our key Structure */
    symKey = PK11_CreateSymKey(slot,type,wincx);
    PK11_FreeSlot(slot);
    if (symKey == NULL) {
	SECITEM_ZfreeItem(mech, PR_TRUE);
	return NULL;
    }
    symKey->size = key_size;

    crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(symKey->slot->session,
			 &mechanism, genTemplate, count, &symKey->objectID);
    if (crv != CKR_OK) {
	SECITEM_ZfreeItem(mech, PR_TRUE);
	PK11_FreeSymKey(symKey);
	PORT_SetError( PK11_MapError(crv) );
	return NULL;
    }
    SECITEM_ZfreeItem(mech, PR_TRUE);
    return symKey;
}

SECStatus 
PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
   SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
				   		SECItem *nickname, void *wincx)
{
    SECKEYPrivateKeyInfo *pki = NULL;
    SECItem dbuf;
    CK_MECHANISM_TYPE mechanism;
    SECItem *pbe_param, crypto_param;
    PK11SymKey *key = NULL;
    SECStatus rv = SECSuccess;
    CK_MECHANISM cryptoMech, pbeMech;
    CK_RV crv;
    void *cx;
    int bs;

    if((epki == NULL) || (pwitem == NULL))
	return SECFailure;

    dbuf.data = NULL;
    crypto_param.data = NULL;

    mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag(
    	&epki->algorithm.algorithm));
    pbe_param = PK11_ParamFromAlgid(&epki->algorithm);
    key = PK11_PBEKeyGen(slot, &epki->algorithm, pwitem, wincx);
    if((key == NULL) || (pbe_param == NULL)) {
	rv = SECFailure;
	goto loser;
    }

    pbeMech.mechanism = mechanism;
    pbeMech.pParameter = pbe_param->data;
    pbeMech.ulParameterLen = pbe_param->len;

    crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem);
    if(crv != CKR_OK) {
	rv = SECFailure;
	goto loser;
    }
    crypto_param.data = cryptoMech.pParameter;
    crypto_param.len = cryptoMech.ulParameterLen;

    dbuf.data = (unsigned char *)PORT_ZAlloc(epki->encryptedData.len + 64);
    if(dbuf.data == NULL) {
	rv = SECFailure;
        goto loser;
    }
    dbuf.len = epki->encryptedData.len + 64;

    cx = PK11_CreateContextBySymKey(cryptoMech.mechanism, CKA_DECRYPT, 
    				    key, &crypto_param);
    if(cx == NULL) {
	rv = SECFailure;
	goto loser;
    }
    rv = PK11_CipherOp(cx, dbuf.data, (int *)(&dbuf.len), (int)dbuf.len, 
    			epki->encryptedData.data, (int)epki->encryptedData.len);
    PK11_DestroyContext(cx, PR_TRUE);
    if(rv != SECSuccess) {
	goto loser;
    }

    /* remove pad characters 
     * assumes padding via RC2 cbc or DES cbc variant
     */
    bs = PK11_GetBlockSize(cryptoMech.mechanism, &crypto_param);
    if(bs) {
	if(((int)dbuf.data[dbuf.len-1] > 0) && 
	  ((int)dbuf.data[dbuf.len-1] <= bs)) {
	    dbuf.len = (dbuf.len - (int)dbuf.data[dbuf.len-1]);
	} else {
	    /* set an error? */
	    rv = SECFailure;
	    goto loser;
	}
    }

    pki = (SECKEYPrivateKeyInfo *)PORT_ZAlloc(sizeof(SECKEYPrivateKeyInfo));
    if(pki == NULL) {
	rv = SECFailure;
	goto loser;
    }
    rv = SEC_ASN1DecodeItem(NULL, pki, SECKEY_PrivateKeyInfoTemplate, &dbuf);
    if(rv != SECFailure) {
	rv = PK11_ImportPrivateKeyInfo(slot, pki, nickname, wincx);
    }

loser:
    if(pbe_param != NULL) {
	SECITEM_ZfreeItem(pbe_param, PR_TRUE);
	pbe_param = NULL;
    }

    if(crypto_param.data != NULL) {
	SECITEM_ZfreeItem(&crypto_param, PR_FALSE);
    }

    if(dbuf.data != NULL) {
	SECITEM_ZfreeItem(&dbuf, PR_FALSE);
    }

    if(key != NULL) {
    	PK11_FreeSymKey(key);
    }

    return rv;
}

/*
 * import a pprivate key info into the desired slot
 */
SECStatus
PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, SECKEYPrivateKeyInfo *pki, 
						SECItem *nickname, void *wincx) 
{
    CK_BBOOL cktrue = TRUE; /* sigh */
    CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
    CK_KEY_TYPE keyType = CKK_RSA;
    CK_OBJECT_HANDLE objectID;
    CK_ATTRIBUTE *template = NULL;
    int templateCount = 0;
    SECStatus rv = SECFailure;
    SECKEYLowPrivateKey *lpk = NULL;

    /* need to change this to use RSA/DSA keys */
    lpk = (SECKEYLowPrivateKey *)PORT_ZAlloc(sizeof(SECKEYLowPrivateKey));
    if(lpk == NULL) {
	goto loser;
    }
    rv = SEC_ASN1DecodeItem(NULL, lpk, SECKEY_RSAPrivateKeyTemplate,
			    &pki->privateKey);
    lpk->keyType = rsaKey; /* XXX RSA */
    lpk->arena = NULL;
    if(rv == SECFailure) {
	return SECFailure;
    }

    rv = SECFailure;

    /* now import the key */
    {
	CK_ATTRIBUTE rsaTemplate[] = {
	    { CKA_CLASS, NULL, 0 },
	    { CKA_KEY_TYPE, NULL, 0 },
	    { CKA_TOKEN, NULL, 0 },
	    { CKA_SENSITIVE, NULL, 0 },
	    { CKA_PRIVATE, NULL, 0 },
	    { CKA_UNWRAP,  NULL, 0 },
	    { CKA_DECRYPT, NULL, 0 },
	    { CKA_SIGN, NULL, 0 },
	    { CKA_SIGN_RECOVER, NULL, 0 },
	    { CKA_ID, NULL, 0 },
	    { CKA_LABEL, NULL, 0 }, 
	    { CKA_MODULUS, NULL, 0 },
	    { CKA_PUBLIC_EXPONENT, NULL, 0 },
	    { CKA_PRIVATE_EXPONENT, NULL, 0 },
	    { CKA_PRIME_1, NULL, 0 },
	    { CKA_PRIME_2, NULL, 0 },
	    { CKA_EXPONENT_1, NULL, 0 },
	    { CKA_EXPONENT_2, NULL, 0 },
	    { CKA_COEFFICIENT, NULL, 0 },
	};
        CK_ATTRIBUTE *attrs;

	CK_ATTRIBUTE dsaTemplate[] = {
	    { CKA_CLASS, NULL, 0},
	    { CKA_KEY_TYPE, NULL, 0},
	    { CKA_TOKEN, NULL, 0 },
	    { CKA_PRIVATE, NULL, 0 },
	    { CKA_SIGN, NULL, 0 },
	    { CKA_SIGN_RECOVER, NULL, 0 },
	    { CKA_ID, NULL, 0 },
	    { CKA_LABEL, NULL, 0 }, 
	    { CKA_PRIME, NULL, 0},
	    { CKA_SUBPRIME, NULL, 0},
	    { CKA_BASE, NULL, 0},
	    { CKA_VALUE, NULL, 0},
	};

        switch (lpk->keyType) {
        case rsaKey:
	    attrs = template = rsaTemplate;
	    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, 
			  			sizeof(keyClass) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, 
						sizeof(keyType) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_SENSITIVE, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_PRIVATE, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_UNWRAP, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_DECRYPT, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_ID, lpk->u.rsa.modulus.data,
	    				 lpk->u.rsa.modulus.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); attrs++; 
	    PK11_SETATTRS(attrs, CKA_MODULUS, lpk->u.rsa.modulus.data,
						lpk->u.rsa.modulus.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, 
	     					lpk->u.rsa.publicExponent.data,
					 	lpk->u.rsa.publicExponent.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_PRIVATE_EXPONENT, 
	     					lpk->u.rsa.privateExponent.data,
					 	lpk->u.rsa.privateExponent.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_PRIME_1, 
	     					lpk->u.rsa.prime[0].data,
					 	lpk->u.rsa.prime[0].len); attrs++;
	    PK11_SETATTRS(attrs, CKA_PRIME_2, 
	     					lpk->u.rsa.prime[1].data,
					 	lpk->u.rsa.prime[1].len); attrs++;
	    PK11_SETATTRS(attrs, CKA_EXPONENT_1, 
	     					lpk->u.rsa.primeExponent[0].data,
					 	lpk->u.rsa.primeExponent[0].len); attrs++;
	    PK11_SETATTRS(attrs, CKA_EXPONENT_2, 
	     					lpk->u.rsa.primeExponent[1].data,
					 	lpk->u.rsa.primeExponent[1].len); attrs++;
	    PK11_SETATTRS(attrs, CKA_COEFFICIENT, 
	     					lpk->u.rsa.coefficient.data,
					 	lpk->u.rsa.coefficient.len);
	    templateCount = sizeof(rsaTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_RSA;
	    break;
        case dsaKey:
	    attrs = template = dsaTemplate;
	    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, 
						sizeof(keyClass)); attrs++;
	    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, 
						sizeof(keyType)); attrs++;
	    PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_PRIVATE, &cktrue, sizeof(CK_BBOOL) ); attrs++;
	    PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); attrs++;
	    PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL)); attrs++;
	    PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); attrs++; 
	    PK11_SETATTRS(attrs, CKA_ID,    lpk->u.dsa.publicValue.data, 
					lpk->u.dsa.publicValue.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_PRIME,    lpk->u.dsa.params.prime.data,
				lpk->u.dsa.params.prime.len); attrs++;
	    PK11_SETATTRS(attrs,CKA_SUBPRIME,lpk->u.dsa.params.subPrime.data,
				lpk->u.dsa.params.subPrime.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_BASE,  lpk->u.dsa.params.base.data,
					lpk->u.dsa.params.base.len); attrs++;
	    PK11_SETATTRS(attrs, CKA_VALUE,    lpk->u.dsa.publicValue.data, 
					lpk->u.dsa.publicValue.len); attrs++;
	    templateCount = sizeof(dsaTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_DSA;
	    break;
#ifdef notdef
        case dhKey:
	    template = dhTemplate;
	    templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE);
	    keyType = CKK_DH;
	    break;
#endif
	/* what about fortezza??? */
	default:
	    PORT_SetError(SEC_ERROR_BAD_KEY);
	    goto loser;
	}

        rv = PK11_CreateNewObject(slot,template,templateCount, &objectID);

    }

loser:
    if(lpk != NULL) {
	SECKEY_LowDestroyPrivateKey(lpk);
    }

    return rv;
}

SECKEYPrivateKeyInfo *
PK11_ExportPrivateKeyInfo(SECItem *nickname, CERTCertificate *cert,
			  					void *wincx)
{
    SECKEYPrivateKey *pk = NULL;
    SECKEYPrivateKeyInfo *pki = NULL;
    SECKEYLowPrivateKey *lk = NULL;
    SECItem *dlk = NULL;
    PRArenaPool *arena = NULL;
    SECStatus rv = SECFailure;
    SECItem pkitem;
    CK_RV crv;
    CK_MECHANISM mechanism;
    void *dummy;

    pk = PK11_FindKeyByAnyCert(cert, wincx);
    if(pk == NULL) {
	return NULL;
    }

    pkitem.data = (unsigned char *)PORT_ZAlloc(sizeof(SECKEYLowPrivateKey *));
    mechanism.mechanism = CKM_NETSCAPE_NULL_ENCRYPT;

    /* XXX we are extracting an unencrypted SECKEYLowPrivateKey structure.
     * which needs to be freed along with the buffer into which it is
     * returned.  eventually, we should retrieve an encrypted key using
     * pkcs8/pkcs5.
     */
    crv = PK11_GETTAB(pk->pkcs11Slot)->C_WrapKey(pk->pkcs11Slot->session, 
    		&mechanism, 0, pk->pkcs11ID, pkitem.data, 
		(CK_ULONG_PTR)(&pkitem.len)); 
    if(crv != CKR_OK) {
	rv = SECFailure;
	goto loser;
    }

    lk = *((SECKEYLowPrivateKey **)pkitem.data);
    if(lk == NULL) {
	rv = SECFailure;
	goto loser;
    }

    PORT_Assert(lk->keyType == rsaKey); /* XXX RSA */
    dlk = SEC_ASN1EncodeItem(NULL, NULL, lk, SECKEY_RSAPrivateKeyTemplate);
    if(dlk == NULL) {
	rv = SECFailure;
	goto loser;
    }

    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    if(arena == NULL) {
	rv = SECFailure;
	goto loser;
    }
    pki = (SECKEYPrivateKeyInfo *)PORT_ArenaZAlloc(arena, 
    					sizeof(SECKEYPrivateKeyInfo));
    if(pki == NULL) {
	rv = SECFailure;
	goto loser;
    }
    dummy = SEC_ASN1EncodeInteger(arena, &(pki->version), 
				  SEC_PRIVATE_KEY_INFO_VERSION);
    if(dummy == NULL) {
	rv = SECFailure;
	goto loser;
    }
    pki->arena = arena;
    switch(lk->keyType) {
	case rsaKey:
	case dsaKey:
	    rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, 
	    	SEC_OID_PKCS1_RSA_ENCRYPTION, NULL);
	    break;
	default:
	    rv = SECFailure;
	    goto loser;
    }
    rv = SECITEM_CopyItem(arena, &pki->privateKey, dlk);

loser:
    if(pk != NULL) {
	SECKEY_DestroyPrivateKey(pk);
	pk = NULL;
    }

    if(dlk != NULL) {
	SECITEM_ZfreeItem(dlk, PR_TRUE);
	dlk = NULL;
    }

    if(lk != NULL) {
	SECKEY_LowDestroyPrivateKey(lk);
	lk = NULL;
    }

    if(pkitem.data != NULL) {
	PORT_Free(pkitem.data);
	pkitem.data = NULL;
    }

    if(rv == SECFailure) {
	if(arena != NULL) {
	    PORT_FreeArena(arena, PR_TRUE);
	    arena = NULL;
	}
	pki = NULL;
    }

    return pki;
}

SECKEYEncryptedPrivateKeyInfo * 
PK11_ExportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, SECOidTag algTag,
   SECItem *pwitem, SECItem *nickname, CERTCertificate *cert, 
				int iteration, int keyLength, void *wincx)
{
    SECKEYPrivateKeyInfo *pki = NULL;
    SECKEYEncryptedPrivateKeyInfo *epki = NULL;
    PRArenaPool *arena = NULL;
    SECAlgorithmID *algid;
    SECItem *dbuf;
    CK_MECHANISM_TYPE mechanism;
    SECItem *pbe_param = NULL, crypto_param;
    PK11SymKey *key = NULL;
    SECStatus rv = SECSuccess;
    void *cx;
    CK_MECHANISM pbeMech;
    CK_MECHANISM cryptoMech;
    CK_RV crv;
    SECItem *blocked_data = NULL;
    int bs;

    if((pwitem == NULL) || (nickname == NULL))
	return NULL;

    pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx);
    if(pki == NULL) {
	return NULL;
    }
    dbuf = SEC_ASN1EncodeItem(NULL, NULL, pki, SECKEY_PrivateKeyInfoTemplate);
    if(dbuf == NULL) {
	rv = SECFailure;
	goto loser;
    }

    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(arena, 
    		sizeof(SECKEYEncryptedPrivateKeyInfo));
    if(epki == NULL) {
	rv = SECFailure;
	goto loser;
    }
    epki->arena = arena;
    algid = SEC_PKCS5CreateAlgorithmID(algTag, NULL, iteration, 	
    		keyLength, NULL, NULL);
    if(algid == NULL) {
	rv = SECFailure;
	goto loser;
    }

    mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm));
    pbe_param = PK11_ParamFromAlgid(algid);
    pbeMech.mechanism = mechanism;
    pbeMech.pParameter = pbe_param->data;
    pbeMech.ulParameterLen = pbe_param->len;
    key = PK11_PBEKeyGen(slot, algid, pwitem, wincx);

    if((key == NULL) || (pbe_param == NULL)) {
	rv = SECFailure;
	goto loser;
    }

    crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem);
    if(crv != CKR_OK) {
	rv = SECFailure;
	goto loser;
    }
    crypto_param.data = (unsigned char *)cryptoMech.pParameter;
    crypto_param.len = cryptoMech.ulParameterLen;

    epki->encryptedData.data = (unsigned char *)PORT_ArenaZAlloc(arena,
    							dbuf->len + 64);
    if(epki->encryptedData.data == NULL) {
	rv = SECFailure;
	goto loser;
    }

    /* block according to PKCS 8 */
    bs = PK11_GetBlockSize(cryptoMech.mechanism, &crypto_param);
    rv = SECSuccess;
    if(bs) {
	char pad_char;
	pad_char = (char)(bs - (dbuf->len % bs));
	if(dbuf->len % bs) {
	    rv = SECSuccess;
	    blocked_data = PK11_BlockData(dbuf, bs);
	    if(blocked_data) {
		PORT_Memset((blocked_data->data + blocked_data->len - (int)pad_char), 
			    pad_char, (int)pad_char);
	    } else {
		rv = SECFailure;
		goto loser;
	    }
	} else {
	    blocked_data = SECITEM_DupItem(dbuf);
	    if(blocked_data) {
		blocked_data->data = PORT_Realloc(blocked_data->data,
						  blocked_data->len + bs);
		if(blocked_data->data) {
		    blocked_data->len += bs;
		    PORT_Memset((blocked_data->data + dbuf->len), (char)bs, bs);
		} else {
		    rv = SECFailure;
		    goto loser;
		}
	    } else {
		rv = SECFailure;
		goto loser;
	    }
	 }
    }

    cx = PK11_CreateContextBySymKey(cryptoMech.mechanism, CKA_ENCRYPT, 
		key, &crypto_param);
    if(cx == NULL) {
	rv = SECFailure;
	goto loser;
    }

    rv = PK11_CipherOp(cx, epki->encryptedData.data, 
    			(int *)(&epki->encryptedData.len), (int)(dbuf->len + 64),
    			blocked_data->data, (int)blocked_data->len);
    PK11_DestroyContext(cx, PR_TRUE);
    if(rv != SECSuccess) {
	goto loser;
    }

    rv = SECOID_CopyAlgorithmID(arena, &epki->algorithm, algid);

loser:
    if(pbe_param != NULL) {
	SECITEM_ZfreeItem(pbe_param, PR_TRUE);
	pbe_param = NULL;
    }

    if(blocked_data != NULL) {
	SECITEM_ZfreeItem(blocked_data, PR_TRUE);
	blocked_data = NULL;
    }

    if(crypto_param.data != NULL) {
	SECITEM_ZfreeItem(&crypto_param, PR_FALSE);
	crypto_param.data = NULL;
    }

    if(dbuf != NULL) {
	SECITEM_ZfreeItem(dbuf, PR_TRUE);
    }

    if(key != NULL) {
    	PK11_FreeSymKey(key);
    }

    if(pki != NULL) {
	PORT_FreeArena(pki->arena, PR_FALSE);
    }

    if(rv == SECFailure) {
	if(arena != NULL) {
	    PORT_FreeArena(arena, PR_TRUE);
	}
	epki = NULL;
    }

    return epki;
}