Source code

Revision control

Copy as Markdown

Other Tools

/* 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) {
// As a safeguard against misuse of the library, ensure we don't
// hand out live objects that somehow land in the free list.
PORT_Assert(object->refCount == 0);
if (object->refCount == 0) {
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) {
PZ_Lock(list->lock);
if (list->count < MAX_OBJECT_LIST_SIZE) {
object->next = list->head;
list->head = object;
list->count++;
PZ_Unlock(list->lock);
return;
}
PZ_Unlock(list->lock);
}
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);
PORT_Assert(object->refCount > 0);
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;
}