summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pk11wrap/pk11obj.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/pk11wrap/pk11obj.c
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/pk11wrap/pk11obj.c')
-rw-r--r--security/nss/lib/pk11wrap/pk11obj.c2285
1 files changed, 2285 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11obj.c b/security/nss/lib/pk11wrap/pk11obj.c
new file mode 100644
index 0000000000..5dd4d0fc05
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11obj.c
@@ -0,0 +1,2285 @@
+/* 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 <limits.h>
+#include <stddef.h>
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "secmodti.h"
+#include "pkcs11.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "keyhi.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_HANDLE) {
+ 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_Lock(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;
+}
+
+CK_BBOOL
+PK11_HasAttributeSet(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PRBool haslock)
+{
+ PR_ASSERT(haslock == PR_FALSE);
+ return pk11_HasAttributeSet_Lock(slot, id, type, PR_FALSE);
+}
+
+/*
+ * 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;
+ if (slot->session == CK_INVALID_HANDLE)
+ 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_HANDLE) {
+ 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_HANDLE) {
+ rwsession = slot->session;
+ if (rwsession != CK_INVALID_HANDLE)
+ PK11_EnterSlotMonitor(slot);
+ }
+ if (rwsession == CK_INVALID_HANDLE) {
+ 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_HANDLE) {
+ 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);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ *outLen = len;
+ 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);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ *outLen = len;
+ 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_NSS_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_HANDLE)
+ 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_HANDLE) {
+ 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);
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return newPrivKey;
+ }
+ }
+ if (int_slot)
+ PK11_FreeSlot(int_slot);
+ PORT_SetError(PK11_MapError(crv));
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return NULL;
+ }
+ SECITEM_FreeItem(param_free, PR_TRUE);
+ return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx);
+
+loser:
+ PK11_FreeSymKey(newKey);
+ SECITEM_FreeItem(ck_id, PR_TRUE);
+ SECITEM_FreeItem(param_free, 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->owner = PR_FALSE;
+ 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) {
+ if (object->owner) {
+ PK11_DestroyObject(object->slot, object->objectID);
+ }
+ 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_CreateGenericObjectHelper(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token, PRBool owner)
+{
+ 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->owner = owner;
+ obj->next = NULL;
+ obj->prev = NULL;
+ return obj;
+}
+
+/* This is the classic interface. Applications would call this function to
+ * create new object that would not be destroyed later. This lead to resource
+ * leaks (and thus memory leaks in the PKCS #11 module). To solve this we have
+ * a new interface that automatically marks objects created on the fly to be
+ * destroyed later.
+ * The old interface is preserved because applications like Mozilla purposefully
+ * leak the reference to be found later with PK11_FindGenericObjects. New
+ * applications should use the new interface PK11_CreateManagedGenericObject */
+PK11GenericObject *
+PK11_CreateGenericObject(PK11SlotInfo *slot, const CK_ATTRIBUTE *pTemplate,
+ int count, PRBool token)
+{
+ return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
+ PR_FALSE);
+}
+
+/* Use this interface. It will automatically destroy any temporary objects
+ * (token = PR_FALSE) when the PK11GenericObject is freed. Permanent objects still
+ * need to be destroyed by hand with PK11_DestroyTokenObject.
+ */
+PK11GenericObject *
+PK11_CreateManagedGenericObject(PK11SlotInfo *slot,
+ const CK_ATTRIBUTE *pTemplate, int count, PRBool token)
+{
+ return pk11_CreateGenericObjectHelper(slot, pTemplate, count, token,
+ !token);
+}
+
+CK_OBJECT_HANDLE
+PK11_GetObjectHandle(PK11ObjectType objType, void *objSpec,
+ PK11SlotInfo **slotp)
+{
+ CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE;
+ PK11SlotInfo *slot = NULL;
+
+ 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:
+ handle = PK11_FindObjectForCert((CERTCertificate *)objSpec, NULL,
+ &slot);
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ break;
+ }
+ if (slotp) {
+ *slotp = slot;
+ }
+ /* paranoia. If the object doesn't have a slot, then it's handle isn't
+ * valid either */
+ if (slot == NULL) {
+ handle = CK_INVALID_HANDLE;
+ }
+ return handle;
+}
+
+/*
+ * 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;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ 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_HANDLE) {
+ 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;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+ }
+
+ return PK11_ReadAttribute(slot, handle, attrType, NULL, item);
+}
+
+SECStatus
+PK11_ReadRawAttributes(PLArenaPool *arena, PK11ObjectType objType, void *objSpec,
+ CK_ATTRIBUTE *pTemplate, unsigned int count)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE handle = 0;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return SECFailure;
+ }
+ CK_RV crv = PK11_GetAttributes(arena, slot, handle, pTemplate, count);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * return the object handle that matches the template
+ */
+CK_OBJECT_HANDLE
+pk11_FindObjectByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *theTemplate, size_t 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_HANDLE) {
+ 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,
+ size_t templCount, int *object_count)
+{
+ CK_OBJECT_HANDLE *objID = NULL;
+ CK_ULONG returned_count = 0;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ PRBool haslock = PR_FALSE;
+ CK_RV crv = CKR_SESSION_HANDLE_INVALID;
+
+ session = pk11_GetNewSession(slot, &owner);
+ haslock = (!owner || !(slot->isThreadSafe));
+ if (haslock) {
+ PK11_EnterSlotMonitor(slot);
+ }
+ if (session != CK_INVALID_HANDLE) {
+ crv = PK11_GETTAB(slot)->C_FindObjectsInit(session,
+ findTemplate, templCount);
+ }
+ if (crv != CKR_OK) {
+ if (haslock)
+ PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot, session, owner);
+ 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(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(session);
+ if (haslock) {
+ PK11_ExitSlotMonitor(slot);
+ }
+ pk11_CloseSession(slot, session, owner);
+
+ if (objID && (*object_count == 0)) {
+ PORT_Free(objID);
+ return NULL;
+ }
+ if (objID == NULL)
+ *object_count = -1;
+ return objID;
+}
+
+SECStatus
+PK11_FindRawCertsWithSubject(PK11SlotInfo *slot, SECItem *derSubject,
+ CERTCertificateList **results)
+{
+ if (!slot || !derSubject || !results) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ *results = NULL;
+
+ // derSubject->data may be null. If so, derSubject->len must be 0.
+ if (!derSubject->data && derSubject->len != 0) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ CK_CERTIFICATE_TYPE ckc_x_509 = CKC_X_509;
+ CK_OBJECT_CLASS cko_certificate = CKO_CERTIFICATE;
+ CK_ATTRIBUTE subjectTemplate[] = {
+ { CKA_CERTIFICATE_TYPE, &ckc_x_509, sizeof(ckc_x_509) },
+ { CKA_CLASS, &cko_certificate, sizeof(cko_certificate) },
+ { CKA_SUBJECT, derSubject->data, derSubject->len },
+ };
+ const size_t templateCount = sizeof(subjectTemplate) / sizeof(subjectTemplate[0]);
+ int handleCount = 0;
+ CK_OBJECT_HANDLE *handles =
+ pk11_FindObjectsByTemplate(slot, subjectTemplate, templateCount,
+ &handleCount);
+ if (!handles) {
+ // pk11_FindObjectsByTemplate indicates there was an error by setting
+ // handleCount to -1 (and it has set an error with PORT_SetError).
+ if (handleCount == -1) {
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+ PORT_Assert(handleCount > 0);
+ if (handleCount <= 0) {
+ PORT_Free(handles);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ if (handleCount > INT_MAX / sizeof(SECItem)) {
+ PORT_Free(handles);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ PORT_Free(handles);
+ return SECFailure;
+ }
+ CERTCertificateList *rawCertificates =
+ PORT_ArenaNew(arena, CERTCertificateList);
+ if (!rawCertificates) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ rawCertificates->arena = arena;
+ rawCertificates->certs = PORT_ArenaNewArray(arena, SECItem, handleCount);
+ if (!rawCertificates->certs) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ rawCertificates->len = handleCount;
+ int handleIndex;
+ for (handleIndex = 0; handleIndex < handleCount; handleIndex++) {
+ SECStatus rv =
+ PK11_ReadAttribute(slot, handles[handleIndex], CKA_VALUE, arena,
+ &rawCertificates->certs[handleIndex]);
+ if (rv != SECSuccess) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+ if (!rawCertificates->certs[handleIndex].data) {
+ PORT_Free(handles);
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ }
+ PORT_Free(handles);
+ *results = rawCertificates;
+ return SECSuccess;
+}
+
+/*
+ * 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];
+ const size_t 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_HANDLE) {
+ 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 },
+ };
+ const size_t 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);
+ if (!tokenName) {
+ return CK_INVALID_HANDLE;
+ }
+ 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;
+}
+
+PRBool
+PK11_ObjectGetFIPSStatus(PK11ObjectType objType, void *objSpec)
+{
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE handle = 0;
+
+ handle = PK11_GetObjectHandle(objType, objSpec, &slot);
+ if (handle == CK_INVALID_HANDLE) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_OBJECT_TYPE);
+ return PR_FALSE;
+ }
+ return pk11slot_GetFIPSStatus(slot, slot->session, handle,
+ CKT_NSS_OBJECT_CHECK);
+}