diff nss/lib/softoken/pkcs11c.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/softoken/pkcs11c.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,7448 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+ * This file implements PKCS 11 on top of our existing security modules
+ *
+ * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
+ *   This implementation has two slots:
+ *	slot 1 is our generic crypto support. It does not require login.
+ *   It supports Public Key ops, and all they bulk ciphers and hashes. 
+ *   It can also support Private Key ops for imported Private keys. It does 
+ *   not have any token storage.
+ *	slot 2 is our private key support. It requires a login before use. It
+ *   can store Private Keys and Certs as token objects. Currently only private
+ *   keys and their associated Certificates are saved on the token.
+ *
+ *   In this implementation, session objects are only visible to the session
+ *   that created or generated them.
+ */
+#include "seccomon.h"
+#include "secitem.h"
+#include "secport.h"
+#include "blapi.h"
+#include "pkcs11.h"
+#include "pkcs11i.h"
+#include "lowkeyi.h"
+#include "secder.h"
+#include "secdig.h"
+#include "lowpbe.h"	/* We do PBE below */
+#include "pkcs11t.h"
+#include "secoid.h"
+#include "alghmac.h"
+#include "softoken.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+#include "prprf.h"
+
+#define __PASTE(x,y)    x##y
+
+/*
+ * we renamed all our internal functions, get the correct
+ * definitions for them...
+ */ 
+#undef CK_PKCS11_FUNCTION_INFO
+#undef CK_NEED_ARG_LIST
+
+#define CK_EXTERN extern
+#define CK_PKCS11_FUNCTION_INFO(func) \
+		CK_RV __PASTE(NS,func)
+#define CK_NEED_ARG_LIST	1
+ 
+#include "pkcs11f.h"
+
+typedef struct {
+    PRUint8 client_version[2];
+    PRUint8 random[46];
+} SSL3RSAPreMasterSecret;
+
+static void sftk_Null(void *data, PRBool freeit)
+{
+    return;
+} 
+
+#ifndef NSS_DISABLE_ECC
+#ifdef EC_DEBUG
+#define SEC_PRINT(str1, str2, num, sitem) \
+    printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
+            str1, str2, num, sitem->len); \
+    for (i = 0; i < sitem->len; i++) { \
+	    printf("%02x:", sitem->data[i]); \
+    } \
+    printf("\n") 
+#else
+#define SEC_PRINT(a, b, c, d) 
+#endif
+#endif /* NSS_DISABLE_ECC */
+
+/*
+ * free routines.... Free local type  allocated data, and convert
+ * other free routines to the destroy signature.
+ */
+static void
+sftk_FreePrivKey(NSSLOWKEYPrivateKey *key, PRBool freeit)
+{
+    nsslowkey_DestroyPrivateKey(key);
+}
+
+static void
+sftk_Space(void *data, PRBool freeit)
+{
+    PORT_Free(data);
+} 
+
+/*
+ * map all the SEC_ERROR_xxx error codes that may be returned by freebl
+ * functions to CKR_xxx.  return CKR_DEVICE_ERROR by default for backward
+ * compatibility.
+ */
+static CK_RV
+sftk_MapCryptError(int error)
+{
+    switch (error) {
+	case SEC_ERROR_INVALID_ARGS:
+	case SEC_ERROR_BAD_DATA:  /* MP_RANGE gets mapped to this */
+	    return CKR_ARGUMENTS_BAD;
+	case SEC_ERROR_INPUT_LEN:
+	    return CKR_DATA_LEN_RANGE;
+	case SEC_ERROR_OUTPUT_LEN:
+	    return CKR_BUFFER_TOO_SMALL;
+	case SEC_ERROR_LIBRARY_FAILURE:
+	    return CKR_GENERAL_ERROR;
+	case SEC_ERROR_NO_MEMORY:
+	    return CKR_HOST_MEMORY;
+	case SEC_ERROR_BAD_SIGNATURE:
+	    return CKR_SIGNATURE_INVALID;
+	case SEC_ERROR_INVALID_KEY:
+	    return CKR_KEY_SIZE_RANGE;
+	case SEC_ERROR_BAD_KEY:  /* an EC public key that fails validation */
+	    return CKR_KEY_SIZE_RANGE;  /* the closest error code */
+	case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM:
+	    return CKR_TEMPLATE_INCONSISTENT;
+	/* EC functions set this error if NSS_DISABLE_ECC is defined */
+	case SEC_ERROR_UNSUPPORTED_KEYALG:
+	    return CKR_MECHANISM_INVALID;
+	case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE:
+	    return CKR_DOMAIN_PARAMS_INVALID;
+	/* key pair generation failed after max number of attempts */
+	case SEC_ERROR_NEED_RANDOM:
+	    return CKR_FUNCTION_FAILED;
+    }
+    return CKR_DEVICE_ERROR;
+}
+
+/* used by Decrypt and UnwrapKey (indirectly) */
+static CK_RV
+sftk_MapDecryptError(int error)
+{
+    switch (error) {
+	case SEC_ERROR_BAD_DATA:
+	    return CKR_ENCRYPTED_DATA_INVALID;
+	default:
+	    return sftk_MapCryptError(error);
+    }
+}
+
+/*
+ * return CKR_SIGNATURE_INVALID instead of CKR_DEVICE_ERROR by default for
+ * backward compatibilty.
+ */
+static CK_RV
+sftk_MapVerifyError(int error)
+{
+    CK_RV crv = sftk_MapCryptError(error);
+    if (crv == CKR_DEVICE_ERROR)
+	crv = CKR_SIGNATURE_INVALID;
+    return crv;
+}
+
+
+/*
+ * turn a CDMF key into a des key. CDMF is an old IBM scheme to export DES by
+ * Deprecating a full des key to 40 bit key strenth.
+ */
+static CK_RV
+sftk_cdmf2des(unsigned char *cdmfkey, unsigned char *deskey)
+{
+    unsigned char key1[8] = { 0xc4, 0x08, 0xb0, 0x54, 0x0b, 0xa1, 0xe0, 0xae };
+    unsigned char key2[8] = { 0xef, 0x2c, 0x04, 0x1c, 0xe6, 0x38, 0x2f, 0xe6 };
+    unsigned char enc_src[8];
+    unsigned char enc_dest[8];
+    unsigned int leng,i;
+    DESContext *descx;
+    SECStatus rv;
+    
+    
+    /* zero the parity bits */
+    for (i=0; i < 8; i++) {
+	enc_src[i] = cdmfkey[i] & 0xfe;
+    }
+
+    /* encrypt with key 1 */
+    descx = DES_CreateContext(key1, NULL, NSS_DES, PR_TRUE);
+    if (descx == NULL) return CKR_HOST_MEMORY;
+    rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8);
+    DES_DestroyContext(descx,PR_TRUE);
+    if (rv != SECSuccess) return sftk_MapCryptError(PORT_GetError());
+
+    /* xor source with des, zero the parity bits and deprecate the key*/
+    for (i=0; i < 8; i++) {
+	if (i & 1) {
+	    enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0xfe;
+	} else {
+	    enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0x0e;
+	}
+    }
+
+    /* encrypt with key 2 */
+    descx = DES_CreateContext(key2, NULL, NSS_DES, PR_TRUE);
+    if (descx == NULL) return CKR_HOST_MEMORY;
+    rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8);
+    DES_DestroyContext(descx,PR_TRUE);
+    if (rv != SECSuccess) return sftk_MapCryptError(PORT_GetError());
+
+    /* set the corret parity on our new des key */	
+    sftk_FormatDESKey(deskey, 8);
+    return CKR_OK;
+}
+
+
+/* NSC_DestroyObject destroys an object. */
+CK_RV
+NSC_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+    SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+    SFTKSession *session;
+    SFTKObject *object;
+    SFTKFreeStatus status;
+
+    CHECK_FORK();
+
+    if (slot == NULL) {
+	return CKR_SESSION_HANDLE_INVALID;
+    }
+    /*
+     * This whole block just makes sure we really can destroy the
+     * requested object.
+     */
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) {
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+
+    object = sftk_ObjectFromHandle(hObject,session);
+    if (object == NULL) {
+	sftk_FreeSession(session);
+	return CKR_OBJECT_HANDLE_INVALID;
+    }
+
+    /* don't destroy a private object if we aren't logged in */
+    if ((!slot->isLoggedIn) && (slot->needLogin) &&
+				(sftk_isTrue(object,CKA_PRIVATE))) {
+	sftk_FreeSession(session);
+	sftk_FreeObject(object);
+	return CKR_USER_NOT_LOGGED_IN;
+    }
+
+    /* don't destroy a token object if we aren't in a rw session */
+
+    if (((session->info.flags & CKF_RW_SESSION) == 0) &&
+				(sftk_isTrue(object,CKA_TOKEN))) {
+	sftk_FreeSession(session);
+	sftk_FreeObject(object);
+	return CKR_SESSION_READ_ONLY;
+    }
+
+    sftk_DeleteObject(session,object);
+
+    sftk_FreeSession(session);
+
+    /*
+     * get some indication if the object is destroyed. Note: this is not
+     * 100%. Someone may have an object reference outstanding (though that
+     * should not be the case by here. Also note that the object is "half"
+     * destroyed. Our internal representation is destroyed, but it may still
+     * be in the data base.
+     */
+    status = sftk_FreeObject(object);
+
+    return (status != SFTK_DestroyFailure) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+
+/*
+ ************** Crypto Functions:     Utilities ************************
+ */
+/*
+ * Utility function for converting PSS/OAEP parameter types into
+ * HASH_HashTypes. Note: Only SHA family functions are defined in RFC 3447.
+ */
+static HASH_HashType
+GetHashTypeFromMechanism(CK_MECHANISM_TYPE mech)
+{
+    switch (mech) {
+        case CKM_SHA_1:
+        case CKG_MGF1_SHA1:
+            return HASH_AlgSHA1;
+        case CKM_SHA224:
+        case CKG_MGF1_SHA224:
+            return HASH_AlgSHA224;
+        case CKM_SHA256:
+        case CKG_MGF1_SHA256:
+            return HASH_AlgSHA256;
+        case CKM_SHA384:
+        case CKG_MGF1_SHA384:
+            return HASH_AlgSHA384;
+        case CKM_SHA512:
+        case CKG_MGF1_SHA512:
+            return HASH_AlgSHA512;
+        default:
+            return HASH_AlgNULL;
+    }
+}
+
+/*
+ * Returns true if "params" contains a valid set of PSS parameters
+ */
+static PRBool
+sftk_ValidatePssParams(const CK_RSA_PKCS_PSS_PARAMS *params)
+{
+    if (!params) {
+        return PR_FALSE;
+    }
+    if (GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL ||
+        GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) {
+        return PR_FALSE;
+    }
+    return PR_TRUE;
+}
+
+/*
+ * Returns true if "params" contains a valid set of OAEP parameters
+ */
+static PRBool
+sftk_ValidateOaepParams(const CK_RSA_PKCS_OAEP_PARAMS *params)
+{
+    if (!params) {
+        return PR_FALSE;
+    }
+    /* The requirements of ulSourceLen/pSourceData come from PKCS #11, which
+     * state:
+     *   If the parameter is empty, pSourceData must be NULL and
+     *   ulSourceDataLen must be zero.
+     */
+    if (params->source != CKZ_DATA_SPECIFIED ||
+        (GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL) ||
+        (GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) ||
+        (params->ulSourceDataLen == 0 && params->pSourceData != NULL) ||
+        (params->ulSourceDataLen != 0 && params->pSourceData == NULL)) {
+        return PR_FALSE;
+    }
+    return PR_TRUE;
+}
+
+/* 
+ * return a context based on the SFTKContext type.
+ */
+SFTKSessionContext *
+sftk_ReturnContextByType(SFTKSession *session, SFTKContextType type)
+{
+    switch (type) {
+	case SFTK_ENCRYPT:
+	case SFTK_DECRYPT:
+	    return session->enc_context;
+	case SFTK_HASH:
+	    return session->hash_context;
+	case SFTK_SIGN:
+	case SFTK_SIGN_RECOVER:
+	case SFTK_VERIFY:
+	case SFTK_VERIFY_RECOVER:
+	    return session->hash_context;
+    }
+    return NULL;
+}
+
+/* 
+ * change a context based on the SFTKContext type.
+ */
+void
+sftk_SetContextByType(SFTKSession *session, SFTKContextType type, 
+						SFTKSessionContext *context)
+{
+    switch (type) {
+	case SFTK_ENCRYPT:
+	case SFTK_DECRYPT:
+	    session->enc_context = context;
+	    break;
+	case SFTK_HASH:
+	    session->hash_context = context;
+	    break;
+	case SFTK_SIGN:
+	case SFTK_SIGN_RECOVER:
+	case SFTK_VERIFY:
+	case SFTK_VERIFY_RECOVER:
+	    session->hash_context = context;
+	    break;
+    }
+    return;
+}
+
+/*
+ * code to grab the context. Needed by every C_XXXUpdate, C_XXXFinal,
+ * and C_XXX function. The function takes a session handle, the context type,
+ * and wether or not the session needs to be multipart. It returns the context,
+ * and optionally returns the session pointer (if sessionPtr != NULL) if session
+ * pointer is returned, the caller is responsible for freeing it.
+ */
+static CK_RV
+sftk_GetContext(CK_SESSION_HANDLE handle,SFTKSessionContext **contextPtr,
+	SFTKContextType type, PRBool needMulti, SFTKSession **sessionPtr)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+
+    session = sftk_SessionFromHandle(handle);
+    if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+    context = sftk_ReturnContextByType(session,type);
+    /* make sure the context is valid */
+    if((context==NULL)||(context->type!=type)||(needMulti&&!(context->multi))){
+        sftk_FreeSession(session);
+	return CKR_OPERATION_NOT_INITIALIZED;
+    }
+    *contextPtr = context;
+    if (sessionPtr != NULL) {
+	*sessionPtr = session;
+    } else {
+	sftk_FreeSession(session);
+    }
+    return CKR_OK;
+}
+
+/** Terminate operation (in the PKCS#11 spec sense).
+ *  Intuitive name for FreeContext/SetNullContext pair.
+ */
+static void
+sftk_TerminateOp( SFTKSession *session, SFTKContextType ctype,
+        SFTKSessionContext *context )
+{
+    sftk_FreeContext( context );
+    sftk_SetContextByType( session, ctype, NULL );
+}
+
+/*
+ ************** Crypto Functions:     Encrypt ************************
+ */
+
+/*
+ * All the NSC_InitXXX functions have a set of common checks and processing they
+ * all need to do at the beginning. This is done here.
+ */
+static CK_RV
+sftk_InitGeneric(SFTKSession *session,SFTKSessionContext **contextPtr,
+		 SFTKContextType ctype,SFTKObject **keyPtr,
+		 CK_OBJECT_HANDLE hKey, CK_KEY_TYPE *keyTypePtr,
+		 CK_OBJECT_CLASS pubKeyType, CK_ATTRIBUTE_TYPE operation)
+{
+    SFTKObject *key = NULL;
+    SFTKAttribute *att;
+    SFTKSessionContext *context;
+
+    /* We can only init if there is not current context active */
+    if (sftk_ReturnContextByType(session,ctype) != NULL) {
+	return CKR_OPERATION_ACTIVE;
+    }
+
+    /* find the key */
+    if (keyPtr) {
+        key = sftk_ObjectFromHandle(hKey,session);
+        if (key == NULL) {
+	    return CKR_KEY_HANDLE_INVALID;
+    	}
+
+	/* make sure it's a valid  key for this operation */
+	if (((key->objclass != CKO_SECRET_KEY) && (key->objclass != pubKeyType))
+					|| !sftk_isTrue(key,operation)) {
+	    sftk_FreeObject(key);
+	    return CKR_KEY_TYPE_INCONSISTENT;
+	}
+	/* get the key type */
+	att = sftk_FindAttribute(key,CKA_KEY_TYPE);
+	if (att == NULL) {
+	    sftk_FreeObject(key);
+	    return CKR_KEY_TYPE_INCONSISTENT;
+	}
+	PORT_Assert(att->attrib.ulValueLen == sizeof(CK_KEY_TYPE));
+	if (att->attrib.ulValueLen != sizeof(CK_KEY_TYPE)) {
+	    sftk_FreeAttribute(att);
+	    sftk_FreeObject(key);
+	    return CKR_ATTRIBUTE_VALUE_INVALID;
+	}
+	PORT_Memcpy(keyTypePtr, att->attrib.pValue, sizeof(CK_KEY_TYPE));
+	sftk_FreeAttribute(att);
+	*keyPtr = key;
+    }
+
+    /* allocate the context structure */
+    context = (SFTKSessionContext *)PORT_Alloc(sizeof(SFTKSessionContext));
+    if (context == NULL) {
+	if (key) sftk_FreeObject(key);
+	return CKR_HOST_MEMORY;
+    }
+    context->type = ctype;
+    context->multi = PR_TRUE;
+    context->rsa = PR_FALSE;
+    context->cipherInfo = NULL;
+    context->hashInfo = NULL;
+    context->doPad = PR_FALSE;
+    context->padDataLength = 0;
+    context->key = key;
+    context->blockSize = 0;
+    context->maxLen = 0;
+
+    *contextPtr = context;
+    return CKR_OK;
+}
+
+static int
+sftk_aes_mode(CK_MECHANISM_TYPE mechanism)
+{
+    switch (mechanism) {
+    case CKM_AES_CBC_PAD:
+    case CKM_AES_CBC:
+	return NSS_AES_CBC;
+    case CKM_AES_ECB:
+	return NSS_AES;
+    case CKM_AES_CTS:
+	return NSS_AES_CTS;
+    case CKM_AES_CTR:
+	return NSS_AES_CTR;
+    case CKM_AES_GCM:
+	return NSS_AES_GCM;
+    }
+    return -1;
+}
+
+static SECStatus
+sftk_RSAEncryptRaw(NSSLOWKEYPublicKey *key, unsigned char *output,
+                   unsigned int *outputLen, unsigned int maxLen,
+                   const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    rv = RSA_EncryptRaw(&key->u.rsa, output, outputLen, maxLen, input,
+                        inputLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+
+    return rv;
+}
+
+static SECStatus
+sftk_RSADecryptRaw(NSSLOWKEYPrivateKey *key, unsigned char *output,
+                   unsigned int *outputLen, unsigned int maxLen,
+                   const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    rv = RSA_DecryptRaw(&key->u.rsa, output, outputLen, maxLen, input,
+                        inputLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+
+    return rv;
+}
+
+static SECStatus
+sftk_RSAEncrypt(NSSLOWKEYPublicKey *key, unsigned char *output,
+                unsigned int *outputLen, unsigned int maxLen,
+                const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    rv = RSA_EncryptBlock(&key->u.rsa, output, outputLen, maxLen, input,
+                          inputLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+
+    return rv;
+}
+
+static SECStatus
+sftk_RSADecrypt(NSSLOWKEYPrivateKey *key, unsigned char *output,
+                unsigned int *outputLen, unsigned int maxLen,
+                const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    rv = RSA_DecryptBlock(&key->u.rsa, output, outputLen, maxLen, input,
+                          inputLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+
+    return rv;
+}
+
+static SECStatus
+sftk_RSAEncryptOAEP(SFTKOAEPEncryptInfo *info, unsigned char *output,
+                    unsigned int *outputLen, unsigned int maxLen,
+                    const unsigned char *input, unsigned int inputLen)
+{
+    HASH_HashType hashAlg;
+    HASH_HashType maskHashAlg;
+
+    PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
+    if (info->key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    hashAlg = GetHashTypeFromMechanism(info->params->hashAlg);
+    maskHashAlg = GetHashTypeFromMechanism(info->params->mgf);
+
+    return RSA_EncryptOAEP(&info->key->u.rsa, hashAlg, maskHashAlg,
+                           (const unsigned char*)info->params->pSourceData,
+                           info->params->ulSourceDataLen, NULL, 0,
+                           output, outputLen, maxLen, input, inputLen);
+}
+
+static SECStatus
+sftk_RSADecryptOAEP(SFTKOAEPDecryptInfo *info, unsigned char *output,
+                    unsigned int *outputLen, unsigned int maxLen,
+                    const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+    HASH_HashType hashAlg;
+    HASH_HashType maskHashAlg;
+
+    PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
+    if (info->key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    hashAlg = GetHashTypeFromMechanism(info->params->hashAlg);
+    maskHashAlg = GetHashTypeFromMechanism(info->params->mgf);
+
+    rv = RSA_DecryptOAEP(&info->key->u.rsa, hashAlg, maskHashAlg,
+                         (const unsigned char*)info->params->pSourceData,
+                         info->params->ulSourceDataLen,
+                         output, outputLen, maxLen, input, inputLen);
+     if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+    return rv;
+}
+
+static SFTKChaCha20Poly1305Info *
+sftk_ChaCha20Poly1305_CreateContext(const unsigned char *key,
+				    unsigned int keyLen,
+				    const CK_NSS_AEAD_PARAMS* params)
+{
+    SFTKChaCha20Poly1305Info *ctx;
+
+    if (params->ulIvLen != sizeof(ctx->nonce)) {
+	PORT_SetError(SEC_ERROR_INPUT_LEN);
+	return NULL;
+    }
+
+    ctx = PORT_New(SFTKChaCha20Poly1305Info);
+    if (ctx == NULL) {
+	return NULL;
+    }
+
+    if (ChaCha20Poly1305_InitContext(&ctx->freeblCtx, key, keyLen,
+				     params->ulTagLen) != SECSuccess) {
+	PORT_Free(ctx);
+	return NULL;
+    }
+
+    memcpy(ctx->nonce, params->pIv, sizeof(ctx->nonce));
+
+    if (params->ulAADLen > sizeof(ctx->ad)) {
+	/* Need to allocate an overflow buffer for the additional data. */
+	ctx->adOverflow = (unsigned char *)PORT_Alloc(params->ulAADLen);
+	if (!ctx->adOverflow) {
+	    PORT_Free(ctx);
+	    return NULL;
+	}
+	memcpy(ctx->adOverflow, params->pAAD, params->ulAADLen);
+    } else {
+	ctx->adOverflow = NULL;
+	memcpy(ctx->ad, params->pAAD, params->ulAADLen);
+    }
+    ctx->adLen = params->ulAADLen;
+
+    return ctx;
+}
+
+static void
+sftk_ChaCha20Poly1305_DestroyContext(SFTKChaCha20Poly1305Info *ctx,
+				     PRBool freeit)
+{
+    ChaCha20Poly1305_DestroyContext(&ctx->freeblCtx, PR_FALSE);
+    if (ctx->adOverflow != NULL) {
+	PORT_Free(ctx->adOverflow);
+	ctx->adOverflow = NULL;
+    }
+    ctx->adLen = 0;
+    if (freeit) {
+	PORT_Free(ctx);
+    }
+}
+
+static SECStatus
+sftk_ChaCha20Poly1305_Encrypt(const SFTKChaCha20Poly1305Info *ctx,
+			      unsigned char *output, unsigned int *outputLen,
+			      unsigned int maxOutputLen,
+			      const unsigned char *input, unsigned int inputLen)
+{
+    const unsigned char *ad = ctx->adOverflow;
+
+    if (ad == NULL) {
+	ad = ctx->ad;
+    }
+
+    return ChaCha20Poly1305_Seal(&ctx->freeblCtx, output, outputLen,
+				 maxOutputLen, input, inputLen, ctx->nonce,
+				 sizeof(ctx->nonce), ad, ctx->adLen);
+}
+
+static SECStatus
+sftk_ChaCha20Poly1305_Decrypt(const SFTKChaCha20Poly1305Info *ctx,
+			      unsigned char *output, unsigned int *outputLen,
+			      unsigned int maxOutputLen,
+			      const unsigned char *input, unsigned int inputLen)
+{
+    const unsigned char *ad = ctx->adOverflow;
+
+    if (ad == NULL) {
+	ad = ctx->ad;
+    }
+
+    return ChaCha20Poly1305_Open(&ctx->freeblCtx, output, outputLen,
+				 maxOutputLen, input, inputLen, ctx->nonce,
+				 sizeof(ctx->nonce), ad, ctx->adLen);
+}
+
+/** NSC_CryptInit initializes an encryption/Decryption operation.
+ *
+ * Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey.
+ * Called by NSC_SignInit, NSC_VerifyInit (via sftk_InitCBCMac) only for block
+ *  ciphers MAC'ing.
+ */
+static CK_RV
+sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+     CK_OBJECT_HANDLE hKey,
+     CK_ATTRIBUTE_TYPE mechUsage, CK_ATTRIBUTE_TYPE keyUsage,
+		 SFTKContextType contextType, PRBool isEncrypt)
+{
+    SFTKSession *session;
+    SFTKObject *key;
+    SFTKSessionContext *context;
+    SFTKAttribute *att;
+    CK_RC2_CBC_PARAMS *rc2_param;
+#if NSS_SOFTOKEN_DOES_RC5
+    CK_RC5_CBC_PARAMS *rc5_param;
+    SECItem rc5Key;
+#endif
+    CK_KEY_TYPE key_type;
+    CK_RV crv = CKR_OK;
+    unsigned effectiveKeyLength;
+    unsigned char newdeskey[24];
+    PRBool useNewKey=PR_FALSE;
+    int t;
+
+    crv = sftk_MechAllowsOperation(pMechanism->mechanism, mechUsage );
+    if (crv != CKR_OK) 
+    	return crv;
+
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+    crv = sftk_InitGeneric(session,&context,contextType,&key,hKey,&key_type,
+    			isEncrypt ?CKO_PUBLIC_KEY:CKO_PRIVATE_KEY, keyUsage);
+						
+    if (crv != CKR_OK) {
+	sftk_FreeSession(session);
+	return crv;
+    }
+
+    context->doPad = PR_FALSE;
+    switch(pMechanism->mechanism) {
+    case CKM_RSA_PKCS:
+    case CKM_RSA_X_509:
+	if (key_type != CKK_RSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	context->multi = PR_FALSE;
+	context->rsa = PR_TRUE;
+	if (isEncrypt) {
+	    NSSLOWKEYPublicKey *pubKey = sftk_GetPubKey(key,CKK_RSA,&crv);
+	    if (pubKey == NULL) {
+	        crv = CKR_KEY_HANDLE_INVALID;
+		break;
+	    }
+	    context->maxLen = nsslowkey_PublicModulusLen(pubKey);
+	    context->cipherInfo =  (void *)pubKey;
+	    context->update = (SFTKCipher) 
+		(pMechanism->mechanism == CKM_RSA_X_509
+					? sftk_RSAEncryptRaw : sftk_RSAEncrypt);
+	} else {
+	    NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(key,CKK_RSA,&crv);
+	    if (privKey == NULL) {
+	        crv = CKR_KEY_HANDLE_INVALID;
+		break;
+	    }
+	    context->maxLen = nsslowkey_PrivateModulusLen(privKey);
+	    context->cipherInfo =  (void *)privKey;
+	    context->update = (SFTKCipher) 
+		(pMechanism->mechanism == CKM_RSA_X_509
+					? sftk_RSADecryptRaw : sftk_RSADecrypt);
+	}
+	context->destroy = sftk_Null;
+	break;
+    case CKM_RSA_PKCS_OAEP:
+	if (key_type != CKK_RSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS) ||
+	    !sftk_ValidateOaepParams((CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter)) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	context->multi = PR_FALSE;
+	context->rsa = PR_TRUE;
+	if (isEncrypt) {
+	    SFTKOAEPEncryptInfo *info = PORT_New(SFTKOAEPEncryptInfo);
+	    if (info == NULL) {
+	        crv = CKR_HOST_MEMORY;
+	        break;
+	    }
+	    info->params = pMechanism->pParameter;
+	    info->key = sftk_GetPubKey(key, CKK_RSA, &crv);
+	    if (info->key == NULL) {
+	        PORT_Free(info);
+	        crv = CKR_KEY_HANDLE_INVALID;
+	        break;
+	    }
+	    context->update = (SFTKCipher) sftk_RSAEncryptOAEP;
+	    context->maxLen = nsslowkey_PublicModulusLen(info->key);
+	    context->cipherInfo = info;
+	} else {
+	    SFTKOAEPDecryptInfo *info = PORT_New(SFTKOAEPDecryptInfo);
+	    if (info == NULL) {
+	        crv = CKR_HOST_MEMORY;
+	        break;
+	    }
+	    info->params = pMechanism->pParameter;
+	    info->key = sftk_GetPrivKey(key, CKK_RSA, &crv);
+	    if (info->key == NULL) {
+	        PORT_Free(info);
+	        crv = CKR_KEY_HANDLE_INVALID;
+	        break;
+	    }
+	    context->update = (SFTKCipher) sftk_RSADecryptOAEP;
+	    context->maxLen = nsslowkey_PrivateModulusLen(info->key);
+	    context->cipherInfo = info;
+	}
+	context->destroy = (SFTKDestroy) sftk_Space;
+	break;
+    case CKM_RC2_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_RC2_ECB:
+    case CKM_RC2_CBC:
+	context->blockSize = 8;
+	if (key_type != CKK_RC2) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter;
+	effectiveKeyLength = (rc2_param->ulEffectiveBits+7)/8;
+	context->cipherInfo = 
+	    RC2_CreateContext((unsigned char*)att->attrib.pValue,
+			      att->attrib.ulValueLen, rc2_param->iv,
+			      pMechanism->mechanism == CKM_RC2_ECB ? NSS_RC2 :
+			      NSS_RC2_CBC,effectiveKeyLength);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ? RC2_Encrypt : RC2_Decrypt);
+	context->destroy = (SFTKDestroy) RC2_DestroyContext;
+	break;
+#if NSS_SOFTOKEN_DOES_RC5
+    case CKM_RC5_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_RC5_ECB:
+    case CKM_RC5_CBC:
+	if (key_type != CKK_RC5) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter;
+	context->blockSize = rc5_param->ulWordsize*2;
+	rc5Key.data = (unsigned char*)att->attrib.pValue;
+	rc5Key.len = att->attrib.ulValueLen;
+	context->cipherInfo = RC5_CreateContext(&rc5Key,rc5_param->ulRounds,
+	   rc5_param->ulWordsize,rc5_param->pIv,
+		 pMechanism->mechanism == CKM_RC5_ECB ? NSS_RC5 : NSS_RC5_CBC);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ? RC5_Encrypt : RC5_Decrypt);
+	context->destroy = (SFTKDestroy) RC5_DestroyContext;
+	break;
+#endif
+    case CKM_RC4:
+	if (key_type != CKK_RC4) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	context->cipherInfo = 
+	    RC4_CreateContext((unsigned char*)att->attrib.pValue,
+			      att->attrib.ulValueLen);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;  /* WRONG !!! */
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ? RC4_Encrypt : RC4_Decrypt);
+	context->destroy = (SFTKDestroy) RC4_DestroyContext;
+	break;
+    case CKM_CDMF_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_CDMF_ECB:
+    case CKM_CDMF_CBC:
+	if (key_type != CKK_CDMF) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	t = (pMechanism->mechanism == CKM_CDMF_ECB) ? NSS_DES : NSS_DES_CBC;
+	if (crv != CKR_OK) break;
+	goto finish_des;
+    case CKM_DES_ECB:
+	if (key_type != CKK_DES) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	t = NSS_DES;
+	goto finish_des;
+    case CKM_DES_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_DES_CBC:
+	if (key_type != CKK_DES) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	t = NSS_DES_CBC;
+	goto finish_des;
+    case CKM_DES3_ECB:
+	if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	t = NSS_DES_EDE3;
+	goto finish_des;
+    case CKM_DES3_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_DES3_CBC:
+	if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	t = NSS_DES_EDE3_CBC;
+finish_des:
+	context->blockSize = 8;
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	if (key_type == CKK_DES2 && 
+            (t == NSS_DES_EDE3_CBC || t == NSS_DES_EDE3)) {
+	    /* extend DES2 key to DES3 key. */
+	    memcpy(newdeskey, att->attrib.pValue, 16);
+	    memcpy(newdeskey + 16, newdeskey, 8);
+	    useNewKey=PR_TRUE;
+	} else if (key_type == CKK_CDMF) {
+	    crv = sftk_cdmf2des((unsigned char*)att->attrib.pValue,newdeskey);
+	    if (crv != CKR_OK) {
+		sftk_FreeAttribute(att);
+		break;
+	    }
+	    useNewKey=PR_TRUE;
+	}
+	context->cipherInfo = DES_CreateContext(
+		useNewKey ? newdeskey : (unsigned char*)att->attrib.pValue,
+		(unsigned char*)pMechanism->pParameter,t, isEncrypt);
+	if (useNewKey) 
+	    memset(newdeskey, 0, sizeof newdeskey);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ? DES_Encrypt : DES_Decrypt);
+	context->destroy = (SFTKDestroy) DES_DestroyContext;
+	break;
+    case CKM_SEED_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_SEED_CBC:
+        if (!pMechanism->pParameter ||
+	     pMechanism->ulParameterLen != 16) {
+            crv = CKR_MECHANISM_PARAM_INVALID;
+            break;
+        }
+        /* fall thru */
+    case CKM_SEED_ECB:
+	context->blockSize = 16;
+	if (key_type != CKK_SEED) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	context->cipherInfo = SEED_CreateContext(
+	    (unsigned char*)att->attrib.pValue,
+	    (unsigned char*)pMechanism->pParameter,
+	    pMechanism->mechanism == CKM_SEED_ECB ? NSS_SEED : NSS_SEED_CBC,
+	    isEncrypt);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher)(isEncrypt ? SEED_Encrypt : SEED_Decrypt);
+	context->destroy = (SFTKDestroy) SEED_DestroyContext;
+	break;
+
+    case CKM_CAMELLIA_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_CAMELLIA_CBC:
+	if (!pMechanism->pParameter ||
+		 pMechanism->ulParameterLen != 16) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	/* fall thru */
+    case CKM_CAMELLIA_ECB:
+	context->blockSize = 16;
+	if (key_type != CKK_CAMELLIA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	context->cipherInfo = Camellia_CreateContext(
+	    (unsigned char*)att->attrib.pValue,
+	    (unsigned char*)pMechanism->pParameter,
+	    pMechanism->mechanism ==
+	    CKM_CAMELLIA_ECB ? NSS_CAMELLIA : NSS_CAMELLIA_CBC,
+	    isEncrypt, att->attrib.ulValueLen);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ?
+					Camellia_Encrypt : Camellia_Decrypt);
+	context->destroy = (SFTKDestroy) Camellia_DestroyContext;
+	break;
+
+    case CKM_AES_CBC_PAD:
+	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_AES_ECB:
+    case CKM_AES_CBC:
+	context->blockSize = 16;
+    case CKM_AES_CTS:
+    case CKM_AES_CTR:
+    case CKM_AES_GCM:
+	if (pMechanism->mechanism == CKM_AES_GCM) {
+	    context->multi = PR_FALSE;
+	}
+	if (key_type != CKK_AES) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	context->cipherInfo = AES_CreateContext(
+	    (unsigned char*)att->attrib.pValue,
+	    (unsigned char*)pMechanism->pParameter,
+	    sftk_aes_mode(pMechanism->mechanism),
+	    isEncrypt, att->attrib.ulValueLen, 16);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ? AES_Encrypt : AES_Decrypt);
+	context->destroy = (SFTKDestroy) AES_DestroyContext;
+	break;
+
+    case CKM_NSS_CHACHA20_POLY1305:
+	if (pMechanism->ulParameterLen != sizeof(CK_NSS_AEAD_PARAMS)) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	context->multi = PR_FALSE;
+	if (key_type != CKK_NSS_CHACHA20) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	context->cipherInfo = sftk_ChaCha20Poly1305_CreateContext(
+		(unsigned char*) att->attrib.pValue, att->attrib.ulValueLen,
+		(CK_NSS_AEAD_PARAMS*) pMechanism->pParameter);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ?
+					sftk_ChaCha20Poly1305_Encrypt :
+					sftk_ChaCha20Poly1305_Decrypt);
+	context->destroy = (SFTKDestroy) sftk_ChaCha20Poly1305_DestroyContext;
+	break;
+
+    case CKM_NETSCAPE_AES_KEY_WRAP_PAD:
+    	context->doPad = PR_TRUE;
+	/* fall thru */
+    case CKM_NETSCAPE_AES_KEY_WRAP:
+	context->multi = PR_FALSE;
+	context->blockSize = 8;
+	if (key_type != CKK_AES) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att = sftk_FindAttribute(key,CKA_VALUE);
+	if (att == NULL) {
+	    crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	context->cipherInfo = AESKeyWrap_CreateContext(
+	    (unsigned char*)att->attrib.pValue,
+	    (unsigned char*)pMechanism->pParameter,
+	    isEncrypt, att->attrib.ulValueLen);
+	sftk_FreeAttribute(att);
+	if (context->cipherInfo == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->update = (SFTKCipher) (isEncrypt ? AESKeyWrap_Encrypt 
+	                                          : AESKeyWrap_Decrypt);
+	context->destroy = (SFTKDestroy) AESKeyWrap_DestroyContext;
+	break;
+
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    if (crv != CKR_OK) {
+        sftk_FreeContext(context);
+	sftk_FreeSession(session);
+	return crv;
+    }
+    sftk_SetContextByType(session, contextType, context);
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+/* NSC_EncryptInit initializes an encryption operation. */
+CK_RV NSC_EncryptInit(CK_SESSION_HANDLE hSession,
+		 CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+    CHECK_FORK();
+    return sftk_CryptInit(hSession, pMechanism, hKey, CKA_ENCRYPT, CKA_ENCRYPT,
+						SFTK_ENCRYPT, PR_TRUE);
+}
+
+/* NSC_EncryptUpdate continues a multiple-part encryption operation. */
+CK_RV NSC_EncryptUpdate(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,	
+					CK_ULONG_PTR pulEncryptedPartLen)
+{
+    SFTKSessionContext *context;
+    unsigned int outlen,i;
+    unsigned int padoutlen = 0;
+    unsigned int maxout = *pulEncryptedPartLen;
+    CK_RV crv;
+    SECStatus rv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_TRUE,NULL);
+    if (crv != CKR_OK) return crv;
+
+    if (!pEncryptedPart) {
+	if (context->doPad) {
+	    CK_ULONG totalDataAvailable = ulPartLen + context->padDataLength;
+	    CK_ULONG blocksToSend = totalDataAvailable/context->blockSize;
+
+	    *pulEncryptedPartLen = blocksToSend * context->blockSize;
+	    return CKR_OK;
+	}
+	*pulEncryptedPartLen = ulPartLen;
+	return CKR_OK;
+    }
+
+    /* do padding */
+    if (context->doPad) {
+	/* deal with previous buffered data */
+	if (context->padDataLength != 0) {
+	    /* fill in the padded to a full block size */
+	    for (i=context->padDataLength; 
+			(ulPartLen != 0) && i < context->blockSize; i++) {
+		context->padBuf[i] = *pPart++;
+		ulPartLen--;
+		context->padDataLength++;
+	    }
+
+	    /* not enough data to encrypt yet? then return */
+	    if (context->padDataLength != context->blockSize) {
+		*pulEncryptedPartLen = 0;
+		return CKR_OK;
+	    }
+	    /* encrypt the current padded data */
+    	    rv = (*context->update)(context->cipherInfo, pEncryptedPart, 
+		&padoutlen, context->blockSize, context->padBuf,
+							context->blockSize);
+	    if (rv != SECSuccess) {
+		return sftk_MapCryptError(PORT_GetError());
+	    }
+	    pEncryptedPart += padoutlen;
+	    maxout -= padoutlen;
+	}
+	/* save the residual */
+	context->padDataLength = ulPartLen % context->blockSize;
+	if (context->padDataLength) {
+	    PORT_Memcpy(context->padBuf,
+			&pPart[ulPartLen-context->padDataLength],
+							context->padDataLength);
+	    ulPartLen -= context->padDataLength;
+	}
+	/* if we've exhausted our new buffer, we're done */
+	if (ulPartLen == 0) {
+	    *pulEncryptedPartLen = padoutlen;
+	    return CKR_OK;
+	}
+    }
+
+
+    /* do it: NOTE: this assumes buf size in is >= buf size out! */
+    rv = (*context->update)(context->cipherInfo,pEncryptedPart, 
+					&outlen, maxout, pPart, ulPartLen);
+    *pulEncryptedPartLen = (CK_ULONG) (outlen + padoutlen);
+    return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
+}
+
+
+/* NSC_EncryptFinal finishes a multiple-part encryption operation. */
+CK_RV NSC_EncryptFinal(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int outlen,i;
+    unsigned int maxout = *pulLastEncryptedPartLen;
+    CK_RV crv;
+    SECStatus rv = SECSuccess;
+    PRBool contextFinished = PR_TRUE;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_TRUE,&session);
+    if (crv != CKR_OK) return crv;
+
+    *pulLastEncryptedPartLen = 0;
+    if (!pLastEncryptedPart) {
+	/* caller is checking the amount of remaining data */
+	if (context->blockSize > 0 && context->doPad) {
+	    *pulLastEncryptedPartLen = context->blockSize;
+	    contextFinished = PR_FALSE; /* still have padding to go */
+	}
+	goto finish;
+    }
+
+    /* do padding */
+    if (context->doPad) {
+	unsigned char  padbyte = (unsigned char) 
+				(context->blockSize - context->padDataLength); 
+	/* fill out rest of pad buffer with pad magic*/
+	for (i=context->padDataLength; i < context->blockSize; i++) {
+	    context->padBuf[i] = padbyte;
+	}
+	rv = (*context->update)(context->cipherInfo,pLastEncryptedPart, 
+			&outlen, maxout, context->padBuf, context->blockSize);
+	if (rv == SECSuccess) *pulLastEncryptedPartLen = (CK_ULONG) outlen;
+    }
+
+finish:
+    if (contextFinished)
+        sftk_TerminateOp( session, SFTK_ENCRYPT, context );
+    sftk_FreeSession(session);
+    return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
+}
+
+/* NSC_Encrypt encrypts single-part data. */
+CK_RV NSC_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+    		   CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData,
+		   CK_ULONG_PTR pulEncryptedDataLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int outlen;
+    unsigned int maxoutlen = *pulEncryptedDataLen;
+    CK_RV crv;
+    CK_RV crv2;
+    SECStatus rv = SECSuccess;
+    SECItem   pText;
+
+    pText.type = siBuffer;
+    pText.data = pData;
+    pText.len  = ulDataLen;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_FALSE,&session);
+    if (crv != CKR_OK) return crv;
+
+    if (!pEncryptedData) {
+	*pulEncryptedDataLen = context->rsa ? context->maxLen :
+		ulDataLen + 2 * context->blockSize;
+	goto finish;
+    }
+
+    if (context->doPad) {
+	if (context->multi) {
+	    CK_ULONG finalLen;
+	    /* padding is fairly complicated, have the update and final 
+	     * code deal with it */
+	    sftk_FreeSession(session);
+	    crv = NSC_EncryptUpdate(hSession, pData, ulDataLen, pEncryptedData, 
+	                            pulEncryptedDataLen);
+	    if (crv != CKR_OK) 
+	    	*pulEncryptedDataLen = 0;
+	    maxoutlen      -= *pulEncryptedDataLen;
+	    pEncryptedData += *pulEncryptedDataLen;
+	    finalLen = maxoutlen;
+	    crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen);
+	    if (crv2 == CKR_OK) 
+	    	*pulEncryptedDataLen += finalLen;
+	    return crv == CKR_OK ? crv2 : crv;
+	}
+	/* doPad without multi means that padding must be done on the first
+	** and only update.  There will be no final.
+	*/
+	PORT_Assert(context->blockSize > 1);
+	if (context->blockSize > 1) {
+	    CK_ULONG remainder = ulDataLen % context->blockSize;
+	    CK_ULONG padding   = context->blockSize - remainder;
+	    pText.len += padding;
+	    pText.data = PORT_ZAlloc(pText.len);
+	    if (pText.data) {
+		memcpy(pText.data, pData, ulDataLen);
+		memset(pText.data + ulDataLen, padding, padding);
+	    } else {
+		crv = CKR_HOST_MEMORY;
+		goto fail;
+	    }
+	}
+    }
+
+    /* do it: NOTE: this assumes buf size is big enough. */
+    rv = (*context->update)(context->cipherInfo, pEncryptedData, 
+			    &outlen, maxoutlen, pText.data, pText.len);
+    crv = (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
+    *pulEncryptedDataLen = (CK_ULONG) outlen;
+    if (pText.data != pData)
+    	PORT_ZFree(pText.data, pText.len);
+fail:
+    sftk_TerminateOp( session, SFTK_ENCRYPT, context );
+finish:
+    sftk_FreeSession(session);
+
+    return crv;
+}
+
+
+/*
+ ************** Crypto Functions:     Decrypt ************************
+ */
+
+/* NSC_DecryptInit initializes a decryption operation. */
+CK_RV NSC_DecryptInit( CK_SESSION_HANDLE hSession,
+			 CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+    CHECK_FORK();
+    return sftk_CryptInit(hSession, pMechanism, hKey, CKA_DECRYPT, CKA_DECRYPT,
+						SFTK_DECRYPT, PR_FALSE);
+}
+
+/* NSC_DecryptUpdate continues a multiple-part decryption operation. */
+CK_RV NSC_DecryptUpdate(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen,
+    				CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen)
+{
+    SFTKSessionContext *context;
+    unsigned int padoutlen = 0;
+    unsigned int outlen;
+    unsigned int maxout = *pulPartLen;
+    CK_RV crv;
+    SECStatus rv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_TRUE,NULL);
+    if (crv != CKR_OK) return crv;
+
+    /* this can only happen on an NSS programming error */
+    PORT_Assert((context->padDataLength == 0) 
+		|| context->padDataLength == context->blockSize);
+
+
+    if (context->doPad) {
+	/* Check the data length for block ciphers. If we are padding,
+	 * then we must be using a block cipher. In the non-padding case
+	 * the error will be returned by the underlying decryption
+	 * function when we do the actual decrypt. We need to do the
+	 * check here to avoid returning a negative length to the caller
+	 * or reading before the beginning of the pEncryptedPart buffer.
+ 	 */
+	if ((ulEncryptedPartLen == 0) ||
+	    (ulEncryptedPartLen % context->blockSize) != 0) {
+	    return CKR_ENCRYPTED_DATA_LEN_RANGE;
+	}
+    }
+
+    if (!pPart) {
+	if (context->doPad) {
+	    *pulPartLen = 
+		ulEncryptedPartLen + context->padDataLength - context->blockSize;
+	    return CKR_OK;
+	}
+	/* for stream ciphers there is are no constraints on ulEncryptedPartLen.
+	 * for block ciphers, it must be a multiple of blockSize. The error is
+	 * detected when this function is called again do decrypt the output.
+	 */
+	*pulPartLen = ulEncryptedPartLen;
+	return CKR_OK;
+    }
+
+    if (context->doPad) {
+	/* first decrypt our saved buffer */
+	if (context->padDataLength != 0) {
+    	    rv = (*context->update)(context->cipherInfo, pPart, &padoutlen,
+		 maxout, context->padBuf, context->blockSize);
+    	    if (rv != SECSuccess) return sftk_MapDecryptError(PORT_GetError());
+	    pPart += padoutlen;
+	    maxout -= padoutlen;
+	}
+	/* now save the final block for the next decrypt or the final */
+	PORT_Memcpy(context->padBuf,&pEncryptedPart[ulEncryptedPartLen -
+				context->blockSize], context->blockSize);
+	context->padDataLength = context->blockSize;
+	ulEncryptedPartLen -= context->padDataLength;
+    }
+
+    /* do it: NOTE: this assumes buf size in is >= buf size out! */
+    rv = (*context->update)(context->cipherInfo,pPart, &outlen,
+		 maxout, pEncryptedPart, ulEncryptedPartLen);
+    *pulPartLen = (CK_ULONG) (outlen + padoutlen);
+    return (rv == SECSuccess)  ? CKR_OK : sftk_MapDecryptError(PORT_GetError());
+}
+
+
+/* NSC_DecryptFinal finishes a multiple-part decryption operation. */
+CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int outlen;
+    unsigned int maxout = *pulLastPartLen;
+    CK_RV crv;
+    SECStatus rv = SECSuccess;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_TRUE,&session);
+    if (crv != CKR_OK) return crv;
+
+    *pulLastPartLen = 0;
+    if (!pLastPart) {
+	/* caller is checking the amount of remaining data */
+	if (context->padDataLength > 0) {
+	    *pulLastPartLen = context->padDataLength;
+	}
+	goto finish;
+    }
+
+    if (context->doPad) {
+	/* decrypt our saved buffer */
+	if (context->padDataLength != 0) {
+	    /* this assumes that pLastPart is big enough to hold the *whole*
+	     * buffer!!! */
+    	    rv = (*context->update)(context->cipherInfo, pLastPart, &outlen,
+		 maxout, context->padBuf, context->blockSize);
+	    if (rv != SECSuccess) {
+		crv = sftk_MapDecryptError(PORT_GetError());
+	    } else {
+		unsigned int padSize = 
+			    (unsigned int) pLastPart[context->blockSize-1];
+		if ((padSize > context->blockSize) || (padSize == 0)) {
+		    crv = CKR_ENCRYPTED_DATA_INVALID;
+		} else {
+		    unsigned int i;
+		    unsigned int badPadding = 0;  /* used as a boolean */
+		    for (i = 0; i < padSize; i++) {
+			badPadding |=
+			    (unsigned int) pLastPart[context->blockSize-1-i] ^
+			    padSize;
+		    }
+		    if (badPadding) {
+			crv = CKR_ENCRYPTED_DATA_INVALID;
+		    } else {
+			*pulLastPartLen = outlen - padSize;
+		    }
+		}
+	    }
+	}
+    }
+
+    sftk_TerminateOp( session, SFTK_DECRYPT, context );
+finish:
+    sftk_FreeSession(session);
+    return crv;
+}
+
+/* NSC_Decrypt decrypts encrypted data in a single part. */
+CK_RV NSC_Decrypt(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pEncryptedData,CK_ULONG ulEncryptedDataLen,CK_BYTE_PTR pData,
+    						CK_ULONG_PTR pulDataLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int outlen;
+    unsigned int maxoutlen = *pulDataLen;
+    CK_RV crv;
+    CK_RV crv2;
+    SECStatus rv = SECSuccess;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_FALSE,&session);
+    if (crv != CKR_OK) return crv;
+
+    if (!pData) {
+	*pulDataLen = ulEncryptedDataLen + context->blockSize;
+	goto finish;
+    }
+
+    if (context->doPad && context->multi) {
+	CK_ULONG finalLen;
+	/* padding is fairly complicated, have the update and final 
+	 * code deal with it */
+	sftk_FreeSession(session);
+	crv = NSC_DecryptUpdate(hSession,pEncryptedData,ulEncryptedDataLen,
+							pData, pulDataLen);
+	if (crv != CKR_OK) 
+	    *pulDataLen = 0;
+	maxoutlen -= *pulDataLen;
+	pData     += *pulDataLen;
+	finalLen = maxoutlen;
+	crv2 = NSC_DecryptFinal(hSession, pData, &finalLen);
+	if (crv2 == CKR_OK) 
+	    *pulDataLen += finalLen;
+	return crv == CKR_OK ? crv2 : crv;
+    }
+
+    rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, 
+					pEncryptedData, ulEncryptedDataLen);
+    /* XXX need to do MUCH better error mapping than this. */
+    crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError());
+    if (rv == SECSuccess && context->doPad) {
+	unsigned int padding = pData[outlen - 1];
+	if (padding > context->blockSize || !padding) {
+	    crv = CKR_ENCRYPTED_DATA_INVALID;
+	} else {
+	    unsigned int i;
+	    unsigned int badPadding = 0;  /* used as a boolean */
+	    for (i = 0; i < padding; i++) {
+		badPadding |= (unsigned int) pData[outlen - 1 - i] ^ padding;
+	    }
+	    if (badPadding) {
+		crv = CKR_ENCRYPTED_DATA_INVALID;
+	    } else {
+		outlen -= padding;
+	    }
+	}
+    }
+    *pulDataLen = (CK_ULONG) outlen;
+    sftk_TerminateOp( session, SFTK_DECRYPT, context );
+finish:
+    sftk_FreeSession(session);
+    return crv;
+}
+
+
+
+/*
+ ************** Crypto Functions:     Digest (HASH)  ************************
+ */
+
+/* NSC_DigestInit initializes a message-digesting operation. */
+CK_RV NSC_DigestInit(CK_SESSION_HANDLE hSession,
+    					CK_MECHANISM_PTR pMechanism)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    CK_RV crv = CKR_OK;
+
+    CHECK_FORK();
+
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) 
+    	return CKR_SESSION_HANDLE_INVALID;
+    crv = sftk_InitGeneric(session,&context,SFTK_HASH,NULL,0,NULL, 0, 0);
+    if (crv != CKR_OK) {
+	sftk_FreeSession(session);
+	return crv;
+    }
+
+
+#define INIT_MECH(mech,mmm) \
+    case mech: { \
+	mmm ## Context * mmm ## _ctx = mmm ## _NewContext(); \
+	context->cipherInfo    = (void *)mmm ## _ctx; \
+	context->cipherInfoLen = mmm ## _FlattenSize(mmm ## _ctx); \
+	context->currentMech   = mech; \
+	context->hashUpdate    = (SFTKHash)    mmm ## _Update; \
+	context->end           = (SFTKEnd)     mmm ## _End; \
+	context->destroy       = (SFTKDestroy) mmm ## _DestroyContext; \
+	context->maxLen        = mmm ## _LENGTH; \
+        if (mmm ## _ctx) \
+	    mmm ## _Begin(mmm ## _ctx); \
+	else  \
+	    crv = CKR_HOST_MEMORY; \
+	break; \
+    }
+
+    switch(pMechanism->mechanism) {
+    INIT_MECH(CKM_MD2,    MD2)
+    INIT_MECH(CKM_MD5,    MD5)
+    INIT_MECH(CKM_SHA_1,  SHA1)
+    INIT_MECH(CKM_SHA224, SHA224)
+    INIT_MECH(CKM_SHA256, SHA256)
+    INIT_MECH(CKM_SHA384, SHA384)
+    INIT_MECH(CKM_SHA512, SHA512)
+
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    if (crv != CKR_OK) {
+        sftk_FreeContext(context);
+	sftk_FreeSession(session);
+	return crv;
+    }
+    sftk_SetContextByType(session, SFTK_HASH, context);
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+
+/* NSC_Digest digests data in a single part. */
+CK_RV NSC_Digest(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest,
+    						CK_ULONG_PTR pulDigestLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int digestLen;
+    unsigned int maxout = *pulDigestLen;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_HASH,PR_FALSE,&session);
+    if (crv != CKR_OK) return crv;
+
+    if (pDigest == NULL) {
+	*pulDigestLen = context->maxLen;
+	goto finish;
+    }
+
+    /* do it: */
+    (*context->hashUpdate)(context->cipherInfo, pData, ulDataLen);
+    /*  NOTE: this assumes buf size is bigenough for the algorithm */
+    (*context->end)(context->cipherInfo, pDigest, &digestLen,maxout);
+    *pulDigestLen = digestLen;
+
+    sftk_TerminateOp( session, SFTK_HASH, context );
+finish:
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+
+/* NSC_DigestUpdate continues a multiple-part message-digesting operation. */
+CK_RV NSC_DigestUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+					    CK_ULONG ulPartLen)
+{
+    SFTKSessionContext *context;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_HASH,PR_TRUE,NULL);
+    if (crv != CKR_OK) return crv;
+    /* do it: */
+    (*context->hashUpdate)(context->cipherInfo, pPart, ulPartLen);
+    return CKR_OK;
+}
+
+
+/* NSC_DigestFinal finishes a multiple-part message-digesting operation. */
+CK_RV NSC_DigestFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pDigest,
+    						CK_ULONG_PTR pulDigestLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int maxout = *pulDigestLen;
+    unsigned int digestLen;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session);
+    if (crv != CKR_OK) return crv;
+
+    if (pDigest != NULL) {
+        (*context->end)(context->cipherInfo, pDigest, &digestLen, maxout);
+        *pulDigestLen = digestLen;
+        sftk_TerminateOp( session, SFTK_HASH, context );
+    } else {
+	*pulDigestLen = context->maxLen;
+    }
+
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+/*
+ * these helper functions are used by Generic Macing and Signing functions
+ * that use hashes as part of their operations. 
+ */
+#define DOSUB(mmm) \
+static CK_RV \
+sftk_doSub ## mmm(SFTKSessionContext *context) { \
+    mmm ## Context * mmm ## _ctx = mmm ## _NewContext(); \
+    context->hashInfo    = (void *)      mmm ## _ctx; \
+    context->hashUpdate  = (SFTKHash)    mmm ## _Update; \
+    context->end         = (SFTKEnd)     mmm ## _End; \
+    context->hashdestroy = (SFTKDestroy) mmm ## _DestroyContext; \
+    if (!context->hashInfo) { \
+	return CKR_HOST_MEMORY; \
+    } \
+    mmm ## _Begin( mmm ## _ctx ); \
+    return CKR_OK; \
+}
+
+DOSUB(MD2)
+DOSUB(MD5)
+DOSUB(SHA1)
+DOSUB(SHA224)
+DOSUB(SHA256)
+DOSUB(SHA384)
+DOSUB(SHA512)
+
+static SECStatus
+sftk_SignCopy(
+	CK_ULONG *copyLen,
+	void *out, unsigned int *outLength,
+	unsigned int maxLength,
+	const unsigned char *hashResult,
+	unsigned int hashResultLength)
+{
+    unsigned int toCopy = *copyLen;
+    if (toCopy > maxLength) {
+	toCopy = maxLength;
+    }
+    if (toCopy > hashResultLength) {
+	toCopy = hashResultLength;
+    }
+    memcpy(out, hashResult, toCopy);
+    if (outLength) {
+	*outLength = toCopy;
+    }
+    return SECSuccess;
+}
+
+/* Verify is just a compare for HMAC */
+static SECStatus
+sftk_HMACCmp(CK_ULONG *copyLen,unsigned char *sig,unsigned int sigLen,
+				unsigned char *hash, unsigned int hashLen)
+{
+    return (PORT_Memcmp(sig,hash,*copyLen) == 0) ? SECSuccess : SECFailure ; 
+}
+
+/*
+ * common HMAC initalization routine
+ */
+static CK_RV
+sftk_doHMACInit(SFTKSessionContext *context,HASH_HashType hash,
+					SFTKObject *key, CK_ULONG mac_size)
+{
+    SFTKAttribute *keyval;
+    HMACContext *HMACcontext;
+    CK_ULONG *intpointer;
+    const SECHashObject *hashObj = HASH_GetRawHashObject(hash);
+    PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID);
+
+    /* required by FIPS 198 Section 4 */
+    if (isFIPS && (mac_size < 4 || mac_size < hashObj->length/2)) {
+	return CKR_BUFFER_TOO_SMALL;
+    }
+
+    keyval = sftk_FindAttribute(key,CKA_VALUE);
+    if (keyval == NULL) return CKR_KEY_SIZE_RANGE;
+
+    HMACcontext = HMAC_Create(hashObj, 
+		(const unsigned char*)keyval->attrib.pValue,
+		keyval->attrib.ulValueLen, isFIPS);
+    context->hashInfo = HMACcontext;
+    context->multi = PR_TRUE;
+    sftk_FreeAttribute(keyval);
+    if (context->hashInfo == NULL) {
+	if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
+	    return CKR_KEY_SIZE_RANGE;
+	}
+	return CKR_HOST_MEMORY;
+    }
+    context->hashUpdate = (SFTKHash) HMAC_Update;
+    context->end = (SFTKEnd) HMAC_Finish;
+
+    context->hashdestroy = (SFTKDestroy) HMAC_Destroy;
+    intpointer = PORT_New(CK_ULONG);
+    if (intpointer == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+    *intpointer = mac_size;
+    context->cipherInfo = intpointer;
+    context->destroy = (SFTKDestroy) sftk_Space;
+    context->update = (SFTKCipher) sftk_SignCopy;
+    context->verify = (SFTKVerify) sftk_HMACCmp;
+    context->maxLen = hashObj->length;
+    HMAC_Begin(HMACcontext);
+    return CKR_OK;
+}
+
+/*
+ *  SSL Macing support. SSL Macs are inited, then update with the base
+ * hashing algorithm, then finalized in sign and verify
+ */
+
+/*
+ * FROM SSL:
+ * 60 bytes is 3 times the maximum length MAC size that is supported.
+ * We probably should have one copy of this table. We still need this table
+ * in ssl to 'sign' the handshake hashes.
+ */
+static unsigned char ssl_pad_1 [60] = {
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+    0x36, 0x36, 0x36, 0x36
+};
+static unsigned char ssl_pad_2 [60] = {
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+    0x5c, 0x5c, 0x5c, 0x5c
+};
+
+static SECStatus
+sftk_SSLMACSign(SFTKSSLMACInfo *info,unsigned char *sig,unsigned int *sigLen,
+		unsigned int maxLen,unsigned char *hash, unsigned int hashLen)
+{
+    unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH];
+    unsigned int out;
+
+    info->begin(info->hashContext);
+    info->update(info->hashContext,info->key,info->keySize);
+    info->update(info->hashContext,ssl_pad_2,info->padSize);
+    info->update(info->hashContext,hash,hashLen);
+    info->end(info->hashContext,tmpBuf,&out,SFTK_MAX_MAC_LENGTH);
+    PORT_Memcpy(sig,tmpBuf,info->macSize);
+    *sigLen = info->macSize;
+    return SECSuccess;
+}
+
+static SECStatus
+sftk_SSLMACVerify(SFTKSSLMACInfo *info,unsigned char *sig,unsigned int sigLen,
+		unsigned char *hash, unsigned int hashLen)
+{
+    unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH];
+    unsigned int out;
+
+    info->begin(info->hashContext);
+    info->update(info->hashContext,info->key,info->keySize);
+    info->update(info->hashContext,ssl_pad_2,info->padSize);
+    info->update(info->hashContext,hash,hashLen);
+    info->end(info->hashContext,tmpBuf,&out,SFTK_MAX_MAC_LENGTH);
+    return (PORT_Memcmp(sig,tmpBuf,info->macSize) == 0) ? 
+						SECSuccess : SECFailure;
+}
+
+/*
+ * common HMAC initalization routine
+ */
+static CK_RV
+sftk_doSSLMACInit(SFTKSessionContext *context,SECOidTag oid,
+					SFTKObject *key, CK_ULONG mac_size)
+{
+    SFTKAttribute *keyval;
+    SFTKBegin begin;
+    int padSize;
+    SFTKSSLMACInfo *sslmacinfo;
+    CK_RV crv = CKR_MECHANISM_INVALID;
+
+    if (oid == SEC_OID_SHA1) {
+	crv = sftk_doSubSHA1(context);
+	if (crv != CKR_OK) return crv;
+	begin = (SFTKBegin) SHA1_Begin;
+	padSize = 40;
+    } else {
+	crv = sftk_doSubMD5(context);
+	if (crv != CKR_OK) return crv;
+	begin = (SFTKBegin) MD5_Begin;
+	padSize = 48;
+    }
+    context->multi = PR_TRUE;
+
+    keyval = sftk_FindAttribute(key,CKA_VALUE);
+    if (keyval == NULL) return CKR_KEY_SIZE_RANGE;
+
+    context->hashUpdate(context->hashInfo,keyval->attrib.pValue,
+						 keyval->attrib.ulValueLen);
+    context->hashUpdate(context->hashInfo,ssl_pad_1,padSize);
+    sslmacinfo = (SFTKSSLMACInfo *) PORT_Alloc(sizeof(SFTKSSLMACInfo));
+    if (sslmacinfo == NULL) {
+        sftk_FreeAttribute(keyval);
+	return CKR_HOST_MEMORY;
+    }
+    sslmacinfo->macSize = mac_size;
+    sslmacinfo->hashContext = context->hashInfo;
+    PORT_Memcpy(sslmacinfo->key,keyval->attrib.pValue,
+					keyval->attrib.ulValueLen);
+    sslmacinfo->keySize = keyval->attrib.ulValueLen;
+    sslmacinfo->begin = begin;
+    sslmacinfo->end = context->end;
+    sslmacinfo->update = context->hashUpdate;
+    sslmacinfo->padSize = padSize;
+    sftk_FreeAttribute(keyval);
+    context->cipherInfo = (void *) sslmacinfo;
+    context->destroy = (SFTKDestroy) sftk_Space;
+    context->update = (SFTKCipher) sftk_SSLMACSign;
+    context->verify = (SFTKVerify) sftk_SSLMACVerify;
+    context->maxLen = mac_size;
+    return CKR_OK;
+}
+
+/*
+ ************** Crypto Functions:     Sign  ************************
+ */
+
+/** 
+ * Check if We're using CBCMacing and initialize the session context if we are.
+ *  @param contextType SFTK_SIGN or SFTK_VERIFY
+ *  @param keyUsage    check whether key allows this usage
+ */
+static CK_RV
+sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+ 	CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE keyUsage,
+						 SFTKContextType contextType)
+	
+{
+    CK_MECHANISM cbc_mechanism;
+    CK_ULONG mac_bytes = SFTK_INVALID_MAC_SIZE;
+    CK_RC2_CBC_PARAMS rc2_params;
+#if NSS_SOFTOKEN_DOES_RC5
+    CK_RC5_CBC_PARAMS rc5_params;
+    CK_RC5_MAC_GENERAL_PARAMS *rc5_mac;
+#endif
+    unsigned char ivBlock[SFTK_MAX_BLOCK_SIZE];
+    SFTKSessionContext *context;
+    CK_RV crv;
+    unsigned int blockSize;
+
+    switch (pMechanism->mechanism) {
+    case CKM_RC2_MAC_GENERAL:
+	mac_bytes = 
+	    ((CK_RC2_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength;
+	/* fall through */
+    case CKM_RC2_MAC:
+	/* this works because ulEffectiveBits is in the same place in both the
+	 * CK_RC2_MAC_GENERAL_PARAMS and CK_RC2_CBC_PARAMS */
+	rc2_params.ulEffectiveBits = ((CK_RC2_MAC_GENERAL_PARAMS *)
+				pMechanism->pParameter)->ulEffectiveBits;
+	PORT_Memset(rc2_params.iv,0,sizeof(rc2_params.iv));
+	cbc_mechanism.mechanism = CKM_RC2_CBC;
+	cbc_mechanism.pParameter = &rc2_params;
+	cbc_mechanism.ulParameterLen = sizeof(rc2_params);
+	blockSize = 8;
+	break;
+#if NSS_SOFTOKEN_DOES_RC5
+    case CKM_RC5_MAC_GENERAL:
+	mac_bytes = 
+	    ((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength;
+	/* fall through */
+    case CKM_RC5_MAC:
+	/* this works because ulEffectiveBits is in the same place in both the
+	 * CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */
+	rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter;
+	rc5_params.ulWordsize = rc5_mac->ulWordsize;
+	rc5_params.ulRounds = rc5_mac->ulRounds;
+	rc5_params.pIv = ivBlock;
+	if( (blockSize = rc5_mac->ulWordsize*2) > SFTK_MAX_BLOCK_SIZE )
+            return CKR_MECHANISM_PARAM_INVALID;
+	rc5_params.ulIvLen = blockSize;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_RC5_CBC;
+	cbc_mechanism.pParameter = &rc5_params;
+	cbc_mechanism.ulParameterLen = sizeof(rc5_params);
+	break;
+#endif
+    /* add cast and idea later */
+    case CKM_DES_MAC_GENERAL:
+	mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+	/* fall through */
+    case CKM_DES_MAC:
+	blockSize = 8;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_DES_CBC;
+	cbc_mechanism.pParameter = &ivBlock;
+	cbc_mechanism.ulParameterLen = blockSize;
+	break;
+    case CKM_DES3_MAC_GENERAL:
+	mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+	/* fall through */
+    case CKM_DES3_MAC:
+	blockSize = 8;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_DES3_CBC;
+	cbc_mechanism.pParameter = &ivBlock;
+	cbc_mechanism.ulParameterLen = blockSize;
+	break;
+    case CKM_CDMF_MAC_GENERAL:
+	mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+	/* fall through */
+    case CKM_CDMF_MAC:
+	blockSize = 8;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_CDMF_CBC;
+	cbc_mechanism.pParameter = &ivBlock;
+	cbc_mechanism.ulParameterLen = blockSize;
+	break;
+    case CKM_SEED_MAC_GENERAL:
+	mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+	/* fall through */
+    case CKM_SEED_MAC:
+	blockSize = 16;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_SEED_CBC;
+	cbc_mechanism.pParameter = &ivBlock;
+	cbc_mechanism.ulParameterLen = blockSize;
+	break;
+    case CKM_CAMELLIA_MAC_GENERAL:
+	mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+	/* fall through */
+    case CKM_CAMELLIA_MAC:
+	blockSize = 16;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_CAMELLIA_CBC;
+	cbc_mechanism.pParameter = &ivBlock;
+	cbc_mechanism.ulParameterLen = blockSize;
+	break;
+    case CKM_AES_MAC_GENERAL:
+	mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+	/* fall through */
+    case CKM_AES_MAC:
+	blockSize = 16;
+	PORT_Memset(ivBlock,0,blockSize);
+	cbc_mechanism.mechanism = CKM_AES_CBC;
+	cbc_mechanism.pParameter = &ivBlock;
+	cbc_mechanism.ulParameterLen = blockSize;
+	break;
+    default:
+	return CKR_FUNCTION_NOT_SUPPORTED;
+    }
+
+    /* if MAC size is externally supplied, it should be checked.
+     */
+    if (mac_bytes == SFTK_INVALID_MAC_SIZE)
+        mac_bytes = blockSize >> 1;
+    else {
+        if( mac_bytes > blockSize )
+            return CKR_MECHANISM_PARAM_INVALID;
+    }
+
+    crv = sftk_CryptInit(hSession, &cbc_mechanism, hKey,
+            CKA_ENCRYPT, /* CBC mech is able to ENCRYPT, not SIGN/VERIFY */
+            keyUsage, contextType, PR_TRUE );
+    if (crv != CKR_OK) return crv;
+    crv = sftk_GetContext(hSession,&context,contextType,PR_TRUE,NULL);
+
+    /* this shouldn't happen! */
+    PORT_Assert(crv == CKR_OK);
+    if (crv != CKR_OK) return crv;
+    context->blockSize = blockSize;
+    context->macSize = mac_bytes;
+    return CKR_OK;
+}
+
+/*
+ * encode RSA PKCS #1 Signature data before signing... 
+ */
+static SECStatus
+sftk_RSAHashSign(SFTKHashSignInfo *info, unsigned char *sig,
+                 unsigned int *sigLen, unsigned int maxLen,
+                 const unsigned char *hash, unsigned int hashLen)
+{
+    PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
+    if (info->key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    return RSA_HashSign(info->hashOid, info->key, sig, sigLen, maxLen,
+                        hash, hashLen);
+}
+
+/* XXX Old template; want to expunge it eventually. */
+static DERTemplate SECAlgorithmIDTemplate[] = {
+    { DER_SEQUENCE,
+	  0, NULL, sizeof(SECAlgorithmID) },
+    { DER_OBJECT_ID,
+	  offsetof(SECAlgorithmID,algorithm), },
+    { DER_OPTIONAL | DER_ANY,
+	  offsetof(SECAlgorithmID,parameters), },
+    { 0, }
+};
+
+/*
+ * XXX OLD Template.  Once all uses have been switched over to new one,
+ * remove this.
+ */
+static DERTemplate SGNDigestInfoTemplate[] = {
+    { DER_SEQUENCE,
+	  0, NULL, sizeof(SGNDigestInfo) },
+    { DER_INLINE,
+	  offsetof(SGNDigestInfo,digestAlgorithm),
+	  SECAlgorithmIDTemplate, },
+    { DER_OCTET_STRING,
+	  offsetof(SGNDigestInfo,digest), },
+    { 0, }
+};
+
+/*
+ * encode RSA PKCS #1 Signature data before signing... 
+ */
+SECStatus
+RSA_HashSign(SECOidTag hashOid, NSSLOWKEYPrivateKey *key,
+             unsigned char *sig, unsigned int *sigLen, unsigned int maxLen,
+             const unsigned char *hash, unsigned int hashLen)
+{
+    SECStatus rv = SECFailure;
+    SECItem digder;
+    PLArenaPool *arena = NULL;
+    SGNDigestInfo *di = NULL;
+
+    digder.data = NULL;
+
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!arena) {
+        goto loser;
+    }
+
+    /* Construct digest info */
+    di = SGN_CreateDigestInfo(hashOid, hash, hashLen);
+    if (!di) {
+        goto loser;
+    }
+
+    /* Der encode the digest as a DigestInfo */
+    rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    /*
+    ** Encrypt signature after constructing appropriate PKCS#1 signature
+    ** block
+    */
+    rv = RSA_Sign(&key->u.rsa, sig, sigLen, maxLen, digder.data,
+                  digder.len);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+
+  loser:
+    SGN_DestroyDigestInfo(di);
+    if (arena != NULL) {
+        PORT_FreeArena(arena, PR_FALSE);
+    }
+    return rv;
+}
+
+static SECStatus
+sftk_RSASign(NSSLOWKEYPrivateKey *key, unsigned char *output,
+             unsigned int *outputLen, unsigned int maxOutputLen,
+             const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    rv = RSA_Sign(&key->u.rsa, output, outputLen, maxOutputLen, input,
+                  inputLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+    return rv;
+}
+
+static SECStatus
+sftk_RSASignRaw(NSSLOWKEYPrivateKey *key, unsigned char *output,
+                unsigned int *outputLen, unsigned int maxOutputLen,
+                const unsigned char *input, unsigned int inputLen)
+{
+    SECStatus rv = SECFailure;
+
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    rv = RSA_SignRaw(&key->u.rsa, output, outputLen, maxOutputLen, input,
+                     inputLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+    return rv;
+    
+}
+
+static SECStatus
+sftk_RSASignPSS(SFTKHashSignInfo *info, unsigned char *sig,
+                unsigned int *sigLen, unsigned int maxLen,
+                const unsigned char *hash, unsigned int hashLen)
+{
+    SECStatus rv = SECFailure;
+    HASH_HashType hashAlg;
+    HASH_HashType maskHashAlg;
+    CK_RSA_PKCS_PSS_PARAMS *params = (CK_RSA_PKCS_PSS_PARAMS *)info->params;
+
+    PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
+    if (info->key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    hashAlg = GetHashTypeFromMechanism(params->hashAlg);
+    maskHashAlg = GetHashTypeFromMechanism(params->mgf);
+
+    rv = RSA_SignPSS(&info->key->u.rsa, hashAlg, maskHashAlg, NULL,
+                     params->sLen, sig, sigLen, maxLen, hash, hashLen);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+        sftk_fatalError = PR_TRUE;
+    }
+    return rv;
+}
+
+static SECStatus
+nsc_DSA_Verify_Stub(void *ctx, void *sigBuf, unsigned int sigLen,
+                               void *dataBuf, unsigned int dataLen)
+{
+    SECItem signature, digest;
+    NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx;
+
+    signature.data = (unsigned char *)sigBuf;
+    signature.len = sigLen;
+    digest.data = (unsigned char *)dataBuf;
+    digest.len = dataLen;
+    return DSA_VerifyDigest(&(key->u.dsa), &signature, &digest);
+}
+
+static SECStatus
+nsc_DSA_Sign_Stub(void *ctx, void *sigBuf,
+                  unsigned int *sigLen, unsigned int maxSigLen,
+                  void *dataBuf, unsigned int dataLen)
+{
+    SECItem signature, digest;
+    SECStatus rv;
+    NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx;
+
+    signature.data = (unsigned char *)sigBuf;
+    signature.len = maxSigLen;
+    digest.data = (unsigned char *)dataBuf;
+    digest.len = dataLen;
+    rv = DSA_SignDigest(&(key->u.dsa), &signature, &digest);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+	sftk_fatalError = PR_TRUE;
+    }
+    *sigLen = signature.len;
+    return rv;
+}
+
+#ifndef NSS_DISABLE_ECC
+static SECStatus
+nsc_ECDSAVerifyStub(void *ctx, void *sigBuf, unsigned int sigLen,
+                    void *dataBuf, unsigned int dataLen)
+{
+    SECItem signature, digest;
+    NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx;
+
+    signature.data = (unsigned char *)sigBuf;
+    signature.len = sigLen;
+    digest.data = (unsigned char *)dataBuf;
+    digest.len = dataLen;
+    return ECDSA_VerifyDigest(&(key->u.ec), &signature, &digest);
+}
+
+static SECStatus
+nsc_ECDSASignStub(void *ctx, void *sigBuf,
+                  unsigned int *sigLen, unsigned int maxSigLen,
+                  void *dataBuf, unsigned int dataLen)
+{
+    SECItem signature, digest;
+    SECStatus rv;
+    NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx;
+
+    signature.data = (unsigned char *)sigBuf;
+    signature.len = maxSigLen;
+    digest.data = (unsigned char *)dataBuf;
+    digest.len = dataLen;
+    rv = ECDSA_SignDigest(&(key->u.ec), &signature, &digest);
+    if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+	sftk_fatalError = PR_TRUE;
+    }
+    *sigLen = signature.len;
+    return rv;
+}
+#endif /* NSS_DISABLE_ECC */
+
+/* NSC_SignInit setups up the signing operations. There are three basic
+ * types of signing:
+ *	(1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied
+ *  to data in a single Sign operation (which often looks a lot like an
+ *  encrypt, with data coming in and data going out).
+ *	(2) Hash based signing, where we continually hash the data, then apply
+ *  some sort of signature to the end.
+ *	(3) Block Encryption CBC MAC's, where the Data is encrypted with a key,
+ *  and only the final block is part of the mac.
+ *
+ *  For case number 3, we initialize a context much like the Encryption Context
+ *  (in fact we share code). We detect case 3 in C_SignUpdate, C_Sign, and 
+ *  C_Final by the following method... if it's not multi-part, and it's doesn't
+ *  have a hash context, it must be a block Encryption CBC MAC.
+ *
+ *  For case number 2, we initialize a hash structure, as well as make it 
+ *  multi-part. Updates are simple calls to the hash update function. Final
+ *  calls the hashend, then passes the result to the 'update' function (which
+ *  operates as a final signature function). In some hash based MAC'ing (as
+ *  opposed to hash base signatures), the update function is can be simply a 
+ *  copy (as is the case with HMAC).
+ */
+CK_RV NSC_SignInit(CK_SESSION_HANDLE hSession,
+		 CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+    SFTKSession *session;
+    SFTKObject *key;
+    SFTKSessionContext *context;
+    CK_KEY_TYPE key_type;
+    CK_RV crv = CKR_OK;
+    NSSLOWKEYPrivateKey *privKey;
+    SFTKHashSignInfo *info = NULL;
+
+    CHECK_FORK();
+
+    /* Block Cipher MACing Algorithms use a different Context init method..*/
+    crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_SIGN, SFTK_SIGN);
+    if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv;
+
+    /* we're not using a block cipher mac */
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+    crv = sftk_InitGeneric(session,&context,SFTK_SIGN,&key,hKey,&key_type,
+						CKO_PRIVATE_KEY,CKA_SIGN);
+    if (crv != CKR_OK) {
+	sftk_FreeSession(session);
+	return crv;
+    }
+
+    context->multi = PR_FALSE;
+
+#define INIT_RSA_SIGN_MECH(mmm) \
+    case CKM_ ## mmm ## _RSA_PKCS: \
+        context->multi = PR_TRUE; \
+	crv = sftk_doSub ## mmm (context); \
+	if (crv != CKR_OK) break; \
+	context->update = (SFTKCipher) sftk_RSAHashSign; \
+	info = PORT_New(SFTKHashSignInfo); \
+	if (info == NULL) { crv = CKR_HOST_MEMORY; break; } \
+	info->hashOid = SEC_OID_ ## mmm ; \
+	goto finish_rsa; 
+
+    switch(pMechanism->mechanism) {
+    INIT_RSA_SIGN_MECH(MD5)
+    INIT_RSA_SIGN_MECH(MD2)
+    INIT_RSA_SIGN_MECH(SHA1)
+    INIT_RSA_SIGN_MECH(SHA224)
+    INIT_RSA_SIGN_MECH(SHA256)
+    INIT_RSA_SIGN_MECH(SHA384)
+    INIT_RSA_SIGN_MECH(SHA512)
+
+    case CKM_RSA_PKCS:
+	context->update = (SFTKCipher) sftk_RSASign;
+	goto finish_rsa;
+    case CKM_RSA_X_509:
+	context->update = (SFTKCipher) sftk_RSASignRaw;
+finish_rsa:
+	if (key_type != CKK_RSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	context->rsa = PR_TRUE;
+	privKey = sftk_GetPrivKey(key,CKK_RSA,&crv);
+	if (privKey == NULL) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	/* OK, info is allocated only if we're doing hash and sign mechanism.
+	 * It's necessary to be able to set the correct OID in the final 
+	 * signature.
+	 */
+	if (info) {
+	    info->key = privKey;
+	    context->cipherInfo = info;
+	    context->destroy = (SFTKDestroy)sftk_Space;
+	} else {
+	    context->cipherInfo = privKey;
+	    context->destroy = (SFTKDestroy)sftk_Null;
+	}
+	context->maxLen = nsslowkey_PrivateModulusLen(privKey);
+	break;
+    case CKM_RSA_PKCS_PSS:
+	if (key_type != CKK_RSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	} 
+	context->rsa = PR_TRUE;
+	if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+	    !sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	info = PORT_New(SFTKHashSignInfo);
+	if (info == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	info->params = pMechanism->pParameter;
+	info->key = sftk_GetPrivKey(key,CKK_RSA,&crv);
+	if (info->key == NULL) {
+	    PORT_Free(info);
+	    break;
+	}
+	context->cipherInfo = info;
+	context->destroy = (SFTKDestroy) sftk_Space;
+	context->update = (SFTKCipher) sftk_RSASignPSS;
+	context->maxLen = nsslowkey_PrivateModulusLen(info->key);
+	break;	
+
+    case CKM_DSA_SHA1:
+        context->multi = PR_TRUE;
+	crv = sftk_doSubSHA1(context);
+	if (crv != CKR_OK) break;
+	/* fall through */
+    case CKM_DSA:
+	if (key_type != CKK_DSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	privKey = sftk_GetPrivKey(key,CKK_DSA,&crv);
+	if (privKey == NULL) {
+	    break;
+	}
+	context->cipherInfo = privKey;
+	context->update     = (SFTKCipher) nsc_DSA_Sign_Stub;
+	context->destroy    = (privKey == key->objectInfo) ?
+		(SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey;
+	context->maxLen     = DSA_MAX_SIGNATURE_LEN;
+
+	break;
+
+#ifndef NSS_DISABLE_ECC
+    case CKM_ECDSA_SHA1:
+	context->multi = PR_TRUE;
+	crv = sftk_doSubSHA1(context);
+	if (crv != CKR_OK) break;
+	/* fall through */
+    case CKM_ECDSA:
+	if (key_type != CKK_EC) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	privKey = sftk_GetPrivKey(key,CKK_EC,&crv);
+	if (privKey == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->cipherInfo = privKey;
+	context->update     = (SFTKCipher) nsc_ECDSASignStub;
+	context->destroy    = (privKey == key->objectInfo) ?
+		(SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey;
+	context->maxLen     = MAX_ECKEY_LEN * 2;
+
+	break;
+#endif /* NSS_DISABLE_ECC */
+
+#define INIT_HMAC_MECH(mmm) \
+    case CKM_ ## mmm ## _HMAC_GENERAL: \
+	crv = sftk_doHMACInit(context, HASH_Alg ## mmm ,key, \
+				*(CK_ULONG *)pMechanism->pParameter); \
+	break; \
+    case CKM_ ## mmm ## _HMAC: \
+	crv = sftk_doHMACInit(context, HASH_Alg ## mmm ,key, mmm ## _LENGTH); \
+	break; 
+
+    INIT_HMAC_MECH(MD2)
+    INIT_HMAC_MECH(MD5)
+    INIT_HMAC_MECH(SHA224)
+    INIT_HMAC_MECH(SHA256)
+    INIT_HMAC_MECH(SHA384)
+    INIT_HMAC_MECH(SHA512)
+
+    case CKM_SHA_1_HMAC_GENERAL:
+	crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,
+				*(CK_ULONG *)pMechanism->pParameter);
+	break;
+    case CKM_SHA_1_HMAC:
+	crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH);
+	break;
+
+    case CKM_SSL3_MD5_MAC:
+	crv = sftk_doSSLMACInit(context,SEC_OID_MD5,key,
+					*(CK_ULONG *)pMechanism->pParameter);
+	break;
+    case CKM_SSL3_SHA1_MAC:
+	crv = sftk_doSSLMACInit(context,SEC_OID_SHA1,key,
+					*(CK_ULONG *)pMechanism->pParameter);
+	break;
+    case CKM_TLS_PRF_GENERAL:
+	crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL);
+	break;
+    case CKM_NSS_TLS_PRF_GENERAL_SHA256:
+	crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256);
+	break;
+
+    case CKM_NSS_HMAC_CONSTANT_TIME: {
+	sftk_MACConstantTimeCtx *ctx =
+	    sftk_HMACConstantTime_New(pMechanism,key);
+	CK_ULONG *intpointer;
+
+	if (ctx == NULL) {
+	    crv = CKR_ARGUMENTS_BAD;
+	    break;
+	}
+	intpointer = PORT_New(CK_ULONG);
+	if (intpointer == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	*intpointer = ctx->hash->length;
+
+	context->cipherInfo    = intpointer;
+	context->hashInfo      = ctx;
+	context->currentMech   = pMechanism->mechanism;
+	context->hashUpdate    = sftk_HMACConstantTime_Update;
+	context->hashdestroy   = sftk_MACConstantTime_DestroyContext;
+	context->end           = sftk_MACConstantTime_EndHash;
+	context->update        = (SFTKCipher) sftk_SignCopy;
+	context->destroy       = sftk_Space;
+	context->maxLen        = 64;
+	context->multi         = PR_TRUE;
+	break;
+    }
+
+    case CKM_NSS_SSL3_MAC_CONSTANT_TIME: {
+	sftk_MACConstantTimeCtx *ctx =
+	    sftk_SSLv3MACConstantTime_New(pMechanism,key);
+	CK_ULONG *intpointer;
+
+	if (ctx == NULL) {
+	    crv = CKR_ARGUMENTS_BAD;
+	    break;
+	}
+	intpointer = PORT_New(CK_ULONG);
+	if (intpointer == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	*intpointer = ctx->hash->length;
+
+	context->cipherInfo    = intpointer;
+	context->hashInfo      = ctx;
+	context->currentMech   = pMechanism->mechanism;
+	context->hashUpdate    = sftk_SSLv3MACConstantTime_Update;
+	context->hashdestroy   = sftk_MACConstantTime_DestroyContext;
+	context->end           = sftk_MACConstantTime_EndHash;
+	context->update        = (SFTKCipher) sftk_SignCopy;
+	context->destroy       = sftk_Space;
+	context->maxLen        = 64;
+	context->multi         = PR_TRUE;
+	break;
+    }
+
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    if (crv != CKR_OK) {
+	if (info) PORT_Free(info);
+        sftk_FreeContext(context);
+        sftk_FreeSession(session);
+        return crv;
+    }
+    sftk_SetContextByType(session, SFTK_SIGN, context);
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+/** MAC one block of data by block cipher
+ */
+static CK_RV
+sftk_MACBlock( SFTKSessionContext *ctx, void *blk )
+{
+    unsigned int outlen;
+    return ( SECSuccess == (ctx->update)( ctx->cipherInfo, ctx->macBuf, &outlen,
+                SFTK_MAX_BLOCK_SIZE, blk, ctx->blockSize ))
+            ? CKR_OK : sftk_MapCryptError(PORT_GetError());
+}
+
+/** MAC last (incomplete) block of data by block cipher
+ *
+ *  Call once, then terminate MACing operation.
+ */
+static CK_RV
+sftk_MACFinal( SFTKSessionContext *ctx )
+{
+    unsigned int padLen = ctx->padDataLength;
+    /* pad and proceed the residual */
+    if( padLen ) {
+        /* shd clr ctx->padLen to make sftk_MACFinal idempotent */
+        PORT_Memset( ctx->padBuf + padLen, 0, ctx->blockSize - padLen );
+        return sftk_MACBlock( ctx, ctx->padBuf );
+    } else
+        return CKR_OK;
+}
+
+/** The common implementation for {Sign,Verify}Update. (S/V only vary in their
+ * setup and final operations).
+ * 
+ * A call which results in an error terminates the operation [PKCS#11,v2.11]
+ */
+static CK_RV
+sftk_MACUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+    					CK_ULONG ulPartLen,SFTKContextType type)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    CK_RV crv;
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,type, PR_TRUE, &session );
+    if (crv != CKR_OK) return crv;
+
+    if (context->hashInfo) {
+	(*context->hashUpdate)(context->hashInfo, pPart, ulPartLen);
+    } else {   
+    	/* must be block cipher MACing */
+
+        unsigned int  blkSize   = context->blockSize;
+        unsigned char *residual = /* free room in context->padBuf */
+                                context->padBuf + context->padDataLength;
+        unsigned int  minInput  = /* min input for MACing at least one block */
+                                blkSize - context->padDataLength;
+
+        /* not enough data even for one block */
+        if( ulPartLen < minInput ) {
+            PORT_Memcpy( residual, pPart, ulPartLen );
+            context->padDataLength += ulPartLen;
+            goto cleanup;
+        }
+        /* MACing residual */
+        if( context->padDataLength ) {
+            PORT_Memcpy( residual, pPart, minInput );
+            ulPartLen -= minInput;
+            pPart     += minInput;
+            if( CKR_OK != (crv = sftk_MACBlock( context, context->padBuf )) )
+                goto terminate;
+        }
+        /* MACing full blocks */
+        while( ulPartLen >= blkSize )
+        {
+            if( CKR_OK != (crv = sftk_MACBlock( context, pPart )) )
+                goto terminate;
+            ulPartLen -= blkSize;
+            pPart     += blkSize;
+        }
+        /* save the residual */
+        if( (context->padDataLength = ulPartLen) )
+            PORT_Memcpy( context->padBuf, pPart, ulPartLen );
+    } /* blk cipher MACing */
+
+    goto  cleanup;
+
+terminate:
+    sftk_TerminateOp( session, type, context );
+cleanup:
+    sftk_FreeSession(session);
+    return crv;
+}
+
+/* NSC_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data, 
+ * and plaintext cannot be recovered from the signature 
+ *
+ * A call which results in an error terminates the operation [PKCS#11,v2.11]
+ */
+CK_RV NSC_SignUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+    							CK_ULONG ulPartLen)
+{
+    CHECK_FORK();
+    return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_SIGN);
+}
+
+
+/* NSC_SignFinal finishes a multiple-part signature operation, 
+ * returning the signature. */
+CK_RV NSC_SignFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pSignature,
+					    CK_ULONG_PTR pulSignatureLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int outlen;
+    unsigned int maxoutlen = *pulSignatureLen;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_SIGN,PR_TRUE,&session);
+    if (crv != CKR_OK) return crv;
+
+    if (context->hashInfo) {
+        unsigned int digestLen;
+        unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH];
+
+        if( !pSignature ) {
+            outlen = context->maxLen; goto finish;
+        }
+        (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf));
+        if( SECSuccess != (context->update)(context->cipherInfo, pSignature,
+                    &outlen, maxoutlen, tmpbuf, digestLen))
+            crv = sftk_MapCryptError(PORT_GetError());
+        /* CKR_BUFFER_TOO_SMALL here isn't continuable, let operation terminate.
+         * Keeping "too small" CK_RV intact is a standard violation, but allows
+         * application read EXACT signature length */
+    } else {
+        /* must be block cipher MACing */
+        outlen = context->macSize;
+        /* null or "too small" buf doesn't terminate operation [PKCS#11,v2.11]*/
+        if( !pSignature  ||  maxoutlen < outlen ) {
+            if( pSignature ) crv = CKR_BUFFER_TOO_SMALL;
+            goto finish;
+        }
+        if( CKR_OK == (crv = sftk_MACFinal( context )) )
+	    PORT_Memcpy(pSignature, context->macBuf, outlen );
+    }
+
+    sftk_TerminateOp( session, SFTK_SIGN, context );
+finish:
+    *pulSignatureLen = outlen;
+    sftk_FreeSession(session);
+    return crv;
+}
+
+/* NSC_Sign signs (encrypts with private key) data in a single part,
+ * where the signature is (will be) an appendix to the data, 
+ * and plaintext cannot be recovered from the signature */
+CK_RV NSC_Sign(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR pData,CK_ULONG ulDataLen,CK_BYTE_PTR pSignature,
+    					CK_ULONG_PTR pulSignatureLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_SIGN,PR_FALSE,&session);
+    if (crv != CKR_OK) return crv;
+
+    if (!pSignature) {
+        /* see also how C_SignUpdate implements this */
+	*pulSignatureLen = (!context->multi || context->hashInfo)
+            ? context->maxLen
+            : context->macSize; /* must be block cipher MACing */
+	goto finish;
+    }
+
+    /* multi part Signing are completely implemented by SignUpdate and
+     * sign Final */
+    if (context->multi) {
+        /* SignFinal can't follow failed SignUpdate */
+	if( CKR_OK == (crv = NSC_SignUpdate(hSession,pData,ulDataLen) ))
+            crv = NSC_SignFinal(hSession, pSignature, pulSignatureLen);
+    } else {   
+    	/* single-part PKC signature (e.g. CKM_ECDSA) */
+        unsigned int outlen;
+        unsigned int maxoutlen = *pulSignatureLen;
+        if( SECSuccess != (*context->update)(context->cipherInfo, pSignature,
+                    &outlen, maxoutlen, pData, ulDataLen))
+            crv = sftk_MapCryptError(PORT_GetError());
+        *pulSignatureLen = (CK_ULONG) outlen;
+        /*  "too small" here is certainly continuable */
+        if( crv != CKR_BUFFER_TOO_SMALL )
+            sftk_TerminateOp(session, SFTK_SIGN, context);
+    } /* single-part */
+
+finish:
+    sftk_FreeSession(session);
+    return crv;
+}
+
+
+/*
+ ************** Crypto Functions:     Sign Recover  ************************
+ */
+/* NSC_SignRecoverInit initializes a signature operation,
+ * where the (digest) data can be recovered from the signature. 
+ * E.g. encryption with the user's private key */
+CK_RV NSC_SignRecoverInit(CK_SESSION_HANDLE hSession,
+			 CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey)
+{
+    CHECK_FORK();
+
+    switch (pMechanism->mechanism) {
+    case CKM_RSA_PKCS:
+    case CKM_RSA_X_509:
+	return NSC_SignInit(hSession,pMechanism,hKey);
+    default:
+	break;
+    }
+    return CKR_MECHANISM_INVALID;
+}
+
+
+/* NSC_SignRecover signs data in a single operation
+ * where the (digest) data can be recovered from the signature. 
+ * E.g. encryption with the user's private key */
+CK_RV NSC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+  CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+    CHECK_FORK();
+
+    return NSC_Sign(hSession,pData,ulDataLen,pSignature,pulSignatureLen);
+}
+
+/*
+ ************** Crypto Functions:     verify  ************************
+ */
+
+/* Handle RSA Signature formatting */
+static SECStatus
+sftk_hashCheckSign(SFTKHashVerifyInfo *info, const unsigned char *sig, 
+                   unsigned int sigLen, const unsigned char *digest,
+                   unsigned int digestLen)
+{
+    PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
+    if (info->key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen, digest,
+                             digestLen);
+}
+
+SECStatus
+RSA_HashCheckSign(SECOidTag hashOid, NSSLOWKEYPublicKey *key,
+                  const unsigned char *sig, unsigned int sigLen,
+                  const unsigned char *hash, unsigned int hashLen)
+{
+    SECItem it;
+    SGNDigestInfo *di = NULL;
+    SECStatus rv = SECSuccess;
+
+    it.data = NULL;
+    it.len = nsslowkey_PublicModulusLen(key);
+    if (!it.len) {
+        goto loser;
+    }
+
+    it.data = (unsigned char *)PORT_Alloc(it.len);
+    if (it.data == NULL) {
+        goto loser;
+    }
+
+    /* decrypt the block */
+    rv = RSA_CheckSignRecover(&key->u.rsa, it.data, &it.len, it.len, sig,
+                              sigLen);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    di = SGN_DecodeDigestInfo(&it);
+    if (di == NULL) {
+        goto loser;
+    }
+    if (di->digest.len != hashLen) {
+        goto loser; 
+    }
+
+    /* make sure the tag is OK */
+    if (SECOID_GetAlgorithmTag(&di->digestAlgorithm) != hashOid) {
+        goto loser;
+    }
+    /* make sure the "parameters" are not too bogus. */
+    if (di->digestAlgorithm.parameters.len > 2) {
+        goto loser;
+    }
+    /* Now check the signature */
+    if (PORT_Memcmp(hash, di->digest.data, di->digest.len) == 0) {
+        goto done;
+    }
+
+  loser:
+    PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+    rv = SECFailure;
+
+  done:
+    if (it.data != NULL) {
+        PORT_Free(it.data);
+    }
+    if (di != NULL) {
+        SGN_DestroyDigestInfo(di);
+    }
+
+    return rv;
+}
+
+static SECStatus
+sftk_RSACheckSign(NSSLOWKEYPublicKey *key, const unsigned char *sig,
+                  unsigned int sigLen, const unsigned char *digest,
+                  unsigned int digestLen)
+{
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    return RSA_CheckSign(&key->u.rsa, sig, sigLen, digest, digestLen);
+}
+
+static SECStatus
+sftk_RSACheckSignRaw(NSSLOWKEYPublicKey *key, const unsigned char *sig,
+                     unsigned int sigLen, const unsigned char *digest,
+                     unsigned int digestLen)
+{
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    return RSA_CheckSignRaw(&key->u.rsa, sig, sigLen, digest, digestLen);
+}
+
+static SECStatus
+sftk_RSACheckSignPSS(SFTKHashVerifyInfo *info, const unsigned char *sig,
+                     unsigned int sigLen, const unsigned char *digest,
+                     unsigned int digestLen)
+{
+    HASH_HashType hashAlg;
+    HASH_HashType maskHashAlg;
+    CK_RSA_PKCS_PSS_PARAMS *params = (CK_RSA_PKCS_PSS_PARAMS *)info->params;
+
+    PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
+    if (info->key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    hashAlg = GetHashTypeFromMechanism(params->hashAlg);
+    maskHashAlg = GetHashTypeFromMechanism(params->mgf);
+
+    return RSA_CheckSignPSS(&info->key->u.rsa, hashAlg, maskHashAlg,
+                            params->sLen, sig, sigLen, digest, digestLen);
+}
+
+/* NSC_VerifyInit initializes a verification operation, 
+ * where the signature is an appendix to the data, 
+ * and plaintext cannot be recovered from the signature (e.g. DSA) */
+CK_RV NSC_VerifyInit(CK_SESSION_HANDLE hSession,
+			   CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) 
+{
+    SFTKSession *session;
+    SFTKObject *key;
+    SFTKSessionContext *context;
+    CK_KEY_TYPE key_type;
+    CK_RV crv = CKR_OK;
+    NSSLOWKEYPublicKey *pubKey;
+    SFTKHashVerifyInfo *info = NULL;
+
+    CHECK_FORK();
+
+    /* Block Cipher MACing Algorithms use a different Context init method..*/
+    crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_VERIFY, SFTK_VERIFY);
+    if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv;
+
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+    crv = sftk_InitGeneric(session,&context,SFTK_VERIFY,&key,hKey,&key_type,
+						CKO_PUBLIC_KEY,CKA_VERIFY);
+    if (crv != CKR_OK) {
+	sftk_FreeSession(session);
+	return crv;
+    }
+
+    context->multi = PR_FALSE;
+
+#define INIT_RSA_VFY_MECH(mmm) \
+    case CKM_ ## mmm ## _RSA_PKCS: \
+        context->multi = PR_TRUE; \
+	crv = sftk_doSub ## mmm (context); \
+	if (crv != CKR_OK) break; \
+	context->verify = (SFTKVerify) sftk_hashCheckSign; \
+	info = PORT_New(SFTKHashVerifyInfo); \
+	if (info == NULL) { crv = CKR_HOST_MEMORY; break; } \
+	info->hashOid = SEC_OID_ ## mmm ; \
+	goto finish_rsa; 
+
+    switch(pMechanism->mechanism) {
+    INIT_RSA_VFY_MECH(MD5) 
+    INIT_RSA_VFY_MECH(MD2) 
+    INIT_RSA_VFY_MECH(SHA1) 
+    INIT_RSA_VFY_MECH(SHA224)
+    INIT_RSA_VFY_MECH(SHA256) 
+    INIT_RSA_VFY_MECH(SHA384) 
+    INIT_RSA_VFY_MECH(SHA512) 
+
+    case CKM_RSA_PKCS:
+	context->verify = (SFTKVerify) sftk_RSACheckSign;
+	goto finish_rsa;
+    case CKM_RSA_X_509:
+	context->verify = (SFTKVerify) sftk_RSACheckSignRaw;
+finish_rsa:
+	if (key_type != CKK_RSA) {
+	    if (info) PORT_Free(info);
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	context->rsa = PR_TRUE;
+	pubKey = sftk_GetPubKey(key,CKK_RSA,&crv);
+	if (pubKey == NULL) {
+	    if (info) PORT_Free(info);
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	if (info) {
+	    info->key = pubKey;
+	    context->cipherInfo = info;
+	    context->destroy = sftk_Space;
+	} else {
+	    context->cipherInfo = pubKey;
+	    context->destroy = sftk_Null;
+	}
+	break;
+    case CKM_RSA_PKCS_PSS:
+	if (key_type != CKK_RSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	} 
+	context->rsa = PR_TRUE;
+	if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+	    !sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	info = PORT_New(SFTKHashVerifyInfo);
+	if (info == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	info->params = pMechanism->pParameter;
+	info->key = sftk_GetPubKey(key,CKK_RSA,&crv);
+	if (info->key == NULL) {
+	    PORT_Free(info);
+	    break;
+	}
+	context->cipherInfo = info;
+	context->destroy = (SFTKDestroy) sftk_Space;
+	context->verify = (SFTKVerify) sftk_RSACheckSignPSS;
+	break;
+    case CKM_DSA_SHA1:
+        context->multi = PR_TRUE;
+	crv = sftk_doSubSHA1(context);
+	if (crv != CKR_OK) break;
+	/* fall through */
+    case CKM_DSA:
+	if (key_type != CKK_DSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	pubKey = sftk_GetPubKey(key,CKK_DSA,&crv);
+	if (pubKey == NULL) {
+	    break;
+	}
+	context->cipherInfo = pubKey;
+	context->verify     = (SFTKVerify) nsc_DSA_Verify_Stub;
+	context->destroy    = sftk_Null;
+	break;
+#ifndef NSS_DISABLE_ECC
+    case CKM_ECDSA_SHA1:
+	context->multi = PR_TRUE;
+	crv = sftk_doSubSHA1(context);
+	if (crv != CKR_OK) break;
+	/* fall through */
+    case CKM_ECDSA:
+	if (key_type != CKK_EC) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	pubKey = sftk_GetPubKey(key,CKK_EC,&crv);
+	if (pubKey == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	context->cipherInfo = pubKey;
+	context->verify     = (SFTKVerify) nsc_ECDSAVerifyStub;
+	context->destroy    = sftk_Null;
+	break;
+#endif /* NSS_DISABLE_ECC */
+
+    INIT_HMAC_MECH(MD2)
+    INIT_HMAC_MECH(MD5)
+    INIT_HMAC_MECH(SHA224)
+    INIT_HMAC_MECH(SHA256)
+    INIT_HMAC_MECH(SHA384)
+    INIT_HMAC_MECH(SHA512)
+
+    case CKM_SHA_1_HMAC_GENERAL:
+	crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,
+				*(CK_ULONG *)pMechanism->pParameter);
+	break;
+    case CKM_SHA_1_HMAC:
+	crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH);
+	break;
+
+    case CKM_SSL3_MD5_MAC:
+	crv = sftk_doSSLMACInit(context,SEC_OID_MD5,key,
+					*(CK_ULONG *)pMechanism->pParameter);
+	break;
+    case CKM_SSL3_SHA1_MAC:
+	crv = sftk_doSSLMACInit(context,SEC_OID_SHA1,key,
+					*(CK_ULONG *)pMechanism->pParameter);
+	break;
+    case CKM_TLS_PRF_GENERAL:
+	crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL);
+	break;
+    case CKM_NSS_TLS_PRF_GENERAL_SHA256:
+	crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256);
+	break;
+
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    if (crv != CKR_OK) {
+	if (info) PORT_Free(info);
+        sftk_FreeContext(context);
+	sftk_FreeSession(session);
+	return crv;
+    }
+    sftk_SetContextByType(session, SFTK_VERIFY, context);
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+/* NSC_Verify verifies a signature in a single-part operation, 
+ * where the signature is an appendix to the data, 
+ * and plaintext cannot be recovered from the signature */
+CK_RV NSC_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+    CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_VERIFY,PR_FALSE,&session);
+    if (crv != CKR_OK) return crv;
+
+    /* multi part Verifying are completely implemented by VerifyUpdate and
+     * VerifyFinal */
+    if (context->multi) {
+        /* VerifyFinal can't follow failed VerifyUpdate */
+	if( CKR_OK == (crv = NSC_VerifyUpdate(hSession, pData, ulDataLen)))
+            crv = NSC_VerifyFinal(hSession, pSignature, ulSignatureLen);
+    } else {
+        if (SECSuccess != (*context->verify)(context->cipherInfo,pSignature,
+                                             ulSignatureLen, pData, ulDataLen))
+            crv = sftk_MapCryptError(PORT_GetError());
+
+        sftk_TerminateOp( session, SFTK_VERIFY, context );
+    }
+    sftk_FreeSession(session);
+    return crv;
+}
+
+
+/* NSC_VerifyUpdate continues a multiple-part verification operation, 
+ * where the signature is an appendix to the data, 
+ * and plaintext cannot be recovered from the signature
+ *
+ * A call which results in an error terminates the operation [PKCS#11,v2.11]
+ */
+CK_RV NSC_VerifyUpdate( CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+						CK_ULONG ulPartLen)
+{
+    CHECK_FORK();
+    return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_VERIFY);
+}
+
+
+/* NSC_VerifyFinal finishes a multiple-part verification operation, 
+ * checking the signature. */
+CK_RV NSC_VerifyFinal(CK_SESSION_HANDLE hSession,
+			CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    if (!pSignature) 
+    	return CKR_ARGUMENTS_BAD;
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_VERIFY,PR_TRUE,&session);
+    if (crv != CKR_OK) 
+    	return crv;
+    
+    if (context->hashInfo) {
+        unsigned int digestLen;
+        unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH];
+        
+        (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf));
+        if( SECSuccess != (context->verify)(context->cipherInfo, pSignature,
+                                            ulSignatureLen, tmpbuf, digestLen))
+            crv = sftk_MapCryptError(PORT_GetError());
+    } else if (ulSignatureLen != context->macSize) {
+	/* must be block cipher MACing */
+	crv = CKR_SIGNATURE_LEN_RANGE;
+    } else if (CKR_OK == (crv = sftk_MACFinal(context))) {
+	if (PORT_Memcmp(pSignature, context->macBuf, ulSignatureLen))
+	    crv = CKR_SIGNATURE_INVALID;
+    }
+
+    sftk_TerminateOp( session, SFTK_VERIFY, context );
+    sftk_FreeSession(session);
+    return crv;
+
+}
+
+/*
+ ************** Crypto Functions:     Verify  Recover ************************
+ */
+static SECStatus
+sftk_RSACheckSignRecover(NSSLOWKEYPublicKey *key, unsigned char *data,
+                         unsigned int *dataLen, unsigned int maxDataLen,
+                         const unsigned char *sig, unsigned int sigLen)
+{
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    return RSA_CheckSignRecover(&key->u.rsa, data, dataLen, maxDataLen,
+                                sig, sigLen);
+}
+
+static SECStatus
+sftk_RSACheckSignRecoverRaw(NSSLOWKEYPublicKey *key, unsigned char *data,
+                            unsigned int *dataLen, unsigned int maxDataLen,
+                            const unsigned char *sig, unsigned int sigLen)
+{
+    PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+    if (key->keyType != NSSLOWKEYRSAKey) {
+        PORT_SetError(SEC_ERROR_INVALID_KEY);
+        return SECFailure;
+    }
+
+    return RSA_CheckSignRecoverRaw(&key->u.rsa, data, dataLen, maxDataLen,
+                                   sig, sigLen);
+}
+
+/* NSC_VerifyRecoverInit initializes a signature verification operation, 
+ * where the data is recovered from the signature. 
+ * E.g. Decryption with the user's public key */
+CK_RV NSC_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
+			CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey)
+{
+    SFTKSession *session;
+    SFTKObject *key;
+    SFTKSessionContext *context;
+    CK_KEY_TYPE key_type;
+    CK_RV crv = CKR_OK;
+    NSSLOWKEYPublicKey *pubKey;
+
+    CHECK_FORK();
+
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+    crv = sftk_InitGeneric(session,&context,SFTK_VERIFY_RECOVER,
+			&key,hKey,&key_type,CKO_PUBLIC_KEY,CKA_VERIFY_RECOVER);
+    if (crv != CKR_OK) {
+	sftk_FreeSession(session);
+	return crv;
+    }
+
+    context->multi = PR_TRUE;
+
+    switch(pMechanism->mechanism) {
+    case CKM_RSA_PKCS:
+    case CKM_RSA_X_509:
+	if (key_type != CKK_RSA) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	context->multi = PR_FALSE;
+	context->rsa = PR_TRUE;
+	pubKey = sftk_GetPubKey(key,CKK_RSA,&crv);
+	if (pubKey == NULL) {
+	    break;
+	}
+	context->cipherInfo = pubKey;
+	context->update = (SFTKCipher) (pMechanism->mechanism == CKM_RSA_X_509
+			? sftk_RSACheckSignRecoverRaw : sftk_RSACheckSignRecover);
+	context->destroy = sftk_Null;
+	break;
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    if (crv != CKR_OK) {
+        PORT_Free(context);
+	sftk_FreeSession(session);
+	return crv;
+    }
+    sftk_SetContextByType(session, SFTK_VERIFY_RECOVER, context);
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+
+/* NSC_VerifyRecover verifies a signature in a single-part operation, 
+ * where the data is recovered from the signature. 
+ * E.g. Decryption with the user's public key */
+CK_RV NSC_VerifyRecover(CK_SESSION_HANDLE hSession,
+		 CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen,
+    				CK_BYTE_PTR pData,CK_ULONG_PTR pulDataLen)
+{
+    SFTKSession *session;
+    SFTKSessionContext *context;
+    unsigned int outlen;
+    unsigned int maxoutlen = *pulDataLen;
+    CK_RV crv;
+    SECStatus rv;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession,&context,SFTK_VERIFY_RECOVER,
+							PR_FALSE,&session);
+    if (crv != CKR_OK) return crv;
+    if (pData == NULL) {
+	/* to return the actual size, we need  to do the decrypt, just return
+	 * the max size, which is the size of the input signature. */
+	*pulDataLen = ulSignatureLen;
+	rv = SECSuccess;
+	goto finish;
+    }
+
+    rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, 
+						pSignature, ulSignatureLen);
+    *pulDataLen = (CK_ULONG) outlen;
+
+    sftk_TerminateOp(session, SFTK_VERIFY_RECOVER, context);
+finish:
+    sftk_FreeSession(session);
+    return (rv == SECSuccess)  ? CKR_OK : sftk_MapVerifyError(PORT_GetError());
+}
+
+/*
+ **************************** Random Functions:  ************************
+ */
+
+/* NSC_SeedRandom mixes additional seed material into the token's random number 
+ * generator. */
+CK_RV NSC_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed,
+    CK_ULONG ulSeedLen) 
+{
+    SECStatus rv;
+
+    CHECK_FORK();
+
+    rv = RNG_RandomUpdate(pSeed, ulSeedLen);
+    return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
+}
+
+/* NSC_GenerateRandom generates random data. */
+CK_RV NSC_GenerateRandom(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR	pRandomData, CK_ULONG ulRandomLen)
+{
+    SECStatus rv;
+
+    CHECK_FORK();
+
+    rv = RNG_GenerateGlobalRandomBytes(pRandomData, ulRandomLen);
+    /*
+     * This may fail with SEC_ERROR_NEED_RANDOM, which means the RNG isn't
+     * seeded with enough entropy.
+     */
+    return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
+}
+
+/*
+ **************************** Key Functions:  ************************
+ */
+
+
+/*
+ * generate a password based encryption key. This code uses
+ * PKCS5 to do the work.
+ */
+static CK_RV
+nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism,
+			void *buf, CK_ULONG *key_length, PRBool faulty3DES)
+{
+    SECItem *pbe_key = NULL, iv, pwitem;
+    CK_PBE_PARAMS *pbe_params = NULL;
+    CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL;
+
+    *key_length = 0;
+    iv.data = NULL; iv.len = 0;
+
+    if (pMechanism->mechanism == CKM_PKCS5_PBKD2) {
+	pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter;
+	pwitem.data = (unsigned char *)pbkd2_params->pPassword;
+	/* was this a typo in the PKCS #11 spec? */
+	pwitem.len = *pbkd2_params->ulPasswordLen;
+    } else {
+	pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
+	pwitem.data = (unsigned char *)pbe_params->pPassword;
+	pwitem.len = pbe_params->ulPasswordLen;
+    }
+    pbe_key = nsspkcs5_ComputeKeyAndIV(pkcs5_pbe, &pwitem, &iv, faulty3DES);
+    if (pbe_key == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    PORT_Memcpy(buf, pbe_key->data, pbe_key->len);
+    *key_length = pbe_key->len;
+    SECITEM_ZfreeItem(pbe_key, PR_TRUE);
+    pbe_key = NULL;
+
+    if (iv.data) {
+        if (pbe_params && pbe_params->pInitVector != NULL) {
+	    PORT_Memcpy(pbe_params->pInitVector, iv.data, iv.len);
+        }
+        PORT_Free(iv.data);
+    }
+
+    return CKR_OK;
+}
+
+/* 
+ * this is coded for "full" support. These selections will be limitted to
+ * the official subset by freebl.
+ */
+static unsigned int
+sftk_GetSubPrimeFromPrime(unsigned int primeBits)
+{
+   if (primeBits <= 1024) {
+	return 160;
+   } else if (primeBits <= 2048) {
+	return 224;
+   } else if (primeBits <= 3072) {
+	return 256;
+   } else if (primeBits <= 7680) {
+	return 384;
+   } else {
+	return 512;
+   }
+}
+
+static CK_RV
+nsc_parameter_gen(CK_KEY_TYPE key_type, SFTKObject *key)
+{
+    SFTKAttribute *attribute;
+    CK_ULONG counter;
+    unsigned int seedBits = 0;
+    unsigned int subprimeBits = 0;
+    unsigned int primeBits;
+    unsigned int j = 8; /* default to 1024 bits */
+    CK_RV crv = CKR_OK;
+    PQGParams *params = NULL;
+    PQGVerify *vfy = NULL;
+    SECStatus rv;
+
+    attribute = sftk_FindAttribute(key, CKA_PRIME_BITS);
+    if (attribute == NULL) {
+	return CKR_TEMPLATE_INCOMPLETE;
+    }
+    primeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue;
+    sftk_FreeAttribute(attribute);
+    if (primeBits < 1024) {
+	j = PQG_PBITS_TO_INDEX(primeBits);
+	if (j == (unsigned int)-1) {
+	    return CKR_ATTRIBUTE_VALUE_INVALID;
+	}
+    }
+
+    attribute = sftk_FindAttribute(key, CKA_NETSCAPE_PQG_SEED_BITS);
+    if (attribute != NULL) {
+	seedBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue;
+	sftk_FreeAttribute(attribute);
+    }
+
+    attribute = sftk_FindAttribute(key, CKA_SUBPRIME_BITS);
+    if (attribute != NULL) {
+	subprimeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue;
+	sftk_FreeAttribute(attribute);
+    }
+
+    sftk_DeleteAttributeType(key,CKA_PRIME_BITS);
+    sftk_DeleteAttributeType(key,CKA_SUBPRIME_BITS);
+    sftk_DeleteAttributeType(key,CKA_NETSCAPE_PQG_SEED_BITS);
+
+    /* use the old PQG interface if we have old input data */
+    if ((primeBits < 1024) || ((primeBits == 1024) && (subprimeBits == 0))) {
+	if (seedBits == 0) {
+	    rv = PQG_ParamGen(j, &params, &vfy);
+	} else {
+	    rv = PQG_ParamGenSeedLen(j,seedBits/8, &params, &vfy);
+	}
+    } else {
+	if (subprimeBits == 0) {
+	    subprimeBits = sftk_GetSubPrimeFromPrime(primeBits);
+        }
+	if (seedBits == 0) {
+	    seedBits = primeBits;
+	}
+	rv = PQG_ParamGenV2(primeBits, subprimeBits, seedBits/8, &params, &vfy);
+    }
+	
+
+
+    if (rv != SECSuccess) {
+	if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+	    sftk_fatalError = PR_TRUE;
+	}
+	return sftk_MapCryptError(PORT_GetError());
+    }
+    crv = sftk_AddAttributeType(key,CKA_PRIME,
+				 params->prime.data, params->prime.len);
+    if (crv != CKR_OK) goto loser;
+    crv = sftk_AddAttributeType(key,CKA_SUBPRIME,
+				 params->subPrime.data, params->subPrime.len);
+    if (crv != CKR_OK) goto loser;
+    crv = sftk_AddAttributeType(key,CKA_BASE,
+				 params->base.data, params->base.len);
+    if (crv != CKR_OK) goto loser;
+    counter = vfy->counter;
+    crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_COUNTER,
+				 &counter, sizeof(counter));
+    crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_SEED,
+				 vfy->seed.data, vfy->seed.len);
+    if (crv != CKR_OK) goto loser;
+    crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_H,
+				 vfy->h.data, vfy->h.len);
+    if (crv != CKR_OK) goto loser;
+
+loser:
+    PQG_DestroyParams(params);
+
+    if (vfy) {
+	PQG_DestroyVerify(vfy);
+    }
+    return crv;
+}
+
+
+static CK_RV
+nsc_SetupBulkKeyGen(CK_MECHANISM_TYPE mechanism, CK_KEY_TYPE *key_type,
+							CK_ULONG *key_length)
+{
+    CK_RV crv = CKR_OK;
+
+    switch (mechanism) {
+    case CKM_RC2_KEY_GEN:
+	*key_type = CKK_RC2;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+#if NSS_SOFTOKEN_DOES_RC5
+    case CKM_RC5_KEY_GEN:
+	*key_type = CKK_RC5;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+#endif
+    case CKM_RC4_KEY_GEN:
+	*key_type = CKK_RC4;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+    case CKM_GENERIC_SECRET_KEY_GEN:
+	*key_type = CKK_GENERIC_SECRET;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+    case CKM_CDMF_KEY_GEN:
+	*key_type = CKK_CDMF;
+	*key_length = 8;
+	break;
+    case CKM_DES_KEY_GEN:
+	*key_type = CKK_DES;
+	*key_length = 8;
+	break;
+    case CKM_DES2_KEY_GEN:
+	*key_type = CKK_DES2;
+	*key_length = 16;
+	break;
+    case CKM_DES3_KEY_GEN:
+	*key_type = CKK_DES3;
+	*key_length = 24;
+	break;
+    case CKM_SEED_KEY_GEN:
+	*key_type = CKK_SEED;
+	*key_length = 16;
+	break;
+    case CKM_CAMELLIA_KEY_GEN:
+	*key_type = CKK_CAMELLIA;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+    case CKM_AES_KEY_GEN:
+	*key_type = CKK_AES;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+    case CKM_NSS_CHACHA20_KEY_GEN:
+	*key_type = CKK_NSS_CHACHA20;
+	if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+	break;
+    default:
+	PORT_Assert(0);
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    return crv;
+}
+
+CK_RV
+nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe)
+{
+    SECItem  salt;
+    CK_PBE_PARAMS *pbe_params = NULL;
+    NSSPKCS5PBEParameter *params;
+    PLArenaPool *arena = NULL;
+    SECStatus rv;
+
+    *pbe = NULL;
+
+    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+    if (arena == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    params = (NSSPKCS5PBEParameter *) PORT_ArenaZAlloc(arena,
+				sizeof(NSSPKCS5PBEParameter));
+    if (params == NULL) {
+	PORT_FreeArena(arena,PR_TRUE);
+	return CKR_HOST_MEMORY;
+    }
+
+    params->poolp = arena;
+    params->ivLen = 0;
+    params->pbeType = NSSPKCS5_PKCS12_V2;
+    params->hashType = HASH_AlgSHA1;
+    params->encAlg = SEC_OID_SHA1; /* any invalid value */
+    params->is2KeyDES = PR_FALSE;
+    params->keyID = pbeBitGenIntegrityKey;
+    pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
+    params->iter = pbe_params->ulIteration;
+
+    salt.data = (unsigned char *)pbe_params->pSalt;
+    salt.len = (unsigned int)pbe_params->ulSaltLen;
+    rv = SECITEM_CopyItem(arena,&params->salt,&salt);
+    if (rv != SECSuccess) {
+	PORT_FreeArena(arena,PR_TRUE);
+	return CKR_HOST_MEMORY;
+    }
+    switch (pMechanism->mechanism) {
+    case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN:
+    case CKM_PBA_SHA1_WITH_SHA1_HMAC:
+	params->hashType = HASH_AlgSHA1; 
+	params->keyLen = 20;
+	break;
+    case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN:
+	params->hashType = HASH_AlgMD5; 
+	params->keyLen = 16;
+	break;
+    case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN:
+	params->hashType = HASH_AlgMD2; 
+	params->keyLen = 16;
+	break;
+    default:
+	PORT_FreeArena(arena,PR_TRUE);
+	return CKR_MECHANISM_INVALID;
+    }
+    *pbe = params;
+    return CKR_OK;
+}
+
+/* maybe this should be table driven? */
+static CK_RV
+nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter  **pbe,
+				CK_KEY_TYPE *key_type, CK_ULONG *key_length)
+{
+    CK_RV crv = CKR_OK;
+    SECOidData *oid;
+    CK_PBE_PARAMS *pbe_params = NULL;
+    NSSPKCS5PBEParameter *params = NULL;
+    CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL;
+    SECItem salt;
+    CK_ULONG iteration = 0;
+
+    *pbe = NULL;
+
+    oid = SECOID_FindOIDByMechanism(pMechanism->mechanism);
+    if (oid == NULL) {
+	return CKR_MECHANISM_INVALID;
+    }
+
+    if (pMechanism->mechanism == CKM_PKCS5_PBKD2) {
+	pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter;
+	if (pbkd2_params->saltSource != CKZ_SALT_SPECIFIED) {
+	    return CKR_MECHANISM_PARAM_INVALID;
+	}
+	salt.data = (unsigned char *)pbkd2_params->pSaltSourceData;
+	salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen;
+	iteration = pbkd2_params->iterations;
+    } else {
+	pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
+	salt.data = (unsigned char *)pbe_params->pSalt;
+	salt.len = (unsigned int)pbe_params->ulSaltLen;
+	iteration = pbe_params->ulIteration;
+    }
+    params=nsspkcs5_NewParam(oid->offset, &salt, iteration);
+    if (params == NULL) {
+	return CKR_MECHANISM_INVALID;
+    }
+
+    switch (params->encAlg) {
+    case SEC_OID_DES_CBC:
+	*key_type = CKK_DES;
+	*key_length = params->keyLen;
+	break;
+    case SEC_OID_DES_EDE3_CBC:
+	*key_type = params->is2KeyDES ? CKK_DES2 : CKK_DES3;
+	*key_length = params->keyLen;
+	break;
+    case SEC_OID_RC2_CBC:
+	*key_type = CKK_RC2;
+	*key_length = params->keyLen;
+	break;
+    case SEC_OID_RC4:
+	*key_type = CKK_RC4;
+	*key_length = params->keyLen;
+	break;
+    case SEC_OID_PKCS5_PBKDF2:
+	/* sigh, PKCS #11 currently only defines SHA1 for the KDF hash type. 
+	 * we do the check here because this where we would handle multiple
+	 * hash types in the future */
+	if (pbkd2_params == NULL || 
+		pbkd2_params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	/* key type must already be set */
+	if (*key_type == CKK_INVALID_KEY_TYPE) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    break;
+	}
+	/* PBKDF2 needs to calculate the key length from the other parameters
+	 */
+	if (*key_length == 0) {
+	    *key_length = sftk_MapKeySize(*key_type);
+	}
+	if (*key_length == 0) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    break;
+	}
+	params->keyLen = *key_length;
+	break;
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	nsspkcs5_DestroyPBEParameter(params);
+	break;
+    }
+    if (crv == CKR_OK) {
+    	*pbe = params;
+    }
+    return crv;
+}
+
+/* NSC_GenerateKey generates a secret key, creating a new key object. */
+CK_RV NSC_GenerateKey(CK_SESSION_HANDLE hSession,
+    CK_MECHANISM_PTR pMechanism,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount,
+    						CK_OBJECT_HANDLE_PTR phKey)
+{
+    SFTKObject *key;
+    SFTKSession *session;
+    PRBool checkWeak = PR_FALSE;
+    CK_ULONG key_length = 0;
+    CK_KEY_TYPE key_type = CKK_INVALID_KEY_TYPE;
+    CK_OBJECT_CLASS objclass = CKO_SECRET_KEY;
+    CK_RV crv = CKR_OK;
+    CK_BBOOL cktrue = CK_TRUE;
+    int i;
+    SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+    unsigned char buf[MAX_KEY_LEN];
+    enum {nsc_pbe, nsc_ssl, nsc_bulk, nsc_param, nsc_jpake} key_gen_type;
+    NSSPKCS5PBEParameter *pbe_param;
+    SSL3RSAPreMasterSecret *rsa_pms;
+    CK_VERSION *version;
+    /* in very old versions of NSS, there were implementation errors with key 
+     * generation methods.  We want to beable to read these, but not 
+     * produce them any more.  The affected algorithm was 3DES.
+     */
+    PRBool faultyPBE3DES = PR_FALSE;
+    HASH_HashType hashType;
+
+    CHECK_FORK();
+
+    if (!slot) {
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+    /*
+     * now lets create an object to hang the attributes off of
+     */
+    key = sftk_NewObject(slot); /* fill in the handle later */
+    if (key == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    /*
+     * load the template values into the object
+     */
+    for (i=0; i < (int) ulCount; i++) {
+	if (pTemplate[i].type == CKA_VALUE_LEN) {
+	    key_length = *(CK_ULONG *)pTemplate[i].pValue;
+	    continue;
+	}
+	/* some algorithms need keytype specified */
+	if (pTemplate[i].type == CKA_KEY_TYPE) {
+	    key_type = *(CK_ULONG *)pTemplate[i].pValue;
+	    continue;
+	}
+
+	crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i]));
+	if (crv != CKR_OK) break;
+    }
+    if (crv != CKR_OK) {
+	sftk_FreeObject(key);
+	return crv;
+    }
+
+    /* make sure we don't have any class, key_type, or value fields */
+    sftk_DeleteAttributeType(key,CKA_CLASS);
+    sftk_DeleteAttributeType(key,CKA_KEY_TYPE);
+    sftk_DeleteAttributeType(key,CKA_VALUE);
+
+    /* Now Set up the parameters to generate the key (based on mechanism) */
+    key_gen_type = nsc_bulk; /* bulk key by default */
+    switch (pMechanism->mechanism) {
+    case CKM_CDMF_KEY_GEN:
+    case CKM_DES_KEY_GEN:
+    case CKM_DES2_KEY_GEN:
+    case CKM_DES3_KEY_GEN:
+	checkWeak = PR_TRUE;
+        /* fall through */
+    case CKM_RC2_KEY_GEN:
+    case CKM_RC4_KEY_GEN:
+    case CKM_GENERIC_SECRET_KEY_GEN:
+    case CKM_SEED_KEY_GEN:
+    case CKM_CAMELLIA_KEY_GEN:
+    case CKM_AES_KEY_GEN:
+    case CKM_NSS_CHACHA20_KEY_GEN:
+#if NSS_SOFTOKEN_DOES_RC5
+    case CKM_RC5_KEY_GEN:
+#endif
+	crv = nsc_SetupBulkKeyGen(pMechanism->mechanism,&key_type,&key_length);
+	break;
+    case CKM_SSL3_PRE_MASTER_KEY_GEN:
+	key_type = CKK_GENERIC_SECRET;
+	key_length = 48;
+	key_gen_type = nsc_ssl;
+	break;
+    case CKM_PBA_SHA1_WITH_SHA1_HMAC:
+    case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN:
+    case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN:
+    case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN:
+	key_gen_type = nsc_pbe;
+	key_type = CKK_GENERIC_SECRET;
+	crv = nsc_SetupHMACKeyGen(pMechanism, &pbe_param);
+	break;
+    case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+	faultyPBE3DES = PR_TRUE;
+        /* fall through */
+    case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+    case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+    case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+    case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+    case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+    case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+    case CKM_PBE_SHA1_DES3_EDE_CBC:
+    case CKM_PBE_SHA1_DES2_EDE_CBC:
+    case CKM_PBE_SHA1_RC2_128_CBC:
+    case CKM_PBE_SHA1_RC2_40_CBC:
+    case CKM_PBE_SHA1_RC4_128:
+    case CKM_PBE_SHA1_RC4_40:
+    case CKM_PBE_MD5_DES_CBC:
+    case CKM_PBE_MD2_DES_CBC:
+    case CKM_PKCS5_PBKD2:
+	key_gen_type = nsc_pbe;
+	crv = nsc_SetupPBEKeyGen(pMechanism,&pbe_param, &key_type, &key_length);
+	break;
+    case CKM_DSA_PARAMETER_GEN:
+	key_gen_type = nsc_param;
+	key_type = CKK_DSA;
+	objclass = CKO_KG_PARAMETERS;
+	crv = CKR_OK;
+	break;
+    case CKM_NSS_JPAKE_ROUND1_SHA1:   hashType = HASH_AlgSHA1;   goto jpake1;
+    case CKM_NSS_JPAKE_ROUND1_SHA256: hashType = HASH_AlgSHA256; goto jpake1;
+    case CKM_NSS_JPAKE_ROUND1_SHA384: hashType = HASH_AlgSHA384; goto jpake1;
+    case CKM_NSS_JPAKE_ROUND1_SHA512: hashType = HASH_AlgSHA512; goto jpake1;
+jpake1:
+	key_gen_type = nsc_jpake;
+	key_type = CKK_NSS_JPAKE_ROUND1;
+        objclass = CKO_PRIVATE_KEY;
+        if (pMechanism->pParameter == NULL ||
+            pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound1Params)) {
+            crv = CKR_MECHANISM_PARAM_INVALID;
+            break;
+        }
+        if (sftk_isTrue(key, CKA_TOKEN)) {
+            crv = CKR_TEMPLATE_INCONSISTENT;
+            break;
+        }
+        crv = CKR_OK;
+        break;
+    default:
+	crv = CKR_MECHANISM_INVALID;
+	break;
+    }
+
+    /* make sure we aren't going to overflow the buffer */
+    if (sizeof(buf) < key_length) {
+	/* someone is getting pretty optimistic about how big their key can
+	 * be... */
+        crv = CKR_TEMPLATE_INCONSISTENT;
+    }
+
+    if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+    /* if there was no error,
+     * key_type *MUST* be set in the switch statement above */
+    PORT_Assert( key_type != CKK_INVALID_KEY_TYPE );
+
+    /*
+     * now to the actual key gen.
+     */
+    switch (key_gen_type) {
+    case nsc_pbe:
+	crv = nsc_pbe_key_gen(pbe_param, pMechanism, buf, &key_length,
+			       faultyPBE3DES);
+	nsspkcs5_DestroyPBEParameter(pbe_param);
+	break;
+    case nsc_ssl:
+	rsa_pms = (SSL3RSAPreMasterSecret *)buf;
+	version = (CK_VERSION *)pMechanism->pParameter;
+	rsa_pms->client_version[0] = version->major;
+        rsa_pms->client_version[1] = version->minor;
+        crv = 
+	    NSC_GenerateRandom(0,&rsa_pms->random[0], sizeof(rsa_pms->random));
+	break;
+    case nsc_bulk:
+	/* get the key, check for weak keys and repeat if found */
+	do {
+            crv = NSC_GenerateRandom(0, buf, key_length);
+	} while (crv == CKR_OK && checkWeak && sftk_IsWeakKey(buf,key_type));
+	break;
+    case nsc_param:
+	/* generate parameters */
+	*buf = 0;
+	crv = nsc_parameter_gen(key_type,key);
+	break;
+    case nsc_jpake:
+        crv = jpake_Round1(hashType,
+                           (CK_NSS_JPAKERound1Params *) pMechanism->pParameter,
+                           key);
+        break;
+    }
+
+    if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+    /* Add the class, key_type, and value */
+    crv = sftk_AddAttributeType(key,CKA_CLASS,&objclass,sizeof(CK_OBJECT_CLASS));
+    if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+    crv = sftk_AddAttributeType(key,CKA_KEY_TYPE,&key_type,sizeof(CK_KEY_TYPE));
+    if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+    if (key_length != 0) {
+	crv = sftk_AddAttributeType(key,CKA_VALUE,buf,key_length);
+	if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+    }
+
+    /* get the session */
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) {
+	sftk_FreeObject(key);
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+
+    /*
+     * handle the base object stuff
+     */
+    crv = sftk_handleObject(key,session);
+    sftk_FreeSession(session);
+    if (sftk_isTrue(key,CKA_SENSITIVE)) {
+	sftk_forceAttribute(key,CKA_ALWAYS_SENSITIVE,&cktrue,sizeof(CK_BBOOL));
+    }
+    if (!sftk_isTrue(key,CKA_EXTRACTABLE)) {
+	sftk_forceAttribute(key,CKA_NEVER_EXTRACTABLE,&cktrue,sizeof(CK_BBOOL));
+    }
+
+    *phKey = key->handle;
+    sftk_FreeObject(key);
+    return crv;
+}
+
+#define PAIRWISE_DIGEST_LENGTH			SHA1_LENGTH /* 160-bits */
+#define PAIRWISE_MESSAGE_LENGTH			20          /* 160-bits */
+
+/*
+ * FIPS 140-2 pairwise consistency check utilized to validate key pair.
+ *
+ * This function returns
+ *   CKR_OK               if pairwise consistency check passed
+ *   CKR_GENERAL_ERROR    if pairwise consistency check failed
+ *   other error codes    if paiswise consistency check could not be
+ *                        performed, for example, CKR_HOST_MEMORY.
+ */
+static CK_RV
+sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession,
+    SFTKObject *publicKey, SFTKObject *privateKey, CK_KEY_TYPE keyType)
+{
+    /*
+     *                      Key type    Mechanism type
+     *                      --------------------------------
+     * For encrypt/decrypt: CKK_RSA  => CKM_RSA_PKCS
+     *                      others   => CKM_INVALID_MECHANISM
+     *
+     * For sign/verify:     CKK_RSA  => CKM_RSA_PKCS
+     *                      CKK_DSA  => CKM_DSA
+     *                      CKK_EC   => CKM_ECDSA
+     *                      others   => CKM_INVALID_MECHANISM
+     *
+     * None of these mechanisms has a parameter.
+     */
+    CK_MECHANISM mech = {0, NULL, 0};
+
+    CK_ULONG modulusLen;
+    CK_ULONG subPrimeLen;
+    PRBool isEncryptable = PR_FALSE;
+    PRBool canSignVerify = PR_FALSE;
+    PRBool isDerivable = PR_FALSE;
+    CK_RV crv;
+
+    /* Variables used for Encrypt/Decrypt functions. */
+    unsigned char *known_message = (unsigned char *)"Known Crypto Message";
+    unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH];
+    CK_ULONG bytes_decrypted;
+    unsigned char *ciphertext;
+    unsigned char *text_compared;
+    CK_ULONG bytes_encrypted;
+    CK_ULONG bytes_compared;
+    CK_ULONG pairwise_digest_length = PAIRWISE_DIGEST_LENGTH;
+
+    /* Variables used for Signature/Verification functions. */
+    /* Must be at least 256 bits for DSA2 digest */
+    unsigned char *known_digest = (unsigned char *)
+				"Mozilla Rules the World through NSS!";
+    unsigned char *signature;
+    CK_ULONG signature_length;
+
+    if (keyType == CKK_RSA) {
+	SFTKAttribute *attribute;
+
+	/* Get modulus length of private key. */
+	attribute = sftk_FindAttribute(privateKey, CKA_MODULUS);
+	if (attribute == NULL) {
+	    return CKR_DEVICE_ERROR;
+	}
+	modulusLen = attribute->attrib.ulValueLen;
+	if (*(unsigned char *)attribute->attrib.pValue == 0) {
+	    modulusLen--;
+	}
+	sftk_FreeAttribute(attribute);
+    } else if (keyType == CKK_DSA) {
+	SFTKAttribute *attribute;
+
+	/* Get subprime length of private key. */
+	attribute = sftk_FindAttribute(privateKey, CKA_SUBPRIME);
+	if (attribute == NULL) {
+	    return CKR_DEVICE_ERROR;
+	}
+	subPrimeLen = attribute->attrib.ulValueLen;
+	if (subPrimeLen > 1 && *(unsigned char *)attribute->attrib.pValue == 0) {
+	    subPrimeLen--;
+	}
+	sftk_FreeAttribute(attribute);
+    }
+
+    /**************************************************/
+    /* Pairwise Consistency Check of Encrypt/Decrypt. */
+    /**************************************************/
+
+    isEncryptable = sftk_isTrue(privateKey, CKA_DECRYPT); 
+
+    /*
+     * If the decryption attribute is set, attempt to encrypt
+     * with the public key and decrypt with the private key.
+     */
+    if (isEncryptable) {
+	if (keyType != CKK_RSA) {
+	    return CKR_DEVICE_ERROR;
+	}
+	bytes_encrypted = modulusLen;
+	mech.mechanism = CKM_RSA_PKCS;
+
+	/* Allocate space for ciphertext. */
+	ciphertext = (unsigned char *) PORT_ZAlloc(bytes_encrypted);
+	if (ciphertext == NULL) {
+	    return CKR_HOST_MEMORY;
+	}
+
+	/* Prepare for encryption using the public key. */
+	crv = NSC_EncryptInit(hSession, &mech, publicKey->handle);
+	if (crv != CKR_OK) {
+	    PORT_Free(ciphertext);
+	    return crv;
+	}
+
+	/* Encrypt using the public key. */
+	crv = NSC_Encrypt(hSession,
+			  known_message,
+			  PAIRWISE_MESSAGE_LENGTH,
+			  ciphertext,
+			  &bytes_encrypted);
+	if (crv != CKR_OK) {
+	    PORT_Free(ciphertext);
+	    return crv;
+	}
+
+	/* Always use the smaller of these two values . . . */
+	bytes_compared = PR_MIN(bytes_encrypted, PAIRWISE_MESSAGE_LENGTH);
+
+	/*
+	 * If there was a failure, the plaintext
+	 * goes at the end, therefore . . .
+	 */
+	text_compared = ciphertext + bytes_encrypted - bytes_compared;
+
+	/*
+	 * Check to ensure that ciphertext does
+	 * NOT EQUAL known input message text
+	 * per FIPS PUB 140-2 directive.
+	 */
+	if (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 CKR_GENERAL_ERROR;
+	}
+
+	/* Prepare for decryption using the private key. */
+	crv = NSC_DecryptInit(hSession, &mech, privateKey->handle);
+	if (crv != CKR_OK) {
+	    PORT_Free(ciphertext);
+	    return crv;
+	}
+
+	memset(plaintext, 0, PAIRWISE_MESSAGE_LENGTH);
+
+	/*
+	 * 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 = NSC_Decrypt(hSession,
+			  ciphertext,
+			  bytes_encrypted,
+			  plaintext,
+			  &bytes_decrypted);
+
+	/* Finished with ciphertext; free it. */
+	PORT_Free(ciphertext);
+
+	if (crv != CKR_OK) {
+	    return crv;
+	}
+
+	/*
+	 * 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);
+	    return CKR_GENERAL_ERROR;
+	}
+    }
+
+    /**********************************************/
+    /* Pairwise Consistency Check of Sign/Verify. */
+    /**********************************************/
+
+    canSignVerify = sftk_isTrue(privateKey, CKA_SIGN);
+    
+    if (canSignVerify) {
+	/* Determine length of signature. */
+	switch (keyType) {
+	case CKK_RSA:
+	    signature_length = modulusLen;
+	    mech.mechanism = CKM_RSA_PKCS;
+	    break;
+	case CKK_DSA:
+	    signature_length = DSA_MAX_SIGNATURE_LEN;
+	    pairwise_digest_length = subPrimeLen;
+	    mech.mechanism = CKM_DSA;
+	    break;
+#ifndef NSS_DISABLE_ECC
+	case CKK_EC:
+	    signature_length = MAX_ECKEY_LEN * 2;
+	    mech.mechanism = CKM_ECDSA;
+	    break;
+#endif
+	default:
+	    return CKR_DEVICE_ERROR;
+	}
+	
+	/* Allocate space for signature data. */
+	signature = (unsigned char *) PORT_ZAlloc(signature_length);
+	if (signature == NULL) {
+	    return CKR_HOST_MEMORY;
+	}
+	
+	/* Sign the known hash using the private key. */
+	crv = NSC_SignInit(hSession, &mech, privateKey->handle);
+	if (crv != CKR_OK) {
+	    PORT_Free(signature);
+	    return crv;
+	}
+
+	crv = NSC_Sign(hSession,
+		       known_digest,
+		       pairwise_digest_length,
+		       signature,
+		       &signature_length);
+	if (crv != CKR_OK) {
+	    PORT_Free(signature);
+	    return crv;
+	}
+	
+	/* Verify the known hash using the public key. */
+	crv = NSC_VerifyInit(hSession, &mech, publicKey->handle);
+	if (crv != CKR_OK) {
+	    PORT_Free(signature);
+	    return crv;
+	}
+
+	crv = NSC_Verify(hSession,
+			 known_digest,
+			 pairwise_digest_length,
+			 signature,
+			 signature_length);
+
+	/* Free signature data. */
+	PORT_Free(signature);
+
+	if ((crv == CKR_SIGNATURE_LEN_RANGE) ||
+		(crv == CKR_SIGNATURE_INVALID)) {
+	    return CKR_GENERAL_ERROR;
+	}
+	if (crv != CKR_OK) {
+	    return crv;
+	}
+    }
+
+    /**********************************************/
+    /* Pairwise Consistency Check for Derivation  */
+    /**********************************************/
+
+    isDerivable = sftk_isTrue(privateKey, CKA_DERIVE);
+    
+    if (isDerivable) {
+	/* 
+	 * We are not doing consistency check for Diffie-Hellman Key - 
+	 * otherwise it would be here
+	 * This is also true for Elliptic Curve Diffie-Hellman keys
+	 * NOTE: EC keys are currently subjected to pairwise
+	 * consistency check for signing/verification.
+	 */
+	/*
+	 * FIPS 140-2 had the following pairwise consistency test for
+	 * public and private keys used for key agreement:
+	 *   If the keys are used to perform key agreement, then the
+	 *   cryptographic module shall create a second, compatible
+	 *   key pair.  The cryptographic module shall perform both
+	 *   sides of the key agreement algorithm and shall compare
+	 *   the resulting shared values.  If the shared values are
+	 *   not equal, the test shall fail.
+	 * This test was removed in Change Notice 3.
+	 */
+
+    }
+
+    return CKR_OK;
+}
+
+/* NSC_GenerateKeyPair generates a public-key/private-key pair, 
+ * creating new key objects. */
+CK_RV NSC_GenerateKeyPair (CK_SESSION_HANDLE hSession,
+    CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+    CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+    CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey,
+    					CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+    SFTKObject *	publicKey,*privateKey;
+    SFTKSession *	session;
+    CK_KEY_TYPE 	key_type;
+    CK_RV 		crv 	= CKR_OK;
+    CK_BBOOL 		cktrue 	= CK_TRUE;
+    SECStatus 		rv;
+    CK_OBJECT_CLASS 	pubClass = CKO_PUBLIC_KEY;
+    CK_OBJECT_CLASS 	privClass = CKO_PRIVATE_KEY;
+    int 		i;
+    SFTKSlot *		slot 	= sftk_SlotFromSessionHandle(hSession);
+    unsigned int bitSize;
+
+    /* RSA */
+    int 		public_modulus_bits = 0;
+    SECItem 		pubExp;
+    RSAPrivateKey *	rsaPriv;
+
+    /* DSA */
+    PQGParams 		pqgParam;
+    DHParams  		dhParam;
+    DSAPrivateKey *	dsaPriv;
+
+    /* Diffie Hellman */
+    int 		private_value_bits = 0;
+    DHPrivateKey *	dhPriv;
+
+#ifndef NSS_DISABLE_ECC
+    /* Elliptic Curve Cryptography */
+    SECItem  		ecEncodedParams;  /* DER Encoded parameters */
+    ECPrivateKey *	ecPriv;
+    ECParams *          ecParams;
+#endif /* NSS_DISABLE_ECC */
+
+    CHECK_FORK();
+
+    if (!slot) {
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+    /*
+     * now lets create an object to hang the attributes off of
+     */
+    publicKey = sftk_NewObject(slot); /* fill in the handle later */
+    if (publicKey == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    /*
+     * load the template values into the publicKey
+     */
+    for (i=0; i < (int) ulPublicKeyAttributeCount; i++) {
+	if (pPublicKeyTemplate[i].type == CKA_MODULUS_BITS) {
+	    public_modulus_bits = *(CK_ULONG *)pPublicKeyTemplate[i].pValue;
+	    continue;
+	}
+
+	crv = sftk_AddAttributeType(publicKey,
+				    sftk_attr_expand(&pPublicKeyTemplate[i]));
+	if (crv != CKR_OK) break;
+    }
+
+    if (crv != CKR_OK) {
+	sftk_FreeObject(publicKey);
+	return CKR_HOST_MEMORY;
+    }
+
+    privateKey = sftk_NewObject(slot); /* fill in the handle later */
+    if (privateKey == NULL) {
+	sftk_FreeObject(publicKey);
+	return CKR_HOST_MEMORY;
+    }
+    /*
+     * now load the private key template
+     */
+    for (i=0; i < (int) ulPrivateKeyAttributeCount; i++) {
+	if (pPrivateKeyTemplate[i].type == CKA_VALUE_BITS) {
+	    private_value_bits = *(CK_ULONG *)pPrivateKeyTemplate[i].pValue;
+	    continue;
+	}
+
+	crv = sftk_AddAttributeType(privateKey,
+				    sftk_attr_expand(&pPrivateKeyTemplate[i]));
+	if (crv != CKR_OK) break;
+    }
+
+    if (crv != CKR_OK) {
+	sftk_FreeObject(publicKey);
+	sftk_FreeObject(privateKey);
+	return CKR_HOST_MEMORY;
+    }
+    sftk_DeleteAttributeType(privateKey,CKA_CLASS);
+    sftk_DeleteAttributeType(privateKey,CKA_KEY_TYPE);
+    sftk_DeleteAttributeType(privateKey,CKA_VALUE);
+    sftk_DeleteAttributeType(publicKey,CKA_CLASS);
+    sftk_DeleteAttributeType(publicKey,CKA_KEY_TYPE);
+    sftk_DeleteAttributeType(publicKey,CKA_VALUE);
+
+    /* Now Set up the parameters to generate the key (based on mechanism) */
+    switch (pMechanism->mechanism) {
+    case CKM_RSA_PKCS_KEY_PAIR_GEN:
+	/* format the keys */
+    	sftk_DeleteAttributeType(publicKey,CKA_MODULUS);
+    	sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+    	sftk_DeleteAttributeType(privateKey,CKA_MODULUS);
+    	sftk_DeleteAttributeType(privateKey,CKA_PRIVATE_EXPONENT);
+    	sftk_DeleteAttributeType(privateKey,CKA_PUBLIC_EXPONENT);
+    	sftk_DeleteAttributeType(privateKey,CKA_PRIME_1);
+    	sftk_DeleteAttributeType(privateKey,CKA_PRIME_2);
+    	sftk_DeleteAttributeType(privateKey,CKA_EXPONENT_1);
+    	sftk_DeleteAttributeType(privateKey,CKA_EXPONENT_2);
+    	sftk_DeleteAttributeType(privateKey,CKA_COEFFICIENT);
+	key_type = CKK_RSA;
+	if (public_modulus_bits == 0) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    break;
+	}
+	if (public_modulus_bits < RSA_MIN_MODULUS_BITS) {
+	    crv = CKR_ATTRIBUTE_VALUE_INVALID;
+	    break;
+	}
+	if (public_modulus_bits % 2 != 0) {
+	    crv = CKR_ATTRIBUTE_VALUE_INVALID;
+	    break;
+	}
+
+	/* extract the exponent */
+	crv=sftk_Attribute2SSecItem(NULL,&pubExp,publicKey,CKA_PUBLIC_EXPONENT);
+	if (crv != CKR_OK) break;
+        bitSize = sftk_GetLengthInBits(pubExp.data, pubExp.len);
+	if (bitSize < 2) {
+	    crv = CKR_ATTRIBUTE_VALUE_INVALID;
+	    break;
+	}
+        crv = sftk_AddAttributeType(privateKey,CKA_PUBLIC_EXPONENT,
+				    		    sftk_item_expand(&pubExp));
+	if (crv != CKR_OK) {
+	    PORT_Free(pubExp.data);
+	    break;
+	}
+
+	rsaPriv = RSA_NewKey(public_modulus_bits, &pubExp);
+	PORT_Free(pubExp.data);
+	if (rsaPriv == NULL) {
+	    if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+		sftk_fatalError = PR_TRUE;
+	    }
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+        /* now fill in the RSA dependent paramenters in the public key */
+        crv = sftk_AddAttributeType(publicKey,CKA_MODULUS,
+			   sftk_item_expand(&rsaPriv->modulus));
+	if (crv != CKR_OK) goto kpg_done;
+        /* now fill in the RSA dependent paramenters in the private key */
+        crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+			   sftk_item_expand(&rsaPriv->modulus));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_MODULUS,
+			   sftk_item_expand(&rsaPriv->modulus));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_PRIVATE_EXPONENT,
+			   sftk_item_expand(&rsaPriv->privateExponent));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_PRIME_1,
+			   sftk_item_expand(&rsaPriv->prime1));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_PRIME_2,
+			   sftk_item_expand(&rsaPriv->prime2));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_EXPONENT_1,
+			   sftk_item_expand(&rsaPriv->exponent1));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_EXPONENT_2,
+			   sftk_item_expand(&rsaPriv->exponent2));
+	if (crv != CKR_OK) goto kpg_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_COEFFICIENT,
+			   sftk_item_expand(&rsaPriv->coefficient));
+kpg_done:
+	/* Should zeroize the contents first, since this func doesn't. */
+	PORT_FreeArena(rsaPriv->arena, PR_TRUE);
+	break;
+    case CKM_DSA_KEY_PAIR_GEN:
+    	sftk_DeleteAttributeType(publicKey,CKA_VALUE);
+    	sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+	sftk_DeleteAttributeType(privateKey,CKA_PRIME);
+	sftk_DeleteAttributeType(privateKey,CKA_SUBPRIME);
+	sftk_DeleteAttributeType(privateKey,CKA_BASE);
+	key_type = CKK_DSA;
+
+	/* extract the necessary parameters and copy them to the private key */
+	crv=sftk_Attribute2SSecItem(NULL,&pqgParam.prime,publicKey,CKA_PRIME);
+	if (crv != CKR_OK) break;
+	crv=sftk_Attribute2SSecItem(NULL,&pqgParam.subPrime,publicKey,
+	                            CKA_SUBPRIME);
+	if (crv != CKR_OK) {
+	    PORT_Free(pqgParam.prime.data);
+	    break;
+	}
+	crv=sftk_Attribute2SSecItem(NULL,&pqgParam.base,publicKey,CKA_BASE);
+	if (crv != CKR_OK) {
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    break;
+	}
+        crv = sftk_AddAttributeType(privateKey,CKA_PRIME,
+				    sftk_item_expand(&pqgParam.prime));
+	if (crv != CKR_OK) {
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    PORT_Free(pqgParam.base.data);
+	    break;
+	}
+        crv = sftk_AddAttributeType(privateKey,CKA_SUBPRIME,
+			    	    sftk_item_expand(&pqgParam.subPrime));
+	if (crv != CKR_OK) {
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    PORT_Free(pqgParam.base.data);
+	    break;
+	}
+        crv = sftk_AddAttributeType(privateKey,CKA_BASE,
+			    	    sftk_item_expand(&pqgParam.base));
+	if (crv != CKR_OK) {
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    PORT_Free(pqgParam.base.data);
+	    break;
+	}
+
+	/*
+	 * these are checked by DSA_NewKey
+	 */
+        bitSize = sftk_GetLengthInBits(pqgParam.subPrime.data, 
+							pqgParam.subPrime.len);
+        if ((bitSize < DSA_MIN_Q_BITS) || (bitSize > DSA_MAX_Q_BITS))  {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    PORT_Free(pqgParam.base.data);
+	    break;
+	}
+        bitSize = sftk_GetLengthInBits(pqgParam.prime.data,pqgParam.prime.len);
+        if ((bitSize <  DSA_MIN_P_BITS) || (bitSize > DSA_MAX_P_BITS)) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    PORT_Free(pqgParam.base.data);
+	    break;
+	}
+        bitSize = sftk_GetLengthInBits(pqgParam.base.data,pqgParam.base.len);
+        if ((bitSize <  2) || (bitSize > DSA_MAX_P_BITS)) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    PORT_Free(pqgParam.prime.data);
+	    PORT_Free(pqgParam.subPrime.data);
+	    PORT_Free(pqgParam.base.data);
+	    break;
+	}
+	    
+	/* Generate the key */
+	rv = DSA_NewKey(&pqgParam, &dsaPriv);
+
+	PORT_Free(pqgParam.prime.data);
+	PORT_Free(pqgParam.subPrime.data);
+	PORT_Free(pqgParam.base.data);
+
+	if (rv != SECSuccess) {
+	    if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+		sftk_fatalError = PR_TRUE;
+	    }
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+
+	/* store the generated key into the attributes */
+        crv = sftk_AddAttributeType(publicKey,CKA_VALUE,
+			   sftk_item_expand(&dsaPriv->publicValue));
+	if (crv != CKR_OK) goto dsagn_done;
+
+        /* now fill in the RSA dependent paramenters in the private key */
+        crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+			   sftk_item_expand(&dsaPriv->publicValue));
+	if (crv != CKR_OK) goto dsagn_done;
+        crv = sftk_AddAttributeType(privateKey,CKA_VALUE,
+			   sftk_item_expand(&dsaPriv->privateValue));
+
+dsagn_done:
+	/* should zeroize, since this function doesn't. */
+	PORT_FreeArena(dsaPriv->params.arena, PR_TRUE);
+	break;
+
+    case CKM_DH_PKCS_KEY_PAIR_GEN:
+	sftk_DeleteAttributeType(privateKey,CKA_PRIME);
+	sftk_DeleteAttributeType(privateKey,CKA_BASE);
+	sftk_DeleteAttributeType(privateKey,CKA_VALUE);
+    	sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+	key_type = CKK_DH;
+
+	/* extract the necessary parameters and copy them to private keys */
+        crv = sftk_Attribute2SSecItem(NULL, &dhParam.prime, publicKey, 
+				      CKA_PRIME);
+	if (crv != CKR_OK) break;
+	crv = sftk_Attribute2SSecItem(NULL, &dhParam.base, publicKey, CKA_BASE);
+	if (crv != CKR_OK) {
+	    PORT_Free(dhParam.prime.data);
+	    break;
+	}
+	crv = sftk_AddAttributeType(privateKey, CKA_PRIME, 
+				    sftk_item_expand(&dhParam.prime));
+	if (crv != CKR_OK) {
+	    PORT_Free(dhParam.prime.data);
+	    PORT_Free(dhParam.base.data);
+	    break;
+	}
+	crv = sftk_AddAttributeType(privateKey, CKA_BASE, 
+				    sftk_item_expand(&dhParam.base));
+	if (crv != CKR_OK) {
+	    PORT_Free(dhParam.prime.data);
+	    PORT_Free(dhParam.base.data);
+	    break;
+	}
+        bitSize = sftk_GetLengthInBits(dhParam.prime.data,dhParam.prime.len);
+        if ((bitSize <  DH_MIN_P_BITS) || (bitSize > DH_MAX_P_BITS)) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    PORT_Free(dhParam.prime.data);
+	    PORT_Free(dhParam.base.data);
+	    break;
+	}
+        bitSize = sftk_GetLengthInBits(dhParam.base.data,dhParam.base.len);
+        if ((bitSize <  1) || (bitSize > DH_MAX_P_BITS)) {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    PORT_Free(dhParam.prime.data);
+	    PORT_Free(dhParam.base.data);
+	    break;
+	}
+
+	rv = DH_NewKey(&dhParam, &dhPriv);
+	PORT_Free(dhParam.prime.data);
+	PORT_Free(dhParam.base.data);
+	if (rv != SECSuccess) { 
+	    if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+		sftk_fatalError = PR_TRUE;
+	    }
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+
+	crv=sftk_AddAttributeType(publicKey, CKA_VALUE, 
+				sftk_item_expand(&dhPriv->publicValue));
+	if (crv != CKR_OK) goto dhgn_done;
+
+        crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+			   sftk_item_expand(&dhPriv->publicValue));
+	if (crv != CKR_OK) goto dhgn_done;
+
+	crv=sftk_AddAttributeType(privateKey, CKA_VALUE, 
+			      sftk_item_expand(&dhPriv->privateValue));
+
+dhgn_done:
+	/* should zeroize, since this function doesn't. */
+	PORT_FreeArena(dhPriv->arena, PR_TRUE);
+	break;
+
+#ifndef NSS_DISABLE_ECC
+    case CKM_EC_KEY_PAIR_GEN:
+	sftk_DeleteAttributeType(privateKey,CKA_EC_PARAMS);
+	sftk_DeleteAttributeType(privateKey,CKA_VALUE);
+    	sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+	key_type = CKK_EC;
+
+	/* extract the necessary parameters and copy them to private keys */
+	crv = sftk_Attribute2SSecItem(NULL, &ecEncodedParams, publicKey, 
+				      CKA_EC_PARAMS);
+	if (crv != CKR_OK) break;
+
+	crv = sftk_AddAttributeType(privateKey, CKA_EC_PARAMS, 
+				    sftk_item_expand(&ecEncodedParams));
+	if (crv != CKR_OK) {
+	  PORT_Free(ecEncodedParams.data);
+	  break;
+	}
+
+	/* Decode ec params before calling EC_NewKey */
+	rv = EC_DecodeParams(&ecEncodedParams, &ecParams);
+	PORT_Free(ecEncodedParams.data);
+	if (rv != SECSuccess) {
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+	rv = EC_NewKey(ecParams, &ecPriv);
+	PORT_FreeArena(ecParams->arena, PR_TRUE);
+	if (rv != SECSuccess) { 
+	    if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+		sftk_fatalError = PR_TRUE;
+	    }
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+
+	if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) {
+	    crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, 
+				sftk_item_expand(&ecPriv->publicValue));
+	} else {
+	    SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL, 
+					&ecPriv->publicValue, 
+					SEC_ASN1_GET(SEC_OctetStringTemplate));
+	    if (!pubValue) {
+		crv = CKR_ARGUMENTS_BAD;
+		goto ecgn_done;
+	    }
+	    crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, 
+				sftk_item_expand(pubValue));
+	    SECITEM_FreeItem(pubValue, PR_TRUE);
+	}
+	if (crv != CKR_OK) goto ecgn_done;
+
+	crv = sftk_AddAttributeType(privateKey, CKA_VALUE, 
+			      sftk_item_expand(&ecPriv->privateValue));
+	if (crv != CKR_OK) goto ecgn_done;
+
+        crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+			   sftk_item_expand(&ecPriv->publicValue));
+ecgn_done:
+	/* should zeroize, since this function doesn't. */
+	PORT_FreeArena(ecPriv->ecParams.arena, PR_TRUE);
+	break;
+#endif /* NSS_DISABLE_ECC */
+
+    default:
+	crv = CKR_MECHANISM_INVALID;
+    }
+
+    if (crv != CKR_OK) {
+	sftk_FreeObject(privateKey);
+	sftk_FreeObject(publicKey);
+	return crv;
+    }
+
+
+    /* Add the class, key_type The loop lets us check errors blow out
+     *  on errors and clean up at the bottom */
+    session = NULL; /* make pedtantic happy... session cannot leave the*/
+		    /* loop below NULL unless an error is set... */
+    do {
+	crv = sftk_AddAttributeType(privateKey,CKA_CLASS,&privClass,
+						sizeof(CK_OBJECT_CLASS));
+        if (crv != CKR_OK) break;
+	crv = sftk_AddAttributeType(publicKey,CKA_CLASS,&pubClass,
+						sizeof(CK_OBJECT_CLASS));
+        if (crv != CKR_OK) break;
+	crv = sftk_AddAttributeType(privateKey,CKA_KEY_TYPE,&key_type,
+						sizeof(CK_KEY_TYPE));
+        if (crv != CKR_OK) break;
+	crv = sftk_AddAttributeType(publicKey,CKA_KEY_TYPE,&key_type,
+						sizeof(CK_KEY_TYPE));
+        if (crv != CKR_OK) break;
+        session = sftk_SessionFromHandle(hSession);
+        if (session == NULL) crv = CKR_SESSION_HANDLE_INVALID;
+    } while (0);
+
+    if (crv != CKR_OK) {
+	 sftk_FreeObject(privateKey);
+	 sftk_FreeObject(publicKey);
+	 return crv;
+    }
+
+    /*
+     * handle the base object cleanup for the public Key
+     */
+    crv = sftk_handleObject(privateKey,session);
+    if (crv != CKR_OK) {
+        sftk_FreeSession(session);
+	sftk_FreeObject(privateKey);
+	sftk_FreeObject(publicKey);
+	return crv;
+    }
+
+    /*
+     * handle the base object cleanup for the private Key
+     * If we have any problems, we destroy the public Key we've
+     * created and linked.
+     */
+    crv = sftk_handleObject(publicKey,session);
+    sftk_FreeSession(session);
+    if (crv != CKR_OK) {
+	sftk_FreeObject(publicKey);
+	NSC_DestroyObject(hSession,privateKey->handle);
+	sftk_FreeObject(privateKey);
+	return crv;
+    }
+    if (sftk_isTrue(privateKey,CKA_SENSITIVE)) {
+	sftk_forceAttribute(privateKey,CKA_ALWAYS_SENSITIVE,
+						&cktrue,sizeof(CK_BBOOL));
+    }
+    if (sftk_isTrue(publicKey,CKA_SENSITIVE)) {
+	sftk_forceAttribute(publicKey,CKA_ALWAYS_SENSITIVE,
+						&cktrue,sizeof(CK_BBOOL));
+    }
+    if (!sftk_isTrue(privateKey,CKA_EXTRACTABLE)) {
+	sftk_forceAttribute(privateKey,CKA_NEVER_EXTRACTABLE,
+						&cktrue,sizeof(CK_BBOOL));
+    }
+    if (!sftk_isTrue(publicKey,CKA_EXTRACTABLE)) {
+	sftk_forceAttribute(publicKey,CKA_NEVER_EXTRACTABLE,
+						&cktrue,sizeof(CK_BBOOL));
+    }
+
+    /* Perform FIPS 140-2 pairwise consistency check. */
+    crv = sftk_PairwiseConsistencyCheck(hSession,
+					publicKey, privateKey, key_type);
+    if (crv != CKR_OK) {
+	NSC_DestroyObject(hSession,publicKey->handle);
+	sftk_FreeObject(publicKey);
+	NSC_DestroyObject(hSession,privateKey->handle);
+	sftk_FreeObject(privateKey);
+	if (sftk_audit_enabled) {
+	    char msg[128];
+	    PR_snprintf(msg,sizeof msg,
+			"C_GenerateKeyPair(hSession=0x%08lX, "
+			"pMechanism->mechanism=0x%08lX)=0x%08lX "
+			"self-test: pair-wise consistency test failed",
+			(PRUint32)hSession,(PRUint32)pMechanism->mechanism,
+			(PRUint32)crv);
+	    sftk_LogAuditMessage(NSS_AUDIT_ERROR, NSS_AUDIT_SELF_TEST, msg);
+	}
+	return crv;
+    }
+
+    *phPrivateKey = privateKey->handle;
+    *phPublicKey = publicKey->handle;
+    sftk_FreeObject(publicKey);
+    sftk_FreeObject(privateKey);
+
+    return CKR_OK;
+}
+
+static SECItem *sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp)
+{
+    NSSLOWKEYPrivateKey *lk = NULL;
+    NSSLOWKEYPrivateKeyInfo *pki = NULL;
+    SFTKAttribute *attribute = NULL;
+    PLArenaPool *arena = NULL;
+    SECOidTag algorithm = SEC_OID_UNKNOWN;
+    void *dummy, *param = NULL;
+    SECStatus rv = SECSuccess;
+    SECItem *encodedKey = NULL;
+#ifndef NSS_DISABLE_ECC
+    SECItem *fordebug;
+    int savelen;
+#endif
+
+    if(!key) {
+	*crvp = CKR_KEY_HANDLE_INVALID; /* really can't happen */
+	return NULL;
+    }
+
+    attribute = sftk_FindAttribute(key, CKA_KEY_TYPE);
+    if(!attribute) {
+	*crvp = CKR_KEY_TYPE_INCONSISTENT;
+	return NULL;
+    }
+
+    lk = sftk_GetPrivKey(key, *(CK_KEY_TYPE *)attribute->attrib.pValue, crvp);
+    sftk_FreeAttribute(attribute);
+    if(!lk) {
+	return NULL;
+    }
+
+    arena = PORT_NewArena(2048); 	/* XXX different size? */
+    if(!arena) {
+	*crvp = CKR_HOST_MEMORY;
+	rv = SECFailure;
+	goto loser;
+    }
+
+    pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, 
+					sizeof(NSSLOWKEYPrivateKeyInfo));
+    if(!pki) {
+	*crvp = CKR_HOST_MEMORY;
+	rv = SECFailure;
+	goto loser;
+    }
+    pki->arena = arena;
+
+    param = NULL;
+    switch(lk->keyType) {
+	case NSSLOWKEYRSAKey:
+	    prepare_low_rsa_priv_key_for_asn1(lk);
+	    dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+				       nsslowkey_RSAPrivateKeyTemplate);
+	    algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION;
+	    break;
+	case NSSLOWKEYDSAKey:
+            prepare_low_dsa_priv_key_export_for_asn1(lk);
+	    dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+				       nsslowkey_DSAPrivateKeyExportTemplate);
+	    prepare_low_pqg_params_for_asn1(&lk->u.dsa.params);
+	    param = SEC_ASN1EncodeItem(NULL, NULL, &(lk->u.dsa.params),
+				       nsslowkey_PQGParamsTemplate);
+	    algorithm = SEC_OID_ANSIX9_DSA_SIGNATURE;
+	    break;
+#ifndef NSS_DISABLE_ECC
+        case NSSLOWKEYECKey:
+            prepare_low_ec_priv_key_for_asn1(lk);
+	    /* Public value is encoded as a bit string so adjust length
+	     * to be in bits before ASN encoding and readjust 
+	     * immediately after.
+	     *
+	     * Since the SECG specification recommends not including the
+	     * parameters as part of ECPrivateKey, we zero out the curveOID
+	     * length before encoding and restore it later.
+	     */
+	    lk->u.ec.publicValue.len <<= 3;
+	    savelen = lk->u.ec.ecParams.curveOID.len;
+	    lk->u.ec.ecParams.curveOID.len = 0;
+	    dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+				       nsslowkey_ECPrivateKeyTemplate);
+	    lk->u.ec.ecParams.curveOID.len = savelen;
+	    lk->u.ec.publicValue.len >>= 3;
+
+	    fordebug = &pki->privateKey;
+	    SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKey", lk->keyType,
+		      fordebug);
+
+	    param = SECITEM_DupItem(&lk->u.ec.ecParams.DEREncoding);
+
+	    algorithm = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+	    break;
+#endif /* NSS_DISABLE_ECC */
+	case NSSLOWKEYDHKey:
+	default:
+	    dummy = NULL;
+	    break;
+    }
+ 
+    if(!dummy || ((lk->keyType == NSSLOWKEYDSAKey) && !param)) {
+	*crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
+	rv = SECFailure;
+	goto loser;
+    }
+    
+    rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, algorithm, 
+			       (SECItem*)param);
+    if(rv != SECSuccess) {
+	*crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
+	rv = SECFailure;
+	goto loser;
+    }
+
+    dummy = SEC_ASN1EncodeInteger(arena, &pki->version,
+				  NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
+    if(!dummy) {
+	*crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
+	rv = SECFailure;
+	goto loser;
+    }
+
+    encodedKey = SEC_ASN1EncodeItem(NULL, NULL, pki, 
+				    nsslowkey_PrivateKeyInfoTemplate);
+    *crvp = encodedKey ? CKR_OK : CKR_DEVICE_ERROR;
+
+#ifndef NSS_DISABLE_ECC
+    fordebug = encodedKey;
+    SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKeyInfo", lk->keyType,
+	      fordebug);
+#endif
+loser:
+    if(arena) {
+	PORT_FreeArena(arena, PR_TRUE);
+    }
+
+    if(lk && (lk != key->objectInfo)) {
+	nsslowkey_DestroyPrivateKey(lk);
+    }
+ 
+    if(param) {
+	SECITEM_ZfreeItem((SECItem*)param, PR_TRUE);
+    }
+
+    if(rv != SECSuccess) {
+	return NULL;
+    }
+
+    return encodedKey;
+}
+    
+/* it doesn't matter yet, since we colapse error conditions in the
+ * level above, but we really should map those few key error differences */
+static CK_RV 
+sftk_mapWrap(CK_RV crv) 
+{ 
+    switch (crv) {
+    case CKR_ENCRYPTED_DATA_INVALID:  crv = CKR_WRAPPED_KEY_INVALID; break;
+    }
+    return crv; 
+}
+
+/* NSC_WrapKey wraps (i.e., encrypts) a key. */
+CK_RV NSC_WrapKey(CK_SESSION_HANDLE hSession,
+    CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey,
+    CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey,
+					 CK_ULONG_PTR pulWrappedKeyLen)
+{
+    SFTKSession *session;
+    SFTKAttribute *attribute;
+    SFTKObject *key;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) {
+    	return CKR_SESSION_HANDLE_INVALID;
+    }
+
+    key = sftk_ObjectFromHandle(hKey,session);
+    sftk_FreeSession(session);
+    if (key == NULL) {
+	return CKR_KEY_HANDLE_INVALID;
+    }
+
+    switch(key->objclass) {
+	case CKO_SECRET_KEY:
+	  {
+	    SFTKSessionContext *context = NULL;
+	    SECItem pText;
+
+	    attribute = sftk_FindAttribute(key,CKA_VALUE);
+
+	    if (attribute == NULL) {
+		crv = CKR_KEY_TYPE_INCONSISTENT;
+		break;
+	    }
+	    crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey, 
+				CKA_WRAP, CKA_WRAP, SFTK_ENCRYPT, PR_TRUE);
+	    if (crv != CKR_OK) {
+		sftk_FreeAttribute(attribute);
+		break;
+	    }
+
+	    pText.type = siBuffer;
+	    pText.data = (unsigned char *)attribute->attrib.pValue;
+	    pText.len  = attribute->attrib.ulValueLen;
+
+	    /* Find out if this is a block cipher. */
+	    crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_FALSE,NULL);
+	    if (crv != CKR_OK || !context) 
+	        break;
+	    if (context->blockSize > 1) {
+		unsigned int remainder = pText.len % context->blockSize;
+	        if (!context->doPad && remainder) {
+		    /* When wrapping secret keys with unpadded block ciphers, 
+		    ** the keys are zero padded, if necessary, to fill out 
+		    ** a full block.
+		    */
+		    pText.len += context->blockSize - remainder;
+		    pText.data = PORT_ZAlloc(pText.len);
+		    if (pText.data)
+			memcpy(pText.data, attribute->attrib.pValue,
+			                   attribute->attrib.ulValueLen);
+		    else {
+			crv = CKR_HOST_MEMORY;
+			break;
+		    }
+		}
+	    }
+
+	    crv = NSC_Encrypt(hSession, (CK_BYTE_PTR)pText.data, 
+		              pText.len, pWrappedKey, pulWrappedKeyLen);
+	    /* always force a finalize, both on errors and when
+	     * we are just getting the size */
+	    if (crv != CKR_OK || pWrappedKey == NULL) {
+	    	CK_RV lcrv ;
+		lcrv = sftk_GetContext(hSession,&context,
+				       SFTK_ENCRYPT,PR_FALSE,NULL);
+		sftk_SetContextByType(session, SFTK_ENCRYPT, NULL);
+	    	if (lcrv == CKR_OK && context) {
+		    sftk_FreeContext(context);
+		}
+    	    }
+
+	    if (pText.data != (unsigned char *)attribute->attrib.pValue) 
+	    	PORT_ZFree(pText.data, pText.len);
+	    sftk_FreeAttribute(attribute);
+	    break;
+	  }
+
+	case CKO_PRIVATE_KEY:
+	    {
+		SECItem *bpki = sftk_PackagePrivateKey(key, &crv);
+		SFTKSessionContext *context = NULL;
+
+		if(!bpki) {
+		    break;
+		}
+
+		crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey,
+				CKA_WRAP, CKA_WRAP, SFTK_ENCRYPT, PR_TRUE);
+		if(crv != CKR_OK) {
+		    SECITEM_ZfreeItem(bpki, PR_TRUE);
+		    crv = CKR_KEY_TYPE_INCONSISTENT;
+		    break;
+		}
+
+		crv = NSC_Encrypt(hSession, bpki->data, bpki->len,
+					pWrappedKey, pulWrappedKeyLen);
+		/* always force a finalize */
+		if (crv != CKR_OK || pWrappedKey == NULL) {
+	    	    CK_RV lcrv ;
+		    lcrv = sftk_GetContext(hSession,&context,
+					   SFTK_ENCRYPT,PR_FALSE,NULL);
+		    sftk_SetContextByType(session, SFTK_ENCRYPT, NULL);
+	    	    if (lcrv == CKR_OK && context)  {
+			sftk_FreeContext(context);
+		    }
+		}
+		SECITEM_ZfreeItem(bpki, PR_TRUE);
+		break;
+	    }
+
+	default:
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+    }
+    sftk_FreeObject(key);
+
+    return sftk_mapWrap(crv);
+}
+
+/*
+ * import a pprivate key info into the desired slot
+ */
+static SECStatus
+sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
+{
+    CK_BBOOL cktrue = CK_TRUE; 
+    CK_KEY_TYPE keyType = CKK_RSA;
+    SECStatus rv = SECFailure;
+    const SEC_ASN1Template *keyTemplate, *paramTemplate;
+    void *paramDest = NULL;
+    PLArenaPool *arena;
+    NSSLOWKEYPrivateKey *lpk = NULL;
+    NSSLOWKEYPrivateKeyInfo *pki = NULL;
+    CK_RV crv = CKR_KEY_TYPE_INCONSISTENT;
+
+    arena = PORT_NewArena(2048);
+    if(!arena) {
+	return SECFailure;
+    }
+
+    pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, 
+					sizeof(NSSLOWKEYPrivateKeyInfo));
+    if(!pki) {
+	PORT_FreeArena(arena, PR_FALSE);
+	return SECFailure;
+    }
+
+    if(SEC_ASN1DecodeItem(arena, pki, nsslowkey_PrivateKeyInfoTemplate, bpki) 
+				!= SECSuccess) {
+	PORT_FreeArena(arena, PR_TRUE);
+	return SECFailure;
+    }
+
+    lpk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(arena,
+						  sizeof(NSSLOWKEYPrivateKey));
+    if(lpk == NULL) {
+	goto loser;
+    }
+    lpk->arena = arena;
+
+    switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
+	case SEC_OID_PKCS1_RSA_ENCRYPTION:
+	    keyTemplate = nsslowkey_RSAPrivateKeyTemplate;
+	    paramTemplate = NULL;
+	    paramDest = NULL;
+	    lpk->keyType = NSSLOWKEYRSAKey;
+	    prepare_low_rsa_priv_key_for_asn1(lpk);
+	    break;
+	case SEC_OID_ANSIX9_DSA_SIGNATURE:
+	    keyTemplate = nsslowkey_DSAPrivateKeyExportTemplate;
+	    paramTemplate = nsslowkey_PQGParamsTemplate;
+	    paramDest = &(lpk->u.dsa.params);
+	    lpk->keyType = NSSLOWKEYDSAKey;
+	    prepare_low_dsa_priv_key_export_for_asn1(lpk);
+	    prepare_low_pqg_params_for_asn1(&lpk->u.dsa.params);
+	    break;
+	/* case NSSLOWKEYDHKey: */
+#ifndef NSS_DISABLE_ECC
+        case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+	    keyTemplate = nsslowkey_ECPrivateKeyTemplate;
+	    paramTemplate = NULL;
+	    paramDest = &(lpk->u.ec.ecParams.DEREncoding);
+	    lpk->keyType = NSSLOWKEYECKey;
+	    prepare_low_ec_priv_key_for_asn1(lpk);
+	    prepare_low_ecparams_for_asn1(&lpk->u.ec.ecParams);
+	    break;
+#endif /* NSS_DISABLE_ECC */
+	default:
+	    keyTemplate = NULL;
+	    paramTemplate = NULL;
+	    paramDest = NULL;
+	    break;
+    }
+
+    if(!keyTemplate) {
+	goto loser;
+    }
+
+    /* decode the private key and any algorithm parameters */
+    rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
+
+#ifndef NSS_DISABLE_ECC
+    if (lpk->keyType == NSSLOWKEYECKey) {
+        /* convert length in bits to length in bytes */
+	lpk->u.ec.publicValue.len >>= 3;
+        rv = SECITEM_CopyItem(arena, 
+			      &(lpk->u.ec.ecParams.DEREncoding),
+	                      &(pki->algorithm.parameters));
+	if(rv != SECSuccess) {
+	    goto loser;
+	}
+    }
+#endif /* NSS_DISABLE_ECC */
+
+    if(rv != SECSuccess) {
+	goto loser;
+    }
+    if(paramDest && paramTemplate) {
+	rv = SEC_QuickDERDecodeItem(arena, paramDest, paramTemplate, 
+				 &(pki->algorithm.parameters));
+	if(rv != SECSuccess) {
+	    goto loser;
+	}
+    }
+
+    rv = SECFailure;
+
+    switch (lpk->keyType) {
+        case NSSLOWKEYRSAKey:
+	    keyType = CKK_RSA;
+	    if(sftk_hasAttribute(key, CKA_NETSCAPE_DB)) {
+		sftk_DeleteAttributeType(key, CKA_NETSCAPE_DB);
+	    }
+	    crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, 
+					sizeof(keyType));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_UNWRAP, &cktrue, 
+					sizeof(CK_BBOOL));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_DECRYPT, &cktrue, 
+					sizeof(CK_BBOOL));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, 
+					sizeof(CK_BBOOL));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, 
+				    sizeof(CK_BBOOL));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_MODULUS, 
+				sftk_item_expand(&lpk->u.rsa.modulus));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_PUBLIC_EXPONENT, 
+	     			sftk_item_expand(&lpk->u.rsa.publicExponent));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_PRIVATE_EXPONENT, 
+	     			sftk_item_expand(&lpk->u.rsa.privateExponent));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_PRIME_1, 
+				sftk_item_expand(&lpk->u.rsa.prime1));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_PRIME_2, 
+	     			sftk_item_expand(&lpk->u.rsa.prime2));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_EXPONENT_1, 
+	     			sftk_item_expand(&lpk->u.rsa.exponent1));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_EXPONENT_2, 
+	     			sftk_item_expand(&lpk->u.rsa.exponent2));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_COEFFICIENT, 
+	     			sftk_item_expand(&lpk->u.rsa.coefficient));
+	    break;
+        case NSSLOWKEYDSAKey:
+	    keyType = CKK_DSA;
+	    crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK :
+						CKR_KEY_TYPE_INCONSISTENT;
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, 
+						sizeof(keyType));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, 
+						sizeof(CK_BBOOL));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, 
+						sizeof(CK_BBOOL)); 
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_PRIME,    
+				    sftk_item_expand(&lpk->u.dsa.params.prime));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SUBPRIME,
+				 sftk_item_expand(&lpk->u.dsa.params.subPrime));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_BASE,  
+				    sftk_item_expand(&lpk->u.dsa.params.base));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_VALUE, 
+			sftk_item_expand(&lpk->u.dsa.privateValue));
+	    if(crv != CKR_OK) break;
+	    break;
+#ifdef notdef
+        case NSSLOWKEYDHKey:
+	    template = dhTemplate;
+	    templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE);
+	    keyType = CKK_DH;
+	    break;
+#endif
+	/* what about fortezza??? */
+#ifndef NSS_DISABLE_ECC
+        case NSSLOWKEYECKey:
+	    keyType = CKK_EC;
+	    crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK :
+						CKR_KEY_TYPE_INCONSISTENT;
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, 
+						sizeof(keyType));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, 
+						sizeof(CK_BBOOL));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, 
+						sizeof(CK_BBOOL)); 
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_DERIVE, &cktrue, 
+						sizeof(CK_BBOOL)); 
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_EC_PARAMS,
+				 sftk_item_expand(&lpk->u.ec.ecParams.DEREncoding));
+	    if(crv != CKR_OK) break;
+	    crv = sftk_AddAttributeType(key, CKA_VALUE, 
+			sftk_item_expand(&lpk->u.ec.privateValue));
+	    if(crv != CKR_OK) break;
+	    /* XXX Do we need to decode the EC Params here ?? */
+	    break;
+#endif /* NSS_DISABLE_ECC */
+	default:
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+    }
+
+loser:
+    if(lpk) {
+	nsslowkey_DestroyPrivateKey(lpk);
+    }
+
+    if(crv != CKR_OK) {
+	return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+
+/* NSC_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */
+CK_RV NSC_UnwrapKey(CK_SESSION_HANDLE hSession,
+    CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey,
+    CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen,
+    CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+						 CK_OBJECT_HANDLE_PTR phKey)
+{
+    SFTKObject *key = NULL;
+    SFTKSession *session;
+    CK_ULONG key_length = 0;
+    unsigned char * buf = NULL;
+    CK_RV crv = CKR_OK;
+    int i;
+    CK_ULONG bsize = ulWrappedKeyLen;
+    SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+    SECItem bpki;
+    CK_OBJECT_CLASS target_type = CKO_SECRET_KEY;
+
+    CHECK_FORK();
+
+    if (!slot) {
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+    /*
+     * now lets create an object to hang the attributes off of
+     */
+    key = sftk_NewObject(slot); /* fill in the handle later */
+    if (key == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+
+    /*
+     * load the template values into the object
+     */
+    for (i=0; i < (int) ulAttributeCount; i++) {
+	if (pTemplate[i].type == CKA_VALUE_LEN) {
+	    key_length = *(CK_ULONG *)pTemplate[i].pValue;
+	    continue;
+	}
+        if (pTemplate[i].type == CKA_CLASS) {
+	    target_type = *(CK_OBJECT_CLASS *)pTemplate[i].pValue;
+	}
+	crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i]));
+	if (crv != CKR_OK) break;
+    }
+    if (crv != CKR_OK) {
+	sftk_FreeObject(key);
+	return crv;
+    }
+
+    crv = sftk_CryptInit(hSession,pMechanism,hUnwrappingKey,CKA_UNWRAP,
+					CKA_UNWRAP, SFTK_DECRYPT, PR_FALSE);
+    if (crv != CKR_OK) {
+	sftk_FreeObject(key);
+	return sftk_mapWrap(crv);
+    }
+
+    /* allocate the buffer to decrypt into 
+     * this assumes the unwrapped key is never larger than the
+     * wrapped key. For all the mechanisms we support this is true */
+    buf = (unsigned char *)PORT_Alloc( ulWrappedKeyLen);
+    bsize = ulWrappedKeyLen;
+
+    crv = NSC_Decrypt(hSession, pWrappedKey, ulWrappedKeyLen, buf, &bsize);
+    if (crv != CKR_OK) {
+	sftk_FreeObject(key);
+	PORT_Free(buf);
+	return sftk_mapWrap(crv);
+    }
+
+    switch(target_type) {
+	case CKO_SECRET_KEY:
+	    if (!sftk_hasAttribute(key,CKA_KEY_TYPE)) {
+		crv = CKR_TEMPLATE_INCOMPLETE;
+		break;
+	    }
+
+	    if (key_length == 0 || key_length > bsize) {
+		key_length = bsize;
+	    }
+	    if (key_length > MAX_KEY_LEN) {
+		crv = CKR_TEMPLATE_INCONSISTENT;
+		break;
+	    }
+    
+	    /* add the value */
+	    crv = sftk_AddAttributeType(key,CKA_VALUE,buf,key_length);
+	    break;
+	case CKO_PRIVATE_KEY:
+	    bpki.data = (unsigned char *)buf;
+	    bpki.len = bsize;
+	    crv = CKR_OK;
+	    if(sftk_unwrapPrivateKey(key, &bpki) != SECSuccess) {
+		crv = CKR_TEMPLATE_INCOMPLETE;
+	    }
+	    break;
+	default:
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+    }
+
+    PORT_ZFree(buf, bsize);
+    if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+    /* get the session */
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) {
+	sftk_FreeObject(key);
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+
+    /*
+     * handle the base object stuff
+     */
+    crv = sftk_handleObject(key,session);
+    *phKey = key->handle;
+    sftk_FreeSession(session);
+    sftk_FreeObject(key);
+
+    return crv;
+
+}
+
+/*
+ * The SSL key gen mechanism create's lots of keys. This function handles the
+ * details of each of these key creation.
+ */
+static CK_RV
+sftk_buildSSLKey(CK_SESSION_HANDLE hSession, SFTKObject *baseKey, 
+    PRBool isMacKey, unsigned char *keyBlock, unsigned int keySize,
+						 CK_OBJECT_HANDLE *keyHandle)
+{
+    SFTKObject *key;
+    SFTKSession *session;
+    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+    CK_BBOOL cktrue = CK_TRUE;
+    CK_BBOOL ckfalse = CK_FALSE;
+    CK_RV crv = CKR_HOST_MEMORY;
+
+    /*
+     * now lets create an object to hang the attributes off of
+     */
+    *keyHandle = CK_INVALID_HANDLE;
+    key = sftk_NewObject(baseKey->slot); 
+    if (key == NULL) return CKR_HOST_MEMORY;
+    sftk_narrowToSessionObject(key)->wasDerived = PR_TRUE;
+
+    crv = sftk_CopyObject(key,baseKey);
+    if (crv != CKR_OK) goto loser;
+    if (isMacKey) {
+	crv = sftk_forceAttribute(key,CKA_KEY_TYPE,&keyType,sizeof(keyType));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_ENCRYPT,&ckfalse,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_DECRYPT,&ckfalse,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_SIGN,&cktrue,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_WRAP,&ckfalse,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+	crv = sftk_forceAttribute(key,CKA_UNWRAP,&ckfalse,sizeof(CK_BBOOL));
+	if (crv != CKR_OK) goto loser;
+    }
+    crv = sftk_forceAttribute(key,CKA_VALUE,keyBlock,keySize);
+    if (crv != CKR_OK) goto loser;
+
+    /* get the session */
+    crv = CKR_HOST_MEMORY;
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) { goto loser; }
+
+    crv = sftk_handleObject(key,session);
+    sftk_FreeSession(session);
+    *keyHandle = key->handle;
+loser:
+    if (key) sftk_FreeObject(key);
+    return crv;
+}
+
+/*
+ * if there is an error, we need to free the keys we already created in SSL
+ * This is the routine that will do it..
+ */
+static void
+sftk_freeSSLKeys(CK_SESSION_HANDLE session,
+				CK_SSL3_KEY_MAT_OUT *returnedMaterial ) 
+{
+	if (returnedMaterial->hClientMacSecret != CK_INVALID_HANDLE) {
+	   NSC_DestroyObject(session,returnedMaterial->hClientMacSecret);
+	}
+	if (returnedMaterial->hServerMacSecret != CK_INVALID_HANDLE) {
+	   NSC_DestroyObject(session, returnedMaterial->hServerMacSecret);
+	}
+	if (returnedMaterial->hClientKey != CK_INVALID_HANDLE) {
+	   NSC_DestroyObject(session, returnedMaterial->hClientKey);
+	}
+	if (returnedMaterial->hServerKey != CK_INVALID_HANDLE) {
+	   NSC_DestroyObject(session, returnedMaterial->hServerKey);
+	}
+}
+
+/*
+ * when deriving from sensitive and extractable keys, we need to preserve some
+ * of the semantics in the derived key. This helper routine maintains these
+ * semantics.
+ */
+static CK_RV
+sftk_DeriveSensitiveCheck(SFTKObject *baseKey,SFTKObject *destKey) 
+{
+    PRBool hasSensitive;
+    PRBool sensitive = PR_FALSE;
+    PRBool hasExtractable;
+    PRBool extractable = PR_TRUE;
+    CK_RV crv = CKR_OK;
+    SFTKAttribute *att;
+
+    hasSensitive = PR_FALSE;
+    att = sftk_FindAttribute(destKey,CKA_SENSITIVE);
+    if (att) {
+        hasSensitive = PR_TRUE;
+	sensitive = (PRBool) *(CK_BBOOL *)att->attrib.pValue;
+	sftk_FreeAttribute(att);
+    }
+
+    hasExtractable = PR_FALSE;
+    att = sftk_FindAttribute(destKey,CKA_EXTRACTABLE);
+    if (att) {
+        hasExtractable = PR_TRUE;
+	extractable = (PRBool) *(CK_BBOOL *)att->attrib.pValue;
+	sftk_FreeAttribute(att);
+    }
+
+
+    /* don't make a key more accessible */
+    if (sftk_isTrue(baseKey,CKA_SENSITIVE) && hasSensitive && 
+						(sensitive == PR_FALSE)) {
+	return CKR_KEY_FUNCTION_NOT_PERMITTED;
+    }
+    if (!sftk_isTrue(baseKey,CKA_EXTRACTABLE) && hasExtractable && 
+						(extractable == PR_TRUE)) {
+	return CKR_KEY_FUNCTION_NOT_PERMITTED;
+    }
+
+    /* inherit parent's sensitivity */
+    if (!hasSensitive) {
+        att = sftk_FindAttribute(baseKey,CKA_SENSITIVE);
+	if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT;
+	crv = sftk_defaultAttribute(destKey,sftk_attr_expand(&att->attrib));
+	sftk_FreeAttribute(att);
+	if (crv != CKR_OK) return crv;
+    }
+    if (!hasExtractable) {
+        att = sftk_FindAttribute(baseKey,CKA_EXTRACTABLE);
+	if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT;
+	crv = sftk_defaultAttribute(destKey,sftk_attr_expand(&att->attrib));
+	sftk_FreeAttribute(att);
+	if (crv != CKR_OK) return crv;
+    }
+
+    /* we should inherit the parent's always extractable/ never sensitive info,
+     * but handleObject always forces this attributes, so we would need to do
+     * something special. */
+    return CKR_OK;
+}
+
+/*
+ * make known fixed PKCS #11 key types to their sizes in bytes
+ */	
+unsigned long
+sftk_MapKeySize(CK_KEY_TYPE keyType) 
+{
+    switch (keyType) {
+    case CKK_CDMF:
+	return 8;
+    case CKK_DES:
+	return 8;
+    case CKK_DES2:
+	return 16;
+    case CKK_DES3:
+	return 24;
+    /* IDEA and CAST need to be added */
+    default:
+	break;
+    }
+    return 0;
+}
+
+#ifndef NSS_DISABLE_ECC
+/* Inputs:
+ *  key_len: Length of derived key to be generated.
+ *  SharedSecret: a shared secret that is the output of a key agreement primitive.
+ *  SharedInfo: (Optional) some data shared by the entities computing the secret key.
+ *  SharedInfoLen: the length in octets of SharedInfo
+ *  Hash: The hash function to be used in the KDF
+ *  HashLen: the length in octets of the output of Hash
+ * Output:
+ *  key: Pointer to a buffer containing derived key, if return value is SECSuccess.
+ */
+static CK_RV sftk_compute_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, SECItem *SharedSecret,
+		CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen,
+		SECStatus Hash(unsigned char *, const unsigned char *, PRUint32),
+		CK_ULONG HashLen)
+{
+    unsigned char *buffer = NULL, *output_buffer = NULL;
+    PRUint32 buffer_len, max_counter, i;
+    SECStatus rv;
+    CK_RV crv;
+
+    /* Check that key_len isn't too long.  The maximum key length could be
+     * greatly increased if the code below did not limit the 4-byte counter
+     * to a maximum value of 255. */
+    if (key_len > 254 * HashLen)
+	return CKR_ARGUMENTS_BAD;
+
+    if (SharedInfo == NULL)
+	SharedInfoLen = 0;
+
+    buffer_len = SharedSecret->len + 4 + SharedInfoLen;
+    buffer = (CK_BYTE *)PORT_Alloc(buffer_len);
+    if (buffer == NULL) {
+	crv = CKR_HOST_MEMORY;
+	goto loser;
+    }
+
+    max_counter = key_len/HashLen;
+    if (key_len > max_counter * HashLen)
+	max_counter++;
+
+    output_buffer = (CK_BYTE *)PORT_Alloc(max_counter * HashLen);
+    if (output_buffer == NULL) {
+	crv = CKR_HOST_MEMORY;
+	goto loser;
+    }
+
+    /* Populate buffer with SharedSecret || Counter || [SharedInfo]
+     * where Counter is 0x00000001 */
+    PORT_Memcpy(buffer, SharedSecret->data, SharedSecret->len);
+    buffer[SharedSecret->len] = 0;
+    buffer[SharedSecret->len + 1] = 0;
+    buffer[SharedSecret->len + 2] = 0;
+    buffer[SharedSecret->len + 3] = 1;
+    if (SharedInfo) {
+	PORT_Memcpy(&buffer[SharedSecret->len + 4], SharedInfo, SharedInfoLen);
+    }
+
+    for(i=0; i < max_counter; i++) {
+	rv = Hash(&output_buffer[i * HashLen], buffer, buffer_len);
+	if (rv != SECSuccess) {
+	    /* 'Hash' should not fail. */
+	    crv = CKR_FUNCTION_FAILED;
+	    goto loser;
+	}
+
+	/* Increment counter (assumes max_counter < 255) */
+	buffer[SharedSecret->len + 3]++;
+    }
+
+    PORT_ZFree(buffer, buffer_len);
+    if (key_len < max_counter * HashLen) {
+	PORT_Memset(output_buffer + key_len, 0, max_counter * HashLen - key_len);
+    }
+    *key = output_buffer;
+
+    return CKR_OK;
+
+    loser:
+	if (buffer) {
+	    PORT_ZFree(buffer, buffer_len);
+	}
+	if (output_buffer) {
+	    PORT_ZFree(output_buffer, max_counter * HashLen);
+	}
+	return crv;
+}
+
+static CK_RV sftk_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len,
+		SECItem *SharedSecret,
+		CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen,
+		CK_EC_KDF_TYPE kdf)
+{
+    if (kdf == CKD_SHA1_KDF)
+	return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo,
+		   		 SharedInfoLen, SHA1_HashBuf, SHA1_LENGTH);
+    else if (kdf == CKD_SHA224_KDF)
+	return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo,
+		   		 SharedInfoLen, SHA224_HashBuf, SHA224_LENGTH);
+    else if (kdf == CKD_SHA256_KDF)
+	return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo,
+		   		 SharedInfoLen, SHA256_HashBuf, SHA256_LENGTH);
+    else if (kdf == CKD_SHA384_KDF)
+	return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo,
+		   		 SharedInfoLen, SHA384_HashBuf, SHA384_LENGTH);
+    else if (kdf == CKD_SHA512_KDF)
+	return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo,
+		   		 SharedInfoLen, SHA512_HashBuf, SHA512_LENGTH);
+    else
+	return CKR_MECHANISM_INVALID;
+}
+#endif /* NSS_DISABLE_ECC */
+
+/*
+ * SSL Key generation given pre master secret
+ */
+#define NUM_MIXERS 9
+static const char * const mixers[NUM_MIXERS] = { 
+    "A", 
+    "BB", 
+    "CCC", 
+    "DDDD", 
+    "EEEEE", 
+    "FFFFFF", 
+    "GGGGGGG",
+    "HHHHHHHH",
+    "IIIIIIIII" };
+#define SSL3_PMS_LENGTH 48
+#define SSL3_MASTER_SECRET_LENGTH 48
+#define SSL3_RANDOM_LENGTH 32
+
+
+/* NSC_DeriveKey derives a key from a base key, creating a new key object. */
+CK_RV NSC_DeriveKey( CK_SESSION_HANDLE hSession,
+	 CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey,
+	 CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, 
+						CK_OBJECT_HANDLE_PTR phKey)
+{
+    SFTKSession *   session;
+    SFTKSlot    *   slot	= sftk_SlotFromSessionHandle(hSession);
+    SFTKObject  *   key;
+    SFTKObject  *   sourceKey;
+    SFTKAttribute * att = NULL;
+    SFTKAttribute * att2 = NULL;
+    unsigned char * buf;
+    SHA1Context *   sha;
+    MD5Context *    md5;
+    MD2Context *    md2;
+    CK_ULONG        macSize;
+    CK_ULONG        tmpKeySize;
+    CK_ULONG        IVSize;
+    CK_ULONG        keySize	= 0;
+    CK_RV           crv 	= CKR_OK;
+    CK_BBOOL        cktrue	= CK_TRUE;
+    CK_KEY_TYPE     keyType	= CKK_GENERIC_SECRET;
+    CK_OBJECT_CLASS classType	= CKO_SECRET_KEY;
+    CK_KEY_DERIVATION_STRING_DATA *stringPtr;
+    PRBool          isTLS = PR_FALSE;
+    PRBool          isSHA256 = PR_FALSE;
+    PRBool          isDH = PR_FALSE;
+    SECStatus       rv;
+    int             i;
+    unsigned int    outLen;
+    unsigned char   sha_out[SHA1_LENGTH];
+    unsigned char   key_block[NUM_MIXERS * MD5_LENGTH];
+    unsigned char   key_block2[MD5_LENGTH];
+    PRBool          isFIPS;		
+    HASH_HashType   hashType;
+    PRBool          extractValue = PR_TRUE;
+
+    CHECK_FORK();
+
+    if (!slot) {
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+    /*
+     * now lets create an object to hang the attributes off of
+     */
+    if (phKey) *phKey = CK_INVALID_HANDLE;
+
+    key = sftk_NewObject(slot); /* fill in the handle later */
+    if (key == NULL) {
+	return CKR_HOST_MEMORY;
+    }
+    isFIPS = (slot->slotID == FIPS_SLOT_ID);
+
+    /*
+     * load the template values into the object
+     */
+    for (i=0; i < (int) ulAttributeCount; i++) {
+	crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i]));
+	if (crv != CKR_OK) break;
+
+	if (pTemplate[i].type == CKA_KEY_TYPE) {
+	    keyType = *(CK_KEY_TYPE *)pTemplate[i].pValue;
+	}
+	if (pTemplate[i].type == CKA_VALUE_LEN) {
+	    keySize = *(CK_ULONG *)pTemplate[i].pValue;
+	}
+    }
+    if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+    if (keySize == 0) {
+	keySize = sftk_MapKeySize(keyType);
+    }
+
+    switch (pMechanism->mechanism) {
+      case CKM_NSS_JPAKE_ROUND2_SHA1:   /* fall through */
+      case CKM_NSS_JPAKE_ROUND2_SHA256: /* fall through */
+      case CKM_NSS_JPAKE_ROUND2_SHA384: /* fall through */
+      case CKM_NSS_JPAKE_ROUND2_SHA512:
+          extractValue = PR_FALSE;
+          classType = CKO_PRIVATE_KEY;
+          break;
+      case CKM_NSS_JPAKE_FINAL_SHA1:   /* fall through */
+      case CKM_NSS_JPAKE_FINAL_SHA256: /* fall through */
+      case CKM_NSS_JPAKE_FINAL_SHA384: /* fall through */
+      case CKM_NSS_JPAKE_FINAL_SHA512:
+          extractValue = PR_FALSE;
+          /* fall through */
+      default:
+          classType = CKO_SECRET_KEY;
+    }
+    
+    crv = sftk_forceAttribute (key,CKA_CLASS,&classType,sizeof(classType));
+    if (crv != CKR_OK) {
+	sftk_FreeObject(key);
+	return crv;
+    }
+
+    /* look up the base key we're deriving with */ 
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) {
+	sftk_FreeObject(key);
+        return CKR_SESSION_HANDLE_INVALID;
+    }
+
+    sourceKey = sftk_ObjectFromHandle(hBaseKey,session);
+    sftk_FreeSession(session);
+    if (sourceKey == NULL) {
+	sftk_FreeObject(key);
+        return CKR_KEY_HANDLE_INVALID;
+    }
+
+    if (extractValue) {
+        /* get the value of the base key */
+        att = sftk_FindAttribute(sourceKey,CKA_VALUE);
+        if (att == NULL) {
+            sftk_FreeObject(key);
+            sftk_FreeObject(sourceKey);
+            return CKR_KEY_HANDLE_INVALID;
+        }
+    }
+
+    switch (pMechanism->mechanism) {
+    /*
+     * generate the master secret 
+     */
+    case CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256:
+    case CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256:
+	isSHA256 = PR_TRUE;
+	/* fall thru */
+    case CKM_TLS_MASTER_KEY_DERIVE:
+    case CKM_TLS_MASTER_KEY_DERIVE_DH:
+	isTLS = PR_TRUE;
+	/* fall thru */
+    case CKM_SSL3_MASTER_KEY_DERIVE:
+    case CKM_SSL3_MASTER_KEY_DERIVE_DH:
+      {
+	CK_SSL3_MASTER_KEY_DERIVE_PARAMS *ssl3_master;
+	SSL3RSAPreMasterSecret *          rsa_pms;
+	unsigned char                     crsrdata[SSL3_RANDOM_LENGTH * 2];
+
+        if ((pMechanism->mechanism == CKM_SSL3_MASTER_KEY_DERIVE_DH) ||
+            (pMechanism->mechanism == CKM_TLS_MASTER_KEY_DERIVE_DH) ||
+            (pMechanism->mechanism == CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256))
+		isDH = PR_TRUE;
+
+	/* first do the consistancy checks */
+	if (!isDH && (att->attrib.ulValueLen != SSL3_PMS_LENGTH)) {
+	    crv = CKR_KEY_TYPE_INCONSISTENT;
+	    break;
+	}
+	att2 = sftk_FindAttribute(sourceKey,CKA_KEY_TYPE);
+	if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue !=
+					CKK_GENERIC_SECRET)) {
+	    if (att2) sftk_FreeAttribute(att2);
+	    crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+	    break;
+	}
+	sftk_FreeAttribute(att2);
+	if (keyType != CKK_GENERIC_SECRET) {
+	    crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+	    break;
+	}
+	if ((keySize != 0) && (keySize != SSL3_MASTER_SECRET_LENGTH)) {
+	    crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+	    break;
+	}
+
+	/* finally do the key gen */
+	ssl3_master = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *)
+					pMechanism->pParameter;
+
+	PORT_Memcpy(crsrdata, 
+	            ssl3_master->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH);
+	PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, 
+	            ssl3_master->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH);
+
+	if (ssl3_master->pVersion) {
+	    SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key);
+	    rsa_pms = (SSL3RSAPreMasterSecret *) att->attrib.pValue;
+	    /* don't leak more key material then necessary for SSL to work */
+	    if ((sessKey == NULL) || sessKey->wasDerived) {
+		ssl3_master->pVersion->major = 0xff;
+		ssl3_master->pVersion->minor = 0xff;
+	    } else {
+		ssl3_master->pVersion->major = rsa_pms->client_version[0];
+		ssl3_master->pVersion->minor = rsa_pms->client_version[1];
+	    }
+	}
+	if (ssl3_master->RandomInfo.ulClientRandomLen != SSL3_RANDOM_LENGTH) {
+	   crv = CKR_MECHANISM_PARAM_INVALID;
+	   break;
+	}
+	if (ssl3_master->RandomInfo.ulServerRandomLen != SSL3_RANDOM_LENGTH) {
+	   crv = CKR_MECHANISM_PARAM_INVALID;
+	   break;
+	}
+
+        if (isTLS) {
+	    SECStatus status;
+ 	    SECItem crsr   = { siBuffer, NULL, 0 };
+ 	    SECItem master = { siBuffer, NULL, 0 };
+ 	    SECItem pms    = { siBuffer, NULL, 0 };
+
+ 	    crsr.data   = crsrdata;
+	    crsr.len    = sizeof crsrdata;
+ 	    master.data = key_block;
+	    master.len  = SSL3_MASTER_SECRET_LENGTH;
+ 	    pms.data    = (unsigned char*)att->attrib.pValue;
+	    pms.len     =                 att->attrib.ulValueLen;
+
+	    if (isSHA256) {
+		status = TLS_P_hash(HASH_AlgSHA256, &pms, "master secret",
+				    &crsr, &master, isFIPS);
+	    } else {
+		status = TLS_PRF(&pms, "master secret", &crsr, &master, isFIPS);
+	    }
+	    if (status != SECSuccess) {
+	    	crv = CKR_FUNCTION_FAILED;
+		break;
+	    }
+	} else {
+	    /* now allocate the hash contexts */
+	    md5 = MD5_NewContext();
+	    if (md5 == NULL) { 
+		crv = CKR_HOST_MEMORY;
+		break;
+	    }
+	    sha = SHA1_NewContext();
+	    if (sha == NULL) { 
+		PORT_Free(md5);
+		crv = CKR_HOST_MEMORY;
+		break;
+	    }
+            for (i = 0; i < 3; i++) {
+              SHA1_Begin(sha);
+              SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i]));
+              SHA1_Update(sha, (const unsigned char*)att->attrib.pValue, 
+			  att->attrib.ulValueLen);
+              SHA1_Update(sha, crsrdata, sizeof crsrdata);
+              SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH);
+              PORT_Assert(outLen == SHA1_LENGTH);
+
+              MD5_Begin(md5);
+              MD5_Update(md5, (const unsigned char*)att->attrib.pValue, 
+			 att->attrib.ulValueLen);
+              MD5_Update(md5, sha_out, outLen);
+              MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH);
+              PORT_Assert(outLen == MD5_LENGTH);
+            }
+	    PORT_Free(md5);
+	    PORT_Free(sha);
+	}
+
+	/* store the results */
+	crv = sftk_forceAttribute
+			(key,CKA_VALUE,key_block,SSL3_MASTER_SECRET_LENGTH);
+	if (crv != CKR_OK) break;
+	keyType = CKK_GENERIC_SECRET;
+	crv = sftk_forceAttribute (key,CKA_KEY_TYPE,&keyType,sizeof(keyType));
+	if (isTLS) {
+	    /* TLS's master secret is used to "sign" finished msgs with PRF. */
+	    /* XXX This seems like a hack.   But SFTK_Derive only accepts 
+	     * one "operation" argument. */
+	    crv = sftk_forceAttribute(key,CKA_SIGN,  &cktrue,sizeof(CK_BBOOL));
+	    if (crv != CKR_OK) break;
+	    crv = sftk_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL));
+	    if (crv != CKR_OK) break;
+	    /* While we're here, we might as well force this, too. */
+	    crv = sftk_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL));
+	    if (crv != CKR_OK) break;
+	}
+	break;
+      }
+
+    case CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256:
+	isSHA256 = PR_TRUE;
+	/* fall thru */
+    case CKM_TLS_KEY_AND_MAC_DERIVE:
+	isTLS = PR_TRUE;
+	/* fall thru */
+    case CKM_SSL3_KEY_AND_MAC_DERIVE:
+      {
+	CK_SSL3_KEY_MAT_PARAMS *ssl3_keys;
+	CK_SSL3_KEY_MAT_OUT *   ssl3_keys_out;
+	CK_ULONG                effKeySize;
+	unsigned int            block_needed;
+	unsigned char           srcrdata[SSL3_RANDOM_LENGTH * 2];
+	unsigned char           crsrdata[SSL3_RANDOM_LENGTH * 2];
+
+	crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+	if (crv != CKR_OK) break;
+
+	if (att->attrib.ulValueLen != SSL3_MASTER_SECRET_LENGTH) {
+	    crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+	    break;
+	}
+	att2 = sftk_FindAttribute(sourceKey,CKA_KEY_TYPE);
+	if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue !=
+					CKK_GENERIC_SECRET)) {
+	    if (att2) sftk_FreeAttribute(att2);
+	    crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+	    break;
+	}
+	sftk_FreeAttribute(att2);
+	md5 = MD5_NewContext();
+	if (md5 == NULL) { 
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	sha = SHA1_NewContext();
+	if (sha == NULL) { 
+	    PORT_Free(md5);
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	ssl3_keys = (CK_SSL3_KEY_MAT_PARAMS *) pMechanism->pParameter;
+
+	PORT_Memcpy(srcrdata, 
+	            ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH);
+	PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH, 
+		    ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH);
+
+	PORT_Memcpy(crsrdata, 
+		    ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH);
+	PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, 
+		    ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH);
+
+	/*
+	 * clear out our returned keys so we can recover on failure
+	 */
+	ssl3_keys_out = ssl3_keys->pReturnedKeyMaterial;
+	ssl3_keys_out->hClientMacSecret = CK_INVALID_HANDLE;
+	ssl3_keys_out->hServerMacSecret = CK_INVALID_HANDLE;
+	ssl3_keys_out->hClientKey       = CK_INVALID_HANDLE;
+	ssl3_keys_out->hServerKey       = CK_INVALID_HANDLE;
+
+	/*
+	 * How much key material do we need?
+	 */
+	macSize    = ssl3_keys->ulMacSizeInBits/8;
+	effKeySize = ssl3_keys->ulKeySizeInBits/8;
+	IVSize     = ssl3_keys->ulIVSizeInBits/8;
+	if (keySize == 0) {
+	    effKeySize = keySize;
+	}
+	block_needed = 2 * (macSize + effKeySize + 
+	                    ((!ssl3_keys->bIsExport) * IVSize));
+	PORT_Assert(block_needed <= sizeof key_block);
+	if (block_needed > sizeof key_block)
+	    block_needed = sizeof key_block;
+
+	/*
+	 * generate the key material: This looks amazingly similar to the
+	 * PMS code, and is clearly crying out for a function to provide it.
+	 */
+	if (isTLS) {
+	    SECStatus     status;
+	    SECItem       srcr   = { siBuffer, NULL, 0 };
+	    SECItem       keyblk = { siBuffer, NULL, 0 };
+	    SECItem       master = { siBuffer, NULL, 0 }; 
+
+	    srcr.data   = srcrdata;
+	    srcr.len    = sizeof srcrdata;
+	    keyblk.data = key_block;
+	    keyblk.len  = block_needed;
+	    master.data = (unsigned char*)att->attrib.pValue;
+	    master.len  =                 att->attrib.ulValueLen;
+
+	    if (isSHA256) {
+		status = TLS_P_hash(HASH_AlgSHA256, &master, "key expansion",
+				    &srcr, &keyblk, isFIPS);
+	    } else {
+		status = TLS_PRF(&master, "key expansion", &srcr, &keyblk,
+				 isFIPS);
+	    }
+	    if (status != SECSuccess) {
+		goto key_and_mac_derive_fail;
+	    }
+	} else {
+	    unsigned int block_bytes = 0;
+	    /* key_block = 
+	     *     MD5(master_secret + SHA('A' + master_secret + 
+	     *                      ServerHello.random + ClientHello.random)) +
+	     *     MD5(master_secret + SHA('BB' + master_secret + 
+	     *                      ServerHello.random + ClientHello.random)) +
+	     *     MD5(master_secret + SHA('CCC' + master_secret + 
+	     *                      ServerHello.random + ClientHello.random)) +
+	     *     [...];
+	     */
+	    for (i = 0; i < NUM_MIXERS && block_bytes < block_needed; i++) {
+	      SHA1_Begin(sha);
+	      SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i]));
+	      SHA1_Update(sha, (const unsigned char*)att->attrib.pValue, 
+			  att->attrib.ulValueLen);
+	      SHA1_Update(sha, srcrdata, sizeof srcrdata);
+	      SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH);
+	      PORT_Assert(outLen == SHA1_LENGTH);
+	      MD5_Begin(md5);
+	      MD5_Update(md5, (const unsigned char*)att->attrib.pValue,
+			 att->attrib.ulValueLen);
+	      MD5_Update(md5, sha_out, outLen);
+	      MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH);
+	      PORT_Assert(outLen == MD5_LENGTH);
+	      block_bytes += outLen;
+	    }
+	}
+
+	/*
+	 * Put the key material where it goes.
+	 */
+	i = 0;			/* now shows how much consumed */
+
+	/* 
+	 * The key_block is partitioned as follows:
+	 * client_write_MAC_secret[CipherSpec.hash_size]
+	 */
+	crv = sftk_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize,
+					 &ssl3_keys_out->hClientMacSecret);
+	if (crv != CKR_OK)
+	    goto key_and_mac_derive_fail;
+
+	i += macSize;
+
+	/* 
+	 * server_write_MAC_secret[CipherSpec.hash_size]
+	 */
+	crv = sftk_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize,
+					    &ssl3_keys_out->hServerMacSecret);
+	if (crv != CKR_OK) {
+	    goto key_and_mac_derive_fail;
+	}
+	i += macSize;
+
+	if (keySize) {
+	    if (!ssl3_keys->bIsExport) {
+		/* 
+		** Generate Domestic write keys and IVs.
+		** client_write_key[CipherSpec.key_material]
+		*/
+		crv = sftk_buildSSLKey(hSession,key,PR_FALSE,&key_block[i],
+					keySize, &ssl3_keys_out->hClientKey);
+		if (crv != CKR_OK) {
+		    goto key_and_mac_derive_fail;
+		}
+		i += keySize;
+
+		/* 
+		** server_write_key[CipherSpec.key_material]
+		*/
+		crv = sftk_buildSSLKey(hSession,key,PR_FALSE,&key_block[i],
+					keySize, &ssl3_keys_out->hServerKey);
+		if (crv != CKR_OK) {
+		    goto key_and_mac_derive_fail;
+		}
+		i += keySize;
+
+		/* 
+		** client_write_IV[CipherSpec.IV_size]
+		*/
+		if (IVSize > 0) {
+		    PORT_Memcpy(ssl3_keys_out->pIVClient, 
+		                &key_block[i], IVSize);
+		    i += IVSize;
+		}
+
+		/* 
+		** server_write_IV[CipherSpec.IV_size]
+		*/
+		if (IVSize > 0) {
+		    PORT_Memcpy(ssl3_keys_out->pIVServer, 
+		                &key_block[i], IVSize);
+		    i += IVSize;
+		}
+		PORT_Assert(i <= sizeof key_block);
+
+	    } else if (!isTLS) {
+
+		/*
+		** Generate SSL3 Export write keys and IVs.
+		** client_write_key[CipherSpec.key_material]
+		** final_client_write_key = MD5(client_write_key +
+		**                   ClientHello.random + ServerHello.random);
+		*/
+		MD5_Begin(md5);
+		MD5_Update(md5, &key_block[i], effKeySize);
+            	MD5_Update(md5, crsrdata, sizeof crsrdata);
+		MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+		i += effKeySize;
+		crv = sftk_buildSSLKey(hSession,key,PR_FALSE,key_block2,
+				 	keySize,&ssl3_keys_out->hClientKey);
+		if (crv != CKR_OK) {
+		    goto key_and_mac_derive_fail;
+		}
+
+		/*
+		** server_write_key[CipherSpec.key_material]
+		** final_server_write_key = MD5(server_write_key +
+		**                    ServerHello.random + ClientHello.random);
+		*/
+		MD5_Begin(md5);
+		MD5_Update(md5, &key_block[i], effKeySize);
+            	MD5_Update(md5, srcrdata, sizeof srcrdata);
+		MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+		i += effKeySize;
+		crv = sftk_buildSSLKey(hSession,key,PR_FALSE,key_block2,
+					 keySize,&ssl3_keys_out->hServerKey);
+		if (crv != CKR_OK) {
+		    goto key_and_mac_derive_fail;
+		}
+
+		/*
+		** client_write_IV = 
+		**	MD5(ClientHello.random + ServerHello.random);
+		*/
+		MD5_Begin(md5);
+            	MD5_Update(md5, crsrdata, sizeof crsrdata);
+		MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+		PORT_Memcpy(ssl3_keys_out->pIVClient, key_block2, IVSize);
+
+		/*
+		** server_write_IV = 
+		**	MD5(ServerHello.random + ClientHello.random);
+		*/
+		MD5_Begin(md5);
+            	MD5_Update(md5, srcrdata, sizeof srcrdata);
+		MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+		PORT_Memcpy(ssl3_keys_out->pIVServer, key_block2, IVSize);
+
+	    } else {
+
+		/*
+		** Generate TLS 1.0 Export write keys and IVs.
+		*/
+		SECStatus     status;
+		SECItem       secret = { siBuffer, NULL, 0 };
+		SECItem       crsr   = { siBuffer, NULL, 0 };
+		SECItem       keyblk = { siBuffer, NULL, 0 };
+
+		/*
+		** client_write_key[CipherSpec.key_material]
+		** final_client_write_key = PRF(client_write_key, 
+		**                              "client write key",
+		**                              client_random + server_random);
+		*/
+		secret.data = &key_block[i];
+		secret.len  = effKeySize;
+		i          += effKeySize;
+		crsr.data   = crsrdata;
+		crsr.len    = sizeof crsrdata;
+		keyblk.data = key_block2;
+		keyblk.len  = sizeof key_block2;
+		status = TLS_PRF(&secret, "client write key", &crsr, &keyblk,
+				  isFIPS);
+		if (status != SECSuccess) {
+		    goto key_and_mac_derive_fail;
+		}
+		crv = sftk_buildSSLKey(hSession, key, PR_FALSE, key_block2, 
+				       keySize, &ssl3_keys_out->hClientKey);
+		if (crv != CKR_OK) {
+		    goto key_and_mac_derive_fail;
+		}
+
+		/*
+		** server_write_key[CipherSpec.key_material]
+		** final_server_write_key = PRF(server_write_key,
+		**                              "server write key",
+		**                              client_random + server_random);
+		*/
+		secret.data = &key_block[i];
+		secret.len  = effKeySize;
+		i          += effKeySize;
+		keyblk.data = key_block2;
+		keyblk.len  = sizeof key_block2;
+		status = TLS_PRF(&secret, "server write key", &crsr, &keyblk,
+				  isFIPS);
+		if (status != SECSuccess) {
+		    goto key_and_mac_derive_fail;
+		}
+		crv = sftk_buildSSLKey(hSession, key, PR_FALSE, key_block2, 
+				       keySize, &ssl3_keys_out->hServerKey);
+		if (crv != CKR_OK) {
+		    goto key_and_mac_derive_fail;
+		}
+
+		/*
+		** iv_block = PRF("", "IV block", 
+		**                    client_random + server_random);
+		** client_write_IV[SecurityParameters.IV_size]
+		** server_write_IV[SecurityParameters.IV_size]
+		*/
+		if (IVSize) {
+		    secret.data = NULL;
+		    secret.len  = 0;
+		    keyblk.data = &key_block[i];
+		    keyblk.len  = 2 * IVSize;
+		    status = TLS_PRF(&secret, "IV block", &crsr, &keyblk,
+				      isFIPS);
+		    if (status != SECSuccess) {
+			goto key_and_mac_derive_fail;
+		    }
+		    PORT_Memcpy(ssl3_keys_out->pIVClient, keyblk.data, IVSize);
+		    PORT_Memcpy(ssl3_keys_out->pIVServer, keyblk.data + IVSize,
+                                IVSize);
+		}
+	    }
+	}
+
+	crv = CKR_OK;
+
+	if (0) {
+key_and_mac_derive_fail:
+	    if (crv == CKR_OK)
+	    	crv = CKR_FUNCTION_FAILED;
+	    sftk_freeSSLKeys(hSession, ssl3_keys_out);
+	}
+	MD5_DestroyContext(md5, PR_TRUE);
+	SHA1_DestroyContext(sha, PR_TRUE);
+	sftk_FreeObject(key);
+	key = NULL;
+	break;
+      }
+
+    case CKM_CONCATENATE_BASE_AND_KEY:
+      {
+	SFTKObject *newKey;
+
+	crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+	if (crv != CKR_OK) break;
+
+	session = sftk_SessionFromHandle(hSession);
+	if (session == NULL) {
+            crv = CKR_SESSION_HANDLE_INVALID;
+	    break;
+    	}
+
+	newKey = sftk_ObjectFromHandle(*(CK_OBJECT_HANDLE *)
+					pMechanism->pParameter,session);
+	sftk_FreeSession(session);
+	if ( newKey == NULL) {
+            crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+
+	if (sftk_isTrue(newKey,CKA_SENSITIVE)) {
+	    crv = sftk_forceAttribute(newKey,CKA_SENSITIVE,&cktrue,
+							sizeof(CK_BBOOL));
+	    if (crv != CKR_OK) {
+		sftk_FreeObject(newKey);
+		break;
+	    }
+	}
+
+	att2 = sftk_FindAttribute(newKey,CKA_VALUE);
+	if (att2 == NULL) {
+	    sftk_FreeObject(newKey);
+            crv = CKR_KEY_HANDLE_INVALID;
+	    break;
+	}
+	tmpKeySize = att->attrib.ulValueLen+att2->attrib.ulValueLen;
+	if (keySize == 0) keySize = tmpKeySize;
+	if (keySize > tmpKeySize) {
+	    sftk_FreeObject(newKey);
+	    sftk_FreeAttribute(att2);
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	buf = (unsigned char*)PORT_Alloc(tmpKeySize);
+	if (buf == NULL) {
+	    sftk_FreeAttribute(att2);
+	    sftk_FreeObject(newKey);
+	    crv = CKR_HOST_MEMORY;	
+	    break;
+	}
+
+	PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen);
+	PORT_Memcpy(buf+att->attrib.ulValueLen,
+				att2->attrib.pValue,att2->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+	PORT_ZFree(buf,tmpKeySize);
+	sftk_FreeAttribute(att2);
+	sftk_FreeObject(newKey);
+	break;
+      }
+
+    case CKM_CONCATENATE_BASE_AND_DATA:
+	crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+	if (crv != CKR_OK) break;
+
+	stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter;
+	tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen;
+	if (keySize == 0) keySize = tmpKeySize;
+	if (keySize > tmpKeySize) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	buf = (unsigned char*)PORT_Alloc(tmpKeySize);
+	if (buf == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+
+	PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen);
+	PORT_Memcpy(buf+att->attrib.ulValueLen,stringPtr->pData,
+							stringPtr->ulLen);
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+	PORT_ZFree(buf,tmpKeySize);
+	break;
+    case CKM_CONCATENATE_DATA_AND_BASE:
+	crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+	if (crv != CKR_OK) break;
+
+	stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter;
+	tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen;
+	if (keySize == 0) keySize = tmpKeySize;
+	if (keySize > tmpKeySize) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	buf = (unsigned char*)PORT_Alloc(tmpKeySize);
+	if (buf == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+
+	PORT_Memcpy(buf,stringPtr->pData,stringPtr->ulLen);
+	PORT_Memcpy(buf+stringPtr->ulLen,att->attrib.pValue,
+							att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+	PORT_ZFree(buf,tmpKeySize);
+	break;
+    case CKM_XOR_BASE_AND_DATA:
+	crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+	if (crv != CKR_OK) break;
+
+	stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter;
+	tmpKeySize = PR_MIN(att->attrib.ulValueLen,stringPtr->ulLen);
+	if (keySize == 0) keySize = tmpKeySize;
+	if (keySize > tmpKeySize) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	buf = (unsigned char*)PORT_Alloc(keySize);
+	if (buf == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+
+	
+	PORT_Memcpy(buf,att->attrib.pValue,keySize);
+	for (i=0; i < (int)keySize; i++) {
+	    buf[i] ^= stringPtr->pData[i];
+	}
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+	PORT_ZFree(buf,keySize);
+	break;
+
+    case CKM_EXTRACT_KEY_FROM_KEY:
+      {
+	/* the following assumes 8 bits per byte */
+	CK_ULONG extract = *(CK_EXTRACT_PARAMS *)pMechanism->pParameter;
+	CK_ULONG shift   = extract & 0x7;      /* extract mod 8 the fast way */
+	CK_ULONG offset  = extract >> 3;       /* extract div 8 the fast way */
+
+	crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+	if (crv != CKR_OK) break;
+
+	if (keySize == 0)  {
+	    crv = CKR_TEMPLATE_INCOMPLETE;
+	    break;
+	}
+	/* make sure we have enough bits in the original key */
+	if (att->attrib.ulValueLen < 
+			(offset + keySize + ((shift != 0)? 1 :0)) ) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+	buf = (unsigned char*)PORT_Alloc(keySize);
+	if (buf == NULL) {
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+
+	/* copy the bits we need into the new key */	
+	for (i=0; i < (int)keySize; i++) {
+	    unsigned char *value =
+			 ((unsigned char *)att->attrib.pValue)+offset+i;
+	    if (shift) {
+	        buf[i] = (value[0] << (shift)) | (value[1] >> (8 - shift));
+	    } else {
+		buf[i] = value[0];
+	    }
+	}
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+	PORT_ZFree(buf,keySize);
+	break;
+      }
+    case CKM_MD2_KEY_DERIVATION:
+	if (keySize == 0) keySize = MD2_LENGTH;
+	if (keySize > MD2_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	/* now allocate the hash contexts */
+	md2 = MD2_NewContext();
+	if (md2 == NULL) { 
+	    crv = CKR_HOST_MEMORY;
+	    break;
+	}
+	MD2_Begin(md2);
+	MD2_Update(md2,(const unsigned char*)att->attrib.pValue,
+		   att->attrib.ulValueLen);
+	MD2_End(md2,key_block,&outLen,MD2_LENGTH);
+	MD2_DestroyContext(md2, PR_TRUE);
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize);
+	break;
+    case CKM_MD5_KEY_DERIVATION:
+	if (keySize == 0) keySize = MD5_LENGTH;
+	if (keySize > MD5_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	MD5_HashBuf(key_block,(const unsigned char*)att->attrib.pValue,
+		   att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize);
+	break;
+     case CKM_SHA1_KEY_DERIVATION:
+	if (keySize == 0) keySize = SHA1_LENGTH;
+	if (keySize > SHA1_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	SHA1_HashBuf(key_block,(const unsigned char*)att->attrib.pValue,
+		    att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize);
+	break;
+
+     case CKM_SHA224_KEY_DERIVATION:
+	if (keySize == 0) keySize = SHA224_LENGTH;
+	if (keySize > SHA224_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	SHA224_HashBuf(key_block,(const unsigned char*)att->attrib.pValue,
+		    att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize);
+	break;
+
+     case CKM_SHA256_KEY_DERIVATION:
+	if (keySize == 0) keySize = SHA256_LENGTH;
+	if (keySize > SHA256_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	SHA256_HashBuf(key_block,(const unsigned char*)att->attrib.pValue,
+		    att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize);
+	break;
+
+     case CKM_SHA384_KEY_DERIVATION:
+	if (keySize == 0) keySize = SHA384_LENGTH;
+	if (keySize > SHA384_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	SHA384_HashBuf(key_block,(const unsigned char*)att->attrib.pValue,
+		    att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize);
+	break;
+
+     case CKM_SHA512_KEY_DERIVATION:
+	if (keySize == 0) keySize = SHA512_LENGTH;
+	if (keySize > SHA512_LENGTH) {
+	    crv = CKR_TEMPLATE_INCONSISTENT;
+	    break;
+	}
+	SHA512_HashBuf(key_block,(const unsigned char*)att->attrib.pValue,
+		    att->attrib.ulValueLen);
+
+	crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize);
+	break;
+
+    case CKM_DH_PKCS_DERIVE:
+      {
+	SECItem  derived,  dhPublic;
+	SECItem  dhPrime,  dhValue;
+	/* sourceKey - values for the local existing low key */
+	/* get prime and value attributes */
+	crv = sftk_Attribute2SecItem(NULL, &dhPrime, sourceKey, CKA_PRIME); 
+	if (crv != SECSuccess) break;
+	crv = sftk_Attribute2SecItem(NULL, &dhValue, sourceKey, CKA_VALUE); 
+	if (crv != SECSuccess) {
+ 	    PORT_Free(dhPrime.data);
+	    break;
+	}
+
+	dhPublic.data = pMechanism->pParameter;
+	dhPublic.len  = pMechanism->ulParameterLen;
+
+	/* calculate private value - oct */
+	rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize); 
+
+	PORT_Free(dhPrime.data);
+	PORT_Free(dhValue.data);
+     
+	if (rv == SECSuccess) {
+	    sftk_forceAttribute(key, CKA_VALUE, derived.data, derived.len);
+	    PORT_ZFree(derived.data, derived.len);
+	} else
+	    crv = CKR_HOST_MEMORY;
+	    
+	break;
+      }
+
+#ifndef NSS_DISABLE_ECC
+    case CKM_ECDH1_DERIVE:
+    case CKM_ECDH1_COFACTOR_DERIVE:
+      {
+	SECItem  ecScalar, ecPoint;
+	SECItem  tmp;
+	PRBool   withCofactor = PR_FALSE;
+	unsigned char *secret;
+	unsigned char *keyData = NULL;
+	int secretlen, curveLen, pubKeyLen;
+	CK_ECDH1_DERIVE_PARAMS *mechParams;
+	NSSLOWKEYPrivateKey *privKey;
+	PLArenaPool *arena = NULL;
+
+	/* Check mechanism parameters */
+	mechParams = (CK_ECDH1_DERIVE_PARAMS *) pMechanism->pParameter;
+	if ((pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)) ||
+	    ((mechParams->kdf == CKD_NULL) &&
+		((mechParams->ulSharedDataLen != 0) || 
+		    (mechParams->pSharedData != NULL)))) {
+	    crv = CKR_MECHANISM_PARAM_INVALID;
+	    break;
+	}
+
+	privKey = sftk_GetPrivKey(sourceKey, CKK_EC, &crv);
+	if (privKey == NULL) {
+	    break;
+	}
+
+	/* Now we are working with a non-NULL private key */
+	SECITEM_CopyItem(NULL, &ecScalar, &privKey->u.ec.privateValue);
+
+	ecPoint.data = mechParams->pPublicData;
+	ecPoint.len  = mechParams->ulPublicDataLen;
+
+	curveLen = (privKey->u.ec.ecParams.fieldID.size +7)/8;
+	pubKeyLen = (2*curveLen) + 1;
+
+	/* if the len is too small, can't be a valid point */
+	if (ecPoint.len < pubKeyLen) {
+	    goto ec_loser;
+	}
+	/* if the len is too large, must be an encoded point (length is
+	 * equal case just falls through */
+	if (ecPoint.len > pubKeyLen) {
+	    SECItem newPoint;
+
+	    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+	    if (arena == NULL) {
+		goto ec_loser;
+	    }
+
+	    rv = SEC_QuickDERDecodeItem(arena, &newPoint, 
+					SEC_ASN1_GET(SEC_OctetStringTemplate), 
+					&ecPoint);
+	    if (rv != SECSuccess) {
+		goto ec_loser;
+	    }
+	    ecPoint = newPoint;
+	}
+
+	if (pMechanism->mechanism == CKM_ECDH1_COFACTOR_DERIVE) {
+	    withCofactor = PR_TRUE;
+	} else {
+	    /* When not using cofactor derivation, one should
+	     * validate the public key to avoid small subgroup
+	     * attacks.
+	     */
+	    if (EC_ValidatePublicKey(&privKey->u.ec.ecParams, &ecPoint) 
+		!= SECSuccess) {
+		goto ec_loser;
+	    }
+	}
+
+	rv = ECDH_Derive(&ecPoint, &privKey->u.ec.ecParams, &ecScalar,
+	                 withCofactor, &tmp); 
+	PORT_Free(ecScalar.data);
+	ecScalar.data = NULL;
+	if (privKey != sourceKey->objectInfo) {
+	   nsslowkey_DestroyPrivateKey(privKey);
+	   privKey=NULL;
+	}
+	if (arena) {
+	    PORT_FreeArena(arena,PR_FALSE);
+	    arena=NULL;
+	}
+
+	if (rv != SECSuccess) {
+	    crv = sftk_MapCryptError(PORT_GetError());
+	    break;
+	}
+
+
+	/*
+	 * apply the kdf function.
+	 */
+	if (mechParams->kdf == CKD_NULL) {
+	    /*
+	     * tmp is the raw data created by ECDH_Derive,
+	     * secret and secretlen are the values we will
+	     * eventually pass as our generated key.
+	     */
+	    secret = tmp.data;
+	    secretlen = tmp.len;
+	} else {
+	    secretlen = keySize;
+	    crv = sftk_ANSI_X9_63_kdf(&secret, keySize,
+			&tmp, mechParams->pSharedData,
+			mechParams->ulSharedDataLen, mechParams->kdf);
+	    PORT_ZFree(tmp.data, tmp.len);
+	    if (crv != CKR_OK) {
+		break;
+	    }
+	    tmp.data = secret;
+	    tmp.len = secretlen;
+	}
+
+	/*
+	 * if keySize is supplied, then we are generating a key of a specific 
+	 * length. This is done by taking the least significant 'keySize' 
+	 * bytes from the unsigned value calculated by ECDH. Note: this may 
+	 * mean padding temp with extra leading zeros from what ECDH_Derive 
+	 * already returned (which itself may contain leading zeros).
+ 	 */
+	if (keySize) {
+	    if (secretlen < keySize) {
+	        keyData = PORT_ZAlloc(keySize);
+		if (!keyData) {
+		    PORT_ZFree(tmp.data, tmp.len);
+		    crv = CKR_HOST_MEMORY;
+		    break;
+		}
+		PORT_Memcpy(&keyData[keySize-secretlen],secret,secretlen);
+		secret = keyData;
+	    } else {
+		secret += (secretlen - keySize);
+	    }
+	    secretlen = keySize;
+	}
+
+	sftk_forceAttribute(key, CKA_VALUE, secret, secretlen);
+	PORT_ZFree(tmp.data, tmp.len);
+	if (keyData) {
+	    PORT_ZFree(keyData, keySize);
+	}
+	break;
+
+ec_loser:
+	crv = CKR_ARGUMENTS_BAD;
+	PORT_Free(ecScalar.data);
+	if (privKey != sourceKey->objectInfo)
+	    nsslowkey_DestroyPrivateKey(privKey);
+	if (arena) {
+	    PORT_FreeArena(arena, PR_FALSE);
+	}
+	break;
+
+      }
+#endif /* NSS_DISABLE_ECC */
+
+    /* See RFC 5869 and CK_NSS_HKDFParams for documentation. */
+    case CKM_NSS_HKDF_SHA1:   hashType = HASH_AlgSHA1;   goto hkdf;
+    case CKM_NSS_HKDF_SHA256: hashType = HASH_AlgSHA256; goto hkdf;
+    case CKM_NSS_HKDF_SHA384: hashType = HASH_AlgSHA384; goto hkdf;
+    case CKM_NSS_HKDF_SHA512: hashType = HASH_AlgSHA512; goto hkdf;
+hkdf: {
+        const CK_NSS_HKDFParams * params =
+            (const CK_NSS_HKDFParams *) pMechanism->pParameter;
+        const SECHashObject * rawHash;
+        unsigned hashLen;
+        CK_BYTE buf[HASH_LENGTH_MAX];
+        CK_BYTE * prk;  /* psuedo-random key */
+        CK_ULONG prkLen;
+        CK_BYTE * okm;  /* output keying material */
+
+        rawHash = HASH_GetRawHashObject(hashType);
+        if (rawHash == NULL || rawHash->length > sizeof buf) {
+            crv = CKR_FUNCTION_FAILED;
+            break;
+        }
+        hashLen = rawHash->length;
+
+        if (pMechanism->ulParameterLen != sizeof(CK_NSS_HKDFParams) ||
+            !params || (!params->bExpand && !params->bExtract) ||
+            (params->bExtract && params->ulSaltLen > 0 && !params->pSalt) ||
+            (params->bExpand && params->ulInfoLen > 0 && !params->pInfo)) {
+            crv = CKR_MECHANISM_PARAM_INVALID;
+            break;
+        }
+        if (keySize == 0 || keySize > sizeof key_block ||
+            (!params->bExpand && keySize > hashLen) ||
+            (params->bExpand && keySize > 255 * hashLen)) {
+            crv = CKR_TEMPLATE_INCONSISTENT;
+            break;
+        }
+        crv = sftk_DeriveSensitiveCheck(sourceKey, key);
+        if (crv != CKR_OK)
+            break;
+
+        /* HKDF-Extract(salt, base key value) */
+        if (params->bExtract) {
+            CK_BYTE * salt;
+            CK_ULONG saltLen;
+            HMACContext * hmac;
+            unsigned int bufLen;
+
+            salt = params->pSalt;
+            saltLen = params->ulSaltLen;
+            if (salt == NULL) {
+                saltLen = hashLen;
+                salt = buf;
+                memset(salt, 0, saltLen);
+            }
+            hmac = HMAC_Create(rawHash, salt, saltLen, isFIPS);
+            if (!hmac) {
+                crv = CKR_HOST_MEMORY;
+                break;
+            }
+            HMAC_Begin(hmac);
+            HMAC_Update(hmac, (const unsigned char*) att->attrib.pValue,
+		        att->attrib.ulValueLen);
+            HMAC_Finish(hmac, buf, &bufLen, sizeof(buf));
+            HMAC_Destroy(hmac, PR_TRUE);
+            PORT_Assert(bufLen == rawHash->length);
+            prk = buf;
+            prkLen = bufLen;
+        } else {
+            /* PRK = base key value */
+            prk = (CK_BYTE*) att->attrib.pValue;
+            prkLen = att->attrib.ulValueLen;
+        }
+        
+        /* HKDF-Expand */
+        if (!params->bExpand) {
+            okm = prk;
+        } else {
+            /* T(1) = HMAC-Hash(prk, "" | info | 0x01)
+             * T(n) = HMAC-Hash(prk, T(n-1) | info | n
+             * key material = T(1) | ... | T(n)
+             */
+            HMACContext * hmac;
+            CK_BYTE i;
+            unsigned iterations = PR_ROUNDUP(keySize, hashLen) / hashLen;
+            hmac = HMAC_Create(rawHash, prk, prkLen, isFIPS);
+            if (hmac == NULL) {
+                crv = CKR_HOST_MEMORY;
+                break;
+            }
+            for (i = 1; i <= iterations; ++i) {
+                unsigned len;
+                HMAC_Begin(hmac);
+                if (i > 1) {
+                    HMAC_Update(hmac, key_block + ((i-2) * hashLen), hashLen);
+                }
+                if (params->ulInfoLen != 0) {
+                    HMAC_Update(hmac, params->pInfo, params->ulInfoLen);
+                }
+                HMAC_Update(hmac, &i, 1);
+                HMAC_Finish(hmac, key_block + ((i-1) * hashLen), &len,
+                            hashLen);
+                PORT_Assert(len == hashLen);
+            }
+            HMAC_Destroy(hmac, PR_TRUE);
+            okm = key_block;
+        }
+        /* key material = prk */
+        crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize);
+        break;
+      } /* end of CKM_NSS_HKDF_* */
+
+    case CKM_NSS_JPAKE_ROUND2_SHA1: hashType = HASH_AlgSHA1; goto jpake2;
+    case CKM_NSS_JPAKE_ROUND2_SHA256: hashType = HASH_AlgSHA256; goto jpake2;
+    case CKM_NSS_JPAKE_ROUND2_SHA384: hashType = HASH_AlgSHA384; goto jpake2;
+    case CKM_NSS_JPAKE_ROUND2_SHA512: hashType = HASH_AlgSHA512; goto jpake2;
+jpake2:
+        if (pMechanism->pParameter == NULL ||
+            pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound2Params))
+            crv = CKR_MECHANISM_PARAM_INVALID;
+        if (crv == CKR_OK && sftk_isTrue(key, CKA_TOKEN))
+            crv = CKR_TEMPLATE_INCONSISTENT;
+	if (crv == CKR_OK)
+            crv = sftk_DeriveSensitiveCheck(sourceKey, key);
+	if (crv == CKR_OK)
+            crv = jpake_Round2(hashType,
+                        (CK_NSS_JPAKERound2Params *) pMechanism->pParameter,
+                        sourceKey, key);
+        break;
+
+    case CKM_NSS_JPAKE_FINAL_SHA1: hashType = HASH_AlgSHA1; goto jpakeFinal;
+    case CKM_NSS_JPAKE_FINAL_SHA256: hashType = HASH_AlgSHA256; goto jpakeFinal;
+    case CKM_NSS_JPAKE_FINAL_SHA384: hashType = HASH_AlgSHA384; goto jpakeFinal;
+    case CKM_NSS_JPAKE_FINAL_SHA512: hashType = HASH_AlgSHA512; goto jpakeFinal;
+jpakeFinal:
+        if (pMechanism->pParameter == NULL ||
+            pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKEFinalParams))
+            crv = CKR_MECHANISM_PARAM_INVALID;
+        /* We purposely do not do the derive sensitivity check; we want to be
+           able to derive non-sensitive keys while allowing the ROUND1 and 
+           ROUND2 keys to be sensitive (which they always are, since they are
+           in the CKO_PRIVATE_KEY class). The caller must include CKA_SENSITIVE
+           in the template in order for the resultant keyblock key to be
+           sensitive.
+         */
+        if (crv == CKR_OK)
+            crv = jpake_Final(hashType,
+                        (CK_NSS_JPAKEFinalParams *) pMechanism->pParameter,
+                        sourceKey, key);
+        break;
+
+    default:
+	crv = CKR_MECHANISM_INVALID;
+    }
+    if (att) {
+        sftk_FreeAttribute(att);
+    }
+    sftk_FreeObject(sourceKey);
+    if (crv != CKR_OK) { 
+	if (key) sftk_FreeObject(key);
+	return crv;
+    }
+
+    /* link the key object into the list */
+    if (key) {
+	SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key);
+	PORT_Assert(sessKey);
+	/* get the session */
+	sessKey->wasDerived = PR_TRUE;
+	session = sftk_SessionFromHandle(hSession);
+	if (session == NULL) {
+	    sftk_FreeObject(key);
+	    return CKR_HOST_MEMORY;
+	}
+
+	crv = sftk_handleObject(key,session);
+	sftk_FreeSession(session);
+	*phKey = key->handle;
+	sftk_FreeObject(key);
+    }
+    return crv;
+}
+
+
+/* NSC_GetFunctionStatus obtains an updated status of a function running 
+ * in parallel with an application. */
+CK_RV NSC_GetFunctionStatus(CK_SESSION_HANDLE hSession)
+{
+    CHECK_FORK();
+
+    return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+/* NSC_CancelFunction cancels a function running in parallel */
+CK_RV NSC_CancelFunction(CK_SESSION_HANDLE hSession)
+{
+    CHECK_FORK();
+
+    return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+/* NSC_GetOperationState saves the state of the cryptographic 
+ *operation in a session.
+ * NOTE: This code only works for digest functions for now. eventually need
+ * to add full flatten/resurect to our state stuff so that all types of state
+ * can be saved */
+CK_RV NSC_GetOperationState(CK_SESSION_HANDLE hSession, 
+	CK_BYTE_PTR  pOperationState, CK_ULONG_PTR pulOperationStateLen)
+{
+    SFTKSessionContext *context;
+    SFTKSession *session;
+    CK_RV crv;
+    CK_ULONG pOSLen = *pulOperationStateLen;
+
+    CHECK_FORK();
+
+    /* make sure we're legal */
+    crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session);
+    if (crv != CKR_OK) return crv;
+
+    *pulOperationStateLen = context->cipherInfoLen + sizeof(CK_MECHANISM_TYPE)
+				+ sizeof(SFTKContextType);
+    if (pOperationState == NULL) {
+        sftk_FreeSession(session);
+	return CKR_OK;
+    } else {
+	if (pOSLen < *pulOperationStateLen) {
+	    return CKR_BUFFER_TOO_SMALL;
+	}
+    }
+    PORT_Memcpy(pOperationState,&context->type,sizeof(SFTKContextType));
+    pOperationState += sizeof(SFTKContextType);
+    PORT_Memcpy(pOperationState,&context->currentMech,
+						sizeof(CK_MECHANISM_TYPE));
+    pOperationState += sizeof(CK_MECHANISM_TYPE);
+    PORT_Memcpy(pOperationState,context->cipherInfo,context->cipherInfoLen);
+    sftk_FreeSession(session);
+    return CKR_OK;
+}
+
+
+#define sftk_Decrement(stateSize,len) \
+	stateSize = ((stateSize) > (CK_ULONG)(len)) ? \
+				((stateSize) - (CK_ULONG)(len)) : 0;
+
+/* NSC_SetOperationState restores the state of the cryptographic 
+ * operation in a session. This is coded like it can restore lots of
+ * states, but it only works for truly flat cipher structures. */
+CK_RV NSC_SetOperationState(CK_SESSION_HANDLE hSession, 
+	CK_BYTE_PTR  pOperationState, CK_ULONG  ulOperationStateLen,
+        CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey)
+{
+    SFTKSessionContext *context;
+    SFTKSession *session;
+    SFTKContextType type;
+    CK_MECHANISM mech;
+    CK_RV crv = CKR_OK;
+
+    CHECK_FORK();
+
+    while (ulOperationStateLen != 0) {
+	/* get what type of state we're dealing with... */
+	PORT_Memcpy(&type,pOperationState, sizeof(SFTKContextType));
+
+	/* fix up session contexts based on type */
+	session = sftk_SessionFromHandle(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+	context = sftk_ReturnContextByType(session, type);
+	sftk_SetContextByType(session, type, NULL);
+	if (context) { 
+	     sftk_FreeContext(context);
+	}
+	pOperationState += sizeof(SFTKContextType);
+	sftk_Decrement(ulOperationStateLen,sizeof(SFTKContextType));
+
+
+	/* get the mechanism structure */
+	PORT_Memcpy(&mech.mechanism,pOperationState,sizeof(CK_MECHANISM_TYPE));
+	pOperationState += sizeof(CK_MECHANISM_TYPE);
+	sftk_Decrement(ulOperationStateLen, sizeof(CK_MECHANISM_TYPE));
+	/* should be filled in... but not necessary for hash */
+	mech.pParameter = NULL;
+	mech.ulParameterLen = 0;
+	switch (type) {
+	case SFTK_HASH:
+	    crv = NSC_DigestInit(hSession,&mech);
+	    if (crv != CKR_OK) break;
+	    crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, 
+								NULL);
+	    if (crv != CKR_OK) break;
+	    PORT_Memcpy(context->cipherInfo,pOperationState,
+						context->cipherInfoLen);
+	    pOperationState += context->cipherInfoLen;
+	    sftk_Decrement(ulOperationStateLen,context->cipherInfoLen);
+	    break;
+	default:
+	    /* do sign/encrypt/decrypt later */
+	    crv = CKR_SAVED_STATE_INVALID;
+         }
+         sftk_FreeSession(session);
+	 if (crv != CKR_OK) break;
+    }
+    return crv;
+}
+
+/* Dual-function cryptographic operations */
+
+/* NSC_DigestEncryptUpdate continues a multiple-part digesting and encryption 
+ * operation. */
+CK_RV NSC_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR  pPart,
+    CK_ULONG  ulPartLen, CK_BYTE_PTR  pEncryptedPart,
+					 CK_ULONG_PTR pulEncryptedPartLen)
+{
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart,	
+						      pulEncryptedPartLen);
+    if (crv != CKR_OK) return crv;
+    crv = NSC_DigestUpdate(hSession,pPart,ulPartLen);
+
+    return crv;
+}
+
+
+/* NSC_DecryptDigestUpdate continues a multiple-part decryption and 
+ * digesting operation. */
+CK_RV NSC_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
+    CK_BYTE_PTR  pEncryptedPart, CK_ULONG  ulEncryptedPartLen,
+    				CK_BYTE_PTR  pPart, CK_ULONG_PTR pulPartLen)
+{
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    crv = NSC_DecryptUpdate(hSession,pEncryptedPart, ulEncryptedPartLen, 
+							pPart,	pulPartLen);
+    if (crv != CKR_OK) return crv;
+    crv = NSC_DigestUpdate(hSession,pPart,*pulPartLen);
+
+    return crv;
+}
+
+
+/* NSC_SignEncryptUpdate continues a multiple-part signing and 
+ * encryption operation. */
+CK_RV NSC_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR  pPart,
+	 CK_ULONG  ulPartLen, CK_BYTE_PTR  pEncryptedPart,
+					 CK_ULONG_PTR pulEncryptedPartLen)
+{
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart,	
+						      pulEncryptedPartLen);
+    if (crv != CKR_OK) return crv;
+    crv = NSC_SignUpdate(hSession,pPart,ulPartLen);
+
+    return crv;
+}
+
+
+/* NSC_DecryptVerifyUpdate continues a multiple-part decryption 
+ * and verify operation. */
+CK_RV NSC_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, 
+	CK_BYTE_PTR  pEncryptedData, CK_ULONG  ulEncryptedDataLen, 
+				CK_BYTE_PTR  pData, CK_ULONG_PTR pulDataLen)
+{
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    crv = NSC_DecryptUpdate(hSession,pEncryptedData, ulEncryptedDataLen, 
+							pData,	pulDataLen);
+    if (crv != CKR_OK) return crv;
+    crv = NSC_VerifyUpdate(hSession, pData, *pulDataLen);
+
+    return crv;
+}
+
+/* NSC_DigestKey continues a multi-part message-digesting operation,
+ * by digesting the value of a secret key as part of the data already digested.
+ */
+CK_RV NSC_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) 
+{
+    SFTKSession *session = NULL;
+    SFTKObject *key = NULL;
+    SFTKAttribute *att;
+    CK_RV crv;
+
+    CHECK_FORK();
+
+    session = sftk_SessionFromHandle(hSession);
+    if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+    key = sftk_ObjectFromHandle(hKey,session);
+    sftk_FreeSession(session);
+    if (key == NULL)  return CKR_KEY_HANDLE_INVALID;
+
+    /* PUT ANY DIGEST KEY RESTRICTION CHECKS HERE */
+
+    /* make sure it's a valid  key for this operation */
+    if (key->objclass != CKO_SECRET_KEY) {
+	sftk_FreeObject(key);
+	return CKR_KEY_TYPE_INCONSISTENT;
+    }
+    /* get the key value */
+    att = sftk_FindAttribute(key,CKA_VALUE);
+    sftk_FreeObject(key);
+    if (!att) {
+        return CKR_KEY_HANDLE_INVALID;        
+    }
+    crv = NSC_DigestUpdate(hSession,(CK_BYTE_PTR)att->attrib.pValue,
+			   att->attrib.ulValueLen);
+    sftk_FreeAttribute(att);
+    return crv;
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)