/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * This file manages object type indepentent functions.
 */
#include "seccomon.h"
#include "secmod.h"
#include "secmodi.h"
#include "secmodti.h"
#include "pkcs11.h"
#include "pkcs11t.h"
#include "pk11func.h"
#include "key.h"
#include "secitem.h"
#include "secerr.h"
#include "sslerr.h"

#define PK11_SEARCH_CHUNKSIZE 10

/*
 * Build a block big enough to hold the data
 */
SECItem *
PK11_BlockData(SECItem *data, unsigned long size)
{
    SECItem *newData;

    if (size == 0u)
        return NULL;

    newData = (SECItem *)PORT_Alloc(sizeof(SECItem));
    if (newData == NULL)
        return NULL;

    newData->len = (data->len + (size - 1)) / size;
    newData->len *= size;

    newData->data = (unsigned char *)PORT_ZAlloc(newData->len);
    if (newData->data == NULL) {
        PORT_Free(newData);
        return NULL;
    }
    PORT_Memset(newData->data, newData->len - data->len, newData->len);
    PORT_Memcpy(newData->data, data->data, data->len);
    return newData;
}

SECStatus
PK11_DestroyObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object)
{
    CK_RV crv;

    PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_DestroyObject(slot->session, object);
    PK11_ExitSlotMonitor(slot);
    if (crv != CKR_OK) {
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
PK11_DestroyTokenObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE object)
{
    CK_RV crv;
    SECStatus rv = SECSuccess;
    CK_SESSION_HANDLE rwsession;

    rwsession = PK11_GetRWSession(slot);
    if (rwsession == CK_INVALID_SESSION) {
        PORT_SetError(SEC_ERROR_BAD_DATA);
        return SECFailure;
    }

    crv = PK11_GETTAB(slot)->C_DestroyObject(rwsession, object);
    if (crv != CKR_OK) {
        rv = SECFailure;
        PORT_SetError(PK11_MapError(crv));
    }
    PK11_RestoreROSession(slot, rwsession);
    return rv;
}

/*
 * Read in a single attribute into a SECItem. Allocate space for it with
 * PORT_Alloc unless an arena is supplied. In the latter case use the arena
 * to allocate the space.
 *
 * PK11_ReadAttribute sets the 'data' and 'len' fields of the SECItem but
 * does not modify its 'type' field.
 */
SECStatus
PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
                   CK_ATTRIBUTE_TYPE type, PLArenaPool *arena, SECItem *result)
{
    CK_ATTRIBUTE attr = { 0, NULL, 0 };
    CK_RV crv;

    attr.type = type;

    PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
    if (crv != CKR_OK) {
        PK11_ExitSlotMonitor(slot);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    if (arena) {
        attr.pValue = PORT_ArenaAlloc(arena, attr.ulValueLen);
    } else {
        attr.pValue = PORT_Alloc(attr.ulValueLen);
    }
    if (attr.pValue == NULL) {
        PK11_ExitSlotMonitor(slot);
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
    PK11_ExitSlotMonitor(slot);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        if (!arena)
            PORT_Free(attr.pValue);
        return SECFailure;
    }

    result->data = (unsigned char *)attr.pValue;
    result->len = attr.ulValueLen;

    return SECSuccess;
}

/*
 * Read in a single attribute into As a Ulong.
 */
CK_ULONG
PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
                        CK_ATTRIBUTE_TYPE type)
{
    CK_ATTRIBUTE attr;
    CK_ULONG value = CK_UNAVAILABLE_INFORMATION;
    CK_RV crv;

    PK11_SETATTRS(&attr, type, &value, sizeof(value));

    PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, id, &attr, 1);
    PK11_ExitSlotMonitor(slot);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
    }
    return value;
}

/*
 * check to see if a bool has been set.
 */
CK_BBOOL
PK11_HasAttributeSet(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
                     CK_ATTRIBUTE_TYPE type, PRBool haslock)
{
    CK_BBOOL ckvalue = CK_FALSE;
    CK_ATTRIBUTE theTemplate;
    CK_RV crv;

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

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

    return ckvalue;
}

/*
 * returns a full list of attributes. Allocate space for them. If an arena is
 * provided, allocate space out of the arena.
 */
CK_RV
PK11_GetAttributes(PLArenaPool *arena, PK11SlotInfo *slot,
                   CK_OBJECT_HANDLE obj, CK_ATTRIBUTE *attr, int count)
{
    int i;
    /* make pedantic happy... note that it's only used arena != NULL */
    void *mark = NULL;
    CK_RV crv;
    PORT_Assert(slot->session != CK_INVALID_SESSION);
    if (slot->session == CK_INVALID_SESSION)
        return CKR_SESSION_HANDLE_INVALID;

    /*
     * first get all the lengths of the parameters.
     */
    PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, obj, attr, count);
    if (crv != CKR_OK) {
        PK11_ExitSlotMonitor(slot);
        return crv;
    }

    if (arena) {
        mark = PORT_ArenaMark(arena);
        if (mark == NULL)
            return CKR_HOST_MEMORY;
    }

    /*
     * now allocate space to store the results.
     */
    for (i = 0; i < count; i++) {
        if (attr[i].ulValueLen == 0)
            continue;
        if (arena) {
            attr[i].pValue = PORT_ArenaAlloc(arena, attr[i].ulValueLen);
            if (attr[i].pValue == NULL) {
                /* arena failures, just release the mark */
                PORT_ArenaRelease(arena, mark);
                PK11_ExitSlotMonitor(slot);
                return CKR_HOST_MEMORY;
            }
        } else {
            attr[i].pValue = PORT_Alloc(attr[i].ulValueLen);
            if (attr[i].pValue == NULL) {
                /* Separate malloc failures, loop to release what we have
                 * so far */
                int j;
                for (j = 0; j < i; j++) {
                    PORT_Free(attr[j].pValue);
                    /* don't give the caller pointers to freed memory */
                    attr[j].pValue = NULL;
                }
                PK11_ExitSlotMonitor(slot);
                return CKR_HOST_MEMORY;
            }
        }
    }

    /*
     * finally get the results.
     */
    crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session, obj, attr, count);
    PK11_ExitSlotMonitor(slot);
    if (crv != CKR_OK) {
        if (arena) {
            PORT_ArenaRelease(arena, mark);
        } else {
            for (i = 0; i < count; i++) {
                PORT_Free(attr[i].pValue);
                /* don't give the caller pointers to freed memory */
                attr[i].pValue = NULL;
            }
        }
    } else if (arena && mark) {
        PORT_ArenaUnmark(arena, mark);
    }
    return crv;
}

PRBool
PK11_IsPermObject(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle)
{
    return (PRBool)PK11_HasAttributeSet(slot, handle, CKA_TOKEN, PR_FALSE);
}

char *
PK11_GetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id)
{
    char *nickname = NULL;
    SECItem result;
    SECStatus rv;

    rv = PK11_ReadAttribute(slot, id, CKA_LABEL, NULL, &result);
    if (rv != SECSuccess) {
        return NULL;
    }

    nickname = PORT_ZAlloc(result.len + 1);
    if (nickname == NULL) {
        PORT_Free(result.data);
        return NULL;
    }
    PORT_Memcpy(nickname, result.data, result.len);
    PORT_Free(result.data);
    return nickname;
}

SECStatus
PK11_SetObjectNickname(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
                       const char *nickname)
{
    int len = PORT_Strlen(nickname);
    CK_ATTRIBUTE setTemplate;
    CK_RV crv;
    CK_SESSION_HANDLE rwsession;

    if (len < 0) {
        return SECFailure;
    }

    PK11_SETATTRS(&setTemplate, CKA_LABEL, (CK_CHAR *)nickname, len);
    rwsession = PK11_GetRWSession(slot);
    if (rwsession == CK_INVALID_SESSION) {
        PORT_SetError(SEC_ERROR_BAD_DATA);
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, id,
                                                 &setTemplate, 1);
    PK11_RestoreROSession(slot, rwsession);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

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

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

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

    return session;
}

void
pk11_CloseSession(PK11SlotInfo *slot, CK_SESSION_HANDLE session, PRBool owner)
{
    if (!owner)
        return;
    if (!slot->isThreadSafe)
        PK11_EnterSlotMonitor(slot);
    (void)PK11_GETTAB(slot)->C_CloseSession(session);
    if (!slot->isThreadSafe)
        PK11_ExitSlotMonitor(slot);
}

SECStatus
PK11_CreateNewObject(PK11SlotInfo *slot, CK_SESSION_HANDLE session,
                     const CK_ATTRIBUTE *theTemplate, int count,
                     PRBool token, CK_OBJECT_HANDLE *objectID)
{
    CK_SESSION_HANDLE rwsession;
    CK_RV crv;
    SECStatus rv = SECSuccess;

    rwsession = session;
    if (token) {
        rwsession = PK11_GetRWSession(slot);
    } else if (rwsession == CK_INVALID_SESSION) {
        rwsession = slot->session;
        if (rwsession != CK_INVALID_SESSION)
            PK11_EnterSlotMonitor(slot);
    }
    if (rwsession == CK_INVALID_SESSION) {
        PORT_SetError(SEC_ERROR_BAD_DATA);
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_CreateObject(rwsession,
                                            /* cast away const :-( */ (CK_ATTRIBUTE_PTR)theTemplate,
                                            count, objectID);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        rv = SECFailure;
    }
    if (token) {
        PK11_RestoreROSession(slot, rwsession);
    } else if (session == CK_INVALID_SESSION) {
        PK11_ExitSlotMonitor(slot);
    }

    return rv;
}

/* This function may add a maximum of 9 attributes. */
unsigned int
pk11_OpFlagsToAttributes(CK_FLAGS flags, CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue)
{

    const static CK_ATTRIBUTE_TYPE attrTypes[12] = {
        CKA_ENCRYPT, CKA_DECRYPT, 0 /* DIGEST */, CKA_SIGN,
        CKA_SIGN_RECOVER, CKA_VERIFY, CKA_VERIFY_RECOVER, 0 /* GEN */,
        0 /* GEN PAIR */, CKA_WRAP, CKA_UNWRAP, CKA_DERIVE
    };

    const CK_ATTRIBUTE_TYPE *pType = attrTypes;
    CK_ATTRIBUTE *attr = attrs;
    CK_FLAGS test = CKF_ENCRYPT;

    PR_ASSERT(!(flags & ~CKF_KEY_OPERATION_FLAGS));
    flags &= CKF_KEY_OPERATION_FLAGS;

    for (; flags && test <= CKF_DERIVE; test <<= 1, ++pType) {
        if (test & flags) {
            flags ^= test;
            PR_ASSERT(*pType);
            PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
            ++attr;
        }
    }
    return (attr - attrs);
}

/*
 * Check for conflicting flags, for example, if both PK11_ATTR_PRIVATE
 * and PK11_ATTR_PUBLIC are set.
 */
PRBool
pk11_BadAttrFlags(PK11AttrFlags attrFlags)
{
    PK11AttrFlags trueFlags = attrFlags & 0x55555555;
    PK11AttrFlags falseFlags = (attrFlags >> 1) & 0x55555555;
    return ((trueFlags & falseFlags) != 0);
}

/*
 * This function may add a maximum of 5 attributes.
 * The caller must make sure the attribute flags don't have conflicts.
 */
unsigned int
pk11_AttrFlagsToAttributes(PK11AttrFlags attrFlags, CK_ATTRIBUTE *attrs,
                           CK_BBOOL *ckTrue, CK_BBOOL *ckFalse)
{
    const static CK_ATTRIBUTE_TYPE attrTypes[5] = {
        CKA_TOKEN, CKA_PRIVATE, CKA_MODIFIABLE, CKA_SENSITIVE,
        CKA_EXTRACTABLE
    };

    const CK_ATTRIBUTE_TYPE *pType = attrTypes;
    CK_ATTRIBUTE *attr = attrs;
    PK11AttrFlags test = PK11_ATTR_TOKEN;

    PR_ASSERT(!pk11_BadAttrFlags(attrFlags));

    /* we test two related bitflags in each iteration */
    for (; attrFlags && test <= PK11_ATTR_EXTRACTABLE; test <<= 2, ++pType) {
        if (test & attrFlags) {
            attrFlags ^= test;
            PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
            ++attr;
        } else if ((test << 1) & attrFlags) {
            attrFlags ^= (test << 1);
            PK11_SETATTRS(attr, *pType, ckFalse, sizeof *ckFalse);
            ++attr;
        }
    }
    return (attr - attrs);
}

/*
 * Some non-compliant PKCS #11 vendors do not give us the modulus, so actually
 * set up a signature to get the signaure length.
 */
static int
pk11_backupGetSignLength(SECKEYPrivateKey *key)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_MECHANISM mech = { 0, NULL, 0 };
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_ULONG len;
    CK_RV crv;
    unsigned char h_data[20] = { 0 };
    unsigned char buf[20]; /* obviously to small */
    CK_ULONG smallLen = sizeof(buf);

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

    session = pk11_GetNewSession(slot, &owner);
    if (!owner || !(slot->isThreadSafe))
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, key->pkcs11ID);
    if (crv != CKR_OK) {
        if (!owner || !(slot->isThreadSafe))
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        return -1;
    }
    len = 0;
    crv = PK11_GETTAB(slot)->C_Sign(session, h_data, sizeof(h_data),
                                    NULL, &len);
    /* now call C_Sign with too small a buffer to clear the session state */
    (void)PK11_GETTAB(slot)->C_Sign(session, h_data, sizeof(h_data), buf, &smallLen);

    if (!owner || !(slot->isThreadSafe))
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return -1;
    }
    return len;
}

/*
 * get the length of a signature object based on the key
 */
int
PK11_SignatureLen(SECKEYPrivateKey *key)
{
    int val;
    SECItem attributeItem = { siBuffer, NULL, 0 };
    SECStatus rv;
    int length;

    switch (key->keyType) {
        case rsaKey:
            val = PK11_GetPrivateModulusLen(key);
            if (val == -1) {
                return pk11_backupGetSignLength(key);
            }
            return (unsigned long)val;

        case fortezzaKey:
            return 40;

        case dsaKey:
            rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, CKA_SUBPRIME,
                                    NULL, &attributeItem);
            if (rv == SECSuccess) {
                length = attributeItem.len;
                if ((length > 0) && attributeItem.data[0] == 0) {
                    length--;
                }
                PORT_Free(attributeItem.data);
                return length * 2;
            }
            return pk11_backupGetSignLength(key);

        case ecKey:
            rv = PK11_ReadAttribute(key->pkcs11Slot, key->pkcs11ID, CKA_EC_PARAMS,
                                    NULL, &attributeItem);
            if (rv == SECSuccess) {
                length = SECKEY_ECParamsToBasePointOrderLen(&attributeItem);
                PORT_Free(attributeItem.data);
                if (length != 0) {
                    length = ((length + 7) / 8) * 2;
                    return length;
                }
            }
            return pk11_backupGetSignLength(key);
        default:
            break;
    }
    PORT_SetError(SEC_ERROR_INVALID_KEY);
    return 0;
}

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

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

PRBool
pk11_FindAttrInTemplate(CK_ATTRIBUTE *attr, unsigned int numAttrs,
                        CK_ATTRIBUTE_TYPE target)
{
    for (; numAttrs > 0; ++attr, --numAttrs) {
        if (attr->type == target)
            return PR_TRUE;
    }
    return PR_FALSE;
}

/*
 * Recover the Signed data. We need this because our old verify can't
 * figure out which hash algorithm to use until we decryptted this.
 */
SECStatus
PK11_VerifyRecover(SECKEYPublicKey *key, const SECItem *sig,
                   SECItem *dsig, void *wincx)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_OBJECT_HANDLE id = key->pkcs11ID;
    CK_MECHANISM mech = { 0, NULL, 0 };
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_ULONG len;
    CK_RV crv;

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

    if (slot == NULL) {
        slot = PK11_GetBestSlotWithAttributes(mech.mechanism,
                                              CKF_VERIFY_RECOVER, 0, wincx);
        if (slot == NULL) {
            PORT_SetError(SEC_ERROR_NO_MODULE);
            return SECFailure;
        }
        id = PK11_ImportPublicKey(slot, key, PR_FALSE);
    } else {
        PK11_ReferenceSlot(slot);
    }

    if (id == CK_INVALID_HANDLE) {
        PK11_FreeSlot(slot);
        PORT_SetError(SEC_ERROR_BAD_KEY);
        return SECFailure;
    }

    session = pk11_GetNewSession(slot, &owner);
    if (!owner || !(slot->isThreadSafe))
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(session, &mech, id);
    if (crv != CKR_OK) {
        if (!owner || !(slot->isThreadSafe))
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        PK11_FreeSlot(slot);
        return SECFailure;
    }
    len = dsig->len;
    crv = PK11_GETTAB(slot)->C_VerifyRecover(session, sig->data,
                                             sig->len, dsig->data, &len);
    if (!owner || !(slot->isThreadSafe))
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    dsig->len = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        PK11_FreeSlot(slot);
        return SECFailure;
    }
    PK11_FreeSlot(slot);
    return SECSuccess;
}

/*
 * verify a signature from its hash.
 */
SECStatus
PK11_Verify(SECKEYPublicKey *key, const SECItem *sig, const SECItem *hash,
            void *wincx)
{
    CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
    return PK11_VerifyWithMechanism(key, mech, NULL, sig, hash, wincx);
}

/*
 * Verify a signature from its hash using the given algorithm.
 */
SECStatus
PK11_VerifyWithMechanism(SECKEYPublicKey *key, CK_MECHANISM_TYPE mechanism,
                         const SECItem *param, const SECItem *sig,
                         const SECItem *hash, void *wincx)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_OBJECT_HANDLE id = key->pkcs11ID;
    CK_MECHANISM mech = { 0, NULL, 0 };
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_RV crv;

    mech.mechanism = mechanism;
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }

    if (slot == NULL) {
        unsigned int length = 0;
        if ((mech.mechanism == CKM_DSA) &&
            /* 129 is 1024 bits translated to bytes and
             * padded with an optional '0' to maintain a
             * positive sign */
            (key->u.dsa.params.prime.len > 129)) {
            /* we need to get a slot that not only can do DSA, but can do DSA2
             * key lengths */
            length = key->u.dsa.params.prime.len;
            if (key->u.dsa.params.prime.data[0] == 0) {
                length--;
            }
            /* convert keysize to bits for slot lookup */
            length *= 8;
        }
        slot = PK11_GetBestSlotWithAttributes(mech.mechanism,
                                              CKF_VERIFY, length, wincx);
        if (slot == NULL) {
            PORT_SetError(SEC_ERROR_NO_MODULE);
            return SECFailure;
        }
        id = PK11_ImportPublicKey(slot, key, PR_FALSE);

    } else {
        PK11_ReferenceSlot(slot);
    }

    if (id == CK_INVALID_HANDLE) {
        PK11_FreeSlot(slot);
        PORT_SetError(SEC_ERROR_BAD_KEY);
        return SECFailure;
    }

    session = pk11_GetNewSession(slot, &owner);
    if (!owner || !(slot->isThreadSafe))
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_VerifyInit(session, &mech, id);
    if (crv != CKR_OK) {
        if (!owner || !(slot->isThreadSafe))
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PK11_FreeSlot(slot);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_Verify(session, hash->data,
                                      hash->len, sig->data, sig->len);
    if (!owner || !(slot->isThreadSafe))
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    PK11_FreeSlot(slot);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

/*
 * sign a hash. The algorithm is determined by the key.
 */
SECStatus
PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, const SECItem *hash)
{
    CK_MECHANISM_TYPE mech = PK11_MapSignKeyType(key->keyType);
    return PK11_SignWithMechanism(key, mech, NULL, sig, hash);
}

/*
 * Sign a hash using the given algorithm.
 */
SECStatus
PK11_SignWithMechanism(SECKEYPrivateKey *key, CK_MECHANISM_TYPE mechanism,
                       const SECItem *param, SECItem *sig, const SECItem *hash)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_MECHANISM mech = { 0, NULL, 0 };
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    PRBool haslock = PR_FALSE;
    CK_ULONG len;
    CK_RV crv;

    mech.mechanism = mechanism;
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }

    if (SECKEY_HAS_ATTRIBUTE_SET(key, CKA_PRIVATE)) {
        PK11_HandlePasswordCheck(slot, key->wincx);
    }

    session = pk11_GetNewSession(slot, &owner);
    haslock = (!owner || !(slot->isThreadSafe));
    if (haslock)
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, key->pkcs11ID);
    if (crv != CKR_OK) {
        if (haslock)
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }

    /* PKCS11 2.20 says if CKA_ALWAYS_AUTHENTICATE then
     * do C_Login with CKU_CONTEXT_SPECIFIC
     * between C_SignInit and C_Sign */
    if (SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, CKA_ALWAYS_AUTHENTICATE, haslock)) {
        PK11_DoPassword(slot, session, PR_FALSE, key->wincx, haslock, PR_TRUE);
    }

    len = sig->len;
    crv = PK11_GETTAB(slot)->C_Sign(session, hash->data,
                                    hash->len, sig->data, &len);
    if (haslock)
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    sig->len = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

/*
 * sign data with a MAC key.
 */
SECStatus
PK11_SignWithSymKey(PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism,
                    SECItem *param, SECItem *sig, const SECItem *data)
{
    PK11SlotInfo *slot = symKey->slot;
    CK_MECHANISM mech = { 0, NULL, 0 };
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    PRBool haslock = PR_FALSE;
    CK_ULONG len;
    CK_RV crv;

    mech.mechanism = mechanism;
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }

    session = pk11_GetNewSession(slot, &owner);
    haslock = (!owner || !(slot->isThreadSafe));
    if (haslock)
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_SignInit(session, &mech, symKey->objectID);
    if (crv != CKR_OK) {
        if (haslock)
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }

    len = sig->len;
    crv = PK11_GETTAB(slot)->C_Sign(session, data->data,
                                    data->len, sig->data, &len);
    if (haslock)
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    sig->len = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
PK11_Decrypt(PK11SymKey *symKey,
             CK_MECHANISM_TYPE mechanism, SECItem *param,
             unsigned char *out, unsigned int *outLen,
             unsigned int maxLen,
             const unsigned char *enc, unsigned encLen)
{
    PK11SlotInfo *slot = symKey->slot;
    CK_MECHANISM mech = { 0, NULL, 0 };
    CK_ULONG len = maxLen;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    PRBool haslock = PR_FALSE;
    CK_RV crv;

    mech.mechanism = mechanism;
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }

    session = pk11_GetNewSession(slot, &owner);
    haslock = (!owner || !slot->isThreadSafe);
    if (haslock)
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_DecryptInit(session, &mech, symKey->objectID);
    if (crv != CKR_OK) {
        if (haslock)
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }

    crv = PK11_GETTAB(slot)->C_Decrypt(session, (unsigned char *)enc, encLen,
                                       out, &len);
    if (haslock)
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    *outLen = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
PK11_Encrypt(PK11SymKey *symKey,
             CK_MECHANISM_TYPE mechanism, SECItem *param,
             unsigned char *out, unsigned int *outLen,
             unsigned int maxLen,
             const unsigned char *data, unsigned int dataLen)
{
    PK11SlotInfo *slot = symKey->slot;
    CK_MECHANISM mech = { 0, NULL, 0 };
    CK_ULONG len = maxLen;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    PRBool haslock = PR_FALSE;
    CK_RV crv;

    mech.mechanism = mechanism;
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }

    session = pk11_GetNewSession(slot, &owner);
    haslock = (!owner || !slot->isThreadSafe);
    if (haslock)
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_EncryptInit(session, &mech, symKey->objectID);
    if (crv != CKR_OK) {
        if (haslock)
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_Encrypt(session, (unsigned char *)data,
                                       dataLen, out, &len);
    if (haslock)
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    *outLen = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

static SECStatus
pk11_PrivDecryptRaw(SECKEYPrivateKey *key,
                    unsigned char *data, unsigned *outLen, unsigned int maxLen,
                    const unsigned char *enc, unsigned encLen,
                    CK_MECHANISM_PTR mech)
{
    PK11SlotInfo *slot = key->pkcs11Slot;
    CK_ULONG out = maxLen;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    PRBool haslock = PR_FALSE;
    CK_RV crv;

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

    /* Why do we do a PK11_handle check here? for simple
     * decryption? .. because the user may have asked for 'ask always'
     * and this is a private key operation. In practice, thought, it's mute
     * since only servers wind up using this function */
    if (SECKEY_HAS_ATTRIBUTE_SET(key, CKA_PRIVATE)) {
        PK11_HandlePasswordCheck(slot, key->wincx);
    }
    session = pk11_GetNewSession(slot, &owner);
    haslock = (!owner || !(slot->isThreadSafe));
    if (haslock)
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_DecryptInit(session, mech, key->pkcs11ID);
    if (crv != CKR_OK) {
        if (haslock)
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }

    /* PKCS11 2.20 says if CKA_ALWAYS_AUTHENTICATE then
     * do C_Login with CKU_CONTEXT_SPECIFIC
     * between C_DecryptInit and C_Decrypt
     * ... But see note above about servers */
    if (SECKEY_HAS_ATTRIBUTE_SET_LOCK(key, CKA_ALWAYS_AUTHENTICATE, haslock)) {
        PK11_DoPassword(slot, session, PR_FALSE, key->wincx, haslock, PR_TRUE);
    }

    crv = PK11_GETTAB(slot)->C_Decrypt(session, (unsigned char *)enc, encLen,
                                       data, &out);
    if (haslock)
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    *outLen = out;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
PK11_PubDecryptRaw(SECKEYPrivateKey *key,
                   unsigned char *data, unsigned *outLen, unsigned int maxLen,
                   const unsigned char *enc, unsigned encLen)
{
    CK_MECHANISM mech = { CKM_RSA_X_509, NULL, 0 };
    return pk11_PrivDecryptRaw(key, data, outLen, maxLen, enc, encLen, &mech);
}

SECStatus
PK11_PrivDecryptPKCS1(SECKEYPrivateKey *key,
                      unsigned char *data, unsigned *outLen, unsigned int maxLen,
                      const unsigned char *enc, unsigned encLen)
{
    CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
    return pk11_PrivDecryptRaw(key, data, outLen, maxLen, enc, encLen, &mech);
}

static SECStatus
pk11_PubEncryptRaw(SECKEYPublicKey *key,
                   unsigned char *out, unsigned int *outLen,
                   unsigned int maxLen,
                   const unsigned char *data, unsigned dataLen,
                   CK_MECHANISM_PTR mech, void *wincx)
{
    PK11SlotInfo *slot;
    CK_OBJECT_HANDLE id;
    CK_ULONG len = maxLen;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_RV crv;

    slot = PK11_GetBestSlotWithAttributes(mech->mechanism, CKF_ENCRYPT, 0, wincx);
    if (slot == NULL) {
        PORT_SetError(SEC_ERROR_NO_MODULE);
        return SECFailure;
    }

    id = PK11_ImportPublicKey(slot, key, PR_FALSE);

    if (id == CK_INVALID_HANDLE) {
        PK11_FreeSlot(slot);
        PORT_SetError(SEC_ERROR_BAD_KEY);
        return SECFailure;
    }

    session = pk11_GetNewSession(slot, &owner);
    if (!owner || !(slot->isThreadSafe))
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_EncryptInit(session, mech, id);
    if (crv != CKR_OK) {
        if (!owner || !(slot->isThreadSafe))
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PK11_FreeSlot(slot);
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_Encrypt(session, (unsigned char *)data, dataLen,
                                       out, &len);
    if (!owner || !(slot->isThreadSafe))
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    PK11_FreeSlot(slot);
    *outLen = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
PK11_PubEncryptRaw(SECKEYPublicKey *key,
                   unsigned char *enc,
                   const unsigned char *data, unsigned dataLen,
                   void *wincx)
{
    CK_MECHANISM mech = { CKM_RSA_X_509, NULL, 0 };
    unsigned int outLen;
    if (!key || key->keyType != rsaKey) {
        PORT_SetError(SEC_ERROR_BAD_KEY);
        return SECFailure;
    }
    outLen = SECKEY_PublicKeyStrength(key);
    return pk11_PubEncryptRaw(key, enc, &outLen, outLen, data, dataLen, &mech,
                              wincx);
}

SECStatus
PK11_PubEncryptPKCS1(SECKEYPublicKey *key,
                     unsigned char *enc,
                     const unsigned char *data, unsigned dataLen,
                     void *wincx)
{
    CK_MECHANISM mech = { CKM_RSA_PKCS, NULL, 0 };
    unsigned int outLen;
    if (!key || key->keyType != rsaKey) {
        PORT_SetError(SEC_ERROR_BAD_KEY);
        return SECFailure;
    }
    outLen = SECKEY_PublicKeyStrength(key);
    return pk11_PubEncryptRaw(key, enc, &outLen, outLen, data, dataLen, &mech,
                              wincx);
}

SECStatus
PK11_PrivDecrypt(SECKEYPrivateKey *key,
                 CK_MECHANISM_TYPE mechanism, SECItem *param,
                 unsigned char *out, unsigned int *outLen,
                 unsigned int maxLen,
                 const unsigned char *enc, unsigned encLen)
{
    CK_MECHANISM mech = { mechanism, NULL, 0 };
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }
    return pk11_PrivDecryptRaw(key, out, outLen, maxLen, enc, encLen, &mech);
}

SECStatus
PK11_PubEncrypt(SECKEYPublicKey *key,
                CK_MECHANISM_TYPE mechanism, SECItem *param,
                unsigned char *out, unsigned int *outLen,
                unsigned int maxLen,
                const unsigned char *data, unsigned dataLen,
                void *wincx)
{
    CK_MECHANISM mech = { mechanism, NULL, 0 };
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    }
    return pk11_PubEncryptRaw(key, out, outLen, maxLen, data, dataLen, &mech,
                              wincx);
}

SECKEYPrivateKey *
PK11_UnwrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
                   CK_MECHANISM_TYPE wrapType, SECItem *param,
                   SECItem *wrappedKey, SECItem *label,
                   SECItem *idValue, PRBool perm, PRBool sensitive,
                   CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage,
                   int usageCount, void *wincx)
{
    CK_BBOOL cktrue = CK_TRUE;
    CK_BBOOL ckfalse = CK_FALSE;
    CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
    CK_ATTRIBUTE keyTemplate[15];
    int templateCount = 0;
    CK_OBJECT_HANDLE privKeyID;
    CK_MECHANISM mechanism;
    CK_ATTRIBUTE *attrs = keyTemplate;
    SECItem *param_free = NULL, *ck_id = NULL;
    CK_RV crv;
    CK_SESSION_HANDLE rwsession;
    PK11SymKey *newKey = NULL;
    int i;

    if (!slot || !wrappedKey || !idValue) {
        /* SET AN ERROR!!! */
        return NULL;
    }

    ck_id = PK11_MakeIDFromPubKey(idValue);
    if (!ck_id) {
        return NULL;
    }

    PK11_SETATTRS(attrs, CKA_TOKEN, perm ? &cktrue : &ckfalse,
                  sizeof(cktrue));
    attrs++;
    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
    attrs++;
    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
    attrs++;
    PK11_SETATTRS(attrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse,
                  sizeof(cktrue));
    attrs++;
    PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse,
                  sizeof(cktrue));
    attrs++;
    if (label && label->data) {
        PK11_SETATTRS(attrs, CKA_LABEL, label->data, label->len);
        attrs++;
    }
    PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
    attrs++;
    for (i = 0; i < usageCount; i++) {
        PK11_SETATTRS(attrs, usage[i], &cktrue, sizeof(cktrue));
        attrs++;
    }

    if (PK11_IsInternal(slot)) {
        PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, idValue->data,
                      idValue->len);
        attrs++;
    }

    templateCount = attrs - keyTemplate;
    PR_ASSERT(templateCount <= (sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)));

    mechanism.mechanism = wrapType;
    if (!param)
        param = param_free = PK11_ParamFromIV(wrapType, NULL);
    if (param) {
        mechanism.pParameter = param->data;
        mechanism.ulParameterLen = param->len;
    } else {
        mechanism.pParameter = NULL;
        mechanism.ulParameterLen = 0;
    }

    if (wrappingKey->slot != slot) {
        newKey = pk11_CopyToSlot(slot, wrapType, CKA_UNWRAP, wrappingKey);
    } else {
        newKey = PK11_ReferenceSymKey(wrappingKey);
    }

    if (newKey) {
        if (perm) {
            /* Get RW Session will either lock the monitor if necessary,
             *  or return a thread safe session handle, or fail. */
            rwsession = PK11_GetRWSession(slot);
        } else {
            rwsession = slot->session;
            if (rwsession != CK_INVALID_SESSION)
                PK11_EnterSlotMonitor(slot);
        }
        /* This is a lot a work to deal with fussy PKCS #11 modules
         * that can't bother to return BAD_DATA when presented with an
         * invalid session! */
        if (rwsession == CK_INVALID_SESSION) {
            PORT_SetError(SEC_ERROR_BAD_DATA);
            goto loser;
        }
        crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism,
                                             newKey->objectID,
                                             wrappedKey->data,
                                             wrappedKey->len, keyTemplate,
                                             templateCount, &privKeyID);

        if (perm) {
            PK11_RestoreROSession(slot, rwsession);
        } else {
            PK11_ExitSlotMonitor(slot);
        }
        PK11_FreeSymKey(newKey);
        newKey = NULL;
    } else {
        crv = CKR_FUNCTION_NOT_SUPPORTED;
    }

    SECITEM_FreeItem(ck_id, PR_TRUE);
    ck_id = NULL;

    if (crv != CKR_OK) {
        /* we couldn't unwrap the key, use the internal module to do the
         * unwrap, then load the new key into the token */
        PK11SlotInfo *int_slot = PK11_GetInternalSlot();

        if (int_slot && (slot != int_slot)) {
            SECKEYPrivateKey *privKey = PK11_UnwrapPrivKey(int_slot,
                                                           wrappingKey, wrapType, param, wrappedKey, label,
                                                           idValue, PR_FALSE, PR_FALSE,
                                                           keyType, usage, usageCount, wincx);
            if (privKey) {
                SECKEYPrivateKey *newPrivKey = PK11_LoadPrivKey(slot, privKey,
                                                                NULL, perm, sensitive);
                SECKEY_DestroyPrivateKey(privKey);
                PK11_FreeSlot(int_slot);
                return newPrivKey;
            }
        }
        if (int_slot)
            PK11_FreeSlot(int_slot);
        PORT_SetError(PK11_MapError(crv));
        return NULL;
    }
    return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx);

loser:
    if (newKey) {
        PK11_FreeSymKey(newKey);
    }
    if (ck_id) {
        SECITEM_FreeItem(ck_id, PR_TRUE);
    }
    return NULL;
}

/*
 * Now we're going to wrap a SECKEYPrivateKey with a PK11SymKey
 * The strategy is to get both keys to reside in the same slot,
 * one that can perform the desired crypto mechanism and then
 * call C_WrapKey after all the setup has taken place.
 */
SECStatus
PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
                 SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType,
                 SECItem *param, SECItem *wrappedKey, void *wincx)
{
    PK11SlotInfo *privSlot = privKey->pkcs11Slot; /* The slot where
                                                   * the private key
                                                   * we are going to
                                                   * wrap lives.
                                                   */
    PK11SymKey *newSymKey = NULL;
    SECKEYPrivateKey *newPrivKey = NULL;
    SECItem *param_free = NULL;
    CK_ULONG len = wrappedKey->len;
    CK_MECHANISM mech;
    CK_RV crv;

    if (!privSlot || !PK11_DoesMechanism(privSlot, wrapType)) {
        /* Figure out a slot that does the mechanism and try to import
         * the private key onto that slot.
         */
        PK11SlotInfo *int_slot = PK11_GetInternalSlot();

        privSlot = int_slot; /* The private key has a new home */
        newPrivKey = PK11_LoadPrivKey(privSlot, privKey, NULL, PR_FALSE, PR_FALSE);
        /* newPrivKey has allocated its own reference to the slot, so it's
         * safe until we destroy newPrivkey.
         */
        PK11_FreeSlot(int_slot);
        if (newPrivKey == NULL) {
            return SECFailure;
        }
        privKey = newPrivKey;
    }

    if (privSlot != wrappingKey->slot) {
        newSymKey = pk11_CopyToSlot(privSlot, wrapType, CKA_WRAP,
                                    wrappingKey);
        wrappingKey = newSymKey;
    }

    if (wrappingKey == NULL) {
        if (newPrivKey) {
            SECKEY_DestroyPrivateKey(newPrivKey);
        }
        return SECFailure;
    }
    mech.mechanism = wrapType;
    if (!param) {
        param = param_free = PK11_ParamFromIV(wrapType, NULL);
    }
    if (param) {
        mech.pParameter = param->data;
        mech.ulParameterLen = param->len;
    } else {
        mech.pParameter = NULL;
        mech.ulParameterLen = 0;
    }

    PK11_EnterSlotMonitor(privSlot);
    crv = PK11_GETTAB(privSlot)->C_WrapKey(privSlot->session, &mech,
                                           wrappingKey->objectID,
                                           privKey->pkcs11ID,
                                           wrappedKey->data, &len);
    PK11_ExitSlotMonitor(privSlot);

    if (newSymKey) {
        PK11_FreeSymKey(newSymKey);
    }
    if (newPrivKey) {
        SECKEY_DestroyPrivateKey(newPrivKey);
    }
    if (param_free) {
        SECITEM_FreeItem(param_free, PR_TRUE);
    }

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

    wrappedKey->len = len;
    return SECSuccess;
}

#if 0
/*
 * Sample code relating to linked list returned by PK11_FindGenericObjects
 */

/*
 * You can walk the list with the following code:
 */
    firstObj = PK11_FindGenericObjects(slot, objClass);
    for (thisObj=firstObj;
         thisObj;
         thisObj=PK11_GetNextGenericObject(thisObj)) {
        /* operate on thisObj */
    }
/*
 * If you want a particular object from the list...
 */
    firstObj = PK11_FindGenericObjects(slot, objClass);
    for (thisObj=firstObj;
         thisObj;
         thisObj=PK11_GetNextGenericObject(thisObj)) {
      if (isMyObj(thisObj)) {
       if ( thisObj == firstObj) {
                /* NOTE: firstObj could be NULL at this point */
      firstObj = PK11_GetNextGenericObject(thsObj);
       }
       PK11_UnlinkGenericObject(thisObj);
            myObj = thisObj;
            break;
        }
    }

    PK11_DestroyGenericObjects(firstObj);

      /* use myObj */

    PK11_DestroyGenericObject(myObj);
#endif /* sample code */

/*
 * return a linked, non-circular list of generic objects.
 * If you are only interested
 * in one object, just use the first object in the list. To find the
 * rest of the list use PK11_GetNextGenericObject() to return the next object.
 */
PK11GenericObject *
PK11_FindGenericObjects(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass)
{
    CK_ATTRIBUTE template[1];
    CK_ATTRIBUTE *attrs = template;
    CK_OBJECT_HANDLE *objectIDs = NULL;
    PK11GenericObject *lastObj = NULL, *obj;
    PK11GenericObject *firstObj = NULL;
    int i, count = 0;

    PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass));
    attrs++;

    objectIDs = pk11_FindObjectsByTemplate(slot, template, 1, &count);
    if (objectIDs == NULL) {
        return NULL;
    }

    /* where we connect our object once we've created it.. */
    for (i = 0; i < count; i++) {
        obj = PORT_New(PK11GenericObject);
        if (!obj) {
            if (firstObj) {
                PK11_DestroyGenericObjects(firstObj);
            }
            PORT_Free(objectIDs);
            return NULL;
        }
        /* initialize it */
        obj->slot = PK11_ReferenceSlot(slot);
        obj->objectID = objectIDs[i];
        obj->next = NULL;
        obj->prev = NULL;

        /* link it in */
        if (firstObj == NULL) {
            firstObj = obj;
        } else {
            PK11_LinkGenericObject(lastObj, obj);
        }
        lastObj = obj;
    }
    PORT_Free(objectIDs);
    return firstObj;
}

/*
 * get the Next Object in the list.
 */
PK11GenericObject *
PK11_GetNextGenericObject(PK11GenericObject *object)
{
    return object->next;
}

PK11GenericObject *
PK11_GetPrevGenericObject(PK11GenericObject *object)
{
    return object->prev;
}

/*
 * Link a single object into a new list.
 * if the object is already in another list, remove it first.
 */
SECStatus
PK11_LinkGenericObject(PK11GenericObject *list, PK11GenericObject *object)
{
    PK11_UnlinkGenericObject(object);
    object->prev = list;
    object->next = list->next;
    list->next = object;
    if (object->next != NULL) {
        object->next->prev = object;
    }
    return SECSuccess;
}

/*
 * remove an object from the list. If the object isn't already in
 * a list unlink becomes a noop.
 */
SECStatus
PK11_UnlinkGenericObject(PK11GenericObject *object)
{
    if (object->prev != NULL) {
        object->prev->next = object->next;
    }
    if (object->next != NULL) {
        object->next->prev = object->prev;
    }

    object->next = NULL;
    object->prev = NULL;
    return SECSuccess;
}

/*
 * This function removes a single object from the list and destroys it.
 * For an already unlinked object there is no difference between
 * PK11_DestroyGenericObject and PK11_DestroyGenericObjects
 */
SECStatus
PK11_DestroyGenericObject(PK11GenericObject *object)
{
    if (object == NULL) {
        return SECSuccess;
    }

    PK11_UnlinkGenericObject(object);
    if (object->slot) {
        PK11_FreeSlot(object->slot);
    }
    PORT_Free(object);
    return SECSuccess;
}

/*
 * walk down a link list of generic objects destroying them.
 * This will destroy all objects in a list that the object is linked into.
 * (the list is traversed in both directions).
 */
SECStatus
PK11_DestroyGenericObjects(PK11GenericObject *objects)
{
    PK11GenericObject *nextObject;
    PK11GenericObject *prevObject;

    if (objects == NULL) {
        return SECSuccess;
    }

    nextObject = objects->next;
    prevObject = objects->prev;

    /* delete all the objects after it in the list */
    for (; objects; objects = nextObject) {
        nextObject = objects->next;
        PK11_DestroyGenericObject(objects);
    }
    /* delete all the objects before it in the list */
    for (objects = prevObject; objects; objects = prevObject) {
        prevObject = objects->prev;
        PK11_DestroyGenericObject(objects);
    }
    return SECSuccess;
}

/*
 * Hand Create a new object and return the Generic object for our new object.
 */
PK11GenericObject *
PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
                         int count, PRBool token)
{
    CK_OBJECT_HANDLE objectID;
    PK11GenericObject *obj;
    CK_RV crv;

    PK11_EnterSlotMonitor(slot);
    crv = PK11_CreateNewObject(slot, slot->session, pTemplate, count,
                               token, &objectID);
    PK11_ExitSlotMonitor(slot);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return NULL;
    }

    obj = PORT_New(PK11GenericObject);
    if (!obj) {
        /* error set by PORT_New */
        return NULL;
    }

    /* initialize it */
    obj->slot = PK11_ReferenceSlot(slot);
    obj->objectID = objectID;
    obj->next = NULL;
    obj->prev = NULL;
    return obj;
}

/*
 * Change an attribute on a raw object
 */
SECStatus
PK11_WriteRawAttribute(PK11ObjectType objType, void *objSpec,
                       CK_ATTRIBUTE_TYPE attrType, SECItem *item)
{
    PK11SlotInfo *slot = NULL;
    CK_OBJECT_HANDLE handle = 0;
    CK_ATTRIBUTE setTemplate;
    CK_RV crv;
    CK_SESSION_HANDLE rwsession;

    switch (objType) {
        case PK11_TypeGeneric:
            slot = ((PK11GenericObject *)objSpec)->slot;
            handle = ((PK11GenericObject *)objSpec)->objectID;
            break;
        case PK11_TypePrivKey:
            slot = ((SECKEYPrivateKey *)objSpec)->pkcs11Slot;
            handle = ((SECKEYPrivateKey *)objSpec)->pkcs11ID;
            break;
        case PK11_TypePubKey:
            slot = ((SECKEYPublicKey *)objSpec)->pkcs11Slot;
            handle = ((SECKEYPublicKey *)objSpec)->pkcs11ID;
            break;
        case PK11_TypeSymKey:
            slot = ((PK11SymKey *)objSpec)->slot;
            handle = ((PK11SymKey *)objSpec)->objectID;
            break;
        case PK11_TypeCert: /* don't handle cert case for now */
        default:
            break;
    }
    if (slot == NULL) {
        PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
        return SECFailure;
    }

    PK11_SETATTRS(&setTemplate, attrType, (CK_CHAR *)item->data, item->len);
    rwsession = PK11_GetRWSession(slot);
    if (rwsession == CK_INVALID_SESSION) {
        PORT_SetError(SEC_ERROR_BAD_DATA);
        return SECFailure;
    }
    crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession, handle,
                                                 &setTemplate, 1);
    PK11_RestoreROSession(slot, rwsession);
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
PK11_ReadRawAttribute(PK11ObjectType objType, void *objSpec,
                      CK_ATTRIBUTE_TYPE attrType, SECItem *item)
{
    PK11SlotInfo *slot = NULL;
    CK_OBJECT_HANDLE handle = 0;

    switch (objType) {
        case PK11_TypeGeneric:
            slot = ((PK11GenericObject *)objSpec)->slot;
            handle = ((PK11GenericObject *)objSpec)->objectID;
            break;
        case PK11_TypePrivKey:
            slot = ((SECKEYPrivateKey *)objSpec)->pkcs11Slot;
            handle = ((SECKEYPrivateKey *)objSpec)->pkcs11ID;
            break;
        case PK11_TypePubKey:
            slot = ((SECKEYPublicKey *)objSpec)->pkcs11Slot;
            handle = ((SECKEYPublicKey *)objSpec)->pkcs11ID;
            break;
        case PK11_TypeSymKey:
            slot = ((PK11SymKey *)objSpec)->slot;
            handle = ((PK11SymKey *)objSpec)->objectID;
            break;
        case PK11_TypeCert: /* don't handle cert case for now */
        default:
            break;
    }
    if (slot == NULL) {
        PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
        return SECFailure;
    }

    return PK11_ReadAttribute(slot, handle, attrType, NULL, item);
}

/*
 * return the object handle that matches the template
 */
CK_OBJECT_HANDLE
pk11_FindObjectByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *theTemplate, int tsize)
{
    CK_OBJECT_HANDLE object;
    CK_RV crv = CKR_SESSION_HANDLE_INVALID;
    CK_ULONG objectCount;

    /*
     * issue the find
     */
    PK11_EnterSlotMonitor(slot);
    if (slot->session != CK_INVALID_SESSION) {
        crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
                                                   theTemplate, tsize);
    }
    if (crv != CKR_OK) {
        PK11_ExitSlotMonitor(slot);
        PORT_SetError(PK11_MapError(crv));
        return CK_INVALID_HANDLE;
    }

    crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, &object, 1, &objectCount);
    PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
    PK11_ExitSlotMonitor(slot);
    if ((crv != CKR_OK) || (objectCount < 1)) {
        /* shouldn't use SSL_ERROR... here */
        PORT_SetError(crv != CKR_OK ? PK11_MapError(crv) : SSL_ERROR_NO_CERTIFICATE);
        return CK_INVALID_HANDLE;
    }

    /* blow up if the PKCS #11 module returns us and invalid object handle */
    PORT_Assert(object != CK_INVALID_HANDLE);
    return object;
}

/*
 * return all the object handles that matches the template
 */
CK_OBJECT_HANDLE *
pk11_FindObjectsByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
                           int templCount, int *object_count)
{
    CK_OBJECT_HANDLE *objID = NULL;
    CK_ULONG returned_count = 0;
    CK_RV crv = CKR_SESSION_HANDLE_INVALID;

    PK11_EnterSlotMonitor(slot);
    if (slot->session != CK_INVALID_SESSION) {
        crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
                                                   findTemplate, templCount);
    }
    if (crv != CKR_OK) {
        PK11_ExitSlotMonitor(slot);
        PORT_SetError(PK11_MapError(crv));
        *object_count = -1;
        return NULL;
    }

    /*
     * collect all the Matching Objects
     */
    do {
        CK_OBJECT_HANDLE *oldObjID = objID;

        if (objID == NULL) {
            objID = (CK_OBJECT_HANDLE *)PORT_Alloc(sizeof(CK_OBJECT_HANDLE) *
                                                   (*object_count + PK11_SEARCH_CHUNKSIZE));
        } else {
            objID = (CK_OBJECT_HANDLE *)PORT_Realloc(objID,
                                                     sizeof(CK_OBJECT_HANDLE) * (*object_count + PK11_SEARCH_CHUNKSIZE));
        }

        if (objID == NULL) {
            if (oldObjID)
                PORT_Free(oldObjID);
            break;
        }
        crv = PK11_GETTAB(slot)->C_FindObjects(slot->session,
                                               &objID[*object_count], PK11_SEARCH_CHUNKSIZE, &returned_count);
        if (crv != CKR_OK) {
            PORT_SetError(PK11_MapError(crv));
            PORT_Free(objID);
            objID = NULL;
            break;
        }
        *object_count += returned_count;
    } while (returned_count == PK11_SEARCH_CHUNKSIZE);

    PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
    PK11_ExitSlotMonitor(slot);

    if (objID && (*object_count == 0)) {
        PORT_Free(objID);
        return NULL;
    }
    if (objID == NULL)
        *object_count = -1;
    return objID;
}
/*
 * given a PKCS #11 object, match it's peer based on the KeyID. searchID
 * is typically a privateKey or a certificate while the peer is the opposite
 */
CK_OBJECT_HANDLE
PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE searchID,
               CK_OBJECT_CLASS matchclass)
{
    CK_ATTRIBUTE theTemplate[] = {
        { CKA_ID, NULL, 0 },
        { CKA_CLASS, NULL, 0 }
    };
    /* if you change the array, change the variable below as well */
    CK_ATTRIBUTE *keyclass = &theTemplate[1];
    int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
    /* if you change the array, change the variable below as well */
    CK_OBJECT_HANDLE peerID;
    PORTCheapArenaPool tmpArena;
    CK_RV crv;

    /* now we need to create space for the public key */
    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);

    crv = PK11_GetAttributes(&tmpArena.arena, slot, searchID, theTemplate, tsize);
    if (crv != CKR_OK) {
        PORT_DestroyCheapArena(&tmpArena);
        PORT_SetError(PK11_MapError(crv));
        return CK_INVALID_HANDLE;
    }

    if ((theTemplate[0].ulValueLen == 0) || (theTemplate[0].ulValueLen == -1)) {
        PORT_DestroyCheapArena(&tmpArena);
        if (matchclass == CKO_CERTIFICATE)
            PORT_SetError(SEC_ERROR_BAD_KEY);
        else
            PORT_SetError(SEC_ERROR_NO_KEY);
        return CK_INVALID_HANDLE;
    }

    /*
     * issue the find
     */
    *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass;

    peerID = pk11_FindObjectByTemplate(slot, theTemplate, tsize);
    PORT_DestroyCheapArena(&tmpArena);

    return peerID;
}

/*
 * count the number of objects that match the template.
 */
int
PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
                      int templCount)
{
    CK_OBJECT_HANDLE objID[PK11_SEARCH_CHUNKSIZE];
    int object_count = 0;
    CK_ULONG returned_count = 0;
    CK_RV crv = CKR_SESSION_HANDLE_INVALID;

    PK11_EnterSlotMonitor(slot);
    if (slot->session != CK_INVALID_SESSION) {
        crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
                                                   findTemplate, templCount);
    }
    if (crv != CKR_OK) {
        PK11_ExitSlotMonitor(slot);
        PORT_SetError(PK11_MapError(crv));
        return object_count;
    }

    /*
     * collect all the Matching Objects
     */
    do {
        crv = PK11_GETTAB(slot)->C_FindObjects(slot->session, objID,
                                               PK11_SEARCH_CHUNKSIZE,
                                               &returned_count);
        if (crv != CKR_OK) {
            PORT_SetError(PK11_MapError(crv));
            break;
        }
        object_count += returned_count;
    } while (returned_count == PK11_SEARCH_CHUNKSIZE);

    PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
    PK11_ExitSlotMonitor(slot);
    return object_count;
}

/*
 * Traverse all the objects in a given slot.
 */
SECStatus
PK11_TraverseSlot(PK11SlotInfo *slot, void *arg)
{
    int i;
    CK_OBJECT_HANDLE *objID = NULL;
    int object_count = 0;
    pk11TraverseSlot *slotcb = (pk11TraverseSlot *)arg;

    objID = pk11_FindObjectsByTemplate(slot, slotcb->findTemplate,
                                       slotcb->templateCount, &object_count);

    /*Actually this isn't a failure... there just were no objs to be found*/
    if (object_count == 0) {
        return SECSuccess;
    }

    if (objID == NULL) {
        return SECFailure;
    }

    for (i = 0; i < object_count; i++) {
        (*slotcb->callback)(slot, objID[i], slotcb->callbackArg);
    }
    PORT_Free(objID);
    return SECSuccess;
}

/*
 * Traverse all the objects in all slots.
 */
SECStatus
pk11_TraverseAllSlots(SECStatus (*callback)(PK11SlotInfo *, void *),
                      void *arg, PRBool forceLogin, void *wincx)
{
    PK11SlotList *list;
    PK11SlotListElement *le;
    SECStatus rv;

    /* get them all! */
    list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, wincx);
    if (list == NULL)
        return SECFailure;

    /* look at each slot and authenticate as necessary */
    for (le = list->head; le; le = le->next) {
        if (forceLogin) {
            rv = pk11_AuthenticateUnfriendly(le->slot, PR_FALSE, wincx);
            if (rv != SECSuccess) {
                continue;
            }
        }
        if (callback) {
            (*callback)(le->slot, arg);
        }
    }

    PK11_FreeSlotList(list);

    return SECSuccess;
}

CK_OBJECT_HANDLE *
PK11_FindObjectsFromNickname(char *nickname, PK11SlotInfo **slotptr,
                             CK_OBJECT_CLASS objclass, int *returnCount, void *wincx)
{
    char *tokenName;
    char *delimit;
    PK11SlotInfo *slot;
    CK_OBJECT_HANDLE *objID;
    CK_ATTRIBUTE findTemplate[] = {
        { CKA_LABEL, NULL, 0 },
        { CKA_CLASS, NULL, 0 },
    };
    int findCount = sizeof(findTemplate) / sizeof(findTemplate[0]);
    SECStatus rv;
    PK11_SETATTRS(&findTemplate[1], CKA_CLASS, &objclass, sizeof(objclass));

    *slotptr = slot = NULL;
    *returnCount = 0;
    /* first find the slot associated with this nickname */
    if ((delimit = PORT_Strchr(nickname, ':')) != NULL) {
        int len = delimit - nickname;
        tokenName = (char *)PORT_Alloc(len + 1);
        PORT_Memcpy(tokenName, nickname, len);
        tokenName[len] = 0;

        slot = *slotptr = PK11_FindSlotByName(tokenName);
        PORT_Free(tokenName);
        /* if we couldn't find a slot, assume the nickname is an internal cert
         * with no proceding slot name */
        if (slot == NULL) {
            slot = *slotptr = PK11_GetInternalKeySlot();
        } else {
            nickname = delimit + 1;
        }
    } else {
        *slotptr = slot = PK11_GetInternalKeySlot();
    }
    if (slot == NULL) {
        return CK_INVALID_HANDLE;
    }

    rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx);
    if (rv != SECSuccess) {
        PK11_FreeSlot(slot);
        *slotptr = NULL;
        return CK_INVALID_HANDLE;
    }

    findTemplate[0].pValue = nickname;
    findTemplate[0].ulValueLen = PORT_Strlen(nickname);
    objID = pk11_FindObjectsByTemplate(slot, findTemplate, findCount, returnCount);
    if (objID == NULL) {
        /* PKCS #11 isn't clear on whether or not the NULL is
         * stored in the template.... try the find again with the
         * full null terminated string. */
        findTemplate[0].ulValueLen += 1;
        objID = pk11_FindObjectsByTemplate(slot, findTemplate, findCount,
                                           returnCount);
        if (objID == NULL) {
            /* Well that's the best we can do. It's just not here */
            /* what about faked nicknames? */
            PK11_FreeSlot(slot);
            *slotptr = NULL;
            *returnCount = 0;
        }
    }

    return objID;
}

SECItem *
pk11_GetLowLevelKeyFromHandle(PK11SlotInfo *slot, CK_OBJECT_HANDLE handle)
{
    CK_ATTRIBUTE theTemplate[] = {
        { CKA_ID, NULL, 0 },
    };
    int tsize = sizeof(theTemplate) / sizeof(theTemplate[0]);
    CK_RV crv;
    SECItem *item;

    item = SECITEM_AllocItem(NULL, NULL, 0);

    if (item == NULL) {
        return NULL;
    }

    crv = PK11_GetAttributes(NULL, slot, handle, theTemplate, tsize);
    if (crv != CKR_OK) {
        SECITEM_FreeItem(item, PR_TRUE);
        PORT_SetError(PK11_MapError(crv));
        return NULL;
    }

    item->data = (unsigned char *)theTemplate[0].pValue;
    item->len = theTemplate[0].ulValueLen;

    return item;
}
