diff options
Diffstat (limited to 'security/nss/lib/softoken/pkcs11u.c')
-rw-r--r-- | security/nss/lib/softoken/pkcs11u.c | 2563 |
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; +} |