Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  pk11skey.c   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * This file implements the Symkey wrapper and the PKCS context
 * Interfaces.
 */


#include <stddef.h>
#include <limits.h>

#include "seccomon.h"
#include "secmod.h"
#include "nssilock.h"
#include "secmodi.h"
#include "secmodti.h"
#include "pkcs11.h"
#include "pk11func.h"
#include "secitem.h"
#include "secoid.h"
#include "secerr.h"
#include "hasht.h"

static ECPointEncoding pk11_ECGetPubkeyEncoding(const SECKEYPublicKey *pubKey);

static void
pk11_EnterKeyMonitor(PK11SymKey *symKey)
{
    if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe))
        PK11_EnterSlotMonitor(symKey->slot);
}

static void
pk11_ExitKeyMonitor(PK11SymKey *symKey)
{
    if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe))
        PK11_ExitSlotMonitor(symKey->slot);
}

/*
 * pk11_getKeyFromList returns a symKey that has a session (if needSession
 * was specified), or explicitly does not have a session (if needSession
 * was not specified).
 */

static PK11SymKey *
pk11_getKeyFromList(PK11SlotInfo *slot, PRBool needSession)
{
    PK11SymKey *symKey = NULL;

    PZ_Lock(slot->freeListLock);
    /* own session list are symkeys with sessions that the symkey owns.
     * 'most' symkeys will own their own session. */

    if (needSession) {
        if (slot->freeSymKeysWithSessionHead) {
            symKey = slot->freeSymKeysWithSessionHead;
            slot->freeSymKeysWithSessionHead = symKey->next;
            slot->keyCount--;
        }
    }
    /* if we don't need a symkey with its own session, or we couldn't find
     * one on the owner list, get one from the non-owner free list. */

    if (!symKey) {
        if (slot->freeSymKeysHead) {
            symKey = slot->freeSymKeysHead;
            slot->freeSymKeysHead = symKey->next;
            slot->keyCount--;
        }
    }
    PZ_Unlock(slot->freeListLock);
    if (symKey) {
        symKey->next = NULL;
        if (!needSession) {
            return symKey;
        }
        /* if we are getting an owner key, make sure we have a valid session.
         * session could be invalid if the token has been removed or because
         * we got it from the non-owner free list */

        if ((symKey->series != slot->series) ||
            (symKey->session == CK_INVALID_HANDLE)) {
            symKey->session = pk11_GetNewSession(slot, &symKey->sessionOwner);
        }
        PORT_Assert(symKey->session != CK_INVALID_HANDLE);
        if (symKey->session != CK_INVALID_HANDLE)
            return symKey;
        PK11_FreeSymKey(symKey);
        /* if we are here, we need a session, but couldn't get one, it's
         * unlikely we pk11_GetNewSession will succeed if we call it a second
         * time. */

        return NULL;
    }

    symKey = PORT_New(PK11SymKey);
    if (symKey == NULL) {
        return NULL;
    }

    symKey->next = NULL;
    if (needSession) {
        symKey->session = pk11_GetNewSession(slot, &symKey->sessionOwner);
        PORT_Assert(symKey->session != CK_INVALID_HANDLE);
        if (symKey->session == CK_INVALID_HANDLE) {
            PK11_FreeSymKey(symKey);
            symKey = NULL;
        }
    } else {
        symKey->session = CK_INVALID_HANDLE;
    }
    return symKey;
}

/* Caller MUST hold slot->freeListLock (or ref count == 0?) !! */
void
PK11_CleanKeyList(PK11SlotInfo *slot)
{
    PK11SymKey *symKey = NULL;

    while (slot->freeSymKeysWithSessionHead) {
        symKey = slot->freeSymKeysWithSessionHead;
        slot->freeSymKeysWithSessionHead = symKey->next;
        pk11_CloseSession(slot, symKey->session, symKey->sessionOwner);
        PORT_Free(symKey);
    }
    while (slot->freeSymKeysHead) {
        symKey = slot->freeSymKeysHead;
        slot->freeSymKeysHead = symKey->next;
        pk11_CloseSession(slot, symKey->session, symKey->sessionOwner);
        PORT_Free(symKey);
    }
    return;
}

/*
 * create a symetric key:
 *      Slot is the slot to create the key in.
 *      type is the mechanism type
 *      owner is does this symKey structure own it's object handle (rare
 *        that this is false).
 *      needSession means the returned symKey will return with a valid session
 *        allocated already.
 */

static PK11SymKey *
pk11_CreateSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                  PRBool owner, PRBool needSession, void *wincx)
{

    PK11SymKey *symKey = pk11_getKeyFromList(slot, needSession);

    if (symKey == NULL) {
        return NULL;
    }
    /* if needSession was specified, make sure we have a valid session.
     * callers which specify needSession as false should do their own
     * check of the session before returning the symKey */

    if (needSession && symKey->session == CK_INVALID_HANDLE) {
        PK11_FreeSymKey(symKey);
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return NULL;
    }

    symKey->type = type;
    symKey->data.type = siBuffer;
    symKey->data.data = NULL;
    symKey->data.len = 0;
    symKey->owner = owner;
    symKey->objectID = CK_INVALID_HANDLE;
    symKey->slot = slot;
    symKey->series = slot->series;
    symKey->cx = wincx;
    symKey->size = 0;
    symKey->refCount = 1;
    symKey->origin = PK11_OriginNULL;
    symKey->parent = NULL;
    symKey->freeFunc = NULL;
    symKey->userData = NULL;
    PK11_ReferenceSlot(slot);
    return symKey;
}

/*
 * destroy a symetric key
 */

void
PK11_FreeSymKey(PK11SymKey *symKey)
{
    PK11SlotInfo *slot;
    PRBool freeit = PR_TRUE;

    if (!symKey) {
        return;
    }

    if (PR_ATOMIC_DECREMENT(&symKey->refCount) == 0) {
        PK11SymKey *parent = symKey->parent;

        symKey->parent = NULL;
        if ((symKey->owner) && symKey->objectID != CK_INVALID_HANDLE) {
            pk11_EnterKeyMonitor(symKey);
            (void)PK11_GETTAB(symKey->slot)->C_DestroyObject(symKey->session, symKey->objectID);
            pk11_ExitKeyMonitor(symKey);
        }
        if (symKey->data.data) {
            PORT_Memset(symKey->data.data, 0, symKey->data.len);
            PORT_Free(symKey->data.data);
        }
        /* free any existing data */
        if (symKey->userData && symKey->freeFunc) {
            (*symKey->freeFunc)(symKey->userData);
        }
        slot = symKey->slot;
        PZ_Lock(slot->freeListLock);
        if (slot->keyCount < slot->maxKeyCount) {
            /*
             * freeSymkeysWithSessionHead contain a list of reusable
             *  SymKey structures with valid sessions.
             *    sessionOwner must be true.
             *    session must be valid.
             * freeSymKeysHead contain a list of SymKey structures without
             *  valid session.
             *    session must be CK_INVALID_HANDLE.
             *    though sessionOwner is false, callers should not depend on
             *    this fact.
             */

            if (symKey->sessionOwner) {
                PORT_Assert(symKey->session != CK_INVALID_HANDLE);
                symKey->next = slot->freeSymKeysWithSessionHead;
                slot->freeSymKeysWithSessionHead = symKey;
            } else {
                symKey->session = CK_INVALID_HANDLE;
                symKey->next = slot->freeSymKeysHead;
                slot->freeSymKeysHead = symKey;
            }
            slot->keyCount++;
            symKey->slot = NULL;
            freeit = PR_FALSE;
        }
        PZ_Unlock(slot->freeListLock);
        if (freeit) {
            pk11_CloseSession(symKey->slot, symKey->session,
                              symKey->sessionOwner);
            PORT_Free(symKey);
        }
        PK11_FreeSlot(slot);

        if (parent) {
            PK11_FreeSymKey(parent);
        }
    }
}

PK11SymKey *
PK11_ReferenceSymKey(PK11SymKey *symKey)
{
    PR_ATOMIC_INCREMENT(&symKey->refCount);
    return symKey;
}

/*
 * Accessors
 */

CK_MECHANISM_TYPE
PK11_GetMechanism(PK11SymKey *symKey)
{
    return symKey->type;
}

/*
 * return the slot associated with a symetric key
 */

PK11SlotInfo *
PK11_GetSlotFromKey(PK11SymKey *symKey)
{
    return PK11_ReferenceSlot(symKey->slot);
}

CK_KEY_TYPE
PK11_GetSymKeyType(PK11SymKey *symKey)
{
    return PK11_GetKeyType(symKey->type, symKey->size);
}

PK11SymKey *
PK11_GetNextSymKey(PK11SymKey *symKey)
{
    return symKey ? symKey->next : NULL;
}

char *
PK11_GetSymKeyNickname(PK11SymKey *symKey)
{
    return PK11_GetObjectNickname(symKey->slot, symKey->objectID);
}

SECStatus
PK11_SetSymKeyNickname(PK11SymKey *symKey, const char *nickname)
{
    return PK11_SetObjectNickname(symKey->slot, symKey->objectID, nickname);
}

void *
PK11_GetSymKeyUserData(PK11SymKey *symKey)
{
    return symKey->userData;
}

void
PK11_SetSymKeyUserData(PK11SymKey *symKey, void *userData,
                       PK11FreeDataFunc freeFunc)
{
    /* free any existing data */
    if (symKey->userData && symKey->freeFunc) {
        (*symKey->freeFunc)(symKey->userData);
    }
    symKey->userData = userData;
    symKey->freeFunc = freeFunc;
    return;
}

/*
 * turn key handle into an appropriate key object
 */

PK11SymKey *
PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent, PK11Origin origin,
                      CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID, PRBool owner, void *wincx)
{
    PK11SymKey *symKey;
    PRBool needSession = !(owner && parent);

    if (keyID == CK_INVALID_HANDLE) {
        return NULL;
    }

    symKey = pk11_CreateSymKey(slot, type, owner, needSession, wincx);
    if (symKey == NULL) {
        return NULL;
    }

    symKey->objectID = keyID;
    symKey->origin = origin;

    /* adopt the parent's session */
    /* This is only used by SSL. What we really want here is a session
     * structure with a ref count so  the session goes away only after all the
     * keys do. */

    if (!needSession) {
        symKey->sessionOwner = PR_FALSE;
        symKey->session = parent->session;
        symKey->parent = PK11_ReferenceSymKey(parent);
        /* This is the only case where pk11_CreateSymKey does not explicitly
         * check symKey->session. We need to assert here to make sure.
         * the session isn't invalid. */

        PORT_Assert(parent->session != CK_INVALID_HANDLE);
        if (parent->session == CK_INVALID_HANDLE) {
            PK11_FreeSymKey(symKey);
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return NULL;
        }
    }

    return symKey;
}

/*
 * Restore a symmetric wrapping key that was saved using PK11_SetWrapKey.
 *
 * This function is provided for ABI compatibility; see PK11_SetWrapKey below.
 */

PK11SymKey *
PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, CK_MECHANISM_TYPE type,
                int series, void *wincx)
{
    PK11SymKey *symKey = NULL;
    CK_OBJECT_HANDLE keyHandle;

    PK11_EnterSlotMonitor(slot);
    if (slot->series != series ||
        slot->refKeys[wrap] == CK_INVALID_HANDLE) {
        PK11_ExitSlotMonitor(slot);
        return NULL;
    }

    if (type == CKM_INVALID_MECHANISM) {
        type = slot->wrapMechanism;
    }

    keyHandle = slot->refKeys[wrap];
    PK11_ExitSlotMonitor(slot);
    symKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive,
                                   slot->wrapMechanism, keyHandle, PR_FALSE, wincx);
    return symKey;
}

/*
 * This function sets an attribute on the current slot with a wrapping key.  The
 * data saved is ephemeral; it needs to be run every time the program is
 * invoked.
 *
 * Since NSS 3.45, this function is marginally more thread safe.  It uses the
 * slot lock (if present) and fails silently if a value is already set.  Use
 * PK11_GetWrapKey() after calling this function to get the current wrapping key
 * in case there was an update on another thread.
 *
 * Either way, using this function is inadvisable.  It's provided for ABI
 * compatibility only.
 */

void
PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey)
{
    PK11_EnterSlotMonitor(slot);
    if (wrap >= 0) {
        size_t uwrap = (size_t)wrap;
        if (uwrap < PR_ARRAY_SIZE(slot->refKeys) &&
            slot->refKeys[uwrap] == CK_INVALID_HANDLE) {
            /* save the handle and mechanism for the wrapping key */
            /* mark the key and session as not owned by us so they don't get
             * freed when the key goes way... that lets us reuse the key
             * later */

            slot->refKeys[uwrap] = wrapKey->objectID;
            wrapKey->owner = PR_FALSE;
            wrapKey->sessionOwner = PR_FALSE;
            slot->wrapMechanism = wrapKey->type;
        }
    }
    PK11_ExitSlotMonitor(slot);
}

/*
 * figure out if a key is still valid or if it is stale.
 */

PRBool
PK11_VerifyKeyOK(PK11SymKey *key)
{
    if (!PK11_IsPresent(key->slot)) {
        return PR_FALSE;
    }
    return (PRBool)(key->series == key->slot->series);
}

static PK11SymKey *
pk11_ImportSymKeyWithTempl(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                           PK11Origin origin, PRBool isToken, CK_ATTRIBUTE *keyTemplate,
                           unsigned int templateCount, SECItem *key, void *wincx)
{
    PK11SymKey *symKey;
    SECStatus rv;

    symKey = pk11_CreateSymKey(slot, type, !isToken, PR_TRUE, wincx);
    if (symKey == NULL) {
        return NULL;
    }

    symKey->size = key->len;

    PK11_SETATTRS(&keyTemplate[templateCount], CKA_VALUE, key->data, key->len);
    templateCount++;

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

    symKey->origin = origin;

    /* import the keys */
    rv = PK11_CreateNewObject(slot, symKey->session, keyTemplate,
                              templateCount, isToken, &symKey->objectID);
    if (rv != SECSuccess) {
        PK11_FreeSymKey(symKey);
        return NULL;
    }

    return symKey;
}

/*
 * turn key bits into an appropriate key object
 */

PK11SymKey *
PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                  PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx)
{
    PK11SymKey *symKey;
    unsigned int templateCount = 0;
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_BBOOL cktrue = CK_TRUE; /* sigh */
    CK_ATTRIBUTE keyTemplate[5];
    CK_ATTRIBUTE *attrs = keyTemplate;

    /* CKA_NSS_MESSAGE is a fake operation to distinguish between
     * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
     * it as a real attribute */

    if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
        /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
         * etc. Strip out the real attribute here */

        operation &= ~CKA_NSS_MESSAGE_MASK;
    }

    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
    attrs++;
    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
    attrs++;
    PK11_SETATTRS(attrs, operation, &cktrue, 1);
    attrs++;
    templateCount = attrs - keyTemplate;
    PR_ASSERT(templateCount + 1 <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));

    keyType = PK11_GetKeyType(type, key->len);
    symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, PR_FALSE,
                                        keyTemplate, templateCount, key, wincx);
    return symKey;
}
/* Import a PKCS #11 data object and return it as a key. This key is
 * only useful in a limited number of mechanisms, such as HKDF. */

PK11SymKey *
PK11_ImportDataKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
                   CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx)
{
    CK_OBJECT_CLASS ckoData = CKO_DATA;
    CK_ATTRIBUTE template[2] = { { CKA_CLASS, (CK_BYTE_PTR)&ckoData, sizeof(ckoData) },
                                 { CKA_VALUE, (CK_BYTE_PTR)key->data, key->len } };
    CK_OBJECT_HANDLE handle;
    PK11GenericObject *genObject;

    genObject = PK11_CreateGenericObject(slot, template, PR_ARRAY_SIZE(template), PR_FALSE);
    if (genObject == NULL) {
        return NULL;
    }
    handle = PK11_GetObjectHandle(PK11_TypeGeneric, genObject, NULL);
    /* A note about ownership of the PKCS #11 handle:
     * PK11_CreateGenericObject() will not destroy the object it creates
     * on Free, For that you want PK11_CreateManagedGenericObject().
     * Below we import the handle into the symKey structure. We pass
     * PR_TRUE as the owner so that the symKey will destroy the object
     * once it's freed. This is why it's safe to destroy genObject now. */

    PK11_DestroyGenericObject(genObject);
    if (handle == CK_INVALID_HANDLE) {
        return NULL;
    }
    return PK11_SymKeyFromHandle(slot, NULL, origin, type, handle, PR_TRUE, wincx);
}

/* turn key bits into an appropriate key object */
PK11SymKey *
PK11_ImportSymKeyWithFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                           PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,
                           CK_FLAGS flags, PRBool isPerm, void *wincx)
{
    PK11SymKey *symKey;
    unsigned int templateCount = 0;
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_BBOOL cktrue = CK_TRUE; /* sigh */
    CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
    CK_ATTRIBUTE *attrs = keyTemplate;

    /* CKA_NSS_MESSAGE is a fake operation to distinguish between
     * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
     * it as a real attribute */

    if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
        /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
         * etc. Strip out the real attribute here */

        operation &= ~CKA_NSS_MESSAGE_MASK;
    }

    PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
    attrs++;
    PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
    attrs++;
    if (isPerm) {
        PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
        attrs++;
        /* sigh some tokens think CKA_PRIVATE = false is a reasonable
         * default for secret keys */

        PK11_SETATTRS(attrs, CKA_PRIVATE, &cktrue, sizeof(cktrue));
        attrs++;
    }
    attrs += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
    if ((operation != CKA_FLAGS_ONLY) &&
        !pk11_FindAttrInTemplate(keyTemplate, attrs - keyTemplate, operation)) {
        PK11_SETATTRS(attrs, operation, &cktrue, sizeof(cktrue));
        attrs++;
    }
    templateCount = attrs - keyTemplate;
    PR_ASSERT(templateCount + 1 <= sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE));

    keyType = PK11_GetKeyType(type, key->len);
    symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, isPerm,
                                        keyTemplate, templateCount, key, wincx);
    if (symKey && isPerm) {
        symKey->owner = PR_FALSE;
    }
    return symKey;
}

PK11SymKey *
PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *keyID,
                  void *wincx)
{
    CK_ATTRIBUTE findTemp[4];
    CK_ATTRIBUTE *attrs;
    CK_BBOOL ckTrue = CK_TRUE;
    CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY;
    size_t tsize = 0;
    CK_OBJECT_HANDLE key_id;

    attrs = findTemp;
    PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass));
    attrs++;
    PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue));
    attrs++;
    if (keyID) {
        PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len);
        attrs++;
    }
    tsize = attrs - findTemp;
    PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));

    key_id = pk11_FindObjectByTemplate(slot, findTemp, tsize);
    if (key_id == CK_INVALID_HANDLE) {
        return NULL;
    }
    return PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, type, key_id,
                                 PR_FALSE, wincx);
}

PK11SymKey *
PK11_ListFixedKeysInSlot(PK11SlotInfo *slot, char *nickname, void *wincx)
{
    CK_ATTRIBUTE findTemp[4];
    CK_ATTRIBUTE *attrs;
    CK_BBOOL ckTrue = CK_TRUE;
    CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY;
    int tsize = 0;
    int objCount = 0;
    CK_OBJECT_HANDLE *key_ids;
    PK11SymKey *nextKey = NULL;
    PK11SymKey *topKey = NULL;
    int i, len;

    attrs = findTemp;
    PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass));
    attrs++;
    PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue));
    attrs++;
    if (nickname) {
        len = PORT_Strlen(nickname);
        PK11_SETATTRS(attrs, CKA_LABEL, nickname, len);
        attrs++;
    }
    tsize = attrs - findTemp;
    PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));

    key_ids = pk11_FindObjectsByTemplate(slot, findTemp, tsize, &objCount);
    if (key_ids == NULL) {
        return NULL;
    }

    for (i = 0; i < objCount; i++) {
        SECItem typeData;
        CK_KEY_TYPE type = CKK_GENERIC_SECRET;
        SECStatus rv = PK11_ReadAttribute(slot, key_ids[i],
                                          CKA_KEY_TYPE, NULL, &typeData);
        if (rv == SECSuccess) {
            if (typeData.len == sizeof(CK_KEY_TYPE)) {
                type = *(CK_KEY_TYPE *)typeData.data;
            }
            PORT_Free(typeData.data);
        }
        nextKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive,
                                        PK11_GetKeyMechanism(type), key_ids[i], PR_FALSE, wincx);
        if (nextKey) {
            nextKey->next = topKey;
            topKey = nextKey;
        }
    }
    PORT_Free(key_ids);
    return topKey;
}

void *
PK11_GetWindow(PK11SymKey *key)
{
    return key->cx;
}

/*
 * extract a symmetric key value. NOTE: if the key is sensitive, we will
 * not be able to do this operation. This function is used to move
 * keys from one token to another */

SECStatus
PK11_ExtractKeyValue(PK11SymKey *symKey)
{
    SECStatus rv;

    if (symKey == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (symKey->data.data != NULL) {
        if (symKey->size == 0) {
            symKey->size = symKey->data.len;
        }
        return SECSuccess;
    }

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

    rv = PK11_ReadAttribute(symKey->slot, symKey->objectID, CKA_VALUE, NULL,
                            &symKey->data);
    if (rv == SECSuccess) {
        symKey->size = symKey->data.len;
    }
    return rv;
}

SECStatus
PK11_DeleteTokenSymKey(PK11SymKey *symKey)
{
    if (!PK11_IsPermObject(symKey->slot, symKey->objectID)) {
        return SECFailure;
    }
    PK11_DestroyTokenObject(symKey->slot, symKey->objectID);
    symKey->objectID = CK_INVALID_HANDLE;
    return SECSuccess;
}

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

/* This symbol is exported for backward compatibility. */
SECItem *
__PK11_GetKeyData(PK11SymKey *symKey)
{
    return PK11_GetKeyData(symKey);
}

/*
 * PKCS #11 key Types with predefined length
 */

unsigned int
pk11_GetPredefinedKeyLength(CK_KEY_TYPE keyType)
{
    int length = 0;
    switch (keyType) {
        case CKK_DES:
            length = 8;
            break;
        case CKK_DES2:
            length = 16;
            break;
        case CKK_DES3:
            length = 24;
            break;
        case CKK_SKIPJACK:
            length = 10;
            break;
        case CKK_BATON:
            length = 20;
            break;
        case CKK_JUNIPER:
            length = 20;
            break;
        default:
            break;
    }
    return length;
}

/* return the keylength if possible.  '0' if not */
unsigned int
PK11_GetKeyLength(PK11SymKey *key)
{
    CK_KEY_TYPE keyType;

    if (key->size != 0)
        return key->size;

    /* First try to figure out the key length from its type */
    keyType = PK11_ReadULongAttribute(key->slot, key->objectID, CKA_KEY_TYPE);
    key->size = pk11_GetPredefinedKeyLength(keyType);
    if ((keyType == CKK_GENERIC_SECRET) &&
        (key->type == CKM_SSL3_PRE_MASTER_KEY_GEN)) {
        key->size = 48;
    }

    if (key->size != 0)
        return key->size;

    if (key->data.data == NULL) {
        PK11_ExtractKeyValue(key);
    }
    /* key is probably secret. Look up its length */
    /*  this is new PKCS #11 version 2.0 functionality. */
    if (key->size == 0) {
        CK_ULONG keyLength;

        keyLength = PK11_ReadULongAttribute(key->slot, key->objectID, CKA_VALUE_LEN);
        if (keyLength != CK_UNAVAILABLE_INFORMATION) {
            key->size = (unsigned int)keyLength;
        }
    }

    return key->size;
}

/* return the strength of a key. This is different from length in that
 * 1) it returns the size in bits, and 2) it returns only the secret portions
 * of the key minus any checksums or parity.
 */

unsigned int
PK11_GetKeyStrength(PK11SymKey *key, SECAlgorithmID *algid)
{
    int size = 0;
    CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; /* RC2 only */
    SECItem *param = NULL;                               /* RC2 only */
    CK_RC2_CBC_PARAMS *rc2_params = NULL;                /* RC2 ONLY */
    unsigned int effectiveBits = 0;                      /* RC2 ONLY */

    switch (PK11_GetKeyType(key->type, 0)) {
        case CKK_CDMF:
            return 40;
        case CKK_DES:
            return 56;
        case CKK_DES3:
        case CKK_DES2:
            size = PK11_GetKeyLength(key);
            if (size == 16) {
                /* double des */
                return 112; /* 16*7 */
            }
            return 168;
        /*
         * RC2 has is different than other ciphers in that it allows the user
         * to deprecating keysize while still requiring all the bits for the
         * original key. The info
         * on what the effective key strength is in the parameter for the key.
         * In S/MIME this parameter is stored in the DER encoded algid. In Our
         * other uses of RC2, effectiveBits == keyBits, so this code functions
         * correctly without an algid.
         */

        case CKK_RC2:
            /* if no algid was provided, fall through to default */
            if (!algid) {
                break;
            }
            /* verify that the algid is for RC2 */
            mechanism = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(algid));
            if ((mechanism != CKM_RC2_CBC) && (mechanism != CKM_RC2_ECB)) {
                break;
            }

            /* now get effective bits from the algorithm ID. */
            param = PK11_ParamFromAlgid(algid);
            /* if we couldn't get memory just use key length */
            if (param == NULL) {
                break;
            }

            rc2_params = (CK_RC2_CBC_PARAMS *)param->data;
            /* paranoia... shouldn't happen */
            PORT_Assert(param->data != NULL);
            if (param->data == NULL) {
                SECITEM_FreeItem(param, PR_TRUE);
                break;
            }
            effectiveBits = (unsigned int)rc2_params->ulEffectiveBits;
            SECITEM_FreeItem(param, PR_TRUE);
            param = NULL;
            rc2_params = NULL; /* paranoia */

            /* we have effective bits, is and allocated memory is free, now
             * we need to return the smaller of effective bits and keysize */

            size = PK11_GetKeyLength(key);
            if ((unsigned int)size * 8 > effectiveBits) {
                return effectiveBits;
            }

            return size * 8; /* the actual key is smaller, the strength can't be
                              * greater than the actual key size */


        default:
            break;
    }
    return PK11_GetKeyLength(key) * 8;
}

/*
 * The next three utilities are to deal with the fact that a given operation
 * may be a multi-slot affair. This creates a new key object that is copied
 * into the new slot.
 */

PK11SymKey *
pk11_CopyToSlotPerm(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                    CK_ATTRIBUTE_TYPE operation, CK_FLAGS flags,
                    PRBool isPerm, PK11SymKey *symKey)
{
    SECStatus rv;
    PK11SymKey *newKey = NULL;

    /* Extract the raw key data if possible */
    if (symKey->data.data == NULL) {
        rv = PK11_ExtractKeyValue(symKey);
        /* KEY is sensitive, we're try key exchanging it. */
        if (rv != SECSuccess) {
            return pk11_KeyExchange(slot, type, operation,
                                    flags, isPerm, symKey);
        }
    }

    newKey = PK11_ImportSymKeyWithFlags(slot, type, symKey->origin,
                                        operation, &symKey->data, flags, isPerm, symKey->cx);
    if (newKey == NULL) {
        newKey = pk11_KeyExchange(slot, type, operation, flags, isPerm, symKey);
    }
    return newKey;
}

PK11SymKey *
pk11_CopyToSlot(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey)
{
    return pk11_CopyToSlotPerm(slot, type, operation, 0, PR_FALSE, symKey);
}

/*
 * Make sure the slot we are in is the correct slot for the operation
 * by verifying that it supports all of the specified mechanism types.
 */

PK11SymKey *
pk11_ForceSlotMultiple(PK11SymKey *symKey, CK_MECHANISM_TYPE *type,
                       int mechCount, CK_ATTRIBUTE_TYPE operation)
{
    PK11SlotInfo *slot = symKey->slot;
    PK11SymKey *newKey = NULL;
    PRBool needToCopy = PR_FALSE;
    int i;

    if (slot == NULL) {
        needToCopy = PR_TRUE;
    } else {
        i = 0;
        while ((i < mechCount) && (needToCopy == PR_FALSE)) {
            if (!PK11_DoesMechanism(slot, type[i])) {
                needToCopy = PR_TRUE;
            }
            i++;
        }
    }

    if (needToCopy == PR_TRUE) {
        slot = PK11_GetBestSlotMultiple(type, mechCount, symKey->cx);
        if (slot == NULL) {
            PORT_SetError(SEC_ERROR_NO_MODULE);
            return NULL;
        }
        newKey = pk11_CopyToSlot(slot, type[0], operation, symKey);
        PK11_FreeSlot(slot);
    }
    return newKey;
}

/*
 * Make sure the slot we are in is the correct slot for the operation
 */

PK11SymKey *
pk11_ForceSlot(PK11SymKey *symKey, CK_MECHANISM_TYPE type,
               CK_ATTRIBUTE_TYPE operation)
{
    return pk11_ForceSlotMultiple(symKey, &type, 1, operation);
}

PK11SymKey *
PK11_MoveSymKey(PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation,
                CK_FLAGS flags, PRBool perm, PK11SymKey *symKey)
{
    if (symKey->slot == slot) {
        if (perm) {
            return PK11_ConvertSessionSymKeyToTokenSymKey(symKey, symKey->cx);
        } else {
            return PK11_ReferenceSymKey(symKey);
        }
    }

    return pk11_CopyToSlotPerm(slot, symKey->type,
                               operation, flags, perm, symKey);
}

/*
 * Use the token to generate a key.
 *
 * keySize must be 'zero' for fixed key length algorithms. A nonzero
 *  keySize causes the CKA_VALUE_LEN attribute to be added to the template
 *  for the key. Most PKCS #11 modules fail if you specify the CKA_VALUE_LEN
 *  attribute for keys with fixed length. The exception is DES2. If you
 *  select a CKM_DES3_CBC mechanism, this code will not add the CKA_VALUE_LEN
 *  parameter and use the key size to determine which underlying DES keygen
 *  function to use (CKM_DES2_KEY_GEN or CKM_DES3_KEY_GEN).
 *
 * keyType must be -1 for most algorithms. Some PBE algorthims cannot
 *  determine the correct key type from the mechanism or the parameters,
 *  so key type must be specified. Other PKCS #11 mechanisms may do so in
 *  the future. Currently there is no need to export this publically.
 *  Keep it private until there is a need in case we need to expand the
 *  keygen parameters again...
 *
 * CK_FLAGS flags: key operation flags
 * PK11AttrFlags attrFlags: PK11_ATTR_XXX key attribute flags
 */

PK11SymKey *
pk11_TokenKeyGenWithFlagsAndKeyType(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                                    SECItem *param, CK_KEY_TYPE keyType, int keySize, SECItem *keyid,
                                    CK_FLAGS opFlags, PK11AttrFlags attrFlags, void *wincx)
{
    PK11SymKey *symKey;
    CK_ATTRIBUTE genTemplate[MAX_TEMPL_ATTRS];
    CK_ATTRIBUTE *attrs = genTemplate;
    int count = sizeof(genTemplate) / sizeof(genTemplate[0]);
    CK_MECHANISM_TYPE keyGenType;
    CK_BBOOL cktrue = CK_TRUE;
    CK_BBOOL ckfalse = CK_FALSE;
    CK_ULONG ck_key_size; /* only used for variable-length keys */

    if (pk11_BadAttrFlags(attrFlags)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }

    if ((keySize != 0) && (type != CKM_DES3_CBC) &&
        (type != CKM_DES3_CBC_PAD) && (type != CKM_DES3_ECB)) {
        ck_key_size = keySize; /* Convert to PK11 type */

        PK11_SETATTRS(attrs, CKA_VALUE_LEN, &ck_key_size, sizeof(ck_key_size));
        attrs++;
    }

    if (keyType != -1) {
        PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(CK_KEY_TYPE));
        attrs++;
    }

    /* Include key id value if provided */
    if (keyid) {
        PK11_SETATTRS(attrs, CKA_ID, keyid->data, keyid->len);
        attrs++;
    }

    attrs += pk11_AttrFlagsToAttributes(attrFlags, attrs, &cktrue, &ckfalse);
    attrs += pk11_OpFlagsToAttributes(opFlags, attrs, &cktrue);

    count = attrs - genTemplate;
    PR_ASSERT(count <= sizeof(genTemplate) / sizeof(CK_ATTRIBUTE));

    keyGenType = PK11_GetKeyGenWithSize(type, keySize);
    if (keyGenType == CKM_FAKE_RANDOM) {
        PORT_SetError(SEC_ERROR_NO_MODULE);
        return NULL;
    }
    symKey = PK11_KeyGenWithTemplate(slot, type, keyGenType,
                                     param, genTemplate, count, wincx);
    if (symKey != NULL) {
        symKey->size = keySize;
    }
    return symKey;
}

/*
 * Use the token to generate a key.  - Public
 *
 * keySize must be 'zero' for fixed key length algorithms. A nonzero
 *  keySize causes the CKA_VALUE_LEN attribute to be added to the template
 *  for the key. Most PKCS #11 modules fail if you specify the CKA_VALUE_LEN
 *  attribute for keys with fixed length. The exception is DES2. If you
 *  select a CKM_DES3_CBC mechanism, this code will not add the CKA_VALUE_LEN
 *  parameter and use the key size to determine which underlying DES keygen
 *  function to use (CKM_DES2_KEY_GEN or CKM_DES3_KEY_GEN).
 *
 * CK_FLAGS flags: key operation flags
 * PK11AttrFlags attrFlags: PK11_ATTR_XXX key attribute flags
 */

PK11SymKey *
PK11_TokenKeyGenWithFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                          SECItem *param, int keySize, SECItem *keyid, CK_FLAGS opFlags,
                          PK11AttrFlags attrFlags, void *wincx)
{
    return pk11_TokenKeyGenWithFlagsAndKeyType(slot, type, param, -1, keySize,
                                               keyid, opFlags, attrFlags, wincx);
}

/*
 * Use the token to generate a key. keySize must be 'zero' for fixed key
 * length algorithms. A nonzero keySize causes the CKA_VALUE_LEN attribute
 * to be added to the template for the key. PKCS #11 modules fail if you
 * specify the CKA_VALUE_LEN attribute for keys with fixed length.
 * NOTE: this means to generate a DES2 key from this interface you must
 * specify CKM_DES2_KEY_GEN as the mechanism directly; specifying
 * CKM_DES3_CBC as the mechanism and 16 as keySize currently doesn't work.
 */

PK11SymKey *
PK11_TokenKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param,
                 int keySize, SECItem *keyid, PRBool isToken, void *wincx)
{
    PK11SymKey *symKey;
    PRBool weird = PR_FALSE; /* hack for fortezza */
    CK_FLAGS opFlags = CKF_SIGN;
    PK11AttrFlags attrFlags = 0;

    if ((keySize == -1) && (type == CKM_SKIPJACK_CBC64)) {
        weird = PR_TRUE;
        keySize = 0;
    }

    opFlags |= weird ? CKF_DECRYPT : CKF_ENCRYPT;

    if (isToken) {
        attrFlags |= (PK11_ATTR_TOKEN | PK11_ATTR_PRIVATE);
    }

    symKey = pk11_TokenKeyGenWithFlagsAndKeyType(slot, type, param,
                                                 -1, keySize, keyid, opFlags, attrFlags, wincx);
    if (symKey && weird) {
        PK11_SetFortezzaHack(symKey);
    }

    return symKey;
}

PK11SymKey *
PK11_KeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param,
            int keySize, void *wincx)
{
    return PK11_TokenKeyGen(slot, type, param, keySize, 0, PR_FALSE, wincx);
}

PK11SymKey *
PK11_KeyGenWithTemplate(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                        CK_MECHANISM_TYPE keyGenType,
                        SECItem *param, CK_ATTRIBUTE *attrs,
                        unsigned int attrsCount, void *wincx)
{
    PK11SymKey *symKey;
    CK_SESSION_HANDLE session;
    CK_MECHANISM mechanism;
    CK_RV crv;
    PRBool isToken = CK_FALSE;
    CK_ULONG keySize = 0;
    unsigned i;

    /* Extract the template's CKA_VALUE_LEN into keySize and CKA_TOKEN into
       isToken. */

    for (i = 0; i < attrsCount; ++i) {
        switch (attrs[i].type) {
            case CKA_VALUE_LEN:
                if (attrs[i].pValue == NULL ||
                    attrs[i].ulValueLen != sizeof(CK_ULONG)) {
                    PORT_SetError(PK11_MapError(CKR_TEMPLATE_INCONSISTENT));
                    return NULL;
                }
                keySize = *(CK_ULONG *)attrs[i].pValue;
                break;
            case CKA_TOKEN:
                if (attrs[i].pValue == NULL ||
                    attrs[i].ulValueLen != sizeof(CK_BBOOL)) {
                    PORT_SetError(PK11_MapError(CKR_TEMPLATE_INCONSISTENT));
                    return NULL;
                }
                isToken = (*(CK_BBOOL *)attrs[i].pValue) ? PR_TRUE : PR_FALSE;
                break;
        }
    }

    /* find a slot to generate the key into */
    /* Only do slot management if this is not a token key */
    if (!isToken && (slot == NULL || !PK11_DoesMechanism(slot, type))) {
        PK11SlotInfo *bestSlot = PK11_GetBestSlot(type, wincx);
        if (bestSlot == NULL) {
            PORT_SetError(SEC_ERROR_NO_MODULE);
            return NULL;
        }
        symKey = pk11_CreateSymKey(bestSlot, type, !isToken, PR_TRUE, wincx);
        PK11_FreeSlot(bestSlot);
    } else {
        symKey = pk11_CreateSymKey(slot, type, !isToken, PR_TRUE, wincx);
    }
    if (symKey == NULL)
        return NULL;

    symKey->size = keySize;
    symKey->origin = PK11_OriginGenerated;

    /* Set the parameters for the key gen if provided */
    mechanism.mechanism = keyGenType;
    mechanism.pParameter = NULL;
    mechanism.ulParameterLen = 0;
    if (param) {
        mechanism.pParameter = param->data;
        mechanism.ulParameterLen = param->len;
    }

    /* Get session and perform locking */
    if (isToken) {
        PK11_Authenticate(symKey->slot, PR_TRUE, wincx);
        /* Should always be original slot */
        session = PK11_GetRWSession(symKey->slot);
        symKey->owner = PR_FALSE;
    } else {
        session = symKey->session;
        if (session != CK_INVALID_HANDLE)
            pk11_EnterKeyMonitor(symKey);
    }
    if (session == CK_INVALID_HANDLE) {
        PK11_FreeSymKey(symKey);
        PORT_SetError(SEC_ERROR_BAD_DATA);
        return NULL;
    }

    crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(session, &mechanism, attrs, attrsCount, &symKey->objectID);

    /* Release lock and session */
    if (isToken) {
        PK11_RestoreROSession(symKey->slot, session);
    } else {
        pk11_ExitKeyMonitor(symKey);
    }

    if (crv != CKR_OK) {
        PK11_FreeSymKey(symKey);
        PORT_SetError(PK11_MapError(crv));
        return NULL;
    }

    return symKey;
}

/* --- */
PK11SymKey *
PK11_GenDES3TokenKey(PK11SlotInfo *slot, SECItem *keyid, void *cx)
{
    return PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0, keyid, PR_TRUE, cx);
}

PK11SymKey *
PK11_ConvertSessionSymKeyToTokenSymKey(PK11SymKey *symk, void *wincx)
{
    PK11SlotInfo *slot = symk->slot;
    CK_ATTRIBUTE template[1];
    CK_ATTRIBUTE *attrs = template;
    CK_BBOOL cktrue = CK_TRUE;
    CK_RV crv;
    CK_OBJECT_HANDLE newKeyID;
    CK_SESSION_HANDLE rwsession;

    PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
    attrs++;

    PK11_Authenticate(slot, PR_TRUE, wincx);
    rwsession = PK11_GetRWSession(slot);
    if (rwsession == CK_INVALID_HANDLE) {
        PORT_SetError(SEC_ERROR_BAD_DATA);
        return NULL;
    }
    crv = PK11_GETTAB(slot)->C_CopyObject(rwsession, symk->objectID,
                                          template, 1, &newKeyID);
    PK11_RestoreROSession(slot, rwsession);

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

    return PK11_SymKeyFromHandle(slot, NULL /*parent*/, symk->origin,
                                 symk->type, newKeyID, PR_FALSE /*owner*/, NULL /*wincx*/);
}

/* This function does a straight public key wrap with the CKM_RSA_PKCS
 * mechanism. */

SECStatus
PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
                   PK11SymKey *symKey, SECItem *wrappedKey)
{
    CK_MECHANISM_TYPE inferred = pk11_mapWrapKeyType(pubKey->keyType);
    return PK11_PubWrapSymKeyWithMechanism(pubKey, inferred, NULL, symKey,
                                           wrappedKey);
}

/* This function wraps a symmetric key with a public key, such as with the
 * CKM_RSA_PKCS and CKM_RSA_PKCS_OAEP mechanisms. */

SECStatus
PK11_PubWrapSymKeyWithMechanism(SECKEYPublicKey *pubKey,
                                CK_MECHANISM_TYPE mechType, SECItem *param,
                                PK11SymKey *symKey, SECItem *wrappedKey)
{
    PK11SlotInfo *slot;
    CK_ULONG len = wrappedKey->len;
    PK11SymKey *newKey = NULL;
    CK_OBJECT_HANDLE id;
    CK_MECHANISM mechanism;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_RV crv;

    if (symKey == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

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

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

    slot = symKey->slot;

    mechanism.mechanism = mechType;
    if (param == NULL) {
        mechanism.pParameter = NULL;
        mechanism.ulParameterLen = 0;
    } else {
        mechanism.pParameter = param->data;
        mechanism.ulParameterLen = param->len;
    }

    id = PK11_ImportPublicKey(slot, pubKey, PR_FALSE);
    if (id == CK_INVALID_HANDLE) {
        if (newKey) {
            PK11_FreeSymKey(newKey);
        }
        return SECFailure; /* Error code has been set. */
    }

    session = pk11_GetNewSession(slot, &owner);
    if (!owner || !(slot->isThreadSafe))
        PK11_EnterSlotMonitor(slot);
    crv = PK11_GETTAB(slot)->C_WrapKey(session, &mechanism,
                                       id, symKey->objectID, wrappedKey->data, &len);
    if (!owner || !(slot->isThreadSafe))
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    if (newKey) {
        PK11_FreeSymKey(newKey);
    }

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

/*
 * this little function uses the Encrypt function to wrap a key, just in
 * case we have problems with the wrap implementation for a token.
 */

static SECStatus
pk11_HandWrap(PK11SymKey *wrappingKey, SECItem *param, CK_MECHANISM_TYPE type,
              SECItem *inKey, SECItem *outKey)
{
    PK11SlotInfo *slot;
    CK_ULONG len;
    SECItem *data;
    CK_MECHANISM mech;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_RV crv;

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

    /* keys are almost always aligned, but if we get this far,
     * we've gone above and beyond anyway... */

    data = PK11_BlockData(inKey, PK11_GetBlockSize(type, param));
    if (data == NULL) {
        if (!owner || !(slot->isThreadSafe))
            PK11_ExitSlotMonitor(slot);
        pk11_CloseSession(slot, session, owner);
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return SECFailure;
    }
    len = outKey->len;
    crv = PK11_GETTAB(slot)->C_Encrypt(session, data->data, data->len,
                                       outKey->data, &len);
    if (!owner || !(slot->isThreadSafe))
        PK11_ExitSlotMonitor(slot);
    pk11_CloseSession(slot, session, owner);
    SECITEM_FreeItem(data, PR_TRUE);
    outKey->len = len;
    if (crv != CKR_OK) {
        PORT_SetError(PK11_MapError(crv));
        return SECFailure;
    }
    return SECSuccess;
}

/*
 * helper function which moves two keys into a new slot based on the
 * desired mechanism.
 */

static SECStatus
pk11_moveTwoKeys(CK_MECHANISM_TYPE mech,
                 CK_ATTRIBUTE_TYPE preferedOperation,
                 CK_ATTRIBUTE_TYPE movingOperation,
                 PK11SymKey *preferedKey, PK11SymKey *movingKey,
                 PK11SymKey **newPreferedKey, PK11SymKey **newMovingKey)
{
    PK11SlotInfo *newSlot;
    *newMovingKey = NULL;
    *newPreferedKey = NULL;

    newSlot = PK11_GetBestSlot(mech, preferedKey->cx);
    if (newSlot == NULL) {
        return SECFailure;
    }
    *newMovingKey = pk11_CopyToSlot(newSlot, movingKey->type,
                                    movingOperation, movingKey);
    if (*newMovingKey == NULL) {
        goto loser;
    }
    *newPreferedKey = pk11_CopyToSlot(newSlot, preferedKey->type,
                                      preferedOperation, preferedKey);
    if (*newPreferedKey == NULL) {
        goto loser;
    }

    PK11_FreeSlot(newSlot);
    return SECSuccess;
loser:
    PK11_FreeSlot(newSlot);
    PK11_FreeSymKey(*newMovingKey);
    PK11_FreeSymKey(*newPreferedKey);
    *newMovingKey = NULL;
    *newPreferedKey = NULL;
    return SECFailure;
}

/*
 * To do joint operations, we often need two keys in the same slot.
 * Usually the PKCS #11 wrappers handle this correctly (like for PK11_WrapKey),
 * but sometimes the wrappers don't know about mechanism specific keys in
 * the Mechanism params. This function makes sure the two keys are in the
 * same slot by copying one or both of the keys into a common slot. This
 * functions makes sure the slot can handle the target mechanism. If the copy
 * is warranted, this function will prefer to move the movingKey first, then
 * the preferedKey. If the keys are moved, the new keys are returned in
 * newMovingKey and/or newPreferedKey. The application is responsible
 * for freeing those keys once the operation is complete.
 */

SECStatus
PK11_SymKeysToSameSlot(CK_MECHANISM_TYPE mech,
                       CK_ATTRIBUTE_TYPE preferedOperation,
                       CK_ATTRIBUTE_TYPE movingOperation,
                       PK11SymKey *preferedKey, PK11SymKey *movingKey,
                       PK11SymKey **newPreferedKey, PK11SymKey **newMovingKey)
{
    /* usually don't return new keys */
    *newMovingKey = NULL;
    *newPreferedKey = NULL;
    if (movingKey->slot == preferedKey->slot) {

        /* this should be the most common case */
        if ((preferedKey->slot != NULL) &&
            PK11_DoesMechanism(preferedKey->slot, mech)) {
            return SECSuccess;
        }

        /* we are in the same slot, but it doesn't do the operation,
         * move both keys to an appropriate target slot */

        return pk11_moveTwoKeys(mech, preferedOperation, movingOperation,
                                preferedKey, movingKey,
                                newPreferedKey, newMovingKey);
    }

    /* keys are in different slot, try moving the moving key to the prefered
     * key's slot */

    if ((preferedKey->slot != NULL) &&
        PK11_DoesMechanism(preferedKey->slot, mech)) {
        *newMovingKey = pk11_CopyToSlot(preferedKey->slot, movingKey->type,
                                        movingOperation, movingKey);
        if (*newMovingKey != NULL) {
            return SECSuccess;
        }
    }
    /* couldn't moving the moving key to the prefered slot, try moving
     * the prefered key */

    if ((movingKey->slot != NULL) &&
        PK11_DoesMechanism(movingKey->slot, mech)) {
        *newPreferedKey = pk11_CopyToSlot(movingKey->slot, preferedKey->type,
                                          preferedOperation, preferedKey);
        if (*newPreferedKey != NULL) {
            return SECSuccess;
        }
    }
    /* Neither succeeded, but that could be that they were not in slots that
     * supported the operation, try moving both keys into a common slot that
     * can do the operation. */

    return pk11_moveTwoKeys(mech, preferedOperation, movingOperation,
                            preferedKey, movingKey,
                            newPreferedKey, newMovingKey);
}

/*
 * This function does a symetric based wrap.
 */

SECStatus
PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *param,
                PK11SymKey *wrappingKey, PK11SymKey *symKey,
                SECItem *wrappedKey)
{
    PK11SlotInfo *slot;
    CK_ULONG len = wrappedKey->len;
    PK11SymKey *newSymKey = NULL;
    PK11SymKey *newWrappingKey = NULL;
    SECItem *param_save = NULL;
    CK_MECHANISM mechanism;
    PRBool owner = PR_TRUE;
    CK_SESSION_HANDLE session;
    CK_RV crv;
    SECStatus rv;

    /* force the keys into same slot */
    rv = PK11_SymKeysToSameSlot(type, CKA_ENCRYPT, CKA_WRAP,
                                symKey, wrappingKey,
                                &newSymKey, &newWrappingKey);
    if (rv != SECSuccess) {
        /* Couldn't move the keys as desired, try to hand unwrap if possible */
        if (symKey->data.data == NULL) {
            rv = PK11_ExtractKeyValue(symKey);
            if (rv != SECSuccess) {
                PORT_SetError(SEC_ERROR_NO_MODULE);
                return SECFailure;
            }
        }
        if (param == NULL) {
            param_save = param = PK11_ParamFromIV(type, NULL);
        }
        rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data, wrappedKey);
        if (param_save)
            SECITEM_FreeItem(param_save, PR_TRUE);
        return rv;
    }
    if (newSymKey) {
        symKey = newSymKey;
    }
    if (newWrappingKey) {
        wrappingKey = newWrappingKey;
    }

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

    len = wrappedKey->len;

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

/*
 * This Generates a new key based on a symetricKey
 */

PK11SymKey *
PK11_Derive(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, SECItem *param,
            CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
            int keySize)
{
    return PK11_DeriveWithTemplate(baseKey, derive, param, target, operation,
                                   keySize, NULL, 0, PR_FALSE);
}

PK11SymKey *
PK11_DeriveWithFlags(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
                     SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
                     int keySize, CK_FLAGS flags)
{
    CK_BBOOL ckTrue = CK_TRUE;
    CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
    unsigned int templateCount;

    templateCount = pk11_OpFlagsToAttributes(flags, keyTemplate, &ckTrue);
    return PK11_DeriveWithTemplate(baseKey, derive, param, target, operation,
                                   keySize, keyTemplate, templateCount, PR_FALSE);
}

PK11SymKey *
PK11_DeriveWithFlagsPerm(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
                         SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
                         int keySize, CK_FLAGS flags, PRBool isPerm)
{
    CK_BBOOL cktrue = CK_TRUE;
    CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
    CK_ATTRIBUTE *attrs;
    unsigned int templateCount = 0;

    attrs = keyTemplate;
    if (isPerm) {
        PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
        attrs++;
    }
    templateCount = attrs - keyTemplate;
    templateCount += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
    return PK11_DeriveWithTemplate(baseKey, derive, param, target, operation,
                                   keySize, keyTemplate, templateCount, isPerm);
}

PK11SymKey *
PK11_DeriveWithTemplate(PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
                        SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
                        int keySize, CK_ATTRIBUTE *userAttr, unsigned int numAttrs,
                        PRBool isPerm)
{
    PK11SlotInfo *slot = baseKey->slot;
    PK11SymKey *symKey;
    PK11SymKey *newBaseKey = NULL;
    CK_BBOOL cktrue = CK_TRUE;
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
    CK_ULONG valueLen = 0;
    CK_MECHANISM mechanism;
    CK_RV crv;
#define MAX_ADD_ATTRS 4
    CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS + MAX_ADD_ATTRS];
#undef MAX_ADD_ATTRS
    CK_ATTRIBUTE *attrs = keyTemplate;
    CK_SESSION_HANDLE session;
    unsigned int templateCount;

    if (numAttrs > MAX_TEMPL_ATTRS) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }
    /* CKA_NSS_MESSAGE is a fake operation to distinguish between
     * Normal Encrypt/Decrypt and MessageEncrypt/Decrypt. Don't try to set
     * it as a real attribute */

    if ((operation & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) {
        /* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
         * etc. Strip out the real attribute here */

        operation &= ~CKA_NSS_MESSAGE_MASK;
    }

    /* first copy caller attributes in. */
    for (templateCount = 0; templateCount < numAttrs; ++templateCount) {
        *attrs++ = *userAttr++;
    }

    /* We only add the following attributes to the template if the caller
    ** didn't already supply them.
    */

    if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) {
        PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass);
        attrs++;
    }
    if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) {
        keyType = PK11_GetKeyType(target, keySize);
        PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType);
        attrs++;
    }
    if (keySize > 0 &&
        !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) {
        valueLen = (CK_ULONG)keySize;
        PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen);
        attrs++;
    }
    if ((operation != CKA_FLAGS_ONLY) &&
        !pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) {
        PK11_SETATTRS(attrs, operation, &cktrue, sizeof cktrue);
        attrs++;
    }

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

    /* move the key to a slot that can do the function */
    if (!PK11_DoesMechanism(slot, derive)) {
        /* get a new base key & slot */
        PK11SlotInfo *newSlot = PK11_GetBestSlot(derive, baseKey->cx);

        if (newSlot == NULL)
            return NULL;

        newBaseKey = pk11_CopyToSlot(newSlot, derive, CKA_DERIVE,
                                     baseKey);
        PK11_FreeSlot(newSlot);
        if (newBaseKey == NULL)
            return NULL;
        baseKey = newBaseKey;
        slot = baseKey->slot;
    }

    /* get our key Structure */
    symKey = pk11_CreateSymKey(slot, target, !isPerm, PR_TRUE, baseKey->cx);
    if (symKey == NULL) {
        return NULL;
    }

    symKey->size = keySize;

    mechanism.mechanism = derive;
    if (param) {
        mechanism.pParameter = param->data;
        mechanism.ulParameterLen = param->len;
    } else {
        mechanism.pParameter = NULL;
        mechanism.ulParameterLen = 0;
    }
    symKey->origin = PK11_OriginDerive;

    if (isPerm) {
        session = PK11_GetRWSession(slot);
    } else {
        pk11_EnterKeyMonitor(symKey);
        session = symKey->session;
    }
    if (session == CK_INVALID_HANDLE) {
        if (!isPerm)
            pk11_ExitKeyMonitor(symKey);
        crv = CKR_SESSION_HANDLE_INVALID;
    } else {
        crv = PK11_GETTAB(slot)->C_DeriveKey(session, &mechanism,
                                             baseKey->objectID, keyTemplate, templateCount, &symKey->objectID);
        if (isPerm) {
            PK11_RestoreROSession(slot, session);
        } else {
            pk11_ExitKeyMonitor(symKey);
        }
    }
    if (newBaseKey)
        PK11_FreeSymKey(newBaseKey);
    if (crv != CKR_OK) {
        PK11_FreeSymKey(symKey);
        PORT_SetError(PK11_MapError(crv));
        return NULL;
    }
    return symKey;
}

/* Create a new key by concatenating base and data
 */

static PK11SymKey *
pk11_ConcatenateBaseAndData(PK11SymKey *base,
                            CK_BYTE *data, CK_ULONG dataLen, CK_MECHANISM_TYPE target,
                            CK_ATTRIBUTE_TYPE operation)
{
    CK_KEY_DERIVATION_STRING_DATA mechParams;
    SECItem param;

    if (base == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }

    mechParams.pData = data;
    mechParams.ulLen = dataLen;
    param.data = (unsigned char *)&mechParams;
    param.len = sizeof(CK_KEY_DERIVATION_STRING_DATA);

    return PK11_Derive(base, CKM_CONCATENATE_BASE_AND_DATA,
                       ¶m, target, operation, 0);
}

/* Create a new key by concatenating base and key
 */

static PK11SymKey *
pk11_ConcatenateBaseAndKey(PK11SymKey *base,
                           PK11SymKey *key, CK_MECHANISM_TYPE target,
                           CK_ATTRIBUTE_TYPE operation, CK_ULONG keySize)
{
    SECItem param;

    if ((base == NULL) || (key == NULL)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }

    param.data = (unsigned char *)&(key->objectID);
    param.len = sizeof(CK_OBJECT_HANDLE);

    return PK11_Derive(base, CKM_CONCATENATE_BASE_AND_KEY,
                       ¶m, target, operation, keySize);
}

PK11SymKey *
PK11_ConcatSymKeys(PK11SymKey *left, PK11SymKey *right, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation)
{
    PK11SymKey *out = NULL;
    PK11SymKey *copyOfLeft = NULL;
    PK11SymKey *copyOfRight = NULL;

    if ((left == NULL) || (right == NULL)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }

    SECStatus rv = PK11_SymKeysToSameSlot(CKM_CONCATENATE_BASE_AND_KEY,
                                          CKA_DERIVE, CKA_DERIVE, left, right,
                                          ©OfLeft, ©OfRight);
    if (rv != SECSuccess) {
        /* error code already set */
        return NULL;
    }

    out = pk11_ConcatenateBaseAndKey(copyOfLeft ? copyOfLeft : left, copyOfRight ? copyOfRight : right, target, operation, 0);
    PK11_FreeSymKey(copyOfLeft);
    PK11_FreeSymKey(copyOfRight);
    return out;
}

/* Create a new key whose value is the hash of tobehashed.
 * type is the mechanism for the derived key.
 */

static PK11SymKey *
pk11_HashKeyDerivation(PK11SymKey *toBeHashed,
                       CK_MECHANISM_TYPE hashMechanism, CK_MECHANISM_TYPE target,
                       CK_ATTRIBUTE_TYPE operation, CK_ULONG keySize)
{
    return PK11_Derive(toBeHashed, hashMechanism, NULL, target, operation, keySize);
}

/* This function implements the ANSI X9.63 key derivation function
 */

static PK11SymKey *
pk11_ANSIX963Derive(PK11SymKey *sharedSecret,
                    CK_EC_KDF_TYPE kdf, SECItem *sharedData,
                    CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
                    CK_ULONG keySize)
{
    CK_KEY_TYPE keyType;
    CK_MECHANISM_TYPE hashMechanism, mechanismArray[4];
    CK_ULONG derivedKeySize, HashLen, counter, maxCounter, bufferLen;
    CK_ULONG SharedInfoLen;
    CK_BYTE *buffer = NULL;
    PK11SymKey *toBeHashed, *hashOutput;
    PK11SymKey *newSharedSecret = NULL;
    PK11SymKey *oldIntermediateResult, *intermediateResult = NULL;

    if (sharedSecret == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }

    switch (kdf) {
        case CKD_SHA1_KDF:
            HashLen = SHA1_LENGTH;
            hashMechanism = CKM_SHA1_KEY_DERIVATION;
            break;
        case CKD_SHA224_KDF:
            HashLen = SHA224_LENGTH;
            hashMechanism = CKM_SHA224_KEY_DERIVATION;
            break;
        case CKD_SHA256_KDF:
            HashLen = SHA256_LENGTH;
            hashMechanism = CKM_SHA256_KEY_DERIVATION;
            break;
        case CKD_SHA384_KDF:
            HashLen = SHA384_LENGTH;
            hashMechanism = CKM_SHA384_KEY_DERIVATION;
            break;
        case CKD_SHA512_KDF:
            HashLen = SHA512_LENGTH;
            hashMechanism = CKM_SHA512_KEY_DERIVATION;
            break;
        default:
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
            return NULL;
    }

    derivedKeySize = keySize;
    if (derivedKeySize == 0) {
        keyType = PK11_GetKeyType(target, keySize);
        derivedKeySize = pk11_GetPredefinedKeyLength(keyType);
        if (derivedKeySize == 0) {
            derivedKeySize = HashLen;
        }
    }

    /* Check that key_len isn't too long.  The maximum key length could be
     * greatly increased if the code below did not limit the 4-byte counter
     * to a maximum value of 255. */

    if (derivedKeySize > 254 * HashLen) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }

    maxCounter = derivedKeySize / HashLen;
    if (derivedKeySize > maxCounter * HashLen)
        maxCounter++;

    if ((sharedData == NULL) || (sharedData->data == NULL))
        SharedInfoLen = 0;
    else
        SharedInfoLen = sharedData->len;

    bufferLen = SharedInfoLen + 4;

    /* Populate buffer with Counter || sharedData
     * where Counter is 0x00000001. */

    buffer = (unsigned char *)PORT_Alloc(bufferLen);
    if (buffer == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return NULL;
    }

    buffer[0] = 0;
    buffer[1] = 0;
    buffer[2] = 0;
    buffer[3] = 1;
    if (SharedInfoLen > 0) {
        PORT_Memcpy(&buffer[4], sharedData->data, SharedInfoLen);
    }

    /* Look for a slot that supports the mechanisms needed
     * to implement the ANSI X9.63 KDF as well as the
     * target mechanism.
     */

    mechanismArray[0] = CKM_CONCATENATE_BASE_AND_DATA;
    mechanismArray[1] = hashMechanism;
    mechanismArray[2] = CKM_CONCATENATE_BASE_AND_KEY;
    mechanismArray[3] = target;

    newSharedSecret = pk11_ForceSlotMultiple(sharedSecret,
                                             mechanismArray, 4, operation);
    if (newSharedSecret != NULL) {
        sharedSecret = newSharedSecret;
    }

    for (counter = 1; counter <= maxCounter; counter++) {
        /* Concatenate shared_secret and buffer */
        toBeHashed = pk11_ConcatenateBaseAndData(sharedSecret, buffer,
                                                 bufferLen, hashMechanism, operation);
        if (toBeHashed == NULL) {
            goto loser;
        }

        /* Hash value */
        if (maxCounter == 1) {
            /* In this case the length of the key to be derived is
             * less than or equal to the length of the hash output.
             * So, the output of the hash operation will be the
             * dervied key. */

            hashOutput = pk11_HashKeyDerivation(toBeHashed, hashMechanism,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge