/* 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/. */

/*
 * wrap.c
 *
 * This file contains the routines that actually implement the cryptoki
 * API, using the internal APIs of the NSS Cryptoki Framework.  There is
 * one routine here for every cryptoki routine.  For linking reasons
 * the actual entry points passed back with C_GetFunctionList have to
 * exist in one of the Module's source files; however, those are merely
 * simple wrappers that call these routines.  The intelligence of the
 * implementations is here.
 */

#ifndef CK_T
#include "ck.h"
#endif /* CK_T */

/*
 * NSSCKFWC_Initialize
 * NSSCKFWC_Finalize
 * NSSCKFWC_GetInfo
 * -- NSSCKFWC_GetFunctionList -- see the API insert file
 * NSSCKFWC_GetSlotList
 * NSSCKFWC_GetSlotInfo
 * NSSCKFWC_GetTokenInfo
 * NSSCKFWC_WaitForSlotEvent
 * NSSCKFWC_GetMechanismList
 * NSSCKFWC_GetMechanismInfo
 * NSSCKFWC_InitToken
 * NSSCKFWC_InitPIN
 * NSSCKFWC_SetPIN
 * NSSCKFWC_OpenSession
 * NSSCKFWC_CloseSession
 * NSSCKFWC_CloseAllSessions
 * NSSCKFWC_GetSessionInfo
 * NSSCKFWC_GetOperationState
 * NSSCKFWC_SetOperationState
 * NSSCKFWC_Login
 * NSSCKFWC_Logout
 * NSSCKFWC_CreateObject
 * NSSCKFWC_CopyObject
 * NSSCKFWC_DestroyObject
 * NSSCKFWC_GetObjectSize
 * NSSCKFWC_GetAttributeValue
 * NSSCKFWC_SetAttributeValue
 * NSSCKFWC_FindObjectsInit
 * NSSCKFWC_FindObjects
 * NSSCKFWC_FindObjectsFinal
 * NSSCKFWC_EncryptInit
 * NSSCKFWC_Encrypt
 * NSSCKFWC_EncryptUpdate
 * NSSCKFWC_EncryptFinal
 * NSSCKFWC_DecryptInit
 * NSSCKFWC_Decrypt
 * NSSCKFWC_DecryptUpdate
 * NSSCKFWC_DecryptFinal
 * NSSCKFWC_DigestInit
 * NSSCKFWC_Digest
 * NSSCKFWC_DigestUpdate
 * NSSCKFWC_DigestKey
 * NSSCKFWC_DigestFinal
 * NSSCKFWC_SignInit
 * NSSCKFWC_Sign
 * NSSCKFWC_SignUpdate
 * NSSCKFWC_SignFinal
 * NSSCKFWC_SignRecoverInit
 * NSSCKFWC_SignRecover
 * NSSCKFWC_VerifyInit
 * NSSCKFWC_Verify
 * NSSCKFWC_VerifyUpdate
 * NSSCKFWC_VerifyFinal
 * NSSCKFWC_VerifyRecoverInit
 * NSSCKFWC_VerifyRecover
 * NSSCKFWC_DigestEncryptUpdate
 * NSSCKFWC_DecryptDigestUpdate
 * NSSCKFWC_SignEncryptUpdate
 * NSSCKFWC_DecryptVerifyUpdate
 * NSSCKFWC_GenerateKey
 * NSSCKFWC_GenerateKeyPair
 * NSSCKFWC_WrapKey
 * NSSCKFWC_UnwrapKey
 * NSSCKFWC_DeriveKey
 * NSSCKFWC_SeedRandom
 * NSSCKFWC_GenerateRandom
 * NSSCKFWC_GetFunctionStatus
 * NSSCKFWC_CancelFunction
 */

/* figure out out locking semantics */
static CK_RV
nssCKFW_GetThreadSafeState(CK_C_INITIALIZE_ARGS_PTR pInitArgs,
                           CryptokiLockingState *pLocking_state)
{
    int functionCount = 0;

    /* parsed according to (PKCS #11 Section 11.4) */
    /* no args, the degenerate version of case 1 */
    if (!pInitArgs) {
        *pLocking_state = SingleThreaded;
        return CKR_OK;
    }

    /* CKF_OS_LOCKING_OK set, Cases 2 and 4 */
    if (pInitArgs->flags & CKF_OS_LOCKING_OK) {
        *pLocking_state = MultiThreaded;
        return CKR_OK;
    }
    if ((CK_CREATEMUTEX)NULL != pInitArgs->CreateMutex)
        functionCount++;
    if ((CK_DESTROYMUTEX)NULL != pInitArgs->DestroyMutex)
        functionCount++;
    if ((CK_LOCKMUTEX)NULL != pInitArgs->LockMutex)
        functionCount++;
    if ((CK_UNLOCKMUTEX)NULL != pInitArgs->UnlockMutex)
        functionCount++;

    /* CKF_OS_LOCKING_OK is not set, and not functions supplied,
     * explicit case 1 */
    if (0 == functionCount) {
        *pLocking_state = SingleThreaded;
        return CKR_OK;
    }

    /* OS_LOCKING_OK is not set and functions have been supplied. Since
     * ckfw uses nssbase library which explicitly calls NSPR, and since
     * there is no way to reliably override these explicit calls to NSPR,
     * therefore we can't support applications which have their own threading
     * module.  Return CKR_CANT_LOCK if they supplied the correct number of
     * arguments, or CKR_ARGUMENTS_BAD if they did not in either case we will
     * fail the initialize */
    return (4 == functionCount) ? CKR_CANT_LOCK : CKR_ARGUMENTS_BAD;
}

static PRInt32 liveInstances;

/*
 * NSSCKFWC_Initialize
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Initialize(
    NSSCKFWInstance **pFwInstance,
    NSSCKMDInstance *mdInstance,
    CK_VOID_PTR pInitArgs)
{
    CK_RV error = CKR_OK;
    CryptokiLockingState locking_state;

    if ((NSSCKFWInstance **)NULL == pFwInstance) {
        error = CKR_GENERAL_ERROR;
        goto loser;
    }

    if (*pFwInstance) {
        error = CKR_CRYPTOKI_ALREADY_INITIALIZED;
        goto loser;
    }

    if (!mdInstance) {
        error = CKR_GENERAL_ERROR;
        goto loser;
    }

    error = nssCKFW_GetThreadSafeState(pInitArgs, &locking_state);
    if (CKR_OK != error) {
        goto loser;
    }

    *pFwInstance = nssCKFWInstance_Create(pInitArgs, locking_state, mdInstance, &error);
    if (!*pFwInstance) {
        goto loser;
    }
    PR_ATOMIC_INCREMENT(&liveInstances);
    return CKR_OK;

loser:
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CANT_LOCK:
        case CKR_CRYPTOKI_ALREADY_INITIALIZED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_NEED_TO_CREATE_THREADS:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_Finalize
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Finalize(
    NSSCKFWInstance **pFwInstance)
{
    CK_RV error = CKR_OK;

    if ((NSSCKFWInstance **)NULL == pFwInstance) {
        error = CKR_GENERAL_ERROR;
        goto loser;
    }

    if (!*pFwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    error = nssCKFWInstance_Destroy(*pFwInstance);

    /* In any case */
    *pFwInstance = (NSSCKFWInstance *)NULL;

loser:
    switch (error) {
        PRInt32 remainingInstances;
        case CKR_OK:
            remainingInstances = PR_ATOMIC_DECREMENT(&liveInstances);
            if (!remainingInstances) {
                nssArena_Shutdown();
            }
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
            break;
        default:
            error = CKR_GENERAL_ERROR;
            break;
    }

    /*
     * A thread's error stack is automatically destroyed when the thread
     * terminates or, for the primordial thread, by PR_Cleanup.  On
     * Windows with MinGW, the thread private data destructor PR_Free
     * registered by this module is actually a thunk for PR_Free defined
     * in this module.  When the thread that unloads this module terminates
     * or calls PR_Cleanup, the thunk for PR_Free is already gone with the
     * module.  Therefore we need to destroy the error stack before the
     * module is unloaded.
     */
    nss_DestroyErrorStack();
    return error;
}

/*
 * NSSCKFWC_GetInfo
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetInfo(
    NSSCKFWInstance *fwInstance,
    CK_INFO_PTR pInfo)
{
    CK_RV error = CKR_OK;

    if ((CK_INFO_PTR)CK_NULL_PTR == pInfo) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here means a caller error
     */
    (void)nsslibc_memset(pInfo, 0, sizeof(CK_INFO));

    pInfo->cryptokiVersion = nssCKFWInstance_GetCryptokiVersion(fwInstance);

    error = nssCKFWInstance_GetManufacturerID(fwInstance, pInfo->manufacturerID);
    if (CKR_OK != error) {
        goto loser;
    }

    pInfo->flags = nssCKFWInstance_GetFlags(fwInstance);

    error = nssCKFWInstance_GetLibraryDescription(fwInstance, pInfo->libraryDescription);
    if (CKR_OK != error) {
        goto loser;
    }

    pInfo->libraryVersion = nssCKFWInstance_GetLibraryVersion(fwInstance);

    return CKR_OK;

loser:
    switch (error) {
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
            break;
        default:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * C_GetFunctionList is implemented entirely in the Module's file which
 * includes the Framework API insert file.  It requires no "actual"
 * NSSCKFW routine.
 */

/*
 * NSSCKFWC_GetSlotList
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetSlotList(
    NSSCKFWInstance *fwInstance,
    CK_BBOOL tokenPresent,
    CK_SLOT_ID_PTR pSlotList,
    CK_ULONG_PTR pulCount)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    switch (tokenPresent) {
        case CK_TRUE:
        case CK_FALSE:
            break;
        default:
            error = CKR_ARGUMENTS_BAD;
            goto loser;
    }

    if ((CK_ULONG_PTR)CK_NULL_PTR == pulCount) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((CK_SLOT_ID_PTR)CK_NULL_PTR == pSlotList) {
        *pulCount = nSlots;
        return CKR_OK;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(pSlotList, 0, *pulCount * sizeof(CK_SLOT_ID));

    if (*pulCount < nSlots) {
        *pulCount = nSlots;
        error = CKR_BUFFER_TOO_SMALL;
        goto loser;
    } else {
        CK_ULONG i;
        *pulCount = nSlots;

        /*
         * Our secret "mapping": CK_SLOT_IDs are integers [1,N], and we
         * just index one when we need it.
         */

        for (i = 0; i < nSlots; i++) {
            pSlotList[i] = i + 1;
        }

        return CKR_OK;
    }

loser:
    switch (error) {
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetSlotInfo
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetSlotInfo(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID,
    CK_SLOT_INFO_PTR pInfo)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    if ((CK_SLOT_INFO_PTR)CK_NULL_PTR == pInfo) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(pInfo, 0, sizeof(CK_SLOT_INFO));

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    error = nssCKFWSlot_GetSlotDescription(fwSlot, pInfo->slotDescription);
    if (CKR_OK != error) {
        goto loser;
    }

    error = nssCKFWSlot_GetManufacturerID(fwSlot, pInfo->manufacturerID);
    if (CKR_OK != error) {
        goto loser;
    }

    if (nssCKFWSlot_GetTokenPresent(fwSlot)) {
        pInfo->flags |= CKF_TOKEN_PRESENT;
    }

    if (nssCKFWSlot_GetRemovableDevice(fwSlot)) {
        pInfo->flags |= CKF_REMOVABLE_DEVICE;
    }

    if (nssCKFWSlot_GetHardwareSlot(fwSlot)) {
        pInfo->flags |= CKF_HW_SLOT;
    }

    pInfo->hardwareVersion = nssCKFWSlot_GetHardwareVersion(fwSlot);
    pInfo->firmwareVersion = nssCKFWSlot_GetFirmwareVersion(fwSlot);

    return CKR_OK;

loser:
    switch (error) {
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SLOT_ID_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
    }

    return error;
}

/*
 * NSSCKFWC_GetTokenInfo
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetTokenInfo(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID,
    CK_TOKEN_INFO_PTR pInfo)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken = (NSSCKFWToken *)NULL;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    if ((CK_TOKEN_INFO_PTR)CK_NULL_PTR == pInfo) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(pInfo, 0, sizeof(CK_TOKEN_INFO));

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    error = nssCKFWToken_GetLabel(fwToken, pInfo->label);
    if (CKR_OK != error) {
        goto loser;
    }

    error = nssCKFWToken_GetManufacturerID(fwToken, pInfo->manufacturerID);
    if (CKR_OK != error) {
        goto loser;
    }

    error = nssCKFWToken_GetModel(fwToken, pInfo->model);
    if (CKR_OK != error) {
        goto loser;
    }

    error = nssCKFWToken_GetSerialNumber(fwToken, pInfo->serialNumber);
    if (CKR_OK != error) {
        goto loser;
    }

    if (nssCKFWToken_GetHasRNG(fwToken)) {
        pInfo->flags |= CKF_RNG;
    }

    if (nssCKFWToken_GetIsWriteProtected(fwToken)) {
        pInfo->flags |= CKF_WRITE_PROTECTED;
    }

    if (nssCKFWToken_GetLoginRequired(fwToken)) {
        pInfo->flags |= CKF_LOGIN_REQUIRED;
    }

    if (nssCKFWToken_GetUserPinInitialized(fwToken)) {
        pInfo->flags |= CKF_USER_PIN_INITIALIZED;
    }

    if (nssCKFWToken_GetRestoreKeyNotNeeded(fwToken)) {
        pInfo->flags |= CKF_RESTORE_KEY_NOT_NEEDED;
    }

    if (nssCKFWToken_GetHasClockOnToken(fwToken)) {
        pInfo->flags |= CKF_CLOCK_ON_TOKEN;
    }

    if (nssCKFWToken_GetHasProtectedAuthenticationPath(fwToken)) {
        pInfo->flags |= CKF_PROTECTED_AUTHENTICATION_PATH;
    }

    if (nssCKFWToken_GetSupportsDualCryptoOperations(fwToken)) {
        pInfo->flags |= CKF_DUAL_CRYPTO_OPERATIONS;
    }

    pInfo->ulMaxSessionCount = nssCKFWToken_GetMaxSessionCount(fwToken);
    pInfo->ulSessionCount = nssCKFWToken_GetSessionCount(fwToken);
    pInfo->ulMaxRwSessionCount = nssCKFWToken_GetMaxRwSessionCount(fwToken);
    pInfo->ulRwSessionCount = nssCKFWToken_GetRwSessionCount(fwToken);
    pInfo->ulMaxPinLen = nssCKFWToken_GetMaxPinLen(fwToken);
    pInfo->ulMinPinLen = nssCKFWToken_GetMinPinLen(fwToken);
    pInfo->ulTotalPublicMemory = nssCKFWToken_GetTotalPublicMemory(fwToken);
    pInfo->ulFreePublicMemory = nssCKFWToken_GetFreePublicMemory(fwToken);
    pInfo->ulTotalPrivateMemory = nssCKFWToken_GetTotalPrivateMemory(fwToken);
    pInfo->ulFreePrivateMemory = nssCKFWToken_GetFreePrivateMemory(fwToken);
    pInfo->hardwareVersion = nssCKFWToken_GetHardwareVersion(fwToken);
    pInfo->firmwareVersion = nssCKFWToken_GetFirmwareVersion(fwToken);

    error = nssCKFWToken_GetUTCTime(fwToken, pInfo->utcTime);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_DEVICE_REMOVED:
        case CKR_TOKEN_NOT_PRESENT:
            if (fwToken)
                nssCKFWToken_Destroy(fwToken);
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SLOT_ID_INVALID:
        case CKR_TOKEN_NOT_RECOGNIZED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_WaitForSlotEvent
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_WaitForSlotEvent(
    NSSCKFWInstance *fwInstance,
    CK_FLAGS flags,
    CK_SLOT_ID_PTR pSlot,
    CK_VOID_PTR pReserved)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    CK_BBOOL block;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    CK_ULONG i;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    if (flags & ~CKF_DONT_BLOCK) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    block = (flags & CKF_DONT_BLOCK) ? CK_TRUE : CK_FALSE;

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((CK_SLOT_ID_PTR)CK_NULL_PTR == pSlot) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    if ((CK_VOID_PTR)CK_NULL_PTR != pReserved) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = nssCKFWInstance_WaitForSlotEvent(fwInstance, block, &error);
    if (!fwSlot) {
        goto loser;
    }

    for (i = 0; i < nSlots; i++) {
        if (fwSlot == slots[i]) {
            *pSlot = (CK_SLOT_ID)(CK_ULONG)(i + 1);
            return CKR_OK;
        }
    }

    error = CKR_GENERAL_ERROR; /* returned something not in the slot list */

loser:
    switch (error) {
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_NO_EVENT:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetMechanismList
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetMechanismList(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID,
    CK_MECHANISM_TYPE_PTR pMechanismList,
    CK_ULONG_PTR pulCount)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken = (NSSCKFWToken *)NULL;
    CK_ULONG count;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    if ((CK_ULONG_PTR)CK_NULL_PTR == pulCount) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    count = nssCKFWToken_GetMechanismCount(fwToken);

    if ((CK_MECHANISM_TYPE_PTR)CK_NULL_PTR == pMechanismList) {
        *pulCount = count;
        return CKR_OK;
    }

    if (*pulCount < count) {
        *pulCount = count;
        error = CKR_BUFFER_TOO_SMALL;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(pMechanismList, 0, *pulCount * sizeof(CK_MECHANISM_TYPE));

    *pulCount = count;

    if (0 != count) {
        error = nssCKFWToken_GetMechanismTypes(fwToken, pMechanismList);
    } else {
        error = CKR_OK;
    }

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    switch (error) {
        case CKR_DEVICE_REMOVED:
        case CKR_TOKEN_NOT_PRESENT:
            if (fwToken)
                nssCKFWToken_Destroy(fwToken);
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SLOT_ID_INVALID:
        case CKR_TOKEN_NOT_RECOGNIZED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetMechanismInfo
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetMechanismInfo(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID,
    CK_MECHANISM_TYPE type,
    CK_MECHANISM_INFO_PTR pInfo)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken = (NSSCKFWToken *)NULL;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    if ((CK_MECHANISM_INFO_PTR)CK_NULL_PTR == pInfo) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(pInfo, 0, sizeof(CK_MECHANISM_INFO));

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, type, &error);
    if (!fwMechanism) {
        goto loser;
    }

    pInfo->ulMinKeySize = nssCKFWMechanism_GetMinKeySize(fwMechanism, &error);
    pInfo->ulMaxKeySize = nssCKFWMechanism_GetMaxKeySize(fwMechanism, &error);

    if (nssCKFWMechanism_GetInHardware(fwMechanism, &error)) {
        pInfo->flags |= CKF_HW;
    }
    if (nssCKFWMechanism_GetCanEncrypt(fwMechanism, &error)) {
        pInfo->flags |= CKF_ENCRYPT;
    }
    if (nssCKFWMechanism_GetCanDecrypt(fwMechanism, &error)) {
        pInfo->flags |= CKF_DECRYPT;
    }
    if (nssCKFWMechanism_GetCanDigest(fwMechanism, &error)) {
        pInfo->flags |= CKF_DIGEST;
    }
    if (nssCKFWMechanism_GetCanSign(fwMechanism, &error)) {
        pInfo->flags |= CKF_SIGN;
    }
    if (nssCKFWMechanism_GetCanSignRecover(fwMechanism, &error)) {
        pInfo->flags |= CKF_SIGN_RECOVER;
    }
    if (nssCKFWMechanism_GetCanVerify(fwMechanism, &error)) {
        pInfo->flags |= CKF_VERIFY;
    }
    if (nssCKFWMechanism_GetCanVerifyRecover(fwMechanism, &error)) {
        pInfo->flags |= CKF_VERIFY_RECOVER;
    }
    if (nssCKFWMechanism_GetCanGenerate(fwMechanism, &error)) {
        pInfo->flags |= CKF_GENERATE;
    }
    if (nssCKFWMechanism_GetCanGenerateKeyPair(fwMechanism, &error)) {
        pInfo->flags |= CKF_GENERATE_KEY_PAIR;
    }
    if (nssCKFWMechanism_GetCanWrap(fwMechanism, &error)) {
        pInfo->flags |= CKF_WRAP;
    }
    if (nssCKFWMechanism_GetCanUnwrap(fwMechanism, &error)) {
        pInfo->flags |= CKF_UNWRAP;
    }
    if (nssCKFWMechanism_GetCanDerive(fwMechanism, &error)) {
        pInfo->flags |= CKF_DERIVE;
    }
    nssCKFWMechanism_Destroy(fwMechanism);

    return error;

loser:
    switch (error) {
        case CKR_DEVICE_REMOVED:
        case CKR_TOKEN_NOT_PRESENT:
            if (fwToken)
                nssCKFWToken_Destroy(fwToken);
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_MECHANISM_INVALID:
        case CKR_SLOT_ID_INVALID:
        case CKR_TOKEN_NOT_RECOGNIZED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_InitToken
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_InitToken(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID,
    CK_CHAR_PTR pPin,
    CK_ULONG ulPinLen,
    CK_CHAR_PTR pLabel)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken = (NSSCKFWToken *)NULL;
    NSSItem pin;
    NSSUTF8 *label;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    pin.size = (PRUint32)ulPinLen;
    pin.data = (void *)pPin;
    label = (NSSUTF8 *)pLabel; /* identity conversion */

    error = nssCKFWToken_InitToken(fwToken, &pin, label);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_DEVICE_REMOVED:
        case CKR_TOKEN_NOT_PRESENT:
            if (fwToken)
                nssCKFWToken_Destroy(fwToken);
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_PIN_INCORRECT:
        case CKR_PIN_LOCKED:
        case CKR_SESSION_EXISTS:
        case CKR_SLOT_ID_INVALID:
        case CKR_TOKEN_NOT_RECOGNIZED:
        case CKR_TOKEN_WRITE_PROTECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_InitPIN
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_InitPIN(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_CHAR_PTR pPin,
    CK_ULONG ulPinLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSItem pin, *arg;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_CHAR_PTR)CK_NULL_PTR == pPin) {
        arg = (NSSItem *)NULL;
    } else {
        arg = &pin;
        pin.size = (PRUint32)ulPinLen;
        pin.data = (void *)pPin;
    }

    error = nssCKFWSession_InitPIN(fwSession, arg);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_PIN_INVALID:
        case CKR_PIN_LEN_RANGE:
        case CKR_SESSION_READ_ONLY:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_SetPIN
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SetPIN(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_CHAR_PTR pOldPin,
    CK_ULONG ulOldLen,
    CK_CHAR_PTR pNewPin,
    CK_ULONG ulNewLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSItem oldPin, newPin, *oldArg, *newArg;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_CHAR_PTR)CK_NULL_PTR == pOldPin) {
        oldArg = (NSSItem *)NULL;
    } else {
        oldArg = &oldPin;
        oldPin.size = (PRUint32)ulOldLen;
        oldPin.data = (void *)pOldPin;
    }

    if ((CK_CHAR_PTR)CK_NULL_PTR == pNewPin) {
        newArg = (NSSItem *)NULL;
    } else {
        newArg = &newPin;
        newPin.size = (PRUint32)ulNewLen;
        newPin.data = (void *)pNewPin;
    }

    error = nssCKFWSession_SetPIN(fwSession, oldArg, newArg);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_PIN_INCORRECT:
        case CKR_PIN_INVALID:
        case CKR_PIN_LEN_RANGE:
        case CKR_PIN_LOCKED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TOKEN_WRITE_PROTECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_OpenSession
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_OpenSession(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID,
    CK_FLAGS flags,
    CK_VOID_PTR pApplication,
    CK_NOTIFY Notify,
    CK_SESSION_HANDLE_PTR phSession)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken = (NSSCKFWToken *)NULL;
    NSSCKFWSession *fwSession;
    CK_BBOOL rw;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    if (flags & CKF_RW_SESSION) {
        rw = CK_TRUE;
    } else {
        rw = CK_FALSE;
    }

    if (flags & CKF_SERIAL_SESSION) {
        ;
    } else {
        error = CKR_SESSION_PARALLEL_NOT_SUPPORTED;
        goto loser;
    }

    if (flags & ~(CKF_RW_SESSION | CKF_SERIAL_SESSION)) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    if ((CK_SESSION_HANDLE_PTR)CK_NULL_PTR == phSession) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    *phSession = (CK_SESSION_HANDLE)0;

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwSession = nssCKFWToken_OpenSession(fwToken, rw, pApplication,
                                         Notify, &error);
    if (!fwSession) {
        goto loser;
    }

    *phSession = nssCKFWInstance_CreateSessionHandle(fwInstance,
                                                     fwSession, &error);
    if ((CK_SESSION_HANDLE)0 == *phSession) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SESSION_COUNT:
        case CKR_SESSION_EXISTS:
        case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
        case CKR_SESSION_READ_WRITE_SO_EXISTS:
        case CKR_SLOT_ID_INVALID:
        case CKR_TOKEN_NOT_PRESENT:
        case CKR_TOKEN_NOT_RECOGNIZED:
        case CKR_TOKEN_WRITE_PROTECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_CloseSession
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_CloseSession(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    nssCKFWInstance_DestroySessionHandle(fwInstance, hSession);
    error = nssCKFWSession_Destroy(fwSession, CK_TRUE);

    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_CloseAllSessions
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_CloseAllSessions(
    NSSCKFWInstance *fwInstance,
    CK_SLOT_ID slotID)
{
    CK_RV error = CKR_OK;
    CK_ULONG nSlots;
    NSSCKFWSlot **slots;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken = (NSSCKFWToken *)NULL;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    nSlots = nssCKFWInstance_GetNSlots(fwInstance, &error);
    if ((CK_ULONG)0 == nSlots) {
        goto loser;
    }

    if ((slotID < 1) || (slotID > nSlots)) {
        error = CKR_SLOT_ID_INVALID;
        goto loser;
    }

    slots = nssCKFWInstance_GetSlots(fwInstance, &error);
    if ((NSSCKFWSlot **)NULL == slots) {
        goto loser;
    }

    fwSlot = slots[slotID - 1];

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    error = nssCKFWToken_CloseAllSessions(fwToken);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SLOT_ID_INVALID:
        case CKR_TOKEN_NOT_PRESENT:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetSessionInfo
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetSessionInfo(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_SESSION_INFO_PTR pInfo)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWSlot *fwSlot;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_SESSION_INFO_PTR)CK_NULL_PTR == pInfo) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(pInfo, 0, sizeof(CK_SESSION_INFO));

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR;
        goto loser;
    }

    pInfo->slotID = nssCKFWSlot_GetSlotID(fwSlot);
    pInfo->state = nssCKFWSession_GetSessionState(fwSession);

    if (CK_TRUE == nssCKFWSession_IsRWSession(fwSession)) {
        pInfo->flags |= CKF_RW_SESSION;
    }

    pInfo->flags |= CKF_SERIAL_SESSION; /* Always true */

    pInfo->ulDeviceError = nssCKFWSession_GetDeviceError(fwSession);

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetOperationState
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetOperationState(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pOperationState,
    CK_ULONG_PTR pulOperationStateLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    CK_ULONG len;
    NSSItem buf;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_ULONG_PTR)CK_NULL_PTR == pulOperationStateLen) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    len = nssCKFWSession_GetOperationStateLen(fwSession, &error);
    if (((CK_ULONG)0 == len) && (CKR_OK != error)) {
        goto loser;
    }

    if ((CK_BYTE_PTR)CK_NULL_PTR == pOperationState) {
        *pulOperationStateLen = len;
        return CKR_OK;
    }

    if (*pulOperationStateLen < len) {
        *pulOperationStateLen = len;
        error = CKR_BUFFER_TOO_SMALL;
        goto loser;
    }

    buf.size = (PRUint32)*pulOperationStateLen;
    buf.data = (void *)pOperationState;
    *pulOperationStateLen = len;
    error = nssCKFWSession_GetOperationState(fwSession, &buf);

    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_STATE_UNSAVEABLE:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_SetOperationState
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SetOperationState(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pOperationState,
    CK_ULONG ulOperationStateLen,
    CK_OBJECT_HANDLE hEncryptionKey,
    CK_OBJECT_HANDLE hAuthenticationKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *eKey;
    NSSCKFWObject *aKey;
    NSSItem state;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    if ((CK_BYTE_PTR)CK_NULL_PTR == pOperationState) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * We could loop through the buffer, to catch any purify errors
     * in a place with a "user error" note.
     */

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_OBJECT_HANDLE)0 == hEncryptionKey) {
        eKey = (NSSCKFWObject *)NULL;
    } else {
        eKey = nssCKFWInstance_ResolveObjectHandle(fwInstance, hEncryptionKey);
        if (!eKey) {
            error = CKR_KEY_HANDLE_INVALID;
            goto loser;
        }
    }

    if ((CK_OBJECT_HANDLE)0 == hAuthenticationKey) {
        aKey = (NSSCKFWObject *)NULL;
    } else {
        aKey = nssCKFWInstance_ResolveObjectHandle(fwInstance, hAuthenticationKey);
        if (!aKey) {
            error = CKR_KEY_HANDLE_INVALID;
            goto loser;
        }
    }

    state.data = pOperationState;
    state.size = ulOperationStateLen;

    error = nssCKFWSession_SetOperationState(fwSession, &state, eKey, aKey);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_CHANGED:
        case CKR_KEY_NEEDED:
        case CKR_KEY_NOT_NEEDED:
        case CKR_SAVED_STATE_INVALID:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_Login
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Login(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_USER_TYPE userType,
    CK_CHAR_PTR pPin,
    CK_ULONG ulPinLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSItem pin, *arg;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_CHAR_PTR)CK_NULL_PTR == pPin) {
        arg = (NSSItem *)NULL;
    } else {
        arg = &pin;
        pin.size = (PRUint32)ulPinLen;
        pin.data = (void *)pPin;
    }

    error = nssCKFWSession_Login(fwSession, userType, arg);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_PIN_EXPIRED:
        case CKR_PIN_INCORRECT:
        case CKR_PIN_LOCKED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY_EXISTS:
        case CKR_USER_ALREADY_LOGGED_IN:
        case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
        case CKR_USER_PIN_NOT_INITIALIZED:
        case CKR_USER_TOO_MANY_TYPES:
        case CKR_USER_TYPE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_Logout
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Logout(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Logout(fwSession);
    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_CreateObject
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_CreateObject(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulCount,
    CK_OBJECT_HANDLE_PTR phObject)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_OBJECT_HANDLE_PTR)CK_NULL_PTR == phObject) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    *phObject = (CK_OBJECT_HANDLE)0;

    fwObject = nssCKFWSession_CreateObject(fwSession, pTemplate,
                                           ulCount, &error);
    if (!fwObject) {
        goto loser;
    }

    *phObject = nssCKFWInstance_CreateObjectHandle(fwInstance, fwObject, &error);
    if ((CK_OBJECT_HANDLE)0 == *phObject) {
        nssCKFWObject_Destroy(fwObject);
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCOMPLETE:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_CopyObject
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_CopyObject(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hObject,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulCount,
    CK_OBJECT_HANDLE_PTR phNewObject)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWObject *fwNewObject;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_OBJECT_HANDLE_PTR)CK_NULL_PTR == phNewObject) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    *phNewObject = (CK_OBJECT_HANDLE)0;

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hObject);
    if (!fwObject) {
        error = CKR_OBJECT_HANDLE_INVALID;
        goto loser;
    }

    fwNewObject = nssCKFWSession_CopyObject(fwSession, fwObject,
                                            pTemplate, ulCount, &error);
    if (!fwNewObject) {
        goto loser;
    }

    *phNewObject = nssCKFWInstance_CreateObjectHandle(fwInstance,
                                                      fwNewObject, &error);
    if ((CK_OBJECT_HANDLE)0 == *phNewObject) {
        nssCKFWObject_Destroy(fwNewObject);
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OBJECT_HANDLE_INVALID:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_DestroyObject
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DestroyObject(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hObject)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hObject);
    if (!fwObject) {
        error = CKR_OBJECT_HANDLE_INVALID;
        goto loser;
    }

    nssCKFWInstance_DestroyObjectHandle(fwInstance, hObject);
    nssCKFWObject_Destroy(fwObject);

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OBJECT_HANDLE_INVALID:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TOKEN_WRITE_PROTECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetObjectSize
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetObjectSize(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hObject,
    CK_ULONG_PTR pulSize)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hObject);
    if (!fwObject) {
        error = CKR_OBJECT_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_ULONG_PTR)CK_NULL_PTR == pulSize) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    *pulSize = (CK_ULONG)0;

    *pulSize = nssCKFWObject_GetObjectSize(fwObject, &error);
    if (((CK_ULONG)0 == *pulSize) && (CKR_OK != error)) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_INFORMATION_SENSITIVE:
        case CKR_OBJECT_HANDLE_INVALID:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetAttributeValue
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetAttributeValue(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hObject,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulCount)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    CK_BBOOL sensitive = CK_FALSE;
    CK_BBOOL invalid = CK_FALSE;
    CK_BBOOL tooSmall = CK_FALSE;
    CK_ULONG i;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hObject);
    if (!fwObject) {
        error = CKR_OBJECT_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_ATTRIBUTE_PTR)CK_NULL_PTR == pTemplate) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    for (i = 0; i < ulCount; i++) {
        CK_ULONG size = nssCKFWObject_GetAttributeSize(fwObject,
                                                       pTemplate[i].type, &error);
        if ((CK_ULONG)0 == size) {
            switch (error) {
                case CKR_ATTRIBUTE_SENSITIVE:
                case CKR_INFORMATION_SENSITIVE:
                    sensitive =
                        CK_TRUE;
                    pTemplate[i].ulValueLen =
                        (CK_ULONG)(-1);
                    continue;
                case CKR_ATTRIBUTE_TYPE_INVALID:
                    invalid =
                        CK_TRUE;
                    pTemplate[i].ulValueLen =
                        (CK_ULONG)(-1);
                    continue;
                case CKR_OK:
                    break;
                default:
                    goto loser;
            }
        }

        if ((CK_VOID_PTR)CK_NULL_PTR == pTemplate[i].pValue) {
            pTemplate[i].ulValueLen = size;
        } else {
            NSSItem it, *p;

            if (pTemplate[i].ulValueLen < size) {
                tooSmall = CK_TRUE;
                continue;
            }

            it.size = (PRUint32)pTemplate[i].ulValueLen;
            it.data = (void *)pTemplate[i].pValue;
            p = nssCKFWObject_GetAttribute(fwObject, pTemplate[i].type, &it,
                                           (NSSArena *)NULL, &error);
            if (!p) {
                switch (error) {
                    case CKR_ATTRIBUTE_SENSITIVE:
                    case CKR_INFORMATION_SENSITIVE:
                        sensitive =
                            CK_TRUE;
                        pTemplate[i].ulValueLen =
                            (CK_ULONG)(-1);
                        continue;
                    case CKR_ATTRIBUTE_TYPE_INVALID:
                        invalid =
                            CK_TRUE;
                        pTemplate[i].ulValueLen =
                            (CK_ULONG)(-1);
                        continue;
                    default:
                        goto loser;
                }
            }

            pTemplate[i].ulValueLen = size;
        }
    }

    if (sensitive) {
        error = CKR_ATTRIBUTE_SENSITIVE;
        goto loser;
    } else if (invalid) {
        error = CKR_ATTRIBUTE_TYPE_INVALID;
        goto loser;
    } else if (tooSmall) {
        error = CKR_BUFFER_TOO_SMALL;
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ATTRIBUTE_SENSITIVE:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OBJECT_HANDLE_INVALID:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_SetAttributeValue
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SetAttributeValue(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hObject,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulCount)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    CK_ULONG i;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hObject);
    if (!fwObject) {
        error = CKR_OBJECT_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_ATTRIBUTE_PTR)CK_NULL_PTR == pTemplate) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    for (i = 0; i < ulCount; i++) {
        NSSItem value;

        value.data = pTemplate[i].pValue;
        value.size = pTemplate[i].ulValueLen;

        error = nssCKFWObject_SetAttribute(fwObject, fwSession,
                                           pTemplate[i].type, &value);

        if (CKR_OK != error) {
            goto loser;
        }
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OBJECT_HANDLE_INVALID:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_FindObjectsInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_FindObjectsInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulCount)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWFindObjects *fwFindObjects;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if (((CK_ATTRIBUTE_PTR)CK_NULL_PTR == pTemplate) && (ulCount != 0)) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    fwFindObjects = nssCKFWSession_GetFWFindObjects(fwSession, &error);
    if (fwFindObjects) {
        error = CKR_OPERATION_ACTIVE;
        goto loser;
    }

    if (CKR_OPERATION_NOT_INITIALIZED != error) {
        goto loser;
    }

    fwFindObjects = nssCKFWSession_FindObjectsInit(fwSession,
                                                   pTemplate, ulCount, &error);
    if (!fwFindObjects) {
        goto loser;
    }

    error = nssCKFWSession_SetFWFindObjects(fwSession, fwFindObjects);

    if (CKR_OK != error) {
        nssCKFWFindObjects_Destroy(fwFindObjects);
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_ACTIVE:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_FindObjects
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_FindObjects(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE_PTR phObject,
    CK_ULONG ulMaxObjectCount,
    CK_ULONG_PTR pulObjectCount)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWFindObjects *fwFindObjects;
    CK_ULONG i;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_OBJECT_HANDLE_PTR)CK_NULL_PTR == phObject) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
     * A purify error here indicates caller error.
     */
    (void)nsslibc_memset(phObject, 0, sizeof(CK_OBJECT_HANDLE) * ulMaxObjectCount);
    *pulObjectCount = (CK_ULONG)0;

    fwFindObjects = nssCKFWSession_GetFWFindObjects(fwSession, &error);
    if (!fwFindObjects) {
        goto loser;
    }

    for (i = 0; i < ulMaxObjectCount; i++) {
        NSSCKFWObject *fwObject = nssCKFWFindObjects_Next(fwFindObjects,
                                                          NULL, &error);
        if (!fwObject) {
            break;
        }

        phObject[i] = nssCKFWInstance_FindObjectHandle(fwInstance, fwObject);
        if ((CK_OBJECT_HANDLE)0 == phObject[i]) {
            phObject[i] = nssCKFWInstance_CreateObjectHandle(fwInstance, fwObject, &error);
            /* CreateObjectHandle returns CKR_GENERAL_ERROR if fwObject already
             * has a handle. This happens when another thread creates a handle
             * between our FindObjectHandle and CreateObjectHandle calls.
             */
            if (error == CKR_GENERAL_ERROR) {
                error = CKR_OK;
                phObject[i] = nssCKFWInstance_FindObjectHandle(fwInstance, fwObject);
            }
            if (error != CKR_OK || (CK_OBJECT_HANDLE)0 == phObject[i]) {
                goto loser;
            }
        }
    }

    *pulObjectCount = i;

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_FindObjectsFinal
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_FindObjectsFinal(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWFindObjects *fwFindObjects;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwFindObjects = nssCKFWSession_GetFWFindObjects(fwSession, &error);
    if (!fwFindObjects) {
        error = CKR_OPERATION_NOT_INITIALIZED;
        goto loser;
    }

    nssCKFWFindObjects_Destroy(fwFindObjects);
    error = nssCKFWSession_SetFWFindObjects(fwSession,
                                            (NSSCKFWFindObjects *)NULL);

    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_EncryptInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_EncryptInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_EncryptInit(fwMechanism, pMechanism,
                                         fwSession, fwObject);

    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_FUNCTION_NOT_PERMITTED:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_Encrypt
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Encrypt(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen,
    CK_BYTE_PTR pEncryptedData,
    CK_ULONG_PTR pulEncryptedDataLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_Encrypt,
                                       NSSCKFWCryptoOperationState_EncryptDecrypt,
                                       pData, ulDataLen, pEncryptedData, pulEncryptedDataLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_INVALID:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_CLOSED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_EncryptUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_EncryptUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pPart,
    CK_ULONG ulPartLen,
    CK_BYTE_PTR pEncryptedPart,
    CK_ULONG_PTR pulEncryptedPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Update(fwSession,
                                  NSSCKFWCryptoOperationType_Encrypt,
                                  NSSCKFWCryptoOperationState_EncryptDecrypt,
                                  pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_EncryptFinal
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_EncryptFinal(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pLastEncryptedPart,
    CK_ULONG_PTR pulLastEncryptedPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Final(fwSession,
                                 NSSCKFWCryptoOperationType_Encrypt,
                                 NSSCKFWCryptoOperationState_EncryptDecrypt,
                                 pLastEncryptedPart, pulLastEncryptedPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DecryptInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DecryptInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_DecryptInit(fwMechanism, pMechanism,
                                         fwSession, fwObject);
    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_FUNCTION_NOT_PERMITTED:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_Decrypt
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Decrypt(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pEncryptedData,
    CK_ULONG ulEncryptedDataLen,
    CK_BYTE_PTR pData,
    CK_ULONG_PTR pulDataLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_Decrypt,
                                       NSSCKFWCryptoOperationState_EncryptDecrypt,
                                       pEncryptedData, ulEncryptedDataLen, pData, pulDataLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_ENCRYPTED_DATA_INVALID:
        case CKR_ENCRYPTED_DATA_LEN_RANGE:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        case CKR_DATA_LEN_RANGE:
            error = CKR_ENCRYPTED_DATA_LEN_RANGE;
            break;
        case CKR_DATA_INVALID:
            error = CKR_ENCRYPTED_DATA_INVALID;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DecryptUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DecryptUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pEncryptedPart,
    CK_ULONG ulEncryptedPartLen,
    CK_BYTE_PTR pPart,
    CK_ULONG_PTR pulPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Update(fwSession,
                                  NSSCKFWCryptoOperationType_Decrypt,
                                  NSSCKFWCryptoOperationState_EncryptDecrypt,
                                  pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_ENCRYPTED_DATA_INVALID:
        case CKR_ENCRYPTED_DATA_LEN_RANGE:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        case CKR_DATA_LEN_RANGE:
            error = CKR_ENCRYPTED_DATA_LEN_RANGE;
            break;
        case CKR_DATA_INVALID:
            error = CKR_ENCRYPTED_DATA_INVALID;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DecryptFinal
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DecryptFinal(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pLastPart,
    CK_ULONG_PTR pulLastPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Final(fwSession,
                                 NSSCKFWCryptoOperationType_Decrypt,
                                 NSSCKFWCryptoOperationState_EncryptDecrypt,
                                 pLastPart, pulLastPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_FAILED:
        case CKR_FUNCTION_CANCELED:
        case CKR_ENCRYPTED_DATA_INVALID:
        case CKR_ENCRYPTED_DATA_LEN_RANGE:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        case CKR_DATA_LEN_RANGE:
            error = CKR_ENCRYPTED_DATA_LEN_RANGE;
            break;
        case CKR_DATA_INVALID:
            error = CKR_ENCRYPTED_DATA_INVALID;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DigestInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DigestInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_DigestInit(fwMechanism, pMechanism, fwSession);

    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_Digest
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Digest(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen,
    CK_BYTE_PTR pDigest,
    CK_ULONG_PTR pulDigestLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_Digest,
                                       NSSCKFWCryptoOperationState_Digest,
                                       pData, ulDataLen, pDigest, pulDigestLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DigestUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DigestUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_DigestUpdate(fwSession,
                                        NSSCKFWCryptoOperationType_Digest,
                                        NSSCKFWCryptoOperationState_Digest,
                                        pData, ulDataLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DigestKey
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DigestKey(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_DigestKey(fwSession, fwObject);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_INDIGESTIBLE:
        case CKR_KEY_SIZE_RANGE:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DigestFinal
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DigestFinal(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pDigest,
    CK_ULONG_PTR pulDigestLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Final(fwSession,
                                 NSSCKFWCryptoOperationType_Digest,
                                 NSSCKFWCryptoOperationState_Digest,
                                 pDigest, pulDigestLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SignInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SignInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_SignInit(fwMechanism, pMechanism, fwSession,
                                      fwObject);

    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_FUNCTION_NOT_PERMITTED:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_Sign
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Sign(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen,
    CK_BYTE_PTR pSignature,
    CK_ULONG_PTR pulSignatureLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_Sign,
                                       NSSCKFWCryptoOperationState_SignVerify,
                                       pData, ulDataLen, pSignature, pulSignatureLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_INVALID:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
        case CKR_FUNCTION_REJECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SignUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SignUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pPart,
    CK_ULONG ulPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_DigestUpdate(fwSession,
                                        NSSCKFWCryptoOperationType_Sign,
                                        NSSCKFWCryptoOperationState_SignVerify,
                                        pPart, ulPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SignFinal
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SignFinal(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pSignature,
    CK_ULONG_PTR pulSignatureLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Final(fwSession,
                                 NSSCKFWCryptoOperationType_Sign,
                                 NSSCKFWCryptoOperationState_SignVerify,
                                 pSignature, pulSignatureLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
        case CKR_FUNCTION_REJECTED:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SignRecoverInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SignRecoverInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_SignRecoverInit(fwMechanism, pMechanism, fwSession,
                                             fwObject);

    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_FUNCTION_NOT_PERMITTED:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SignRecover
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SignRecover(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen,
    CK_BYTE_PTR pSignature,
    CK_ULONG_PTR pulSignatureLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_SignRecover,
                                       NSSCKFWCryptoOperationState_SignVerify,
                                       pData, ulDataLen, pSignature, pulSignatureLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_INVALID:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_VerifyInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_VerifyInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_VerifyInit(fwMechanism, pMechanism, fwSession,
                                        fwObject);

    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_FUNCTION_NOT_PERMITTED:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_Verify
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_Verify(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pData,
    CK_ULONG ulDataLen,
    CK_BYTE_PTR pSignature,
    CK_ULONG ulSignatureLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_Verify,
                                       NSSCKFWCryptoOperationState_SignVerify,
                                       pData, ulDataLen, pSignature, &ulSignatureLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_INVALID:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SIGNATURE_INVALID:
        case CKR_SIGNATURE_LEN_RANGE:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_VerifyUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_VerifyUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pPart,
    CK_ULONG ulPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_DigestUpdate(fwSession,
                                        NSSCKFWCryptoOperationType_Verify,
                                        NSSCKFWCryptoOperationState_SignVerify,
                                        pPart, ulPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_VerifyFinal
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_VerifyFinal(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pSignature,
    CK_ULONG ulSignatureLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_Final(fwSession,
                                 NSSCKFWCryptoOperationType_Verify,
                                 NSSCKFWCryptoOperationState_SignVerify,
                                 pSignature, &ulSignatureLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SIGNATURE_INVALID:
        case CKR_SIGNATURE_LEN_RANGE:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_VerifyRecoverInit
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_VerifyRecoverInit(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_VerifyRecoverInit(fwMechanism, pMechanism,
                                               fwSession, fwObject);

    nssCKFWMechanism_Destroy(fwMechanism);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_FUNCTION_NOT_PERMITTED:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_CLOSED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_VerifyRecover
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_VerifyRecover(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pSignature,
    CK_ULONG ulSignatureLen,
    CK_BYTE_PTR pData,
    CK_ULONG_PTR pulDataLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateFinal(fwSession,
                                       NSSCKFWCryptoOperationType_VerifyRecover,
                                       NSSCKFWCryptoOperationState_SignVerify,
                                       pSignature, ulSignatureLen, pData, pulDataLen);
    if (CKR_OK == error) {
        return CKR_OK;
    }
loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_INVALID:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SIGNATURE_INVALID:
        case CKR_SIGNATURE_LEN_RANGE:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DigestEncryptUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DigestEncryptUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pPart,
    CK_ULONG ulPartLen,
    CK_BYTE_PTR pEncryptedPart,
    CK_ULONG_PTR pulEncryptedPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateCombo(fwSession,
                                       NSSCKFWCryptoOperationType_Encrypt,
                                       NSSCKFWCryptoOperationType_Digest,
                                       NSSCKFWCryptoOperationState_Digest,
                                       pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DecryptDigestUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DecryptDigestUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pEncryptedPart,
    CK_ULONG ulEncryptedPartLen,
    CK_BYTE_PTR pPart,
    CK_ULONG_PTR pulPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateCombo(fwSession,
                                       NSSCKFWCryptoOperationType_Decrypt,
                                       NSSCKFWCryptoOperationType_Digest,
                                       NSSCKFWCryptoOperationState_Digest,
                                       pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_ENCRYPTED_DATA_INVALID:
        case CKR_ENCRYPTED_DATA_LEN_RANGE:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        case CKR_DATA_INVALID:
            error = CKR_ENCRYPTED_DATA_INVALID;
            break;
        case CKR_DATA_LEN_RANGE:
            error = CKR_ENCRYPTED_DATA_LEN_RANGE;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SignEncryptUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SignEncryptUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pPart,
    CK_ULONG ulPartLen,
    CK_BYTE_PTR pEncryptedPart,
    CK_ULONG_PTR pulEncryptedPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateCombo(fwSession,
                                       NSSCKFWCryptoOperationType_Encrypt,
                                       NSSCKFWCryptoOperationType_Sign,
                                       NSSCKFWCryptoOperationState_SignVerify,
                                       pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DecryptVerifyUpdate
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DecryptVerifyUpdate(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pEncryptedPart,
    CK_ULONG ulEncryptedPartLen,
    CK_BYTE_PTR pPart,
    CK_ULONG_PTR pulPartLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    error = nssCKFWSession_UpdateCombo(fwSession,
                                       NSSCKFWCryptoOperationType_Decrypt,
                                       NSSCKFWCryptoOperationType_Verify,
                                       NSSCKFWCryptoOperationState_SignVerify,
                                       pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DATA_LEN_RANGE:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_ENCRYPTED_DATA_INVALID:
        case CKR_ENCRYPTED_DATA_LEN_RANGE:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_NOT_INITIALIZED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
            break;
        case CKR_DATA_INVALID:
            error = CKR_ENCRYPTED_DATA_INVALID;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_GenerateKey
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GenerateKey(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulCount,
    CK_OBJECT_HANDLE_PTR phKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    fwObject = nssCKFWMechanism_GenerateKey(
        fwMechanism,
        pMechanism,
        fwSession,
        pTemplate,
        ulCount,
        &error);

    nssCKFWMechanism_Destroy(fwMechanism);
    if (!fwObject) {
        goto loser;
    }
    *phKey = nssCKFWInstance_CreateObjectHandle(fwInstance, fwObject, &error);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCOMPLETE:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_GenerateKeyPair
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GenerateKeyPair(
    NSSCKFWInstance *fwInstance,
    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)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwPrivateKeyObject;
    NSSCKFWObject *fwPublicKeyObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    error = nssCKFWMechanism_GenerateKeyPair(
        fwMechanism,
        pMechanism,
        fwSession,
        pPublicKeyTemplate,
        ulPublicKeyAttributeCount,
        pPublicKeyTemplate,
        ulPublicKeyAttributeCount,
        &fwPublicKeyObject,
        &fwPrivateKeyObject);

    nssCKFWMechanism_Destroy(fwMechanism);
    if (CKR_OK != error) {
        goto loser;
    }
    *phPublicKey = nssCKFWInstance_CreateObjectHandle(fwInstance,
                                                      fwPublicKeyObject,
                                                      &error);
    if (CKR_OK != error) {
        goto loser;
    }
    *phPrivateKey = nssCKFWInstance_CreateObjectHandle(fwInstance,
                                                       fwPrivateKeyObject,
                                                       &error);
    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_DOMAIN_PARAMS_INVALID:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCOMPLETE:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_WrapKey
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_WrapKey(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hWrappingKey,
    CK_OBJECT_HANDLE hKey,
    CK_BYTE_PTR pWrappedKey,
    CK_ULONG_PTR pulWrappedKeyLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwKeyObject;
    NSSCKFWObject *fwWrappingKeyObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;
    NSSItem wrappedKey;
    CK_ULONG wrappedKeyLength = 0;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwWrappingKeyObject = nssCKFWInstance_ResolveObjectHandle(fwInstance,
                                                              hWrappingKey);
    if (!fwWrappingKeyObject) {
        error = CKR_WRAPPING_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwKeyObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hKey);
    if (!fwKeyObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    /*
   * first get the length...
   */
    wrappedKeyLength = nssCKFWMechanism_GetWrapKeyLength(
        fwMechanism,
        pMechanism,
        fwSession,
        fwWrappingKeyObject,
        fwKeyObject,
        &error);
    if ((CK_ULONG)0 == wrappedKeyLength) {
        nssCKFWMechanism_Destroy(fwMechanism);
        goto loser;
    }
    if ((CK_BYTE_PTR)NULL == pWrappedKey) {
        *pulWrappedKeyLen = wrappedKeyLength;
        nssCKFWMechanism_Destroy(fwMechanism);
        return CKR_OK;
    }
    if (wrappedKeyLength > *pulWrappedKeyLen) {
        *pulWrappedKeyLen = wrappedKeyLength;
        nssCKFWMechanism_Destroy(fwMechanism);
        error = CKR_BUFFER_TOO_SMALL;
        goto loser;
    }

    wrappedKey.data = pWrappedKey;
    wrappedKey.size = wrappedKeyLength;

    error = nssCKFWMechanism_WrapKey(
        fwMechanism,
        pMechanism,
        fwSession,
        fwWrappingKeyObject,
        fwKeyObject,
        &wrappedKey);

    nssCKFWMechanism_Destroy(fwMechanism);
    *pulWrappedKeyLen = wrappedKey.size;

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_NOT_WRAPPABLE:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_UNEXTRACTABLE:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_WRAPPING_KEY_HANDLE_INVALID:
        case CKR_WRAPPING_KEY_SIZE_RANGE:
        case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:
            break;
        case CKR_KEY_TYPE_INCONSISTENT:
            error = CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_UnwrapKey
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_UnwrapKey(
    NSSCKFWInstance *fwInstance,
    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)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWObject *fwWrappingKeyObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;
    NSSItem wrappedKey;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwWrappingKeyObject = nssCKFWInstance_ResolveObjectHandle(fwInstance,
                                                              hUnwrappingKey);
    if (!fwWrappingKeyObject) {
        error = CKR_WRAPPING_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    wrappedKey.data = pWrappedKey;
    wrappedKey.size = ulWrappedKeyLen;

    fwObject = nssCKFWMechanism_UnwrapKey(
        fwMechanism,
        pMechanism,
        fwSession,
        fwWrappingKeyObject,
        &wrappedKey,
        pTemplate,
        ulAttributeCount,
        &error);

    nssCKFWMechanism_Destroy(fwMechanism);
    if (!fwObject) {
        goto loser;
    }
    *phKey = nssCKFWInstance_CreateObjectHandle(fwInstance, fwObject, &error);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_BUFFER_TOO_SMALL:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_DOMAIN_PARAMS_INVALID:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCOMPLETE:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_UNWRAPPING_KEY_HANDLE_INVALID:
        case CKR_UNWRAPPING_KEY_SIZE_RANGE:
        case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT:
        case CKR_USER_NOT_LOGGED_IN:
        case CKR_WRAPPED_KEY_INVALID:
        case CKR_WRAPPED_KEY_LEN_RANGE:
            break;
        case CKR_KEY_HANDLE_INVALID:
            error = CKR_UNWRAPPING_KEY_HANDLE_INVALID;
            break;
        case CKR_KEY_SIZE_RANGE:
            error = CKR_UNWRAPPING_KEY_SIZE_RANGE;
            break;
        case CKR_KEY_TYPE_INCONSISTENT:
            error = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
            break;
        case CKR_ENCRYPTED_DATA_INVALID:
            error = CKR_WRAPPED_KEY_INVALID;
            break;
        case CKR_ENCRYPTED_DATA_LEN_RANGE:
            error = CKR_WRAPPED_KEY_LEN_RANGE;
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_DeriveKey
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_DeriveKey(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_MECHANISM_PTR pMechanism,
    CK_OBJECT_HANDLE hBaseKey,
    CK_ATTRIBUTE_PTR pTemplate,
    CK_ULONG ulAttributeCount,
    CK_OBJECT_HANDLE_PTR phKey)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSCKFWObject *fwObject;
    NSSCKFWObject *fwBaseKeyObject;
    NSSCKFWSlot *fwSlot;
    NSSCKFWToken *fwToken;
    NSSCKFWMechanism *fwMechanism;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    fwBaseKeyObject = nssCKFWInstance_ResolveObjectHandle(fwInstance, hBaseKey);
    if (!fwBaseKeyObject) {
        error = CKR_KEY_HANDLE_INVALID;
        goto loser;
    }

    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
    if (!fwSlot) {
        error = CKR_GENERAL_ERROR; /* should never happen! */
        goto loser;
    }

    if (CK_TRUE != nssCKFWSlot_GetTokenPresent(fwSlot)) {
        error = CKR_TOKEN_NOT_PRESENT;
        goto loser;
    }

    fwToken = nssCKFWSlot_GetToken(fwSlot, &error);
    if (!fwToken) {
        goto loser;
    }

    fwMechanism = nssCKFWToken_GetMechanism(fwToken, pMechanism->mechanism, &error);
    if (!fwMechanism) {
        goto loser;
    }

    fwObject = nssCKFWMechanism_DeriveKey(
        fwMechanism,
        pMechanism,
        fwSession,
        fwBaseKeyObject,
        pTemplate,
        ulAttributeCount,
        &error);

    nssCKFWMechanism_Destroy(fwMechanism);
    if (!fwObject) {
        goto loser;
    }
    *phKey = nssCKFWInstance_CreateObjectHandle(fwInstance, fwObject, &error);

    if (CKR_OK == error) {
        return CKR_OK;
    }

loser:
    /* verify error */
    switch (error) {
        case CKR_ARGUMENTS_BAD:
        case CKR_ATTRIBUTE_READ_ONLY:
        case CKR_ATTRIBUTE_TYPE_INVALID:
        case CKR_ATTRIBUTE_VALUE_INVALID:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_DEVICE_REMOVED:
        case CKR_DOMAIN_PARAMS_INVALID:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_KEY_HANDLE_INVALID:
        case CKR_KEY_SIZE_RANGE:
        case CKR_KEY_TYPE_INCONSISTENT:
        case CKR_MECHANISM_INVALID:
        case CKR_MECHANISM_PARAM_INVALID:
        case CKR_OPERATION_ACTIVE:
        case CKR_PIN_EXPIRED:
        case CKR_SESSION_CLOSED:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_SESSION_READ_ONLY:
        case CKR_TEMPLATE_INCOMPLETE:
        case CKR_TEMPLATE_INCONSISTENT:
        case CKR_TOKEN_WRITE_PROTECTED:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }
    return error;
}

/*
 * NSSCKFWC_SeedRandom
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_SeedRandom(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pSeed,
    CK_ULONG ulSeedLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSItem seed;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_BYTE_PTR)CK_NULL_PTR == pSeed) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /* We could read through the buffer in a Purify trap */

    seed.size = (PRUint32)ulSeedLen;
    seed.data = (void *)pSeed;

    error = nssCKFWSession_SeedRandom(fwSession, &seed);

    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_ACTIVE:
        case CKR_RANDOM_SEED_NOT_SUPPORTED:
        case CKR_RANDOM_NO_RNG:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GenerateRandom
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GenerateRandom(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession,
    CK_BYTE_PTR pRandomData,
    CK_ULONG ulRandomLen)
{
    CK_RV error = CKR_OK;
    NSSCKFWSession *fwSession;
    NSSItem buffer;

    if (!fwInstance) {
        error = CKR_CRYPTOKI_NOT_INITIALIZED;
        goto loser;
    }

    fwSession = nssCKFWInstance_ResolveSessionHandle(fwInstance, hSession);
    if (!fwSession) {
        error = CKR_SESSION_HANDLE_INVALID;
        goto loser;
    }

    if ((CK_BYTE_PTR)CK_NULL_PTR == pRandomData) {
        error = CKR_ARGUMENTS_BAD;
        goto loser;
    }

    /*
   * A purify error here indicates caller error.
   */
    (void)nsslibc_memset(pRandomData, 0, ulRandomLen);

    buffer.size = (PRUint32)ulRandomLen;
    buffer.data = (void *)pRandomData;

    error = nssCKFWSession_GetRandom(fwSession, &buffer);

    if (CKR_OK != error) {
        goto loser;
    }

    return CKR_OK;

loser:
    switch (error) {
        case CKR_SESSION_CLOSED:
            /* destroy session? */
            break;
        case CKR_DEVICE_REMOVED:
            /* (void)nssCKFWToken_Destroy(fwToken); */
            break;
        case CKR_ARGUMENTS_BAD:
        case CKR_CRYPTOKI_NOT_INITIALIZED:
        case CKR_DEVICE_ERROR:
        case CKR_DEVICE_MEMORY:
        case CKR_FUNCTION_CANCELED:
        case CKR_FUNCTION_FAILED:
        case CKR_GENERAL_ERROR:
        case CKR_HOST_MEMORY:
        case CKR_OPERATION_ACTIVE:
        case CKR_RANDOM_NO_RNG:
        case CKR_SESSION_HANDLE_INVALID:
        case CKR_USER_NOT_LOGGED_IN:
            break;
        default:
        case CKR_OK:
            error = CKR_GENERAL_ERROR;
            break;
    }

    return error;
}

/*
 * NSSCKFWC_GetFunctionStatus
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_GetFunctionStatus(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession)
{
    return CKR_FUNCTION_NOT_PARALLEL;
}

/*
 * NSSCKFWC_CancelFunction
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWC_CancelFunction(
    NSSCKFWInstance *fwInstance,
    CK_SESSION_HANDLE hSession)
{
    return CKR_FUNCTION_NOT_PARALLEL;
}
