summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/softoken/pkcs11u.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/softoken/pkcs11u.c')
-rw-r--r--security/nss/lib/softoken/pkcs11u.c2563
1 files changed, 2563 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/pkcs11u.c b/security/nss/lib/softoken/pkcs11u.c
new file mode 100644
index 0000000000..f483060986
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11u.c
@@ -0,0 +1,2563 @@
+/* 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/. */
+/*
+ * Internal PKCS #11 functions. Should only be called by pkcs11.c
+ */
+#include "pkcs11.h"
+#include "pkcs11i.h"
+#include "lowkeyi.h"
+#include "secasn1.h"
+#include "blapi.h"
+#include "secerr.h"
+#include "prnetdb.h" /* for PR_ntohl */
+#include "sftkdb.h"
+#include "softoken.h"
+#include "secoid.h"
+#include "softkver.h"
+
+#if !defined(NSS_FIPS_DISABLED) && defined(NSS_ENABLE_FIPS_INDICATORS)
+/* this file should be supplied by the vendor and include all the
+ * algorithms which have Algorithm certs and have been reviewed by
+ * the lab. A blank file is included for the base so that FIPS mode
+ * will still be compiled and run, but FIPS indicators will always
+ * return PR_FALSE
+ */
+#include "fips_algorithms.h"
+#define NSS_HAS_FIPS_INDICATORS 1
+#endif
+
+/*
+ * ******************** Error mapping *******************************
+ */
+/*
+ * map all the SEC_ERROR_xxx error codes that may be returned by freebl
+ * functions to CKR_xxx. return CKR_DEVICE_ERROR by default for backward
+ * compatibility.
+ */
+CK_RV
+sftk_MapCryptError(int error)
+{
+ switch (error) {
+ case SEC_ERROR_INVALID_ARGS:
+ case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */
+ return CKR_ARGUMENTS_BAD;
+ case SEC_ERROR_INPUT_LEN:
+ return CKR_DATA_LEN_RANGE;
+ case SEC_ERROR_OUTPUT_LEN:
+ return CKR_BUFFER_TOO_SMALL;
+ case SEC_ERROR_LIBRARY_FAILURE:
+ return CKR_GENERAL_ERROR;
+ case SEC_ERROR_NO_MEMORY:
+ return CKR_HOST_MEMORY;
+ case SEC_ERROR_BAD_SIGNATURE:
+ return CKR_SIGNATURE_INVALID;
+ case SEC_ERROR_INVALID_KEY:
+ return CKR_KEY_SIZE_RANGE;
+ case SEC_ERROR_BAD_KEY: /* an EC public key that fails validation */
+ return CKR_KEY_SIZE_RANGE; /* the closest error code */
+ case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM:
+ return CKR_TEMPLATE_INCONSISTENT;
+ case SEC_ERROR_UNSUPPORTED_KEYALG:
+ return CKR_MECHANISM_INVALID;
+ case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE:
+ return CKR_DOMAIN_PARAMS_INVALID;
+ /* key pair generation failed after max number of attempts */
+ case SEC_ERROR_NEED_RANDOM:
+ return CKR_FUNCTION_FAILED;
+ }
+ return CKR_DEVICE_ERROR;
+}
+
+/*
+ * functions which adjust the mapping based on different contexts
+ * (Decrypt or Verify).
+ */
+
+/* used by Decrypt and UnwrapKey (indirectly) and Decrypt message */
+CK_RV
+sftk_MapDecryptError(int error)
+{
+ switch (error) {
+ /* usually a padding error, or aead tag mismatch */
+ case SEC_ERROR_BAD_DATA:
+ return CKR_ENCRYPTED_DATA_INVALID;
+ default:
+ return sftk_MapCryptError(error);
+ }
+}
+
+/*
+ * return CKR_SIGNATURE_INVALID instead of CKR_DEVICE_ERROR by default for
+ * backward compatibilty.
+ */
+CK_RV
+sftk_MapVerifyError(int error)
+{
+ CK_RV crv = sftk_MapCryptError(error);
+ if (crv == CKR_DEVICE_ERROR)
+ crv = CKR_SIGNATURE_INVALID;
+ return crv;
+}
+
+/*
+ * ******************** Attribute Utilities *******************************
+ */
+
+/*
+ * create a new attribute with type, value, and length. Space is allocated
+ * to hold value.
+ */
+static SFTKAttribute *
+sftk_NewAttribute(SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type, const void *value, CK_ULONG len)
+{
+ SFTKAttribute *attribute;
+
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+ int index;
+
+ if (so == NULL) {
+ /* allocate new attribute in a buffer */
+ PORT_Assert(0);
+ return NULL;
+ }
+ /*
+ * We attempt to keep down contention on Malloc and Arena locks by
+ * limiting the number of these calls on high traversed paths. This
+ * is done for attributes by 'allocating' them from a pool already
+ * allocated by the parent object.
+ */
+ PZ_Lock(so->attributeLock);
+ index = so->nextAttr++;
+ PZ_Unlock(so->attributeLock);
+ PORT_Assert(index < MAX_OBJS_ATTRS);
+ if (index >= MAX_OBJS_ATTRS)
+ return NULL;
+
+ attribute = &so->attrList[index];
+ attribute->attrib.type = type;
+ attribute->freeAttr = PR_FALSE;
+ attribute->freeData = PR_FALSE;
+ if (value) {
+ if (len <= ATTR_SPACE) {
+ attribute->attrib.pValue = attribute->space;
+ } else {
+ attribute->attrib.pValue = PORT_Alloc(len);
+ attribute->freeData = PR_TRUE;
+ }
+ if (attribute->attrib.pValue == NULL) {
+ return NULL;
+ }
+ PORT_Memcpy(attribute->attrib.pValue, value, len);
+ attribute->attrib.ulValueLen = len;
+ } else {
+ attribute->attrib.pValue = NULL;
+ attribute->attrib.ulValueLen = 0;
+ }
+ attribute->attrib.type = type;
+ attribute->handle = type;
+ attribute->next = attribute->prev = NULL;
+ return attribute;
+}
+
+/*
+ * Free up all the memory associated with an attribute. Reference count
+ * must be zero to call this.
+ */
+static void
+sftk_DestroyAttribute(SFTKAttribute *attribute)
+{
+ if (attribute->attrib.pValue) {
+ /* clear out the data in the attribute value... it may have been
+ * sensitive data */
+ PORT_Memset(attribute->attrib.pValue, 0, attribute->attrib.ulValueLen);
+ if (attribute->freeData) {
+ PORT_Free(attribute->attrib.pValue);
+ attribute->attrib.pValue = NULL;
+ attribute->freeData = PR_FALSE;
+ }
+ }
+ if (attribute->freeAttr) {
+ PORT_Free(attribute);
+ }
+}
+
+/*
+ * release a reference to an attribute structure
+ */
+void
+sftk_FreeAttribute(SFTKAttribute *attribute)
+{
+ if (attribute && attribute->freeAttr) {
+ sftk_DestroyAttribute(attribute);
+ return;
+ }
+}
+
+static SFTKAttribute *
+sftk_FindTokenAttribute(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *myattribute = NULL;
+ SFTKDBHandle *dbHandle = NULL;
+ CK_RV crv = CKR_HOST_MEMORY;
+
+ myattribute = (SFTKAttribute *)PORT_Alloc(sizeof(SFTKAttribute));
+ if (myattribute == NULL) {
+ goto loser;
+ }
+
+ dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
+
+ myattribute->handle = type;
+ myattribute->attrib.type = type;
+ myattribute->attrib.pValue = myattribute->space;
+ myattribute->attrib.ulValueLen = ATTR_SPACE;
+ myattribute->next = myattribute->prev = NULL;
+ myattribute->freeAttr = PR_TRUE;
+ myattribute->freeData = PR_FALSE;
+
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
+ &myattribute->attrib, 1);
+
+ /* attribute is bigger than our attribute space buffer, malloc it */
+ if (crv == CKR_BUFFER_TOO_SMALL) {
+ myattribute->attrib.pValue = NULL;
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
+ &myattribute->attrib, 1);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ myattribute->attrib.pValue = PORT_Alloc(myattribute->attrib.ulValueLen);
+ if (myattribute->attrib.pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ myattribute->freeData = PR_TRUE;
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
+ &myattribute->attrib, 1);
+ }
+loser:
+ if (dbHandle) {
+ sftk_freeDB(dbHandle);
+ }
+ if (crv != CKR_OK) {
+ if (myattribute) {
+ myattribute->attrib.ulValueLen = 0;
+ sftk_FreeAttribute(myattribute);
+ myattribute = NULL;
+ }
+ }
+ return myattribute;
+}
+
+/*
+ * look up and attribute structure from a type and Object structure.
+ * The returned attribute is referenced and needs to be freed when
+ * it is no longer needed.
+ */
+SFTKAttribute *
+sftk_FindAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) {
+ return sftk_FindTokenAttribute(sftk_narrowToTokenObject(object), type);
+ }
+
+ PZ_Lock(sessObject->attributeLock);
+ sftkqueue_find(attribute, type, sessObject->head, sessObject->hashSize);
+ PZ_Unlock(sessObject->attributeLock);
+
+ return (attribute);
+}
+
+/*
+ * Take a buffer and it's length and return it's true size in bits;
+ */
+unsigned int
+sftk_GetLengthInBits(unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int size = bufLen * 8;
+ unsigned int i;
+
+ /* Get the real length in bytes */
+ for (i = 0; i < bufLen; i++) {
+ unsigned char c = *buf++;
+ if (c != 0) {
+ unsigned char m;
+ for (m = 0x80; m > 0; m = m >> 1) {
+ if ((c & m) != 0) {
+ break;
+ }
+ size--;
+ }
+ break;
+ }
+ size -= 8;
+ }
+ return size;
+}
+
+/*
+ * Constrain a big num attribute. to size and padding
+ * minLength means length of the object must be greater than equal to minLength
+ * maxLength means length of the object must be less than equal to maxLength
+ * minMultiple means that object length mod minMultiple must equal 0.
+ * all input sizes are in bits.
+ * if any constraint is '0' that constraint is not checked.
+ */
+CK_RV
+sftk_ConstrainAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ int minLength, int maxLength, int minMultiple)
+{
+ SFTKAttribute *attribute;
+ int size;
+ unsigned char *ptr;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (!attribute) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ ptr = (unsigned char *)attribute->attrib.pValue;
+ if (ptr == NULL) {
+ sftk_FreeAttribute(attribute);
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ size = sftk_GetLengthInBits(ptr, attribute->attrib.ulValueLen);
+ sftk_FreeAttribute(attribute);
+
+ if ((minLength != 0) && (size < minLength)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ if ((maxLength != 0) && (size > maxLength)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ if ((minMultiple != 0) && ((size % minMultiple) != 0)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ return CKR_OK;
+}
+
+PRBool
+sftk_hasAttributeToken(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ CK_ATTRIBUTE template;
+ CK_RV crv;
+ SFTKDBHandle *dbHandle;
+
+ dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
+ template.type = type;
+ template.pValue = NULL;
+ template.ulValueLen = 0;
+
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, &template, 1);
+ sftk_freeDB(dbHandle);
+
+ /* attribute is bigger than our attribute space buffer, malloc it */
+ return (crv == CKR_OK) ? PR_TRUE : PR_FALSE;
+}
+
+/*
+ * return true if object has attribute
+ */
+PRBool
+sftk_hasAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) {
+ return sftk_hasAttributeToken(sftk_narrowToTokenObject(object), type);
+ }
+
+ PZ_Lock(sessObject->attributeLock);
+ sftkqueue_find(attribute, type, sessObject->head, sessObject->hashSize);
+ PZ_Unlock(sessObject->attributeLock);
+
+ return (PRBool)(attribute != NULL);
+}
+
+/*
+ * add an attribute to an object
+ */
+static void
+sftk_AddAttribute(SFTKObject *object, SFTKAttribute *attribute)
+{
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL)
+ return;
+ PZ_Lock(sessObject->attributeLock);
+ sftkqueue_add(attribute, attribute->handle,
+ sessObject->head, sessObject->hashSize);
+ PZ_Unlock(sessObject->attributeLock);
+}
+
+/*
+ * copy an unsigned attribute into a SECItem. Secitem is allocated in
+ * the specified arena.
+ */
+CK_RV
+sftk_Attribute2SSecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+
+ item->data = NULL;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return CKR_TEMPLATE_INCOMPLETE;
+
+ (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen);
+ if (item->data == NULL) {
+ sftk_FreeAttribute(attribute);
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy(item->data, attribute->attrib.pValue, item->len);
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+/*
+ * fetch multiple attributes into SECItems. Secitem data is allocated in
+ * the specified arena.
+ */
+CK_RV
+sftk_MultipleAttribute2SecItem(PLArenaPool *arena, SFTKObject *object,
+ SFTKItemTemplate *itemTemplate, int itemTemplateCount)
+{
+
+ CK_RV crv = CKR_OK;
+ CK_ATTRIBUTE templateSpace[SFTK_MAX_ITEM_TEMPLATE];
+ CK_ATTRIBUTE *template;
+ SFTKTokenObject *tokObject;
+ SFTKDBHandle *dbHandle = NULL;
+ int i;
+
+ tokObject = sftk_narrowToTokenObject(object);
+
+ /* session objects, just loop through the list */
+ if (tokObject == NULL) {
+ for (i = 0; i < itemTemplateCount; i++) {
+ crv = sftk_Attribute2SecItem(arena, itemTemplate[i].item, object,
+ itemTemplate[i].type);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ }
+ return CKR_OK;
+ }
+
+ /* don't do any work if none is required */
+ if (itemTemplateCount == 0) {
+ return CKR_OK;
+ }
+
+ /* don't allocate the template unless we need it */
+ if (itemTemplateCount > SFTK_MAX_ITEM_TEMPLATE) {
+ template = PORT_NewArray(CK_ATTRIBUTE, itemTemplateCount);
+ } else {
+ template = templateSpace;
+ }
+
+ if (template == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
+ if (dbHandle == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto loser;
+ }
+
+ /* set up the PKCS #11 template */
+ for (i = 0; i < itemTemplateCount; i++) {
+ template[i].type = itemTemplate[i].type;
+ template[i].pValue = NULL;
+ template[i].ulValueLen = 0;
+ }
+
+ /* fetch the attribute lengths */
+ crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
+ template, itemTemplateCount);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* allocate space for the attributes */
+ for (i = 0; i < itemTemplateCount; i++) {
+ template[i].pValue = PORT_ArenaAlloc(arena, template[i].ulValueLen);
+ if (template[i].pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ }
+
+ /* fetch the attributes */
+ crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
+ template, itemTemplateCount);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* Fill in the items */
+ for (i = 0; i < itemTemplateCount; i++) {
+ itemTemplate[i].item->data = template[i].pValue;
+ itemTemplate[i].item->len = template[i].ulValueLen;
+ }
+
+loser:
+ if (template != templateSpace) {
+ PORT_Free(template);
+ }
+ if (dbHandle) {
+ sftk_freeDB(dbHandle);
+ }
+
+ return crv;
+}
+
+/*
+ * delete an attribute from an object
+ */
+static void
+sftk_DeleteAttribute(SFTKObject *object, SFTKAttribute *attribute)
+{
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) {
+ return;
+ }
+ PZ_Lock(sessObject->attributeLock);
+ if (sftkqueue_is_queued(attribute, attribute->handle,
+ sessObject->head, sessObject->hashSize)) {
+ sftkqueue_delete(attribute, attribute->handle,
+ sessObject->head, sessObject->hashSize);
+ }
+ PZ_Unlock(sessObject->attributeLock);
+}
+
+/*
+ * this is only valid for CK_BBOOL type attributes. Return the state
+ * of that attribute.
+ */
+PRBool
+sftk_isTrue(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ PRBool tok = PR_FALSE;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL) {
+ return PR_FALSE;
+ }
+ tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue);
+ sftk_FreeAttribute(attribute);
+
+ return tok;
+}
+
+/*
+ * force an attribute to null.
+ * this is for sensitive keys which are stored in the database, we don't
+ * want to keep this info around in memory in the clear.
+ */
+void
+sftk_nullAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return;
+
+ if (attribute->attrib.pValue != NULL) {
+ PORT_Memset(attribute->attrib.pValue, 0, attribute->attrib.ulValueLen);
+ if (attribute->freeData) {
+ PORT_Free(attribute->attrib.pValue);
+ }
+ attribute->freeData = PR_FALSE;
+ attribute->attrib.pValue = NULL;
+ attribute->attrib.ulValueLen = 0;
+ }
+ sftk_FreeAttribute(attribute);
+}
+
+static CK_RV
+sftk_forceTokenAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len)
+{
+ CK_ATTRIBUTE attribute;
+ SFTKDBHandle *dbHandle = NULL;
+ SFTKTokenObject *to = sftk_narrowToTokenObject(object);
+ CK_RV crv;
+
+ PORT_Assert(to);
+ if (to == NULL) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
+
+ attribute.type = type;
+ attribute.pValue = (void *)value;
+ attribute.ulValueLen = len;
+
+ crv = sftkdb_SetAttributeValue(dbHandle, object, &attribute, 1);
+ sftk_freeDB(dbHandle);
+ return crv;
+}
+
+/*
+ * force an attribute to a specifc value.
+ */
+CK_RV
+sftk_forceAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len)
+{
+ SFTKAttribute *attribute;
+ void *att_val = NULL;
+ PRBool freeData = PR_FALSE;
+
+ PORT_Assert(object);
+ PORT_Assert(object->refCount);
+ PORT_Assert(object->slot);
+ if (!object ||
+ !object->refCount ||
+ !object->slot) {
+ return CKR_DEVICE_ERROR;
+ }
+ if (sftk_isToken(object->handle)) {
+ return sftk_forceTokenAttribute(object, type, value, len);
+ }
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return sftk_AddAttributeType(object, type, value, len);
+
+ if (value) {
+ if (len <= ATTR_SPACE) {
+ att_val = attribute->space;
+ } else {
+ att_val = PORT_Alloc(len);
+ freeData = PR_TRUE;
+ }
+ if (att_val == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ if (attribute->attrib.pValue == att_val) {
+ PORT_Memset(attribute->attrib.pValue, 0,
+ attribute->attrib.ulValueLen);
+ }
+ PORT_Memcpy(att_val, value, len);
+ }
+ if (attribute->attrib.pValue != NULL) {
+ if (attribute->attrib.pValue != att_val) {
+ PORT_Memset(attribute->attrib.pValue, 0,
+ attribute->attrib.ulValueLen);
+ }
+ if (attribute->freeData) {
+ PORT_Free(attribute->attrib.pValue);
+ }
+ attribute->freeData = PR_FALSE;
+ attribute->attrib.pValue = NULL;
+ attribute->attrib.ulValueLen = 0;
+ }
+ if (att_val) {
+ attribute->attrib.pValue = att_val;
+ attribute->attrib.ulValueLen = len;
+ attribute->freeData = freeData;
+ }
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+/*
+ * return a null terminated string from attribute 'type'. This string
+ * is allocated and needs to be freed with PORT_Free() When complete.
+ */
+char *
+sftk_getString(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ char *label = NULL;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return NULL;
+
+ if (attribute->attrib.pValue != NULL) {
+ label = (char *)PORT_Alloc(attribute->attrib.ulValueLen + 1);
+ if (label == NULL) {
+ sftk_FreeAttribute(attribute);
+ return NULL;
+ }
+
+ PORT_Memcpy(label, attribute->attrib.pValue,
+ attribute->attrib.ulValueLen);
+ label[attribute->attrib.ulValueLen] = 0;
+ }
+ sftk_FreeAttribute(attribute);
+ return label;
+}
+
+/*
+ * decode when a particular attribute may be modified
+ * SFTK_NEVER: This attribute must be set at object creation time and
+ * can never be modified.
+ * SFTK_ONCOPY: This attribute may be modified only when you copy the
+ * object.
+ * SFTK_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from
+ * CK_FALSE to CK_TRUE.
+ * SFTK_ALWAYS: This attribute can always be modified.
+ * Some attributes vary their modification type based on the class of the
+ * object.
+ */
+SFTKModifyType
+sftk_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
+{
+ /* if we don't know about it, user user defined, always allow modify */
+ SFTKModifyType mtype = SFTK_ALWAYS;
+
+ switch (type) {
+ /* NEVER */
+ case CKA_CLASS:
+ case CKA_CERTIFICATE_TYPE:
+ case CKA_KEY_TYPE:
+ case CKA_MODULUS:
+ case CKA_MODULUS_BITS:
+ case CKA_PUBLIC_EXPONENT:
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME:
+ case CKA_BASE:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ case CKA_VALUE_LEN:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_NEVER_EXTRACTABLE:
+ case CKA_NSS_DB:
+ mtype = SFTK_NEVER;
+ break;
+
+ /* ONCOPY */
+ case CKA_TOKEN:
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ mtype = SFTK_ONCOPY;
+ break;
+
+ /* SENSITIVE */
+ case CKA_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ mtype = SFTK_SENSITIVE;
+ break;
+
+ /* ALWAYS */
+ case CKA_LABEL:
+ case CKA_APPLICATION:
+ case CKA_ID:
+ case CKA_SERIAL_NUMBER:
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_VERIFY:
+ case CKA_SIGN_RECOVER:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ case CKA_UNWRAP:
+ mtype = SFTK_ALWAYS;
+ break;
+
+ /* DEPENDS ON CLASS */
+ case CKA_VALUE:
+ mtype = (inClass == CKO_DATA) ? SFTK_ALWAYS : SFTK_NEVER;
+ break;
+
+ case CKA_SUBPRIME:
+ /* allow the CKA_SUBPRIME to be added to dh private keys */
+ mtype = (inClass == CKO_PRIVATE_KEY) ? SFTK_ALWAYS : SFTK_NEVER;
+ break;
+
+ case CKA_SUBJECT:
+ mtype = (inClass == CKO_CERTIFICATE) ? SFTK_NEVER : SFTK_ALWAYS;
+ break;
+ default:
+ break;
+ }
+ return mtype;
+}
+
+/* decode if a particular attribute is sensitive (cannot be read
+ * back to the user of if the object is set to SENSITIVE) */
+PRBool
+sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
+{
+ switch (type) {
+ /* ALWAYS */
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ return PR_TRUE;
+
+ /* DEPENDS ON CLASS */
+ case CKA_VALUE:
+ /* PRIVATE and SECRET KEYS have SENSITIVE values */
+ return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY));
+
+ default:
+ break;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * copy an attribute into a SECItem. Secitem is allocated in the specified
+ * arena.
+ */
+CK_RV
+sftk_Attribute2SecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type)
+{
+ int len;
+ SFTKAttribute *attribute;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return CKR_TEMPLATE_INCOMPLETE;
+ len = attribute->attrib.ulValueLen;
+
+ if (arena) {
+ item->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
+ } else {
+ item->data = (unsigned char *)PORT_Alloc(len);
+ }
+ if (item->data == NULL) {
+ sftk_FreeAttribute(attribute);
+ return CKR_HOST_MEMORY;
+ }
+ item->len = len;
+ PORT_Memcpy(item->data, attribute->attrib.pValue, len);
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+CK_RV
+sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ CK_ULONG *longData)
+{
+ SFTKAttribute *attribute;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return CKR_TEMPLATE_INCOMPLETE;
+
+ if (attribute->attrib.ulValueLen != sizeof(CK_ULONG)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ *longData = *(CK_ULONG *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+void
+sftk_DeleteAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return;
+ sftk_DeleteAttribute(object, attribute);
+ sftk_DestroyAttribute(attribute);
+}
+
+CK_RV
+sftk_AddAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ const void *valPtr, CK_ULONG length)
+{
+ SFTKAttribute *attribute;
+ attribute = sftk_NewAttribute(object, type, valPtr, length);
+ if (attribute == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ sftk_AddAttribute(object, attribute);
+ return CKR_OK;
+}
+
+/*
+ * ******************** Object Utilities *******************************
+ */
+
+/* must be called holding sftk_tokenKeyLock(slot) */
+static SECItem *
+sftk_lookupTokenKeyByHandle(SFTKSlot *slot, CK_OBJECT_HANDLE handle)
+{
+ return (SECItem *)PL_HashTableLookup(slot->tokObjHashTable, (void *)(uintptr_t)handle);
+}
+
+/*
+ * use the refLock. This operations should be very rare, so the added
+ * contention on the ref lock should be lower than the overhead of adding
+ * a new lock. We use separate functions for this just in case I'm wrong.
+ */
+static void
+sftk_tokenKeyLock(SFTKSlot *slot)
+{
+ SKIP_AFTER_FORK(PZ_Lock(slot->objectLock));
+}
+
+static void
+sftk_tokenKeyUnlock(SFTKSlot *slot)
+{
+ SKIP_AFTER_FORK(PZ_Unlock(slot->objectLock));
+}
+
+static PRIntn
+sftk_freeHashItem(PLHashEntry *entry, PRIntn index, void *arg)
+{
+ SECItem *item = (SECItem *)entry->value;
+
+ SECITEM_FreeItem(item, PR_TRUE);
+ return HT_ENUMERATE_NEXT;
+}
+
+CK_RV
+SFTK_ClearTokenKeyHashTable(SFTKSlot *slot)
+{
+ sftk_tokenKeyLock(slot);
+ PORT_Assert(!slot->present);
+ PL_HashTableEnumerateEntries(slot->tokObjHashTable, sftk_freeHashItem, NULL);
+ sftk_tokenKeyUnlock(slot);
+ return CKR_OK;
+}
+
+/* allocation hooks that allow us to recycle old object structures */
+static SFTKObjectFreeList sessionObjectList = { NULL, NULL, 0 };
+static SFTKObjectFreeList tokenObjectList = { NULL, NULL, 0 };
+
+SFTKObject *
+sftk_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace,
+ SFTKObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject)
+{
+ SFTKObject *object;
+ int size = 0;
+
+ if (!optimizeSpace) {
+ PZ_Lock(list->lock);
+ object = list->head;
+ if (object) {
+ list->head = object->next;
+ list->count--;
+ }
+ PZ_Unlock(list->lock);
+ if (object) {
+ object->next = object->prev = NULL;
+ *hasLocks = PR_TRUE;
+ return object;
+ }
+ }
+ size = isSessionObject ? sizeof(SFTKSessionObject) + hashSize * sizeof(SFTKAttribute *) : sizeof(SFTKTokenObject);
+
+ object = (SFTKObject *)PORT_ZAlloc(size);
+ if (isSessionObject && object) {
+ ((SFTKSessionObject *)object)->hashSize = hashSize;
+ }
+ *hasLocks = PR_FALSE;
+ return object;
+}
+
+static void
+sftk_PutObjectToList(SFTKObject *object, SFTKObjectFreeList *list,
+ PRBool isSessionObject)
+{
+
+ /* the code below is equivalent to :
+ * optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE;
+ * just faster.
+ */
+ PRBool optimizeSpace = isSessionObject &&
+ ((SFTKSessionObject *)object)->optimizeSpace;
+ if (object->refLock && !optimizeSpace && (list->count < MAX_OBJECT_LIST_SIZE)) {
+ PZ_Lock(list->lock);
+ object->next = list->head;
+ list->head = object;
+ list->count++;
+ PZ_Unlock(list->lock);
+ return;
+ }
+ if (isSessionObject) {
+ SFTKSessionObject *so = (SFTKSessionObject *)object;
+ PZ_DestroyLock(so->attributeLock);
+ so->attributeLock = NULL;
+ }
+ if (object->refLock) {
+ PZ_DestroyLock(object->refLock);
+ object->refLock = NULL;
+ }
+ PORT_Free(object);
+}
+
+static SFTKObject *
+sftk_freeObjectData(SFTKObject *object)
+{
+ SFTKObject *next = object->next;
+
+ PORT_Free(object);
+ return next;
+}
+
+static void
+sftk_InitFreeList(SFTKObjectFreeList *list)
+{
+ if (!list->lock) {
+ list->lock = PZ_NewLock(nssILockObject);
+ }
+}
+
+void
+sftk_InitFreeLists(void)
+{
+ sftk_InitFreeList(&sessionObjectList);
+ sftk_InitFreeList(&tokenObjectList);
+}
+
+static void
+sftk_CleanupFreeList(SFTKObjectFreeList *list, PRBool isSessionList)
+{
+ SFTKObject *object;
+
+ if (!list->lock) {
+ return;
+ }
+ SKIP_AFTER_FORK(PZ_Lock(list->lock));
+ for (object = list->head; object != NULL;
+ object = sftk_freeObjectData(object)) {
+ PZ_DestroyLock(object->refLock);
+ if (isSessionList) {
+ PZ_DestroyLock(((SFTKSessionObject *)object)->attributeLock);
+ }
+ }
+ list->count = 0;
+ list->head = NULL;
+ SKIP_AFTER_FORK(PZ_Unlock(list->lock));
+ SKIP_AFTER_FORK(PZ_DestroyLock(list->lock));
+ list->lock = NULL;
+}
+
+void
+sftk_CleanupFreeLists(void)
+{
+ sftk_CleanupFreeList(&sessionObjectList, PR_TRUE);
+ sftk_CleanupFreeList(&tokenObjectList, PR_FALSE);
+}
+
+/*
+ * Create a new object
+ */
+SFTKObject *
+sftk_NewObject(SFTKSlot *slot)
+{
+ SFTKObject *object;
+ SFTKSessionObject *sessObject;
+ PRBool hasLocks = PR_FALSE;
+ unsigned int i;
+ unsigned int hashSize = 0;
+
+ hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE : TIME_ATTRIBUTE_HASH_SIZE;
+
+ object = sftk_GetObjectFromList(&hasLocks, slot->optimizeSpace,
+ &sessionObjectList, hashSize, PR_TRUE);
+ if (object == NULL) {
+ return NULL;
+ }
+ sessObject = (SFTKSessionObject *)object;
+ sessObject->nextAttr = 0;
+
+ for (i = 0; i < MAX_OBJS_ATTRS; i++) {
+ sessObject->attrList[i].attrib.pValue = NULL;
+ sessObject->attrList[i].freeData = PR_FALSE;
+ }
+ sessObject->optimizeSpace = slot->optimizeSpace;
+
+ object->handle = 0;
+ object->next = object->prev = NULL;
+ object->slot = slot;
+ object->isFIPS = sftk_isFIPS(slot->slotID);
+
+ object->refCount = 1;
+ sessObject->sessionList.next = NULL;
+ sessObject->sessionList.prev = NULL;
+ sessObject->sessionList.parent = object;
+ sessObject->session = NULL;
+ sessObject->wasDerived = PR_FALSE;
+ if (!hasLocks)
+ object->refLock = PZ_NewLock(nssILockRefLock);
+ if (object->refLock == NULL) {
+ PORT_Free(object);
+ return NULL;
+ }
+ if (!hasLocks)
+ sessObject->attributeLock = PZ_NewLock(nssILockAttribute);
+ if (sessObject->attributeLock == NULL) {
+ PZ_DestroyLock(object->refLock);
+ PORT_Free(object);
+ return NULL;
+ }
+ for (i = 0; i < sessObject->hashSize; i++) {
+ sessObject->head[i] = NULL;
+ }
+ object->objectInfo = NULL;
+ object->infoFree = NULL;
+ return object;
+}
+
+static CK_RV
+sftk_DestroySessionObjectData(SFTKSessionObject *so)
+{
+ int i;
+
+ for (i = 0; i < MAX_OBJS_ATTRS; i++) {
+ unsigned char *value = so->attrList[i].attrib.pValue;
+ if (value) {
+ PORT_Memset(value, 0, so->attrList[i].attrib.ulValueLen);
+ if (so->attrList[i].freeData) {
+ PORT_Free(value);
+ }
+ so->attrList[i].attrib.pValue = NULL;
+ so->attrList[i].freeData = PR_FALSE;
+ }
+ }
+ /* PZ_DestroyLock(so->attributeLock);*/
+ return CKR_OK;
+}
+
+/*
+ * free all the data associated with an object. Object reference count must
+ * be 'zero'.
+ */
+static CK_RV
+sftk_DestroyObject(SFTKObject *object)
+{
+ CK_RV crv = CKR_OK;
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+ SFTKTokenObject *to = sftk_narrowToTokenObject(object);
+
+ PORT_Assert(object->refCount == 0);
+
+ /* delete the database value */
+ if (to) {
+ if (to->dbKey.data) {
+ PORT_Free(to->dbKey.data);
+ to->dbKey.data = NULL;
+ }
+ }
+ if (so) {
+ sftk_DestroySessionObjectData(so);
+ }
+ if (object->objectInfo) {
+ (*object->infoFree)(object->objectInfo);
+ object->objectInfo = NULL;
+ object->infoFree = NULL;
+ }
+ if (so) {
+ sftk_PutObjectToList(object, &sessionObjectList, PR_TRUE);
+ } else {
+ sftk_PutObjectToList(object, &tokenObjectList, PR_FALSE);
+ }
+ return crv;
+}
+
+void
+sftk_ReferenceObject(SFTKObject *object)
+{
+ PZ_Lock(object->refLock);
+ object->refCount++;
+ PZ_Unlock(object->refLock);
+}
+
+static SFTKObject *
+sftk_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, SFTKSlot *slot)
+{
+ SFTKObject *object;
+ PRUint32 index = sftk_hash(handle, slot->sessObjHashSize);
+
+ if (sftk_isToken(handle)) {
+ return sftk_NewTokenObject(slot, NULL, handle);
+ }
+
+ PZ_Lock(slot->objectLock);
+ sftkqueue_find2(object, handle, index, slot->sessObjHashTable);
+ if (object) {
+ sftk_ReferenceObject(object);
+ }
+ PZ_Unlock(slot->objectLock);
+
+ return (object);
+}
+/*
+ * look up and object structure from a handle. OBJECT_Handles only make
+ * sense in terms of a given session. make a reference to that object
+ * structure returned.
+ */
+SFTKObject *
+sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle, SFTKSession *session)
+{
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+
+ return sftk_ObjectFromHandleOnSlot(handle, slot);
+}
+
+/*
+ * release a reference to an object handle
+ */
+SFTKFreeStatus
+sftk_FreeObject(SFTKObject *object)
+{
+ PRBool destroy = PR_FALSE;
+ CK_RV crv;
+
+ PZ_Lock(object->refLock);
+ if (object->refCount == 1)
+ destroy = PR_TRUE;
+ object->refCount--;
+ PZ_Unlock(object->refLock);
+
+ if (destroy) {
+ crv = sftk_DestroyObject(object);
+ if (crv != CKR_OK) {
+ return SFTK_DestroyFailure;
+ }
+ return SFTK_Destroyed;
+ }
+ return SFTK_Busy;
+}
+
+/* find the next available object handle that isn't currently in use */
+/* NOTE: This function could loop forever if we've exhausted all
+ * 3^31-1 handles. This is highly unlikely (NSS has been running for
+ * decades with this code) uless we start increasing the size of the
+ * SFTK_TOKEN_MASK (which is just the high bit currently). */
+CK_OBJECT_HANDLE
+sftk_getNextHandle(SFTKSlot *slot)
+{
+ CK_OBJECT_HANDLE handle;
+ SFTKObject *duplicateObject = NULL;
+ do {
+ PRUint32 wrappedAround;
+
+ duplicateObject = NULL;
+ PZ_Lock(slot->objectLock);
+ wrappedAround = slot->sessionObjectHandleCount & SFTK_TOKEN_MASK;
+ handle = slot->sessionObjectHandleCount & ~SFTK_TOKEN_MASK;
+ if (!handle) /* don't allow zero handle */
+ handle = NSC_MIN_SESSION_OBJECT_HANDLE;
+ slot->sessionObjectHandleCount = (handle + 1U) | wrappedAround;
+ /* Is there already a session object with this handle? */
+ if (wrappedAround) {
+ sftkqueue_find(duplicateObject, handle, slot->sessObjHashTable,
+ slot->sessObjHashSize);
+ }
+ PZ_Unlock(slot->objectLock);
+ } while (duplicateObject != NULL);
+ return handle;
+}
+
+/*
+ * add an object to a slot and session queue. These two functions
+ * adopt the object.
+ */
+void
+sftk_AddSlotObject(SFTKSlot *slot, SFTKObject *object)
+{
+ PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
+ sftkqueue_init_element(object);
+ PZ_Lock(slot->objectLock);
+ sftkqueue_add2(object, object->handle, index, slot->sessObjHashTable);
+ PZ_Unlock(slot->objectLock);
+}
+
+void
+sftk_AddObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+
+ if (so) {
+ PZ_Lock(session->objectLock);
+ sftkqueue_add(&so->sessionList, 0, session->objects, 0);
+ so->session = session;
+ PZ_Unlock(session->objectLock);
+ }
+ sftk_AddSlotObject(slot, object);
+ sftk_ReferenceObject(object);
+}
+
+/*
+ * delete an object from a slot and session queue
+ */
+CK_RV
+sftk_DeleteObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+ CK_RV crv = CKR_OK;
+ PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
+
+ /* Handle Token case */
+ if (so && so->session) {
+ session = so->session;
+ PZ_Lock(session->objectLock);
+ sftkqueue_delete(&so->sessionList, 0, session->objects, 0);
+ PZ_Unlock(session->objectLock);
+ PZ_Lock(slot->objectLock);
+ sftkqueue_delete2(object, object->handle, index, slot->sessObjHashTable);
+ PZ_Unlock(slot->objectLock);
+ sftkqueue_clear_deleted_element(object);
+ sftk_FreeObject(object); /* free the reference owned by the queue */
+ } else {
+ SFTKDBHandle *handle = sftk_getDBForTokenObject(slot, object->handle);
+#ifdef DEBUG
+ SFTKTokenObject *to = sftk_narrowToTokenObject(object);
+ PORT_Assert(to);
+#endif
+ crv = sftkdb_DestroyObject(handle, object->handle, object->objclass);
+ sftk_freeDB(handle);
+ }
+ return crv;
+}
+
+/*
+ * Token objects don't explicitly store their attributes, so we need to know
+ * what attributes make up a particular token object before we can copy it.
+ * below are the tables by object type.
+ */
+static const CK_ATTRIBUTE_TYPE commonAttrs[] = {
+ CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_MODIFIABLE
+};
+static const CK_ULONG commonAttrsCount =
+ sizeof(commonAttrs) / sizeof(commonAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE commonKeyAttrs[] = {
+ CKA_ID, CKA_START_DATE, CKA_END_DATE, CKA_DERIVE, CKA_LOCAL, CKA_KEY_TYPE
+};
+static const CK_ULONG commonKeyAttrsCount =
+ sizeof(commonKeyAttrs) / sizeof(commonKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE secretKeyAttrs[] = {
+ CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_ENCRYPT, CKA_DECRYPT, CKA_SIGN,
+ CKA_VERIFY, CKA_WRAP, CKA_UNWRAP, CKA_VALUE
+};
+static const CK_ULONG secretKeyAttrsCount =
+ sizeof(secretKeyAttrs) / sizeof(secretKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE commonPubKeyAttrs[] = {
+ CKA_ENCRYPT, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP, CKA_SUBJECT
+};
+static const CK_ULONG commonPubKeyAttrsCount =
+ sizeof(commonPubKeyAttrs) / sizeof(commonPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE rsaPubKeyAttrs[] = {
+ CKA_MODULUS, CKA_PUBLIC_EXPONENT
+};
+static const CK_ULONG rsaPubKeyAttrsCount =
+ sizeof(rsaPubKeyAttrs) / sizeof(rsaPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dsaPubKeyAttrs[] = {
+ CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dsaPubKeyAttrsCount =
+ sizeof(dsaPubKeyAttrs) / sizeof(dsaPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dhPubKeyAttrs[] = {
+ CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dhPubKeyAttrsCount =
+ sizeof(dhPubKeyAttrs) / sizeof(dhPubKeyAttrs[0]);
+static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = {
+ CKA_EC_PARAMS, CKA_EC_POINT
+};
+static const CK_ULONG ecPubKeyAttrsCount =
+ sizeof(ecPubKeyAttrs) / sizeof(ecPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = {
+ CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT,
+ CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NSS_DB, CKA_PUBLIC_KEY_INFO
+};
+static const CK_ULONG commonPrivKeyAttrsCount =
+ sizeof(commonPrivKeyAttrs) / sizeof(commonPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE rsaPrivKeyAttrs[] = {
+ CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
+ CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT
+};
+static const CK_ULONG rsaPrivKeyAttrsCount =
+ sizeof(rsaPrivKeyAttrs) / sizeof(rsaPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dsaPrivKeyAttrs[] = {
+ CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dsaPrivKeyAttrsCount =
+ sizeof(dsaPrivKeyAttrs) / sizeof(dsaPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dhPrivKeyAttrs[] = {
+ CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dhPrivKeyAttrsCount =
+ sizeof(dhPrivKeyAttrs) / sizeof(dhPrivKeyAttrs[0]);
+static const CK_ATTRIBUTE_TYPE ecPrivKeyAttrs[] = {
+ CKA_EC_PARAMS, CKA_VALUE
+};
+static const CK_ULONG ecPrivKeyAttrsCount =
+ sizeof(ecPrivKeyAttrs) / sizeof(ecPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE certAttrs[] = {
+ CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER
+};
+static const CK_ULONG certAttrsCount =
+ sizeof(certAttrs) / sizeof(certAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE trustAttrs[] = {
+ CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
+ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_EMAIL_PROTECTION,
+ CKA_TRUST_CODE_SIGNING, CKA_TRUST_STEP_UP_APPROVED
+};
+static const CK_ULONG trustAttrsCount =
+ sizeof(trustAttrs) / sizeof(trustAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE smimeAttrs[] = {
+ CKA_SUBJECT, CKA_NSS_EMAIL, CKA_NSS_SMIME_TIMESTAMP, CKA_VALUE
+};
+static const CK_ULONG smimeAttrsCount =
+ sizeof(smimeAttrs) / sizeof(smimeAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE crlAttrs[] = {
+ CKA_SUBJECT, CKA_VALUE, CKA_NSS_URL, CKA_NSS_KRL
+};
+static const CK_ULONG crlAttrsCount =
+ sizeof(crlAttrs) / sizeof(crlAttrs[0]);
+
+/* copy an object based on it's table */
+CK_RV
+stfk_CopyTokenAttributes(SFTKObject *destObject, SFTKTokenObject *src_to,
+ const CK_ATTRIBUTE_TYPE *attrArray, CK_ULONG attrCount)
+{
+ SFTKAttribute *attribute;
+ SFTKAttribute *newAttribute;
+ CK_RV crv = CKR_OK;
+ unsigned int i;
+
+ for (i = 0; i < attrCount; i++) {
+ if (!sftk_hasAttribute(destObject, attrArray[i])) {
+ attribute = sftk_FindAttribute(&src_to->obj, attrArray[i]);
+ if (!attribute) {
+ continue; /* return CKR_ATTRIBUTE_VALUE_INVALID; */
+ }
+ /* we need to copy the attribute since each attribute
+ * only has one set of link list pointers */
+ newAttribute = sftk_NewAttribute(destObject,
+ sftk_attr_expand(&attribute->attrib));
+ sftk_FreeAttribute(attribute); /* free the old attribute */
+ if (!newAttribute) {
+ return CKR_HOST_MEMORY;
+ }
+ sftk_AddAttribute(destObject, newAttribute);
+ }
+ }
+ return crv;
+}
+
+CK_RV
+stfk_CopyTokenPrivateKey(SFTKObject *destObject, SFTKTokenObject *src_to)
+{
+ CK_RV crv;
+ CK_KEY_TYPE key_type;
+ SFTKAttribute *attribute;
+
+ /* copy the common attributes for all keys first */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
+ commonKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ /* copy the common attributes for all private keys next */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonPrivKeyAttrs,
+ commonPrivKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ attribute = sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
+ PORT_Assert(attribute); /* if it wasn't here, ww should have failed
+ * copying the common attributes */
+ if (!attribute) {
+ /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
+ * the fact is, the only reason we couldn't get the attribute would
+ * be a memory error or database error (an error in the 'device').
+ * if we have a database error code, we could return it here */
+ crv = CKR_DEVICE_ERROR;
+ goto fail;
+ }
+ key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ /* finally copy the attributes for various private key types */
+ switch (key_type) {
+ case CKK_RSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPrivKeyAttrs,
+ rsaPrivKeyAttrsCount);
+ break;
+ case CKK_DSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPrivKeyAttrs,
+ dsaPrivKeyAttrsCount);
+ break;
+ case CKK_DH:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs,
+ dhPrivKeyAttrsCount);
+ break;
+ case CKK_EC:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs,
+ ecPrivKeyAttrsCount);
+ break;
+ default:
+ crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
+ * of token keys into our database. */
+ }
+fail:
+ return crv;
+}
+
+CK_RV
+stfk_CopyTokenPublicKey(SFTKObject *destObject, SFTKTokenObject *src_to)
+{
+ CK_RV crv;
+ CK_KEY_TYPE key_type;
+ SFTKAttribute *attribute;
+
+ /* copy the common attributes for all keys first */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
+ commonKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+
+ /* copy the common attributes for all public keys next */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonPubKeyAttrs,
+ commonPubKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ attribute = sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
+ PORT_Assert(attribute); /* if it wasn't here, ww should have failed
+ * copying the common attributes */
+ if (!attribute) {
+ /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
+ * the fact is, the only reason we couldn't get the attribute would
+ * be a memory error or database error (an error in the 'device').
+ * if we have a database error code, we could return it here */
+ crv = CKR_DEVICE_ERROR;
+ goto fail;
+ }
+ key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ /* finally copy the attributes for various public key types */
+ switch (key_type) {
+ case CKK_RSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPubKeyAttrs,
+ rsaPubKeyAttrsCount);
+ break;
+ case CKK_DSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPubKeyAttrs,
+ dsaPubKeyAttrsCount);
+ break;
+ case CKK_DH:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs,
+ dhPubKeyAttrsCount);
+ break;
+ case CKK_EC:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs,
+ ecPubKeyAttrsCount);
+ break;
+ default:
+ crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
+ * of token keys into our database. */
+ }
+fail:
+ return crv;
+}
+CK_RV
+stfk_CopyTokenSecretKey(SFTKObject *destObject, SFTKTokenObject *src_to)
+{
+ CK_RV crv;
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
+ commonKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ crv = stfk_CopyTokenAttributes(destObject, src_to, secretKeyAttrs,
+ secretKeyAttrsCount);
+fail:
+ return crv;
+}
+
+/*
+ * Copy a token object. We need to explicitly copy the relevant
+ * attributes since token objects don't store those attributes in
+ * the token itself.
+ */
+CK_RV
+sftk_CopyTokenObject(SFTKObject *destObject, SFTKObject *srcObject)
+{
+ SFTKTokenObject *src_to = sftk_narrowToTokenObject(srcObject);
+ CK_RV crv;
+
+ PORT_Assert(src_to);
+ if (src_to == NULL) {
+ return CKR_DEVICE_ERROR; /* internal state inconsistant */
+ }
+
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonAttrs,
+ commonAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ switch (src_to->obj.objclass) {
+ case CKO_CERTIFICATE:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, certAttrs,
+ certAttrsCount);
+ break;
+ case CKO_NSS_TRUST:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, trustAttrs,
+ trustAttrsCount);
+ break;
+ case CKO_NSS_SMIME:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, smimeAttrs,
+ smimeAttrsCount);
+ break;
+ case CKO_NSS_CRL:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, crlAttrs,
+ crlAttrsCount);
+ break;
+ case CKO_PRIVATE_KEY:
+ crv = stfk_CopyTokenPrivateKey(destObject, src_to);
+ break;
+ case CKO_PUBLIC_KEY:
+ crv = stfk_CopyTokenPublicKey(destObject, src_to);
+ break;
+ case CKO_SECRET_KEY:
+ crv = stfk_CopyTokenSecretKey(destObject, src_to);
+ break;
+ default:
+ crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
+ * of token keys into our database. */
+ }
+fail:
+ return crv;
+}
+
+/*
+ * copy the attributes from one object to another. Don't overwrite existing
+ * attributes. NOTE: This is a pretty expensive operation since it
+ * grabs the attribute locks for the src object for a *long* time.
+ */
+CK_RV
+sftk_CopyObject(SFTKObject *destObject, SFTKObject *srcObject)
+{
+ SFTKAttribute *attribute;
+ SFTKSessionObject *src_so = sftk_narrowToSessionObject(srcObject);
+ unsigned int i;
+
+ destObject->isFIPS = srcObject->isFIPS;
+ if (src_so == NULL) {
+ return sftk_CopyTokenObject(destObject, srcObject);
+ }
+
+ PZ_Lock(src_so->attributeLock);
+ for (i = 0; i < src_so->hashSize; i++) {
+ attribute = src_so->head[i];
+ do {
+ if (attribute) {
+ if (!sftk_hasAttribute(destObject, attribute->handle)) {
+ /* we need to copy the attribute since each attribute
+ * only has one set of link list pointers */
+ SFTKAttribute *newAttribute = sftk_NewAttribute(
+ destObject, sftk_attr_expand(&attribute->attrib));
+ if (newAttribute == NULL) {
+ PZ_Unlock(src_so->attributeLock);
+ return CKR_HOST_MEMORY;
+ }
+ sftk_AddAttribute(destObject, newAttribute);
+ }
+ attribute = attribute->next;
+ }
+ } while (attribute != NULL);
+ }
+ PZ_Unlock(src_so->attributeLock);
+
+ return CKR_OK;
+}
+
+/*
+ * ******************** Search Utilities *******************************
+ */
+
+/* add an object to a search list */
+CK_RV
+AddToList(SFTKObjectListElement **list, SFTKObject *object)
+{
+ SFTKObjectListElement *newElem =
+ (SFTKObjectListElement *)PORT_Alloc(sizeof(SFTKObjectListElement));
+
+ if (newElem == NULL)
+ return CKR_HOST_MEMORY;
+
+ newElem->next = *list;
+ newElem->object = object;
+ sftk_ReferenceObject(object);
+
+ *list = newElem;
+ return CKR_OK;
+}
+
+/* return true if the object matches the template */
+PRBool
+sftk_objectMatch(SFTKObject *object, CK_ATTRIBUTE_PTR theTemplate, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ SFTKAttribute *attribute = sftk_FindAttribute(object, theTemplate[i].type);
+ if (attribute == NULL) {
+ return PR_FALSE;
+ }
+ if (attribute->attrib.ulValueLen == theTemplate[i].ulValueLen) {
+ if (PORT_Memcmp(attribute->attrib.pValue, theTemplate[i].pValue,
+ theTemplate[i].ulValueLen) == 0) {
+ sftk_FreeAttribute(attribute);
+ continue;
+ }
+ }
+ sftk_FreeAttribute(attribute);
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/* search through all the objects in the queue and return the template matches
+ * in the object list.
+ */
+CK_RV
+sftk_searchObjectList(SFTKSearchResults *search, SFTKObject **head,
+ unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate,
+ int count, PRBool isLoggedIn)
+{
+ unsigned int i;
+ SFTKObject *object;
+ CK_RV crv = CKR_OK;
+
+ PZ_Lock(lock);
+ for (i = 0; i < size; i++) {
+ for (object = head[i]; object != NULL; object = object->next) {
+ if (sftk_objectMatch(object, theTemplate, count)) {
+ /* don't return objects that aren't yet visible */
+ if ((!isLoggedIn) && sftk_isTrue(object, CKA_PRIVATE))
+ continue;
+ sftk_addHandle(search, object->handle);
+ }
+ }
+ }
+ PZ_Unlock(lock);
+ return crv;
+}
+
+/*
+ * free a single list element. Return the Next object in the list.
+ */
+SFTKObjectListElement *
+sftk_FreeObjectListElement(SFTKObjectListElement *objectList)
+{
+ SFTKObjectListElement *ol = objectList->next;
+
+ sftk_FreeObject(objectList->object);
+ PORT_Free(objectList);
+ return ol;
+}
+
+/* free an entire object list */
+void
+sftk_FreeObjectList(SFTKObjectListElement *objectList)
+{
+ SFTKObjectListElement *ol;
+
+ for (ol = objectList; ol != NULL; ol = sftk_FreeObjectListElement(ol)) {
+ }
+}
+
+/*
+ * free a search structure
+ */
+void
+sftk_FreeSearch(SFTKSearchResults *search)
+{
+ if (search->handles) {
+ PORT_Free(search->handles);
+ }
+ PORT_Free(search);
+}
+
+/*
+ * ******************** Session Utilities *******************************
+ */
+
+/* update the sessions state based in it's flags and wether or not it's
+ * logged in */
+void
+sftk_update_state(SFTKSlot *slot, SFTKSession *session)
+{
+ if (slot->isLoggedIn) {
+ if (slot->ssoLoggedIn) {
+ session->info.state = CKS_RW_SO_FUNCTIONS;
+ } else if (session->info.flags & CKF_RW_SESSION) {
+ session->info.state = CKS_RW_USER_FUNCTIONS;
+ } else {
+ session->info.state = CKS_RO_USER_FUNCTIONS;
+ }
+ } else {
+ if (session->info.flags & CKF_RW_SESSION) {
+ session->info.state = CKS_RW_PUBLIC_SESSION;
+ } else {
+ session->info.state = CKS_RO_PUBLIC_SESSION;
+ }
+ }
+}
+
+/* update the state of all the sessions on a slot */
+void
+sftk_update_all_states(SFTKSlot *slot)
+{
+ unsigned int i;
+ SFTKSession *session;
+
+ for (i = 0; i < slot->sessHashSize; i++) {
+ PZLock *lock = SFTK_SESSION_LOCK(slot, i);
+ PZ_Lock(lock);
+ for (session = slot->head[i]; session; session = session->next) {
+ sftk_update_state(slot, session);
+ }
+ PZ_Unlock(lock);
+ }
+}
+
+/*
+ * context are cipher and digest contexts that are associated with a session
+ */
+void
+sftk_FreeContext(SFTKSessionContext *context)
+{
+ if (context->cipherInfo) {
+ (*context->destroy)(context->cipherInfo, PR_TRUE);
+ }
+ if (context->hashInfo) {
+ (*context->hashdestroy)(context->hashInfo, PR_TRUE);
+ }
+ if (context->key) {
+ sftk_FreeObject(context->key);
+ context->key = NULL;
+ }
+ PORT_Free(context);
+}
+
+/*
+ * Init a new session. NOTE: The session handle is not set, and the
+ * session is not added to the slot's session queue.
+ */
+CK_RV
+sftk_InitSession(SFTKSession *session, SFTKSlot *slot, CK_SLOT_ID slotID,
+ CK_NOTIFY notify, CK_VOID_PTR pApplication, CK_FLAGS flags)
+{
+ session->next = session->prev = NULL;
+ session->enc_context = NULL;
+ session->hash_context = NULL;
+ session->sign_context = NULL;
+ session->search = NULL;
+ session->objectIDCount = 1;
+ session->objectLock = PZ_NewLock(nssILockObject);
+ if (session->objectLock == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ session->objects[0] = NULL;
+
+ session->slot = slot;
+ session->notify = notify;
+ session->appData = pApplication;
+ session->info.flags = flags;
+ session->info.slotID = slotID;
+ session->info.ulDeviceError = 0;
+ sftk_update_state(slot, session);
+ /* no ops completed yet, so the last one couldn't be a FIPS op */
+ session->lastOpWasFIPS = PR_FALSE;
+ return CKR_OK;
+}
+
+/*
+ * Create a new session and init it.
+ */
+SFTKSession *
+sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication,
+ CK_FLAGS flags)
+{
+ SFTKSession *session;
+ SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
+ CK_RV crv;
+
+ if (slot == NULL)
+ return NULL;
+
+ session = (SFTKSession *)PORT_Alloc(sizeof(SFTKSession));
+ if (session == NULL)
+ return NULL;
+
+ crv = sftk_InitSession(session, slot, slotID, notify, pApplication, flags);
+ if (crv != CKR_OK) {
+ PORT_Free(session);
+ return NULL;
+ }
+ return session;
+}
+
+/* free all the data associated with a session. */
+void
+sftk_ClearSession(SFTKSession *session)
+{
+ SFTKObjectList *op, *next;
+
+ /* clean out the attributes */
+ /* since no one is referencing us, it's safe to walk the chain
+ * without a lock */
+ for (op = session->objects[0]; op != NULL; op = next) {
+ next = op->next;
+ /* paranoia */
+ op->next = op->prev = NULL;
+ sftk_DeleteObject(session, op->parent);
+ }
+ PZ_DestroyLock(session->objectLock);
+ if (session->enc_context) {
+ sftk_FreeContext(session->enc_context);
+ }
+ if (session->hash_context) {
+ sftk_FreeContext(session->hash_context);
+ }
+ if (session->sign_context) {
+ sftk_FreeContext(session->sign_context);
+ }
+ if (session->search) {
+ sftk_FreeSearch(session->search);
+ }
+}
+
+/* free the data associated with the session, and the session */
+void
+sftk_DestroySession(SFTKSession *session)
+{
+ sftk_ClearSession(session);
+ PORT_Free(session);
+}
+
+/*
+ * look up a session structure from a session handle
+ * generate a reference to it.
+ */
+SFTKSession *
+sftk_SessionFromHandle(CK_SESSION_HANDLE handle)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(handle);
+ SFTKSession *session;
+ PZLock *lock;
+
+ if (!slot)
+ return NULL;
+ lock = SFTK_SESSION_LOCK(slot, handle);
+
+ PZ_Lock(lock);
+ sftkqueue_find(session, handle, slot->head, slot->sessHashSize);
+ PZ_Unlock(lock);
+
+ return (session);
+}
+
+/*
+ * release a reference to a session handle. This method of using SFTKSessions
+ * is deprecated, but the pattern should be retained until a future effort
+ * to refactor all SFTKSession users at once is completed.
+ */
+void
+sftk_FreeSession(SFTKSession *session)
+{
+ return;
+}
+
+void
+sftk_addHandle(SFTKSearchResults *search, CK_OBJECT_HANDLE handle)
+{
+ if (search->handles == NULL) {
+ return;
+ }
+ if (search->size >= search->array_size) {
+ search->array_size += NSC_SEARCH_BLOCK_SIZE;
+ search->handles = (CK_OBJECT_HANDLE *)PORT_Realloc(search->handles,
+ sizeof(CK_OBJECT_HANDLE) * search->array_size);
+ if (search->handles == NULL) {
+ return;
+ }
+ }
+ search->handles[search->size] = handle;
+ search->size++;
+}
+
+static CK_RV
+handleToClass(SFTKSlot *slot, CK_OBJECT_HANDLE handle,
+ CK_OBJECT_CLASS *objClass)
+{
+ SFTKDBHandle *dbHandle = sftk_getDBForTokenObject(slot, handle);
+ CK_ATTRIBUTE objClassTemplate;
+ CK_RV crv;
+
+ *objClass = CKO_DATA;
+ objClassTemplate.type = CKA_CLASS;
+ objClassTemplate.pValue = objClass;
+ objClassTemplate.ulValueLen = sizeof(*objClass);
+ crv = sftkdb_GetAttributeValue(dbHandle, handle, &objClassTemplate, 1);
+ sftk_freeDB(dbHandle);
+ return crv;
+}
+
+SFTKObject *
+sftk_NewTokenObject(SFTKSlot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle)
+{
+ SFTKObject *object = NULL;
+ PRBool hasLocks = PR_FALSE;
+ CK_RV crv;
+
+ object = sftk_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList, 0,
+ PR_FALSE);
+ if (object == NULL) {
+ return NULL;
+ }
+
+ object->handle = handle;
+ /* every object must have a class, if we can't get it, the object
+ * doesn't exist */
+ crv = handleToClass(slot, handle, &object->objclass);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ object->slot = slot;
+ object->isFIPS = sftk_isFIPS(slot->slotID);
+ object->objectInfo = NULL;
+ object->infoFree = NULL;
+ if (!hasLocks) {
+ object->refLock = PZ_NewLock(nssILockRefLock);
+ }
+ if (object->refLock == NULL) {
+ goto loser;
+ }
+ object->refCount = 1;
+
+ return object;
+loser:
+ (void)sftk_DestroyObject(object);
+ return NULL;
+}
+
+SFTKTokenObject *
+sftk_convertSessionToToken(SFTKObject *obj)
+{
+ SECItem *key;
+ SFTKSessionObject *so = (SFTKSessionObject *)obj;
+ SFTKTokenObject *to = sftk_narrowToTokenObject(obj);
+ SECStatus rv;
+
+ sftk_DestroySessionObjectData(so);
+ PZ_DestroyLock(so->attributeLock);
+ if (to == NULL) {
+ return NULL;
+ }
+ sftk_tokenKeyLock(so->obj.slot);
+ key = sftk_lookupTokenKeyByHandle(so->obj.slot, so->obj.handle);
+ if (key == NULL) {
+ sftk_tokenKeyUnlock(so->obj.slot);
+ return NULL;
+ }
+ rv = SECITEM_CopyItem(NULL, &to->dbKey, key);
+ sftk_tokenKeyUnlock(so->obj.slot);
+ if (rv == SECFailure) {
+ return NULL;
+ }
+
+ return to;
+}
+
+SFTKSessionObject *
+sftk_narrowToSessionObject(SFTKObject *obj)
+{
+ return !sftk_isToken(obj->handle) ? (SFTKSessionObject *)obj : NULL;
+}
+
+SFTKTokenObject *
+sftk_narrowToTokenObject(SFTKObject *obj)
+{
+ return sftk_isToken(obj->handle) ? (SFTKTokenObject *)obj : NULL;
+}
+
+/* Constant time helper functions */
+
+/* sftk_CKRVToMask returns, in constant time, a mask value of
+ * all ones if rv == CKR_OK. Otherwise it returns zero. */
+unsigned int
+sftk_CKRVToMask(CK_RV rv)
+{
+ PR_STATIC_ASSERT(CKR_OK == 0);
+ return ~PORT_CT_NOT_ZERO(rv);
+}
+
+/* sftk_CheckCBCPadding checks, in constant time, the padding validity and
+ * accordingly sets the pad length. */
+CK_RV
+sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen,
+ unsigned int blockSize, unsigned int *outPadSize)
+{
+ PORT_Assert(outPadSize);
+
+ unsigned int padSize = (unsigned int)pBuf[bufLen - 1];
+
+ /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/
+ unsigned int goodPad = PORT_CT_DUPLICATE_MSB_TO_ALL(~(blockSize - padSize));
+ /* padSize should not be 0 */
+ goodPad &= PORT_CT_NOT_ZERO(padSize);
+
+ unsigned int i;
+ for (i = 0; i < blockSize; i++) {
+ /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/
+ unsigned int loopMask = PORT_CT_DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i));
+ /* Get the padding value (should be padSize) from buffer */
+ unsigned int padVal = pBuf[bufLen - 1 - i];
+ /* Update goodPad only if i < padSize */
+ goodPad &= PORT_CT_SEL(loopMask, ~(padVal ^ padSize), goodPad);
+ }
+
+ /* If any of the final padding bytes had the wrong value, one or more
+ * of the lower eight bits of |goodPad| will be cleared. We AND the
+ * bottom 8 bits together and duplicate the result to all the bits. */
+ goodPad &= goodPad >> 4;
+ goodPad &= goodPad >> 2;
+ goodPad &= goodPad >> 1;
+ goodPad <<= sizeof(goodPad) * 8 - 1;
+ goodPad = PORT_CT_DUPLICATE_MSB_TO_ALL(goodPad);
+
+ /* Set outPadSize to padSize or 0 */
+ *outPadSize = PORT_CT_SEL(goodPad, padSize, 0);
+ /* Return OK if the pad is valid */
+ return PORT_CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID);
+}
+
+void
+sftk_EncodeInteger(PRUint64 integer, CK_ULONG num_bits, CK_BBOOL littleEndian,
+ CK_BYTE_PTR output, CK_ULONG_PTR output_len)
+{
+ if (output_len) {
+ *output_len = (num_bits / 8);
+ }
+
+ PR_ASSERT(num_bits > 0 && num_bits <= 64 && (num_bits % 8) == 0);
+
+ if (littleEndian == CK_TRUE) {
+ for (size_t offset = 0; offset < num_bits / 8; offset++) {
+ output[offset] = (unsigned char)((integer >> (offset * 8)) & 0xFF);
+ }
+ } else {
+ for (size_t offset = 0; offset < num_bits / 8; offset++) {
+ PRUint64 shift = num_bits - (offset + 1) * 8;
+ output[offset] = (unsigned char)((integer >> shift) & 0xFF);
+ }
+ }
+}
+
+CK_FLAGS
+sftk_AttributeToFlags(CK_ATTRIBUTE_TYPE op)
+{
+ CK_FLAGS flags = 0;
+
+ switch (op) {
+ case CKA_ENCRYPT:
+ flags = CKF_ENCRYPT;
+ break;
+ case CKA_DECRYPT:
+ flags = CKF_DECRYPT;
+ break;
+ case CKA_WRAP:
+ flags = CKF_WRAP;
+ break;
+ case CKA_UNWRAP:
+ flags = CKF_UNWRAP;
+ break;
+ case CKA_SIGN:
+ flags = CKF_SIGN;
+ break;
+ case CKA_SIGN_RECOVER:
+ flags = CKF_SIGN_RECOVER;
+ break;
+ case CKA_VERIFY:
+ flags = CKF_VERIFY;
+ break;
+ case CKA_VERIFY_RECOVER:
+ flags = CKF_VERIFY_RECOVER;
+ break;
+ case CKA_DERIVE:
+ flags = CKF_DERIVE;
+ break;
+ /* fake attribute to select digesting */
+ case CKA_DIGEST:
+ flags = CKF_DIGEST;
+ break;
+ case CKA_NSS_MESSAGE | CKA_ENCRYPT:
+ flags = CKF_MESSAGE_ENCRYPT;
+ break;
+ case CKA_NSS_MESSAGE | CKA_DECRYPT:
+ flags = CKF_MESSAGE_DECRYPT;
+ break;
+ case CKA_NSS_MESSAGE | CKA_SIGN:
+ flags = CKF_MESSAGE_SIGN;
+ break;
+ case CKA_NSS_MESSAGE | CKA_VERIFY:
+ flags = CKF_MESSAGE_VERIFY;
+ break;
+ default:
+ break;
+ }
+ return flags;
+}
+
+/*
+ * ******************** Hash Utilities **************************
+ */
+/*
+ * Utility function for converting PSS/OAEP parameter types into
+ * HASH_HashTypes. Note: Only SHA family functions are defined in RFC 3447.
+ */
+HASH_HashType
+sftk_GetHashTypeFromMechanism(CK_MECHANISM_TYPE mech)
+{
+ switch (mech) {
+ case CKM_SHA_1:
+ case CKG_MGF1_SHA1:
+ return HASH_AlgSHA1;
+ case CKM_SHA224:
+ case CKG_MGF1_SHA224:
+ return HASH_AlgSHA224;
+ case CKM_SHA256:
+ case CKG_MGF1_SHA256:
+ return HASH_AlgSHA256;
+ case CKM_SHA384:
+ case CKG_MGF1_SHA384:
+ return HASH_AlgSHA384;
+ case CKM_SHA512:
+ case CKG_MGF1_SHA512:
+ return HASH_AlgSHA512;
+ default:
+ return HASH_AlgNULL;
+ }
+}
+
+#ifdef NSS_HAS_FIPS_INDICATORS
+/**************** FIPS Indicator Utilities *************************/
+/* sigh, we probably need a version of this in secutil so that both
+ * softoken and NSS can use it */
+static SECOidTag
+sftk_quickGetECCCurveOid(SFTKObject *source)
+{
+ SFTKAttribute *attribute = sftk_FindAttribute(source, CKA_EC_PARAMS);
+ unsigned char *encoded;
+ int len;
+ SECItem oid;
+ SECOidTag tag;
+
+ if (attribute == NULL) {
+ return SEC_OID_UNKNOWN;
+ }
+ encoded = attribute->attrib.pValue;
+ len = attribute->attrib.ulValueLen;
+ if ((len < 2) || (encoded[0] != SEC_ASN1_OBJECT_ID) ||
+ (len != encoded[1] + 2)) {
+ sftk_FreeAttribute(attribute);
+ return SEC_OID_UNKNOWN;
+ }
+ oid.data = encoded + 2;
+ oid.len = len - 2;
+ tag = SECOID_FindOIDTag(&oid);
+ sftk_FreeAttribute(attribute);
+ return tag;
+}
+
+/* This function currently only returns valid lengths for
+ * FIPS approved ECC curves. If we want to make this generic
+ * in the future, that Curve determination can be done in
+ * the sftk_handleSpecial. Since it's currently only used
+ * in FIPS indicators, it's currently only compiled with
+ * the FIPS indicator code */
+static int
+sftk_getKeyLength(SFTKObject *source)
+{
+ CK_KEY_TYPE keyType = CK_INVALID_HANDLE;
+ CK_ATTRIBUTE_TYPE keyAttribute;
+ CK_ULONG keyLength = 0;
+ SFTKAttribute *attribute;
+ CK_RV crv;
+
+ /* If we don't have a key, then it doesn't have a length.
+ * this may be OK (say we are hashing). The mech info will
+ * sort this out because algorithms which expect no keys
+ * will accept zero length for the keys */
+ if (source == NULL) {
+ return 0;
+ }
+
+ crv = sftk_GetULongAttribute(source, CKA_KEY_TYPE, &keyType);
+ if (crv != CKR_OK) {
+ /* sometimes we're passed a data object, in that case the
+ * key length is CKA_VALUE, which is the default */
+ keyType = CKK_INVALID_KEY_TYPE;
+ }
+ if (keyType == CKK_EC) {
+ SECOidTag curve = sftk_quickGetECCCurveOid(source);
+ switch (curve) {
+ case SEC_OID_CURVE25519:
+ /* change when we start algorithm testing on curve25519 */
+ return 0;
+ case SEC_OID_SECG_EC_SECP256R1:
+ return 256;
+ case SEC_OID_SECG_EC_SECP384R1:
+ return 384;
+ case SEC_OID_SECG_EC_SECP521R1:
+ /* this is a lie, but it makes the table easier. We don't
+ * have to have a double entry for every ECC mechanism */
+ return 512;
+ default:
+ break;
+ }
+ /* other curves aren't NIST approved, returning 0 will cause these
+ * curves to fail FIPS length criteria */
+ return 0;
+ }
+
+ switch (keyType) {
+ case CKK_RSA:
+ keyAttribute = CKA_MODULUS;
+ break;
+ case CKK_DSA:
+ case CKK_DH:
+ keyAttribute = CKA_PRIME;
+ break;
+ default:
+ keyAttribute = CKA_VALUE;
+ break;
+ }
+ attribute = sftk_FindAttribute(source, keyAttribute);
+ if (attribute) {
+ keyLength = attribute->attrib.ulValueLen * 8;
+ sftk_FreeAttribute(attribute);
+ }
+ return keyLength;
+}
+
+/*
+ * handle specialized FIPS semantics that are too complicated to
+ * handle with just a table. NOTE: this means any additional semantics
+ * would have to be coded here before they can be added to the table */
+static PRBool
+sftk_handleSpecial(SFTKSlot *slot, CK_MECHANISM *mech,
+ SFTKFIPSAlgorithmList *mechInfo, SFTKObject *source)
+{
+ switch (mechInfo->special) {
+ case SFTKFIPSDH: {
+ SECItem dhPrime;
+ const SECItem *dhSubPrime;
+ CK_RV crv = sftk_Attribute2SecItem(NULL, &dhPrime,
+ source, CKA_PRIME);
+ if (crv != CKR_OK) {
+ return PR_FALSE;
+ }
+ dhSubPrime = sftk_VerifyDH_Prime(&dhPrime, PR_TRUE);
+ SECITEM_ZfreeItem(&dhPrime, PR_FALSE);
+ return (dhSubPrime) ? PR_TRUE : PR_FALSE;
+ }
+ case SFTKFIPSNone:
+ return PR_FALSE;
+ case SFTKFIPSECC:
+ /* we've already handled the curve selection in the 'getlength'
+ * function */
+ return PR_TRUE;
+ case SFTKFIPSAEAD: {
+ if (mech->ulParameterLen == 0) {
+ /* AEAD ciphers are only in FIPS mode if we are using the
+ * MESSAGE interface. This takes an empty parameter
+ * in the init function */
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+ }
+ case SFTKFIPSRSAPSS: {
+ /* PSS salt must not be longer than the underlying hash.
+ * We verify that the underlying hash of the
+ * parameters matches Hash of the combined hash mechanisms, so
+ * we don't need to look at the specific PSS mechanism */
+ CK_RSA_PKCS_PSS_PARAMS *pss = (CK_RSA_PKCS_PSS_PARAMS *)
+ mech->pParameter;
+ const SECHashObject *hashObj = NULL;
+ if (mech->ulParameterLen != sizeof(*pss)) {
+ return PR_FALSE;
+ }
+ /* we use the existing hash utilities to find the length of
+ * the hash */
+ hashObj = HASH_GetRawHashObject(sftk_GetHashTypeFromMechanism(
+ pss->hashAlg));
+ if (hashObj == NULL) {
+ return PR_FALSE;
+ }
+ if (pss->sLen > hashObj->length) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+ }
+ default:
+ break;
+ }
+ /* if we didn't understand the special processing, mark it non-fips */
+ return PR_FALSE;
+}
+#endif
+
+PRBool
+sftk_operationIsFIPS(SFTKSlot *slot, CK_MECHANISM *mech, CK_ATTRIBUTE_TYPE op,
+ SFTKObject *source)
+{
+#ifndef NSS_HAS_FIPS_INDICATORS
+ return PR_FALSE;
+#else
+ int i;
+ CK_FLAGS opFlags;
+ CK_ULONG keyLength;
+
+ /* handle all the quick stuff first */
+ if (!sftk_isFIPS(slot->slotID)) {
+ return PR_FALSE;
+ }
+ if (source && !source->isFIPS) {
+ return PR_FALSE;
+ }
+ if (mech == NULL) {
+ return PR_FALSE;
+ }
+
+ /* now get the calculated values */
+ opFlags = sftk_AttributeToFlags(op);
+ if (opFlags == 0) {
+ return PR_FALSE;
+ }
+ keyLength = sftk_getKeyLength(source);
+
+ /* check against our algorithm array */
+ for (i = 0; i < SFTK_NUMBER_FIPS_ALGORITHMS; i++) {
+ SFTKFIPSAlgorithmList *mechs = &sftk_fips_mechs[i];
+ /* if we match the number of records exactly, then we are an
+ * approved algorithm in the approved mode with an approved key */
+ if (((mech->mechanism == mechs->type) &&
+ (opFlags == (mechs->info.flags & opFlags)) &&
+ (keyLength <= mechs->info.ulMaxKeySize) &&
+ (keyLength >= mechs->info.ulMinKeySize) &&
+ ((keyLength - mechs->info.ulMinKeySize) % mechs->step) == 0) &&
+ ((mechs->special == SFTKFIPSNone) ||
+ sftk_handleSpecial(slot, mech, mechs, source))) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+#endif
+}
+
+/*
+ * create the FIPS Validation objects. If the vendor
+ * doesn't supply an NSS_FIPS_MODULE_ID, at compile time,
+ * then we assumethis is an unvalidated module.
+ */
+CK_RV
+sftk_CreateValidationObjects(SFTKSlot *slot)
+{
+ const char *module_id;
+ int module_id_len;
+ CK_RV crv = CKR_OK;
+ /* we currently use vendor specific values until the validation
+ * objects are approved for PKCS #11 v3.2. */
+ CK_OBJECT_CLASS cko_validation = CKO_NSS_VALIDATION;
+ CK_NSS_VALIDATION_TYPE ckv_fips = CKV_NSS_FIPS_140;
+ CK_VERSION fips_version = { 3, 0 }; /* FIPS-140-3 */
+ CK_ULONG fips_level = 1; /* or 2 if you validated at level 2 */
+
+#ifndef NSS_FIPS_MODULE_ID
+#define NSS_FIPS_MODULE_ID "Generic NSS " SOFTOKEN_VERSION " Unvalidated"
+#endif
+ module_id = NSS_FIPS_MODULE_ID;
+ module_id_len = sizeof(NSS_FIPS_MODULE_ID) - 1;
+ SFTKObject *object;
+
+ object = sftk_NewObject(slot); /* fill in the handle later */
+ if (object == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ object->isFIPS = PR_FALSE;
+
+ crv = sftk_AddAttributeType(object, CKA_CLASS,
+ &cko_validation, sizeof(cko_validation));
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftk_AddAttributeType(object, CKA_NSS_VALIDATION_TYPE,
+ &ckv_fips, sizeof(ckv_fips));
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftk_AddAttributeType(object, CKA_NSS_VALIDATION_VERSION,
+ &fips_version, sizeof(fips_version));
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftk_AddAttributeType(object, CKA_NSS_VALIDATION_LEVEL,
+ &fips_level, sizeof(fips_level));
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftk_AddAttributeType(object, CKA_NSS_VALIDATION_MODULE_ID,
+ module_id, module_id_len);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* future, fill in validation certificate information from a supplied
+ * pointer to a config file */
+ object->handle = sftk_getNextHandle(slot);
+ object->slot = slot;
+ sftk_AddObject(&slot->moduleObjects, object);
+loser:
+ sftk_FreeObject(object);
+ return crv;
+}