| /* 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 *)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; |
| } |
| |
| #ifdef NSS_HAS_FIPS_INDICATORS |
| /* 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; |
| } |
| 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; |
| } |