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

/*
 * object.c
 *
 * This file implements the NSSCKFWObject type and methods.
 */

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

/*
 * NSSCKFWObject
 *
 * -- create/destroy --
 *  nssCKFWObject_Create
 *  nssCKFWObject_Finalize
 *  nssCKFWObject_Destroy
 *
 * -- public accessors --
 *  NSSCKFWObject_GetMDObject
 *  NSSCKFWObject_GetArena
 *  NSSCKFWObject_IsTokenObject
 *  NSSCKFWObject_GetAttributeCount
 *  NSSCKFWObject_GetAttributeTypes
 *  NSSCKFWObject_GetAttributeSize
 *  NSSCKFWObject_GetAttribute
 *  NSSCKFWObject_SetAttribute
 *  NSSCKFWObject_GetObjectSize
 *
 * -- implement public accessors --
 *  nssCKFWObject_GetMDObject
 *  nssCKFWObject_GetArena
 *
 * -- private accessors --
 *  nssCKFWObject_SetHandle
 *  nssCKFWObject_GetHandle
 *
 * -- module fronts --
 *  nssCKFWObject_IsTokenObject
 *  nssCKFWObject_GetAttributeCount
 *  nssCKFWObject_GetAttributeTypes
 *  nssCKFWObject_GetAttributeSize
 *  nssCKFWObject_GetAttribute
 *  nssCKFWObject_SetAttribute
 *  nssCKFWObject_GetObjectSize
 */

struct NSSCKFWObjectStr {
    NSSCKFWMutex *mutex; /* merely to serialise the MDObject calls */
    NSSArena *arena;
    NSSArena *localArena;
    NSSCKMDObject *mdObject;
    NSSCKMDSession *mdSession;
    NSSCKFWSession *fwSession;
    NSSCKMDToken *mdToken;
    NSSCKFWToken *fwToken;
    NSSCKMDInstance *mdInstance;
    NSSCKFWInstance *fwInstance;
    CK_OBJECT_HANDLE hObject;
};

#ifdef DEBUG
/*
 * But first, the pointer-tracking stuff.
 *
 * NOTE: the pointer-tracking support in NSS/base currently relies
 * upon NSPR's CallOnce support.  That, however, relies upon NSPR's
 * locking, which is tied into the runtime.  We need a pointer-tracker
 * implementation that uses the locks supplied through C_Initialize.
 * That support, however, can be filled in later.  So for now, I'll
 * just do this routines as no-ops.
 */

static CK_RV
object_add_pointer(
    const NSSCKFWObject *fwObject)
{
    return CKR_OK;
}

static CK_RV
object_remove_pointer(
    const NSSCKFWObject *fwObject)
{
    return CKR_OK;
}

NSS_IMPLEMENT CK_RV
nssCKFWObject_verifyPointer(
    const NSSCKFWObject *fwObject)
{
    return CKR_OK;
}

#endif /* DEBUG */

/*
 * nssCKFWObject_Create
 *
 */
NSS_IMPLEMENT NSSCKFWObject *
nssCKFWObject_Create(
    NSSArena *arena,
    NSSCKMDObject *mdObject,
    NSSCKFWSession *fwSession,
    NSSCKFWToken *fwToken,
    NSSCKFWInstance *fwInstance,
    CK_RV *pError)
{
    NSSArena *objArena = arena;
    NSSArena *localArena = NULL;
    NSSCKFWObject *fwObject;
    nssCKFWHash *mdObjectHash;

#ifdef NSSDEBUG
    if (!pError) {
        return (NSSCKFWObject *)NULL;
    }

    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        *pError = CKR_ARGUMENTS_BAD;
        return (NSSCKFWObject *)NULL;
    }
#endif /* NSSDEBUG */

    if (!fwToken) {
        *pError = CKR_ARGUMENTS_BAD;
        return (NSSCKFWObject *)NULL;
    }
    mdObjectHash = nssCKFWToken_GetMDObjectHash(fwToken);
    if (!mdObjectHash) {
        *pError = CKR_GENERAL_ERROR;
        return (NSSCKFWObject *)NULL;
    }

    if (nssCKFWHash_Exists(mdObjectHash, mdObject)) {
        fwObject = nssCKFWHash_Lookup(mdObjectHash, mdObject);
        return fwObject;
    }

    /* session objects should have their own arena so they can be destroyed in the end. */
    if (arena == NULL) {
        localArena = objArena = NSSArena_Create();
        if (objArena == NULL) {
            *pError = CKR_HOST_MEMORY;
            return (NSSCKFWObject *)NULL;
        }
    }

    fwObject = nss_ZNEW(objArena, NSSCKFWObject);
    if (!fwObject) {
        *pError = CKR_HOST_MEMORY;
        return (NSSCKFWObject *)NULL;
    }

    fwObject->arena = objArena;
    fwObject->localArena = localArena;
    fwObject->mdObject = mdObject;
    fwObject->fwSession = fwSession;

    if (fwSession) {
        fwObject->mdSession = nssCKFWSession_GetMDSession(fwSession);
    }

    fwObject->fwToken = fwToken;
    fwObject->mdToken = nssCKFWToken_GetMDToken(fwToken);
    fwObject->fwInstance = fwInstance;
    fwObject->mdInstance = nssCKFWInstance_GetMDInstance(fwInstance);
    fwObject->mutex = nssCKFWInstance_CreateMutex(fwInstance, objArena, pError);
    if (!fwObject->mutex) {
        if (CKR_OK == *pError) {
            *pError = CKR_GENERAL_ERROR;
        }
        nss_ZFreeIf(fwObject);
        if (localArena) {
            NSSArena_Destroy(localArena);
        }
        return (NSSCKFWObject *)NULL;
    }

    *pError = nssCKFWHash_Add(mdObjectHash, mdObject, fwObject);
    if (CKR_OK != *pError) {
        nss_ZFreeIf(fwObject);
        if (localArena) {
            NSSArena_Destroy(localArena);
        }
        return (NSSCKFWObject *)NULL;
    }

#ifdef DEBUG
    *pError = object_add_pointer(fwObject);
    if (CKR_OK != *pError) {
        nssCKFWHash_Remove(mdObjectHash, mdObject);
        nss_ZFreeIf(fwObject);
        if (localArena) {
            NSSArena_Destroy(localArena);
        }
        return (NSSCKFWObject *)NULL;
    }
#endif /* DEBUG */

    *pError = CKR_OK;
    return fwObject;
}

/*
 * nssCKFWObject_Finalize
 *
 */
NSS_IMPLEMENT void
nssCKFWObject_Finalize(
    NSSCKFWObject *fwObject,
    PRBool removeFromHash)
{
    nssCKFWHash *mdObjectHash;
    NSSArena *arena = NULL;

#ifdef NSSDEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return;
    }
#endif /* NSSDEBUG */

    (void)nssCKFWMutex_Destroy(fwObject->mutex);

    if (fwObject->mdObject->Finalize) {
        fwObject->mdObject->Finalize(fwObject->mdObject, fwObject,
                                     fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                     fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
    }

    if (removeFromHash) {
        mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken);
        if (mdObjectHash) {
            nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject);
        }
    }

    if (fwObject->fwSession) {
        nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
    }
    arena = fwObject->localArena;
    nss_ZFreeIf(fwObject);
    if (arena) {
        NSSArena_Destroy(arena);
    }

#ifdef DEBUG
    (void)object_remove_pointer(fwObject);
#endif /* DEBUG */

    return;
}

/*
 * nssCKFWObject_Destroy
 *
 */
NSS_IMPLEMENT void
nssCKFWObject_Destroy(
    NSSCKFWObject *fwObject)
{
    nssCKFWHash *mdObjectHash;
    NSSArena *arena = NULL;

#ifdef NSSDEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return;
    }
#endif /* NSSDEBUG */

    (void)nssCKFWMutex_Destroy(fwObject->mutex);

    if (fwObject->mdObject->Destroy) {
        fwObject->mdObject->Destroy(fwObject->mdObject, fwObject,
                                    fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                    fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);
    }

    mdObjectHash = nssCKFWToken_GetMDObjectHash(fwObject->fwToken);
    if (mdObjectHash) {
        nssCKFWHash_Remove(mdObjectHash, fwObject->mdObject);
    }

    if (fwObject->fwSession) {
        nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
    }
    arena = fwObject->localArena;
    nss_ZFreeIf(fwObject);
    if (arena) {
        NSSArena_Destroy(arena);
    }

#ifdef DEBUG
    (void)object_remove_pointer(fwObject);
#endif /* DEBUG */

    return;
}

/*
 * nssCKFWObject_GetMDObject
 *
 */
NSS_IMPLEMENT NSSCKMDObject *
nssCKFWObject_GetMDObject(
    NSSCKFWObject *fwObject)
{
#ifdef NSSDEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return (NSSCKMDObject *)NULL;
    }
#endif /* NSSDEBUG */

    return fwObject->mdObject;
}

/*
 * nssCKFWObject_GetArena
 *
 */
NSS_IMPLEMENT NSSArena *
nssCKFWObject_GetArena(
    NSSCKFWObject *fwObject,
    CK_RV *pError)
{
#ifdef NSSDEBUG
    if (!pError) {
        return (NSSArena *)NULL;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (NSSArena *)NULL;
    }
#endif /* NSSDEBUG */

    return fwObject->arena;
}

/*
 * nssCKFWObject_SetHandle
 *
 */
NSS_IMPLEMENT CK_RV
nssCKFWObject_SetHandle(
    NSSCKFWObject *fwObject,
    CK_OBJECT_HANDLE hObject)
{
#ifdef NSSDEBUG
    CK_RV error = CKR_OK;
#endif /* NSSDEBUG */

#ifdef NSSDEBUG
    error = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != error) {
        return error;
    }
#endif /* NSSDEBUG */

    if ((CK_OBJECT_HANDLE)0 != fwObject->hObject) {
        return CKR_GENERAL_ERROR;
    }

    fwObject->hObject = hObject;

    return CKR_OK;
}

/*
 * nssCKFWObject_GetHandle
 *
 */
NSS_IMPLEMENT CK_OBJECT_HANDLE
nssCKFWObject_GetHandle(
    NSSCKFWObject *fwObject)
{
#ifdef NSSDEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return (CK_OBJECT_HANDLE)0;
    }
#endif /* NSSDEBUG */

    return fwObject->hObject;
}

/*
 * nssCKFWObject_IsTokenObject
 *
 */
NSS_IMPLEMENT CK_BBOOL
nssCKFWObject_IsTokenObject(
    NSSCKFWObject *fwObject)
{
    CK_BBOOL b = CK_FALSE;

#ifdef NSSDEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return CK_FALSE;
    }
#endif /* NSSDEBUG */

    if (!fwObject->mdObject->IsTokenObject) {
        NSSItem item;
        NSSItem *pItem;
        CK_RV rv = CKR_OK;

        item.data = (void *)&b;
        item.size = sizeof(b);

        pItem = nssCKFWObject_GetAttribute(fwObject, CKA_TOKEN, &item,
                                           (NSSArena *)NULL, &rv);
        if (!pItem) {
            /* Error of some type */
            b = CK_FALSE;
            goto done;
        }

        goto done;
    }

    b = fwObject->mdObject->IsTokenObject(fwObject->mdObject, fwObject,
                                          fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                          fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance);

done:
    return b;
}

/*
 * nssCKFWObject_GetAttributeCount
 *
 */
NSS_IMPLEMENT CK_ULONG
nssCKFWObject_GetAttributeCount(
    NSSCKFWObject *fwObject,
    CK_RV *pError)
{
    CK_ULONG rv;

#ifdef NSSDEBUG
    if (!pError) {
        return (CK_ULONG)0;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }
#endif /* NSSDEBUG */

    if (!fwObject->mdObject->GetAttributeCount) {
        *pError = CKR_GENERAL_ERROR;
        return (CK_ULONG)0;
    }

    *pError = nssCKFWMutex_Lock(fwObject->mutex);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }

    rv = fwObject->mdObject->GetAttributeCount(fwObject->mdObject, fwObject,
                                               fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                               fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
                                               pError);

    (void)nssCKFWMutex_Unlock(fwObject->mutex);
    return rv;
}

/*
 * nssCKFWObject_GetAttributeTypes
 *
 */
NSS_IMPLEMENT CK_RV
nssCKFWObject_GetAttributeTypes(
    NSSCKFWObject *fwObject,
    CK_ATTRIBUTE_TYPE_PTR typeArray,
    CK_ULONG ulCount)
{
    CK_RV error = CKR_OK;

#ifdef NSSDEBUG
    error = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != error) {
        return error;
    }

    if ((CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray) {
        return CKR_ARGUMENTS_BAD;
    }
#endif /* NSSDEBUG */

    if (!fwObject->mdObject->GetAttributeTypes) {
        return CKR_GENERAL_ERROR;
    }

    error = nssCKFWMutex_Lock(fwObject->mutex);
    if (CKR_OK != error) {
        return error;
    }

    error = fwObject->mdObject->GetAttributeTypes(fwObject->mdObject, fwObject,
                                                  fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                                  fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
                                                  typeArray, ulCount);

    (void)nssCKFWMutex_Unlock(fwObject->mutex);
    return error;
}

/*
 * nssCKFWObject_GetAttributeSize
 *
 */
NSS_IMPLEMENT CK_ULONG
nssCKFWObject_GetAttributeSize(
    NSSCKFWObject *fwObject,
    CK_ATTRIBUTE_TYPE attribute,
    CK_RV *pError)
{
    CK_ULONG rv;

    if (!pError) {
        return (CK_ULONG)0;
    }

    if (!fwObject || !fwObject->mdObject || !fwObject->mdObject->GetAttributeSize) {
        *pError = CKR_GENERAL_ERROR;
        return (CK_ULONG)0;
    }

    *pError = nssCKFWMutex_Lock(fwObject->mutex);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }

    rv = fwObject->mdObject->GetAttributeSize(fwObject->mdObject, fwObject,
                                              fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                              fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
                                              attribute, pError);

    (void)nssCKFWMutex_Unlock(fwObject->mutex);
    return rv;
}

/*
 * nssCKFWObject_GetAttribute
 *
 * Usual NSS allocation rules:
 * If itemOpt is not NULL, it will be returned; otherwise an NSSItem
 * will be allocated.  If itemOpt is not NULL but itemOpt->data is,
 * the buffer will be allocated; otherwise, the buffer will be used.
 * Any allocations will come from the optional arena, if one is
 * specified.
 */
NSS_IMPLEMENT NSSItem *
nssCKFWObject_GetAttribute(
    NSSCKFWObject *fwObject,
    CK_ATTRIBUTE_TYPE attribute,
    NSSItem *itemOpt,
    NSSArena *arenaOpt,
    CK_RV *pError)
{
    NSSItem *rv = (NSSItem *)NULL;
    NSSCKFWItem mdItem;

#ifdef NSSDEBUG
    if (!pError) {
        return (NSSItem *)NULL;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (NSSItem *)NULL;
    }
#endif /* NSSDEBUG */

    if (!fwObject->mdObject->GetAttribute) {
        *pError = CKR_GENERAL_ERROR;
        return (NSSItem *)NULL;
    }

    *pError = nssCKFWMutex_Lock(fwObject->mutex);
    if (CKR_OK != *pError) {
        return (NSSItem *)NULL;
    }

    mdItem = fwObject->mdObject->GetAttribute(fwObject->mdObject, fwObject,
                                              fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                              fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
                                              attribute, pError);

    if (!mdItem.item) {
        if (CKR_OK == *pError) {
            *pError = CKR_GENERAL_ERROR;
        }

        goto done;
    }

    if (!itemOpt) {
        rv = nss_ZNEW(arenaOpt, NSSItem);
        if (!rv) {
            *pError = CKR_HOST_MEMORY;
            goto done;
        }
    } else {
        rv = itemOpt;
    }

    if (!rv->data) {
        rv->size = mdItem.item->size;
        rv->data = nss_ZAlloc(arenaOpt, rv->size);
        if (!rv->data) {
            *pError = CKR_HOST_MEMORY;
            if (!itemOpt) {
                nss_ZFreeIf(rv);
            }
            rv = (NSSItem *)NULL;
            goto done;
        }
    } else {
        if (rv->size >= mdItem.item->size) {
            rv->size = mdItem.item->size;
        } else {
            *pError = CKR_BUFFER_TOO_SMALL;
            /* Should we set rv->size to mdItem->size? */
            /* rv can't have been allocated */
            rv = (NSSItem *)NULL;
            goto done;
        }
    }

    (void)nsslibc_memcpy(rv->data, mdItem.item->data, rv->size);

    if (PR_TRUE == mdItem.needsFreeing) {
        PR_ASSERT(fwObject->mdObject->FreeAttribute);
        if (fwObject->mdObject->FreeAttribute) {
            *pError = fwObject->mdObject->FreeAttribute(&mdItem);
        }
    }

done:
    (void)nssCKFWMutex_Unlock(fwObject->mutex);
    return rv;
}

/*
 * nssCKFWObject_SetAttribute
 *
 */
NSS_IMPLEMENT CK_RV
nssCKFWObject_SetAttribute(
    NSSCKFWObject *fwObject,
    NSSCKFWSession *fwSession,
    CK_ATTRIBUTE_TYPE attribute,
    NSSItem *value)
{
    CK_RV error = CKR_OK;

#ifdef NSSDEBUG
    error = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != error) {
        return error;
    }
#endif /* NSSDEBUG */

    if (CKA_TOKEN == attribute) {
        /*
         * We're changing from a session object to a token object or
         * vice-versa.
         */

        CK_ATTRIBUTE a;
        NSSCKFWObject *newFwObject;
        NSSCKFWObject swab;

        /* CKA_TOKEN must be exactly one CK_BBOOL; reject any other size
         * before forwarding to CopyObject, which assumes sizeof(CK_BBOOL). */
        if (sizeof(CK_BBOOL) != value->size || (CK_VOID_PTR)NULL == value->data) {
            return CKR_ATTRIBUTE_VALUE_INVALID;
        }

        a.type = CKA_TOKEN;
        a.pValue = value->data;
        a.ulValueLen = value->size;

        newFwObject = nssCKFWSession_CopyObject(fwSession, fwObject,
                                                &a, 1, &error);
        if (!newFwObject) {
            if (CKR_OK == error) {
                error = CKR_GENERAL_ERROR;
            }
            return error;
        }

        /*
         * Actually, I bet the locking is worse than this.. this part of
         * the code could probably use some scrutiny and reworking.
         */
        error = nssCKFWMutex_Lock(fwObject->mutex);
        if (CKR_OK != error) {
            nssCKFWObject_Destroy(newFwObject);
            return error;
        }

        error = nssCKFWMutex_Lock(newFwObject->mutex);
        if (CKR_OK != error) {
            nssCKFWMutex_Unlock(fwObject->mutex);
            nssCKFWObject_Destroy(newFwObject);
            return error;
        }

        /*
         * Now, we have our new object, but it has a new fwObject pointer,
         * while we have to keep the existing one.  So quick swap the contents.
         */
        swab = *fwObject;
        *fwObject = *newFwObject;
        *newFwObject = swab;

        /* But keep the mutexes the same */
        swab.mutex = fwObject->mutex;
        fwObject->mutex = newFwObject->mutex;
        newFwObject->mutex = swab.mutex;

        (void)nssCKFWMutex_Unlock(newFwObject->mutex);
        (void)nssCKFWMutex_Unlock(fwObject->mutex);

        /*
         * Either remove or add this to the list of session objects
         */

        if (CK_FALSE == *(CK_BBOOL *)value->data) {
            /*
             * New one is a session object, except since we "stole" the fwObject, it's
             * not in the list.  Add it.
             */
            nssCKFWSession_RegisterSessionObject(fwSession, fwObject);
        } else {
            /*
             * New one is a token object, except since we "stole" the fwObject, it's
             * in the list.  Remove it.
             */
            if (fwObject->fwSession) {
                nssCKFWSession_DeregisterSessionObject(fwObject->fwSession, fwObject);
            }
        }

        /*
         * Now delete the old object.  Remember the names have changed.
         */
        nssCKFWObject_Destroy(newFwObject);

        return CKR_OK;
    } else {
        /*
         * An "ordinary" change.
         */
        if (!fwObject->mdObject->SetAttribute) {
            /* We could fake it with copying, like above.. later */
            return CKR_ATTRIBUTE_READ_ONLY;
        }

        error = nssCKFWMutex_Lock(fwObject->mutex);
        if (CKR_OK != error) {
            return error;
        }

        error = fwObject->mdObject->SetAttribute(fwObject->mdObject, fwObject,
                                                 fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                                 fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
                                                 attribute, value);

        (void)nssCKFWMutex_Unlock(fwObject->mutex);

        return error;
    }
}

/*
 * nssCKFWObject_GetObjectSize
 *
 */
NSS_IMPLEMENT CK_ULONG
nssCKFWObject_GetObjectSize(
    NSSCKFWObject *fwObject,
    CK_RV *pError)
{
    CK_ULONG rv;

#ifdef NSSDEBUG
    if (!pError) {
        return (CK_ULONG)0;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }
#endif /* NSSDEBUG */

    if (!fwObject->mdObject->GetObjectSize) {
        *pError = CKR_INFORMATION_SENSITIVE;
        return (CK_ULONG)0;
    }

    *pError = nssCKFWMutex_Lock(fwObject->mutex);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }

    rv = fwObject->mdObject->GetObjectSize(fwObject->mdObject, fwObject,
                                           fwObject->mdSession, fwObject->fwSession, fwObject->mdToken,
                                           fwObject->fwToken, fwObject->mdInstance, fwObject->fwInstance,
                                           pError);

    (void)nssCKFWMutex_Unlock(fwObject->mutex);
    return rv;
}

/*
 * NSSCKFWObject_GetMDObject
 *
 */
NSS_IMPLEMENT NSSCKMDObject *
NSSCKFWObject_GetMDObject(
    NSSCKFWObject *fwObject)
{
#ifdef DEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return (NSSCKMDObject *)NULL;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetMDObject(fwObject);
}

/*
 * NSSCKFWObject_GetArena
 *
 */
NSS_IMPLEMENT NSSArena *
NSSCKFWObject_GetArena(
    NSSCKFWObject *fwObject,
    CK_RV *pError)
{
#ifdef DEBUG
    if (!pError) {
        return (NSSArena *)NULL;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (NSSArena *)NULL;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetArena(fwObject, pError);
}

/*
 * NSSCKFWObject_IsTokenObject
 *
 */
NSS_IMPLEMENT CK_BBOOL
NSSCKFWObject_IsTokenObject(
    NSSCKFWObject *fwObject)
{
#ifdef DEBUG
    if (CKR_OK != nssCKFWObject_verifyPointer(fwObject)) {
        return CK_FALSE;
    }
#endif /* DEBUG */

    return nssCKFWObject_IsTokenObject(fwObject);
}

/*
 * NSSCKFWObject_GetAttributeCount
 *
 */
NSS_IMPLEMENT CK_ULONG
NSSCKFWObject_GetAttributeCount(
    NSSCKFWObject *fwObject,
    CK_RV *pError)
{
#ifdef DEBUG
    if (!pError) {
        return (CK_ULONG)0;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetAttributeCount(fwObject, pError);
}

/*
 * NSSCKFWObject_GetAttributeTypes
 *
 */
NSS_IMPLEMENT CK_RV
NSSCKFWObject_GetAttributeTypes(
    NSSCKFWObject *fwObject,
    CK_ATTRIBUTE_TYPE_PTR typeArray,
    CK_ULONG ulCount)
{
#ifdef DEBUG
    CK_RV error = CKR_OK;

    error = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != error) {
        return error;
    }

    if ((CK_ATTRIBUTE_TYPE_PTR)NULL == typeArray) {
        return CKR_ARGUMENTS_BAD;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetAttributeTypes(fwObject, typeArray, ulCount);
}

/*
 * NSSCKFWObject_GetAttributeSize
 *
 */
NSS_IMPLEMENT CK_ULONG
NSSCKFWObject_GetAttributeSize(
    NSSCKFWObject *fwObject,
    CK_ATTRIBUTE_TYPE attribute,
    CK_RV *pError)
{
#ifdef DEBUG
    if (!pError) {
        return (CK_ULONG)0;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetAttributeSize(fwObject, attribute, pError);
}

/*
 * NSSCKFWObject_GetAttribute
 *
 */
NSS_IMPLEMENT NSSItem *
NSSCKFWObject_GetAttribute(
    NSSCKFWObject *fwObject,
    CK_ATTRIBUTE_TYPE attribute,
    NSSItem *itemOpt,
    NSSArena *arenaOpt,
    CK_RV *pError)
{
#ifdef DEBUG
    if (!pError) {
        return (NSSItem *)NULL;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (NSSItem *)NULL;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetAttribute(fwObject, attribute, itemOpt, arenaOpt, pError);
}

/*
 * NSSCKFWObject_GetObjectSize
 *
 */
NSS_IMPLEMENT CK_ULONG
NSSCKFWObject_GetObjectSize(
    NSSCKFWObject *fwObject,
    CK_RV *pError)
{
#ifdef DEBUG
    if (!pError) {
        return (CK_ULONG)0;
    }

    *pError = nssCKFWObject_verifyPointer(fwObject);
    if (CKR_OK != *pError) {
        return (CK_ULONG)0;
    }
#endif /* DEBUG */

    return nssCKFWObject_GetObjectSize(fwObject, pError);
}
