/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Netscape security libraries.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are 
 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
 * Rights Reserved.
 * 
 * Portions created by Sun Microsystems, Inc. are Copyright (C) 2003
 * Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Contributor(s):
 *	Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.
 */
/*
 * Internal PKCS #11 functions. Should only be called by pkcs11.c
 */
#include "pkcs11.h"
#include "pkcs11i.h"
#include "pcertt.h"
#include "lowkeyi.h"
#include "pcert.h"
#include "secasn1.h"
#include "blapi.h"
#include "secerr.h"
#include "prnetdb.h" /* for PR_ntohl */

/*
 * ******************** Attribute Utilities *******************************
 */

/*
 * create a new attribute with type, value, and length. Space is allocated
 * to hold value.
 */
static PK11Attribute *
pk11_NewAttribute(PK11Object *object,
	CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG len)
{
    PK11Attribute *attribute;

#ifdef PKCS11_STATIC_ATTRIBUTES
    PK11SessionObject *so = pk11_narrowToSessionObject(object);
    int index;

    if (so == NULL)  {
	/* allocate new attribute in a buffer */
	PORT_Assert(0);
    }
    /* 
     * PKCS11_STATIC_ATTRIBUTES attempts 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.
     */
    PK11_USE_THREADS(PZ_Lock(so->attributeLock);)
    index = so->nextAttr++;
    PK11_USE_THREADS(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;
    }
#else
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    attribute = (PK11Attribute*)PORT_Alloc(sizeof(PK11Attribute));
    attribute->freeAttr = PR_TRUE;
#else
    attribute = (PK11Attribute*)PORT_ArenaAlloc(object->arena,sizeof(PK11Attribute));
    attribute->freeAttr = PR_FALSE;
#endif /* PKCS11_REF_COUNT_ATTRIBUTES */
    if (attribute == NULL) return NULL;
    attribute->freeData = PR_FALSE;

    if (value) {
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
	attribute->attrib.pValue = PORT_Alloc(len);
	attribute->freeData = PR_TRUE;
#else
	attribute->attrib.pValue = PORT_ArenaAlloc(object->arena,len);
#endif /* PKCS11_REF_COUNT_ATTRIBUTES */
	if (attribute->attrib.pValue == NULL) {
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
	    PORT_Free(attribute);
#endif /* PKCS11_REF_COUNT_ATTRIBUTES */
	    return NULL;
	}
	PORT_Memcpy(attribute->attrib.pValue,value,len);
	attribute->attrib.ulValueLen = len;
    } else {
	attribute->attrib.pValue = NULL;
	attribute->attrib.ulValueLen = 0;
    }
#endif /* PKCS11_STATIC_ATTRIBUTES */
    attribute->attrib.type = type;
    attribute->handle = type;
    attribute->next = attribute->prev = NULL;
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    attribute->refCount = 1;
#ifdef PKCS11_USE_THREADS
    attribute->refLock = PZ_NewLock(nssILockRefLock);
    if (attribute->refLock == NULL) {
	if (attribute->attrib.pValue) PORT_Free(attribute->attrib.pValue);
	PORT_Free(attribute);
	return NULL;
    }
#else
    attribute->refLock = NULL;
#endif
#endif /* PKCS11_REF_COUNT_ATTRIBUTES */
    return attribute;
}

static PK11Attribute *
pk11_NewTokenAttribute(CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value,
						CK_ULONG len, PRBool copy)
{
    PK11Attribute *attribute;

    attribute = (PK11Attribute*)PORT_Alloc(sizeof(PK11Attribute));

    if (attribute == NULL) return NULL;
    attribute->attrib.type = type;
    attribute->handle = type;
    attribute->next = attribute->prev = NULL;
    attribute->freeAttr = PR_TRUE;
    attribute->freeData = PR_FALSE;
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    attribute->refCount = 1;
#ifdef PKCS11_USE_THREADS
    attribute->refLock = PZ_NewLock(nssILockRefLock);
    if (attribute->refLock == NULL) {
	PORT_Free(attribute);
	return NULL;
    }
#else
    attribute->refLock = NULL;
#endif
#endif /* PKCS11_REF_COUNT_ATTRIBUTES */
    attribute->attrib.type = type;
    if (!copy) {
	attribute->attrib.pValue = value;
	attribute->attrib.ulValueLen = len;
	return attribute;
    }

    if (value) {
#ifdef PKCS11_STATIC_ATTRIBUTES
        if (len <= ATTR_SPACE) {
	    attribute->attrib.pValue = attribute->space;
	} else {
	    attribute->attrib.pValue = PORT_Alloc(len);
	    attribute->freeData = PR_TRUE;
	}
#else
	attribute->attrib.pValue = PORT_Alloc(len);
	attribute->freeData = PR_TRUE;
#endif
	if (attribute->attrib.pValue == NULL) {
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
	    if (attribute->refLock) {
		PK11_USE_THREADS(PZ_DestroyLock(attribute->refLock);)
	    }
#endif
	    PORT_Free(attribute);
	    return NULL;
	}
	PORT_Memcpy(attribute->attrib.pValue,value,len);
	attribute->attrib.ulValueLen = len;
    } else {
	attribute->attrib.pValue = NULL;
	attribute->attrib.ulValueLen = 0;
    }
    return attribute;
}

static PK11Attribute *
pk11_NewTokenAttributeSigned(CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, 
						CK_ULONG len, PRBool copy)
{
    unsigned char * dval = (unsigned char *)value;
    if (*dval == 0) {
	dval++;
	len--;
    }
    return pk11_NewTokenAttribute(type,dval,len,copy);
}

/*
 * Free up all the memory associated with an attribute. Reference count
 * must be zero to call this.
 */
static void
pk11_DestroyAttribute(PK11Attribute *attribute)
{
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    PORT_Assert(attribute->refCount == 0);
    PK11_USE_THREADS(PZ_DestroyLock(attribute->refLock);)
#endif
    if (attribute->freeData) {
	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);
	}
	PORT_Free(attribute->attrib.pValue);
    }
    PORT_Free(attribute);
}

/*
 * release a reference to an attribute structure
 */
void
pk11_FreeAttribute(PK11Attribute *attribute)
{
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    PRBool destroy = PR_FALSE;
#endif

    if (attribute->freeAttr) {
	pk11_DestroyAttribute(attribute);
	return;
    }

#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    PK11_USE_THREADS(PZ_Lock(attribute->refLock);)
    if (attribute->refCount == 1) destroy = PR_TRUE;
    attribute->refCount--;
    PK11_USE_THREADS(PZ_Unlock(attribute->refLock);)

    if (destroy) pk11_DestroyAttribute(attribute);
#endif
}

#ifdef PKCS11_REF_COUNT_ATTRIBUTES
#define PK11_DEF_ATTRIBUTE(value,len) \
   { NULL, NULL, PR_FALSE, PR_FALSE, 1, NULL, 0, { 0, value, len } }

#else
#define PK11_DEF_ATTRIBUTE(value,len) \
   { NULL, NULL, PR_FALSE, PR_FALSE, 0, { 0, value, len } }
#endif

CK_BBOOL pk11_staticTrueValue = CK_TRUE;
CK_BBOOL pk11_staticFalseValue = CK_FALSE;
static const PK11Attribute pk11_StaticTrueAttr = 
  PK11_DEF_ATTRIBUTE(&pk11_staticTrueValue,sizeof(pk11_staticTrueValue));
static const PK11Attribute pk11_StaticFalseAttr = 
  PK11_DEF_ATTRIBUTE(&pk11_staticFalseValue,sizeof(pk11_staticFalseValue));
static const PK11Attribute pk11_StaticNullAttr = PK11_DEF_ATTRIBUTE(NULL,0);
char pk11_StaticOneValue = 1;
static const PK11Attribute pk11_StaticOneAttr = 
  PK11_DEF_ATTRIBUTE(&pk11_StaticOneValue,sizeof(pk11_StaticOneValue));

CK_CERTIFICATE_TYPE pk11_staticX509Value = CKC_X_509;
static const PK11Attribute pk11_StaticX509Attr =
  PK11_DEF_ATTRIBUTE(&pk11_staticX509Value, sizeof(pk11_staticX509Value));
CK_TRUST pk11_staticTrustedValue = CKT_NETSCAPE_TRUSTED;
CK_TRUST pk11_staticTrustedDelegatorValue = CKT_NETSCAPE_TRUSTED_DELEGATOR;
CK_TRUST pk11_staticValidDelegatorValue = CKT_NETSCAPE_VALID_DELEGATOR;
CK_TRUST pk11_staticUnTrustedValue = CKT_NETSCAPE_UNTRUSTED;
CK_TRUST pk11_staticTrustUnknownValue = CKT_NETSCAPE_TRUST_UNKNOWN;
CK_TRUST pk11_staticValidPeerValue = CKT_NETSCAPE_VALID;
CK_TRUST pk11_staticMustVerifyValue = CKT_NETSCAPE_MUST_VERIFY;
static const PK11Attribute pk11_StaticTrustedAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticTrustedValue,
				sizeof(pk11_staticTrustedValue));
static const PK11Attribute pk11_StaticTrustedDelegatorAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticTrustedDelegatorValue,
				sizeof(pk11_staticTrustedDelegatorValue));
static const PK11Attribute pk11_StaticValidDelegatorAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticValidDelegatorValue,
				sizeof(pk11_staticValidDelegatorValue));
static const PK11Attribute pk11_StaticUnTrustedAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticUnTrustedValue,
				sizeof(pk11_staticUnTrustedValue));
static const PK11Attribute pk11_StaticTrustUnknownAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticTrustUnknownValue,
				sizeof(pk11_staticTrustUnknownValue));
static const PK11Attribute pk11_StaticValidPeerAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticValidPeerValue,
				sizeof(pk11_staticValidPeerValue));
static const PK11Attribute pk11_StaticMustVerifyAttr =
  PK11_DEF_ATTRIBUTE(&pk11_staticMustVerifyValue,
				sizeof(pk11_staticMustVerifyValue));

static certDBEntrySMime *
pk11_getSMime(PK11TokenObject *object)
{
    certDBEntrySMime *entry;

    if (object->obj.objclass != CKO_NETSCAPE_SMIME) {
	return NULL;
    }
    if (object->obj.objectInfo) {
	return (certDBEntrySMime *)object->obj.objectInfo;
    }

    entry = nsslowcert_ReadDBSMimeEntry(object->obj.slot->certDB,
						(char *)object->dbKey.data);
    object->obj.objectInfo = (void *)entry;
    object->obj.infoFree = (PK11Free) nsslowcert_DestroyDBEntry;
    return entry;
}

static certDBEntryRevocation *
pk11_getCrl(PK11TokenObject *object)
{
    certDBEntryRevocation *crl;
    PRBool isKrl;

    if (object->obj.objclass != CKO_NETSCAPE_CRL) {
	return NULL;
    }
    if (object->obj.objectInfo) {
	return (certDBEntryRevocation *)object->obj.objectInfo;
    }

    isKrl = (PRBool) object->obj.handle == PK11_TOKEN_KRL_HANDLE;
    crl = nsslowcert_FindCrlByKey(object->obj.slot->certDB,
							&object->dbKey, isKrl);
    object->obj.objectInfo = (void *)crl;
    object->obj.infoFree = (PK11Free) nsslowcert_DestroyDBEntry;
    return crl;
}

static NSSLOWCERTCertificate *
pk11_getCert(PK11TokenObject *object)
{
    NSSLOWCERTCertificate *cert;
    CK_OBJECT_CLASS objClass = object->obj.objclass;

    if ((objClass != CKO_CERTIFICATE) && (objClass != CKO_NETSCAPE_TRUST)) {
	return NULL;
    }
    if (objClass == CKO_CERTIFICATE && object->obj.objectInfo) {
	return (NSSLOWCERTCertificate *)object->obj.objectInfo;
    }
    cert = nsslowcert_FindCertByKey(object->obj.slot->certDB,&object->dbKey);
    if (objClass == CKO_CERTIFICATE) {
	object->obj.objectInfo = (void *)cert;
	object->obj.infoFree = (PK11Free) nsslowcert_DestroyCertificate ;
    }
    return cert;
}

static NSSLOWCERTTrust *
pk11_getTrust(PK11TokenObject *object)
{
    NSSLOWCERTTrust *trust;

    if (object->obj.objclass != CKO_NETSCAPE_TRUST) {
	return NULL;
    }
    if (object->obj.objectInfo) {
	return (NSSLOWCERTTrust *)object->obj.objectInfo;
    }
    trust = nsslowcert_FindTrustByKey(object->obj.slot->certDB,&object->dbKey);
    object->obj.objectInfo = (void *)trust;
    object->obj.infoFree = (PK11Free) nsslowcert_DestroyTrust ;
    return trust;
}

static NSSLOWKEYPublicKey *
pk11_GetPublicKey(PK11TokenObject *object)
{
    NSSLOWKEYPublicKey *pubKey;
    NSSLOWKEYPrivateKey *privKey;

    if (object->obj.objclass != CKO_PUBLIC_KEY) {
	return NULL;
    }
    if (object->obj.objectInfo) {
	return (NSSLOWKEYPublicKey *)object->obj.objectInfo;
    }
    privKey = nsslowkey_FindKeyByPublicKey(object->obj.slot->keyDB,
				&object->dbKey, object->obj.slot->password);
    if (privKey == NULL) {
	return NULL;
    }
    pubKey = nsslowkey_ConvertToPublicKey(privKey);
    nsslowkey_DestroyPrivateKey(privKey);
    object->obj.objectInfo = (void *) pubKey;
    object->obj.infoFree = (PK11Free) nsslowkey_DestroyPublicKey ;
    return pubKey;
}

static NSSLOWKEYPrivateKey *
pk11_GetPrivateKey(PK11TokenObject *object)
{
    NSSLOWKEYPrivateKey *privKey;

    if ((object->obj.objclass != CKO_PRIVATE_KEY) && 
			(object->obj.objclass != CKO_SECRET_KEY)) {
	return NULL;
    }
    if (object->obj.objectInfo) {
	return (NSSLOWKEYPrivateKey *)object->obj.objectInfo;
    }
    privKey = nsslowkey_FindKeyByPublicKey(object->obj.slot->keyDB,
				&object->dbKey, object->obj.slot->password);
    if (privKey == NULL) {
	return NULL;
    }
    object->obj.objectInfo = (void *) privKey;
    object->obj.infoFree = (PK11Free) nsslowkey_DestroyPrivateKey ;
    return privKey;
}

/* pk11_GetPubItem returns data associated with the public key.
 * one only needs to free the public key. This comment is here
 * because this sematic would be non-obvious otherwise. All callers
 * should include this comment.
 */
static SECItem *
pk11_GetPubItem(NSSLOWKEYPublicKey *pubKey) {
    SECItem *pubItem = NULL;
    /* get value to compare from the cert's public key */
    switch ( pubKey->keyType ) {
    case NSSLOWKEYRSAKey:
	    pubItem = &pubKey->u.rsa.modulus;
	    break;
    case NSSLOWKEYDSAKey:
	    pubItem = &pubKey->u.dsa.publicValue;
	    break;
    case NSSLOWKEYDHKey:
	    pubItem = &pubKey->u.dh.publicValue;
	    break;
#ifdef NSS_ENABLE_ECC
    case NSSLOWKEYECKey:
	    pubItem = &pubKey->u.ec.publicValue;
	    break;
#endif /* NSS_ENABLE_ECC */
    default:
	    break;
    }
    return pubItem;
}

static const SEC_ASN1Template pk11_SerialTemplate[] = {
    { SEC_ASN1_INTEGER, offsetof(NSSLOWCERTCertificate,serialNumber) },
    { 0 }
};

static PK11Attribute *
pk11_FindRSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_RSA;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_ENCRYPT:
    case CKA_VERIFY:
    case CKA_VERIFY_RECOVER:
    case CKA_WRAP:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_MODULUS:
	return pk11_NewTokenAttributeSigned(type,key->u.rsa.modulus.data,
					key->u.rsa.modulus.len, PR_FALSE);
    case CKA_PUBLIC_EXPONENT:
	return pk11_NewTokenAttributeSigned(type,key->u.rsa.publicExponent.data,
				key->u.rsa.publicExponent.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

static PK11Attribute *
pk11_FindDSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_DSA;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.dsa.publicValue.data,
						key->u.dsa.publicValue.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
    case CKA_ENCRYPT:
    case CKA_VERIFY_RECOVER:
    case CKA_WRAP:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_VERIFY:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_VALUE:
	return pk11_NewTokenAttributeSigned(type,key->u.dsa.publicValue.data,
					key->u.dsa.publicValue.len, PR_FALSE);
    case CKA_PRIME:
	return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.prime.data,
					key->u.dsa.params.prime.len, PR_FALSE);
    case CKA_SUBPRIME:
	return pk11_NewTokenAttributeSigned(type,
				key->u.dsa.params.subPrime.data,
				key->u.dsa.params.subPrime.len, PR_FALSE);
    case CKA_BASE:
	return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.base.data,
					key->u.dsa.params.base.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

static PK11Attribute *
pk11_FindDHPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_DH;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_ENCRYPT:
    case CKA_VERIFY:
    case CKA_VERIFY_RECOVER:
    case CKA_WRAP:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_VALUE:
	return pk11_NewTokenAttributeSigned(type,key->u.dh.publicValue.data,
					key->u.dh.publicValue.len, PR_FALSE);
    case CKA_PRIME:
	return pk11_NewTokenAttributeSigned(type,key->u.dh.prime.data,
					key->u.dh.prime.len, PR_FALSE);
    case CKA_BASE:
	return pk11_NewTokenAttributeSigned(type,key->u.dh.base.data,
					key->u.dh.base.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

#ifdef NSS_ENABLE_ECC
static PK11Attribute *
pk11_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_EC;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash, key->u.ec.publicValue.data,
		     key->u.ec.publicValue.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
    case CKA_VERIFY:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_ENCRYPT:
    case CKA_VERIFY_RECOVER:
    case CKA_WRAP:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_EC_PARAMS:
        /* XXX Why is the last arg PR_FALSE? */
	return pk11_NewTokenAttributeSigned(type,
					key->u.ec.ecParams.DEREncoding.data,
					key->u.ec.ecParams.DEREncoding.len,
					PR_FALSE);
    case CKA_EC_POINT:
        /* XXX Why is the last arg PR_FALSE? */
	return pk11_NewTokenAttributeSigned(type,key->u.ec.publicValue.data,
					key->u.ec.publicValue.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}
#endif /* NSS_ENABLE_ECC */

static PK11Attribute *
pk11_FindPublicKeyAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    NSSLOWKEYPublicKey *key;
    PK11Attribute *att = NULL;
    char *label;

    switch (type) {
    case CKA_PRIVATE:
    case CKA_SENSITIVE:
    case CKA_ALWAYS_SENSITIVE:
    case CKA_NEVER_EXTRACTABLE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_MODIFIABLE:
    case CKA_EXTRACTABLE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_LABEL:
        label = nsslowkey_FindKeyNicknameByPublicKey(object->obj.slot->keyDB,
				&object->dbKey, object->obj.slot->password);
	if (label == NULL) {
	   return (PK11Attribute *)&pk11_StaticOneAttr;
	}
	att = pk11_NewTokenAttribute(type,label,PORT_Strlen(label), PR_TRUE);
	PORT_Free(label);
	return att;
    default:
	break;
    }

    key = pk11_GetPublicKey(object);
    if (key == NULL) {
	return NULL;
    }

    switch (key->keyType) {
    case NSSLOWKEYRSAKey:
	return pk11_FindRSAPublicKeyAttribute(key,type);
    case NSSLOWKEYDSAKey:
	return pk11_FindDSAPublicKeyAttribute(key,type);
    case NSSLOWKEYDHKey:
	return pk11_FindDHPublicKeyAttribute(key,type);
#ifdef NSS_ENABLE_ECC
    case NSSLOWKEYECKey:
	return pk11_FindECPublicKeyAttribute(key,type);
#endif /* NSS_ENABLE_ECC */
    default:
	break;
    }

    return NULL;
}

static PK11Attribute *
pk11_FindSecretKeyAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    NSSLOWKEYPrivateKey *key;
    char *label;
    unsigned char *keyString;
    PK11Attribute *att;
    int keyTypeLen;
    CK_ULONG keyLen;
    CK_KEY_TYPE keyType;
    PRUint32 keyTypeStorage;

    switch (type) {
    case CKA_PRIVATE:
    case CKA_SENSITIVE:
    case CKA_ALWAYS_SENSITIVE:
    case CKA_EXTRACTABLE:
    case CKA_DERIVE:
    case CKA_ENCRYPT:
    case CKA_DECRYPT:
    case CKA_SIGN:
    case CKA_VERIFY:
    case CKA_WRAP:
    case CKA_UNWRAP:
    case CKA_MODIFIABLE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_NEVER_EXTRACTABLE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_LABEL:
        label = nsslowkey_FindKeyNicknameByPublicKey(object->obj.slot->keyDB,
				&object->dbKey, object->obj.slot->password);
	if (label == NULL) {
	   return (PK11Attribute *)&pk11_StaticNullAttr;
	}
	att = pk11_NewTokenAttribute(type,label,PORT_Strlen(label), PR_TRUE);
	PORT_Free(label);
	return att;
    case CKA_KEY_TYPE:
    case CKA_VALUE_LEN:
    case CKA_VALUE:
	break;
    default:
	return NULL;
    }

    key = pk11_GetPrivateKey(object);
    if (key == NULL) {
	return NULL;
    }
    switch (type) {
    case CKA_KEY_TYPE:
	/* handle legacy databases. In legacy databases key_type was stored
	 * in host order, with any leading zeros stripped off. Only key types
	 * under 0x1f (AES) were stored. We assume that any values which are
	 * either 1 byte long (big endian), or have byte[0] between 0 and 
	 * 0x7f and bytes[1]-bytes[3] equal to '0' (little endian). All other
	 * values are assumed to be from the new database, which is always 4
	 * bytes in network order */
	keyType=0;
	keyString = key->u.rsa.coefficient.data;
	keyTypeLen = key->u.rsa.coefficient.len;


	/*
 	 * Because of various endian and word lengths, the database may have
	 * stored the keyType value in one of the following formats:
	 *   (kt) <= 0x1f 
	 *                                   length data
	 * Big Endian,     pre-3.9, all lengths: 1  (kt)
	 * Little Endian,  pre-3.9, 32 bits:     4  (kt) 0  0  0
	 * Little Endian,  pre-3.9, 64 bits:     8  (kt) 0  0  0   0  0  0  0
	 * All platforms,      3.9, 32 bits:     4    0  0  0 (kt)
	 * Big Endian,         3.9, 64 bits:     8    0  0  0 (kt) 0  0  0  0
	 * Little  Endian,     3.9, 64 bits:     8    0  0  0  0   0  0  0 (kt)
	 * All platforms, >= 3.9.1, all lengths: 4   (a) k1 k2 k3
	 * where (a) is 0 or >= 0x80. currently (a) can only be 0.
	 */
	/*
 	 * This key was written on a 64 bit platform with a using NSS 3.9
	 * or earlier. Reduce the 64 bit possibilities above. When  through,
	 * we will only have:
	 * 
	 * Big Endian,     pre-3.9, all lengths: 1  (kt)
	 * Little Endian,  pre-3.9, all lengths: 4  (kt) 0  0  0
	 * All platforms,      3.9, all lengths: 4    0  0  0 (kt)
	 * All platforms, => 3.9.1, all lengths: 4   (a) k1 k2 k3
	 */
	if (keyTypeLen == 8) {
	    keyTypeStorage = *(PRUint32 *) keyString;
	    if (keyTypeStorage == 0) {
		keyString += sizeof(PRUint32);
	    }
	    keyTypeLen = 4;
	}
	/*
	 * Now Handle:
	 *
	 * All platforms,      3.9, all lengths: 4    0  0  0 (kt)
	 * All platforms, => 3.9.1, all lengths: 4   (a) k1 k2 k3
	 *
	 * NOTE: if  kt == 0 or ak1k2k3 == 0, the test fails and
	 * we handle it as:
	 *
	 * Little Endian,  pre-3.9, all lengths: 4  (kt) 0  0  0
	 */
	if (keyTypeLen == sizeof(keyTypeStorage) &&
	     (((keyString[0] & 0x80) == 0x80) ||
		!((keyString[1] == 0) && (keyString[2] == 0)
	   				    && (keyString[3] == 0))) ) {
	    PORT_Memcpy(&keyTypeStorage, keyString, sizeof(keyTypeStorage));
	    keyType = (CK_KEY_TYPE) PR_ntohl(keyTypeStorage);
	} else {
	/*
	 * Now Handle:
	 *
	 * Big Endian,     pre-3.9, all lengths: 1  (kt)
	 * Little Endian,  pre-3.9, all lengths: 4  (kt) 0  0  0
	 *  -- KeyType == 0 all other cases ---: 4    0  0  0  0
	 */
	    keyType = (CK_KEY_TYPE) keyString[0] ;
        }
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType),PR_TRUE);
    case CKA_VALUE:
	return pk11_NewTokenAttribute(type,key->u.rsa.privateExponent.data,
				key->u.rsa.privateExponent.len, PR_FALSE);
    case CKA_VALUE_LEN:
	keyLen=key->u.rsa.privateExponent.len;
	return pk11_NewTokenAttribute(type, &keyLen, sizeof(CK_ULONG), PR_TRUE);
    }

    return NULL;
}

static PK11Attribute *
pk11_FindRSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key,
							 CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_RSA;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_DECRYPT:
    case CKA_SIGN:
    case CKA_SIGN_RECOVER:
    case CKA_UNWRAP:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_MODULUS:
	return pk11_NewTokenAttributeSigned(type,key->u.rsa.modulus.data,
					key->u.rsa.modulus.len, PR_FALSE);
    case CKA_PUBLIC_EXPONENT:
	return pk11_NewTokenAttributeSigned(type,key->u.rsa.publicExponent.data,
				key->u.rsa.publicExponent.len, PR_FALSE);
    case CKA_PRIVATE_EXPONENT:
    case CKA_PRIME_1:
    case CKA_PRIME_2:
    case CKA_EXPONENT_1:
    case CKA_EXPONENT_2:
    case CKA_COEFFICIENT:
	return (PK11Attribute *) &pk11_StaticNullAttr;
    default:
	break;
    }
    return NULL;
}

static PK11Attribute *
pk11_FindDSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, 
							CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_DSA;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.dsa.publicValue.data,
						key->u.dsa.publicValue.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
    case CKA_DECRYPT:
    case CKA_SIGN_RECOVER:
    case CKA_UNWRAP:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_SIGN:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_VALUE:
	return (PK11Attribute *) &pk11_StaticNullAttr;
    case CKA_PRIME:
	return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.prime.data,
					key->u.dsa.params.prime.len, PR_FALSE);
    case CKA_SUBPRIME:
	return pk11_NewTokenAttributeSigned(type,
				key->u.dsa.params.subPrime.data,
				key->u.dsa.params.subPrime.len, PR_FALSE);
    case CKA_BASE:
	return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.base.data,
					key->u.dsa.params.base.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

static PK11Attribute *
pk11_FindDHPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_DH;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_DECRYPT:
    case CKA_SIGN:
    case CKA_SIGN_RECOVER:
    case CKA_UNWRAP:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_VALUE:
	return (PK11Attribute *) &pk11_StaticNullAttr;
    case CKA_PRIME:
	return pk11_NewTokenAttributeSigned(type,key->u.dh.prime.data,
					key->u.dh.prime.len, PR_FALSE);
    case CKA_BASE:
	return pk11_NewTokenAttributeSigned(type,key->u.dh.base.data,
					key->u.dh.base.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

#ifdef NSS_ENABLE_ECC
static PK11Attribute *
pk11_FindECPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type)
{
    unsigned char hash[SHA1_LENGTH];
    CK_KEY_TYPE keyType = CKK_EC;

    switch (type) {
    case CKA_KEY_TYPE:
	return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE);
    case CKA_ID:
	SHA1_HashBuf(hash,key->u.ec.publicValue.data,key->u.ec.publicValue.len);
	return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE);
    case CKA_DERIVE:
    case CKA_SIGN:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_DECRYPT:
    case CKA_SIGN_RECOVER:
    case CKA_UNWRAP:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_VALUE:
	return (PK11Attribute *) &pk11_StaticNullAttr;
    case CKA_EC_PARAMS:
        /* XXX Why is the last arg PR_FALSE? */
	return pk11_NewTokenAttributeSigned(type,
					key->u.ec.ecParams.DEREncoding.data,
					key->u.ec.ecParams.DEREncoding.len,
					PR_FALSE);
    default:
	break;
    }
    return NULL;
}
#endif /* NSS_ENABLE_ECC */

static PK11Attribute *
pk11_FindPrivateKeyAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    NSSLOWKEYPrivateKey *key;
    char *label;
    PK11Attribute *att;

    switch (type) {
    case CKA_PRIVATE:
    case CKA_SENSITIVE:
    case CKA_ALWAYS_SENSITIVE:
    case CKA_EXTRACTABLE:
    case CKA_MODIFIABLE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_NEVER_EXTRACTABLE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_SUBJECT:
	   return (PK11Attribute *)&pk11_StaticNullAttr;
    case CKA_LABEL:
        label = nsslowkey_FindKeyNicknameByPublicKey(object->obj.slot->keyDB,
				&object->dbKey, object->obj.slot->password);
	if (label == NULL) {
	   return (PK11Attribute *)&pk11_StaticNullAttr;
	}
	att = pk11_NewTokenAttribute(type,label,PORT_Strlen(label), PR_TRUE);
	PORT_Free(label);
	return att;
    default:
	break;
    }
    key = pk11_GetPrivateKey(object);
    if (key == NULL) {
	return NULL;
    }
    switch (key->keyType) {
    case NSSLOWKEYRSAKey:
	return pk11_FindRSAPrivateKeyAttribute(key,type);
    case NSSLOWKEYDSAKey:
	return pk11_FindDSAPrivateKeyAttribute(key,type);
    case NSSLOWKEYDHKey:
	return pk11_FindDHPrivateKeyAttribute(key,type);
#ifdef NSS_ENABLE_ECC
    case NSSLOWKEYECKey:
	return pk11_FindECPrivateKeyAttribute(key,type);
#endif /* NSS_ENABLE_ECC */
    default:
	break;
    }

    return NULL;
}

static PK11Attribute *
pk11_FindSMIMEAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    certDBEntrySMime *entry;
    switch (type) {
    case CKA_PRIVATE:
    case CKA_MODIFIABLE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_NETSCAPE_EMAIL:
	return pk11_NewTokenAttribute(type,object->dbKey.data,
						object->dbKey.len-1, PR_FALSE);
    case CKA_NETSCAPE_SMIME_TIMESTAMP:
    case CKA_SUBJECT:
    case CKA_VALUE:
	break;
    default:
	return NULL;
    }
    entry = pk11_getSMime(object);
    if (entry == NULL) {
	return NULL;
    }
    switch (type) {
    case CKA_NETSCAPE_SMIME_TIMESTAMP:
	return pk11_NewTokenAttribute(type,entry->optionsDate.data,
					entry->optionsDate.len, PR_FALSE);
    case CKA_SUBJECT:
	return pk11_NewTokenAttribute(type,entry->subjectName.data,
					entry->subjectName.len, PR_FALSE);
    case CKA_VALUE:
	return pk11_NewTokenAttribute(type,entry->smimeOptions.data,
					entry->smimeOptions.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

static PK11Attribute *
pk11_FindTrustAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    NSSLOWCERTTrust *trust;
    unsigned char hash[SHA1_LENGTH];
    unsigned int trustFlags;

    switch (type) {
    case CKA_PRIVATE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_MODIFIABLE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_CERT_SHA1_HASH:
    case CKA_CERT_MD5_HASH:
    case CKA_TRUST_CLIENT_AUTH:
    case CKA_TRUST_SERVER_AUTH:
    case CKA_TRUST_EMAIL_PROTECTION:
    case CKA_TRUST_CODE_SIGNING:
	break;
    default:
	return NULL;
    }
    trust = pk11_getTrust(object);
    if (trust == NULL) {
	return NULL;
    }
    switch (type) {
    case CKA_CERT_SHA1_HASH:
	SHA1_HashBuf(hash,trust->derCert->data,trust->derCert->len);
	return pk11_NewTokenAttribute(type, hash, SHA1_LENGTH, PR_TRUE);
    case CKA_CERT_MD5_HASH:
	MD5_HashBuf(hash,trust->derCert->data,trust->derCert->len);
	return pk11_NewTokenAttribute(type, hash, MD5_LENGTH, PR_TRUE);
    case CKA_TRUST_CLIENT_AUTH:
	trustFlags = trust->trust->sslFlags & CERTDB_TRUSTED_CLIENT_CA ?
		trust->trust->sslFlags | CERTDB_TRUSTED_CA : 0 ;
	goto trust;
    case CKA_TRUST_SERVER_AUTH:
	trustFlags = trust->trust->sslFlags;
	goto trust;
    case CKA_TRUST_EMAIL_PROTECTION:
	trustFlags = trust->trust->emailFlags;
	goto trust;
    case CKA_TRUST_CODE_SIGNING:
	trustFlags = trust->trust->objectSigningFlags;
trust:
	if (trustFlags & CERTDB_TRUSTED_CA ) {
	    return (PK11Attribute *)&pk11_StaticTrustedDelegatorAttr;
	}
	if (trustFlags & CERTDB_TRUSTED) {
	    return (PK11Attribute *)&pk11_StaticTrustedAttr;
	}
	if (trustFlags & CERTDB_NOT_TRUSTED) {
	    return (PK11Attribute *)&pk11_StaticUnTrustedAttr;
	}
	if (trustFlags & CERTDB_TRUSTED_UNKNOWN) {
	    return (PK11Attribute *)&pk11_StaticTrustUnknownAttr;
	}
	if (trustFlags & CERTDB_VALID_CA) {
	    return (PK11Attribute *)&pk11_StaticValidDelegatorAttr;
	}
	if (trustFlags & CERTDB_VALID_PEER) {
	    return (PK11Attribute *)&pk11_StaticValidPeerAttr;
	}
	return (PK11Attribute *)&pk11_StaticMustVerifyAttr;
    default:
	break;
    }

#ifdef notdef
    switch (type) {
    case CKA_ISSUER:
	cert = pk11_getCertObject(object);
	if (cert == NULL) break;
	attr = pk11_NewTokenAttribute(type,cert->derIssuer.data,
						cert->derIssuer.len, PR_FALSE);
	
    case CKA_SERIAL_NUMBER:
	cert = pk11_getCertObject(object);
	if (cert == NULL) break;
	item = SEC_ASN1EncodeItem(NULL,NULL,cert,pk11_SerialTemplate);
	if (item == NULL) break;
	attr = pk11_NewTokenAttribute(type, item->data, item->len, PR_TRUE);
	SECITEM_FreeItem(item,PR_TRUE);
    }
    if (cert) {
	NSSLOWCERTDestroyCertificate(cert);	
	return attr;
    }
#endif
    return NULL;
}

static PK11Attribute *
pk11_FindCrlAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    certDBEntryRevocation *crl;

    switch (type) {
    case CKA_PRIVATE:
    case CKA_MODIFIABLE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_NETSCAPE_KRL:
	return (PK11Attribute *) ((object->obj.handle == PK11_TOKEN_KRL_HANDLE) 
			? &pk11_StaticTrueAttr : &pk11_StaticFalseAttr);
    case CKA_SUBJECT:
	return pk11_NewTokenAttribute(type,object->dbKey.data,
						object->dbKey.len, PR_FALSE);	
    case CKA_NETSCAPE_URL:
    case CKA_VALUE:
	break;
    default:
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
    	return NULL;
    }
    crl =  pk11_getCrl(object);
    if (!crl) {
	return NULL;
    }
    switch (type) {
    case CKA_NETSCAPE_URL:
	if (crl->url == NULL) {
	    return (PK11Attribute *) &pk11_StaticNullAttr;
	}
	return pk11_NewTokenAttribute(type, crl->url, 
					PORT_Strlen(crl->url)+1, PR_TRUE);
    case CKA_VALUE:
	return pk11_NewTokenAttribute(type, crl->derCrl.data, 
						crl->derCrl.len, PR_FALSE);
    default:
	break;
    }
    return NULL;
}

static PK11Attribute *
pk11_FindCertAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type)
{
    NSSLOWCERTCertificate *cert;
    NSSLOWKEYPublicKey  *pubKey;
    unsigned char hash[SHA1_LENGTH];
    SECItem *item;

    switch (type) {
    case CKA_PRIVATE:
	return (PK11Attribute *) &pk11_StaticFalseAttr;
    case CKA_MODIFIABLE:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_CERTIFICATE_TYPE:
        /* hardcoding X.509 into here */
        return (PK11Attribute *)&pk11_StaticX509Attr;
    case CKA_VALUE:
    case CKA_ID:
    case CKA_LABEL:
    case CKA_SUBJECT:
    case CKA_ISSUER:
    case CKA_SERIAL_NUMBER:
    case CKA_NETSCAPE_EMAIL:
	break;
    default:
	return NULL;
    }
    cert = pk11_getCert(object);
    if (cert == NULL) {
	return NULL;
    }
    switch (type) {
    case CKA_VALUE:
	return pk11_NewTokenAttribute(type,cert->derCert.data,
						cert->derCert.len,PR_FALSE);
    case CKA_ID:
	if (((cert->trust->sslFlags & CERTDB_USER) == 0) &&
		((cert->trust->emailFlags & CERTDB_USER) == 0) &&
		((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) {
	    return (PK11Attribute *) &pk11_StaticNullAttr;
	}
	pubKey = nsslowcert_ExtractPublicKey(cert);
	if (pubKey == NULL) break;
	item = pk11_GetPubItem(pubKey);
	if (item == NULL) {
	    nsslowkey_DestroyPublicKey(pubKey);
	    break;
	}
	SHA1_HashBuf(hash,item->data,item->len);
	/* item is imbedded in pubKey, just free the key */
	nsslowkey_DestroyPublicKey(pubKey);
	return pk11_NewTokenAttribute(type, hash, SHA1_LENGTH, PR_TRUE);
    case CKA_LABEL:
	return cert->nickname ? pk11_NewTokenAttribute(type, cert->nickname,
				PORT_Strlen(cert->nickname), PR_FALSE) :
					(PK11Attribute *) &pk11_StaticNullAttr;
    case CKA_SUBJECT:
	return pk11_NewTokenAttribute(type,cert->derSubject.data,
						cert->derSubject.len, PR_FALSE);
    case CKA_ISSUER:
	return pk11_NewTokenAttribute(type,cert->derIssuer.data,
						cert->derIssuer.len, PR_FALSE);
    case CKA_SERIAL_NUMBER:
	return pk11_NewTokenAttribute(type,cert->derSN.data,
						cert->derSN.len, PR_FALSE);
    case CKA_NETSCAPE_EMAIL:
	return (cert->emailAddr && cert->emailAddr[0])
	    ? pk11_NewTokenAttribute(type, cert->emailAddr,
	                             PORT_Strlen(cert->emailAddr), PR_FALSE) 
	    : (PK11Attribute *) &pk11_StaticNullAttr;
    default:
	break;
    }
    return NULL;
}
    
static PK11Attribute *    
pk11_FindTokenAttribute(PK11TokenObject *object,CK_ATTRIBUTE_TYPE type)
{
    /* handle the common ones */
    switch (type) {
    case CKA_CLASS:
	return pk11_NewTokenAttribute(type,&object->obj.objclass,
					sizeof(object->obj.objclass),PR_FALSE);
    case CKA_TOKEN:
	return (PK11Attribute *) &pk11_StaticTrueAttr;
    case CKA_LABEL:
	if (  (object->obj.objclass == CKO_CERTIFICATE) 
	   || (object->obj.objclass == CKO_PRIVATE_KEY)
	   || (object->obj.objclass == CKO_PUBLIC_KEY)
	   || (object->obj.objclass == CKO_SECRET_KEY)) {
	    break;
	}
	return (PK11Attribute *) &pk11_StaticNullAttr;
    default:
	break;
    }
    switch (object->obj.objclass) {
    case CKO_CERTIFICATE:
	return pk11_FindCertAttribute(object,type);
    case CKO_NETSCAPE_CRL:
	return pk11_FindCrlAttribute(object,type);
    case CKO_NETSCAPE_TRUST:
	return pk11_FindTrustAttribute(object,type);
    case CKO_NETSCAPE_SMIME:
	return pk11_FindSMIMEAttribute(object,type);
    case CKO_PUBLIC_KEY:
	return pk11_FindPublicKeyAttribute(object,type);
    case CKO_PRIVATE_KEY:
	return pk11_FindPrivateKeyAttribute(object,type);
    case CKO_SECRET_KEY:
	return pk11_FindSecretKeyAttribute(object,type);
    default:
	break;
    }
    PORT_Assert(0);
    return NULL;
} 

/*
 * 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.
 */
PK11Attribute *
pk11_FindAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;
    PK11SessionObject *sessObject = pk11_narrowToSessionObject(object);

    if (sessObject == NULL) {
	return pk11_FindTokenAttribute(pk11_narrowToTokenObject(object),type);
    }

    PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);)
    pk11queue_find(attribute,type,sessObject->head, sessObject->hashSize);
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
    if (attribute) {
	/* atomic increment would be nice here */
	PK11_USE_THREADS(PZ_Lock(attribute->refLock);)
	attribute->refCount++;
	PK11_USE_THREADS(PZ_Unlock(attribute->refLock);)
    }
#endif
    PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);)

    return(attribute);
}

/*
 * Take a buffer and it's length and return it's true size in bits;
 */
unsigned int
pk11_GetLengthInBits(unsigned char *buf, unsigned int bufLen)
{
    unsigned int size = bufLen * 8;
    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
pk11_ConstrainAttribute(PK11Object *object, CK_ATTRIBUTE_TYPE type, 
			int minLength, int maxLength, int minMultiple)
{
    PK11Attribute *attribute;
    unsigned int size;
    unsigned char *ptr;

    attribute = pk11_FindAttribute(object, type);
    if (!attribute) {
	return CKR_TEMPLATE_INCOMPLETE;
    }
    ptr = (unsigned char *) attribute->attrib.pValue;
    if (ptr == NULL) {
	pk11_FreeAttribute(attribute);
	return CKR_ATTRIBUTE_VALUE_INVALID;
    }
    size = pk11_GetLengthInBits(ptr, attribute->attrib.ulValueLen);
    pk11_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
pk11_hasAttributeToken(PK11TokenObject *object)
{
    return PR_FALSE;
}

/*
 * return true if object has attribute
 */
PRBool
pk11_hasAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;
    PK11SessionObject *sessObject = pk11_narrowToSessionObject(object);

    if (sessObject == NULL) {
	return pk11_hasAttributeToken(pk11_narrowToTokenObject(object));
    }

    PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);)
    pk11queue_find(attribute,type,sessObject->head, sessObject->hashSize);
    PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);)

    return (PRBool)(attribute != NULL);
}

/*
 * add an attribute to an object
 */
static void
pk11_AddAttribute(PK11Object *object,PK11Attribute *attribute)
{
    PK11SessionObject *sessObject = pk11_narrowToSessionObject(object);

    if (sessObject == NULL) return;
    PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);)
    pk11queue_add(attribute,attribute->handle,
				sessObject->head, sessObject->hashSize);
    PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);)
}

/* 
 * copy an unsigned attribute into a SECItem. Secitem is allocated in
 * the specified arena.
 */
CK_RV
pk11_Attribute2SSecItem(PLArenaPool *arena,SECItem *item,PK11Object *object,
                                      CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;

    item->data = NULL;

    attribute = pk11_FindAttribute(object, type);
    if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;

    (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen);
    if (item->data == NULL) {
	pk11_FreeAttribute(attribute);
	return CKR_HOST_MEMORY;
    }
    PORT_Memcpy(item->data, attribute->attrib.pValue, item->len);
    pk11_FreeAttribute(attribute);
    return CKR_OK;
}


/*
 * delete an attribute from an object
 */
static void
pk11_DeleteAttribute(PK11Object *object, PK11Attribute *attribute)
{
    PK11SessionObject *sessObject = pk11_narrowToSessionObject(object);

    if (sessObject == NULL) {
	return ;
    }
    PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);)
    if (pk11queue_is_queued(attribute,attribute->handle,
				sessObject->head, sessObject->hashSize)) {
	pk11queue_delete(attribute,attribute->handle,
				sessObject->head, sessObject->hashSize);
    }
    PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);)
    pk11_FreeAttribute(attribute);
}

/*
 * this is only valid for CK_BBOOL type attributes. Return the state
 * of that attribute.
 */
PRBool
pk11_isTrue(PK11Object *object,CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;
    PRBool tok = PR_FALSE;

    attribute=pk11_FindAttribute(object,type);
    if (attribute == NULL) { return PR_FALSE; }
    tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue);
    pk11_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
pk11_nullAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;

    attribute=pk11_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;
    }
    pk11_FreeAttribute(attribute);
}

static CK_RV
pk11_SetCertAttribute(PK11TokenObject *to, CK_ATTRIBUTE_TYPE type, 
						void *value, unsigned int len)
{
    NSSLOWCERTCertificate *cert;
    char *nickname = NULL;
    SECStatus rv;

    /* we can't change  the EMAIL values, but let the
     * upper layers feel better about the fact we tried to set these */
    if (type == CKA_NETSCAPE_EMAIL) {
	return CKR_OK;
    }

    if (to->obj.slot->certDB == NULL) {
	return CKR_TOKEN_WRITE_PROTECTED;
    }

    if ((type != CKA_LABEL)  && (type != CKA_ID)) {
	return CKR_ATTRIBUTE_READ_ONLY;
    }

    cert = pk11_getCert(to);
    if (cert == NULL) {
	return CKR_OBJECT_HANDLE_INVALID;
    }

    /* if the app is trying to set CKA_ID, it's probably because it just
     * imported the key. Look to see if we need to set the CERTDB_USER bits.
     */
    if (type == CKA_ID) {
	if (((cert->trust->sslFlags & CERTDB_USER) == 0) &&
		((cert->trust->emailFlags & CERTDB_USER) == 0) &&
		((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) {
	    PK11Slot *slot = to->obj.slot;

	    if (slot->keyDB && nsslowkey_KeyForCertExists(slot->keyDB,cert)) {
		NSSLOWCERTCertTrust trust = *cert->trust;
		trust.sslFlags |= CERTDB_USER;
		trust.emailFlags |= CERTDB_USER;
		trust.objectSigningFlags |= CERTDB_USER;
		nsslowcert_ChangeCertTrust(slot->certDB,cert,&trust);
	    }
	}
	return CKR_OK;
    }

    /* must be CKA_LABEL */
    if (value != NULL) {
	nickname = PORT_ZAlloc(len+1);
	if (nickname == NULL) {
	    return CKR_HOST_MEMORY;
	}
	PORT_Memcpy(nickname,value,len);
	nickname[len] = 0;
    }
    rv = nsslowcert_AddPermNickname(to->obj.slot->certDB, cert, nickname);
    if (nickname) PORT_Free(nickname);
    if (rv != SECSuccess) {
	return CKR_DEVICE_ERROR;
    }
    return CKR_OK;
}

static CK_RV
pk11_SetPrivateKeyAttribute(PK11TokenObject *to, CK_ATTRIBUTE_TYPE type, 
						void *value, unsigned int len)
{
    NSSLOWKEYPrivateKey *privKey;
    char *nickname = NULL;
    SECStatus rv;

    /* we can't change the ID and we don't store the subject, but let the
     * upper layers feel better about the fact we tried to set these */
    if ((type == CKA_ID) || (type == CKA_SUBJECT)) {
	return CKR_OK;
    }

    if (to->obj.slot->keyDB == NULL) {
	return CKR_TOKEN_WRITE_PROTECTED;
    }
    if (type != CKA_LABEL) {
	return CKR_ATTRIBUTE_READ_ONLY;
    }

    privKey = pk11_GetPrivateKey(to);
    if (privKey == NULL) {
	return CKR_OBJECT_HANDLE_INVALID;
    }
    if (value != NULL) {
	nickname = PORT_ZAlloc(len+1);
	if (nickname == NULL) {
	    return CKR_HOST_MEMORY;
	}
	PORT_Memcpy(nickname,value,len);
	nickname[len] = 0;
    }
    rv = nsslowkey_UpdateNickname(to->obj.slot->keyDB, privKey, &to->dbKey, 
					nickname, to->obj.slot->password);
    if (nickname) PORT_Free(nickname);
    if (rv != SECSuccess) {
	return CKR_DEVICE_ERROR;
    }
    return CKR_OK;
}

static CK_RV
pk11_SetTrustAttribute(PK11TokenObject *to, CK_ATTRIBUTE_TYPE type, 
						void *value, unsigned int len)
{
    unsigned int flags;
    CK_TRUST  trust;
    NSSLOWCERTCertificate *cert;
    NSSLOWCERTCertTrust dbTrust;
    SECStatus rv;

    if (to->obj.slot->certDB == NULL) {
	return CKR_TOKEN_WRITE_PROTECTED;
    }
    if (len != sizeof (CK_TRUST)) {
	return CKR_ATTRIBUTE_VALUE_INVALID;
    }
    trust = *(CK_TRUST *)value;
    flags = pk11_MapTrust(trust, (PRBool) (type == CKA_TRUST_SERVER_AUTH));

    cert = pk11_getCert(to);
    if (cert == NULL) {
	return CKR_OBJECT_HANDLE_INVALID;
    }
    dbTrust = *cert->trust;

    switch (type) {
    case CKA_TRUST_EMAIL_PROTECTION:
	dbTrust.emailFlags = flags |
		(cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS);
	break;
    case CKA_TRUST_CODE_SIGNING:
	dbTrust.objectSigningFlags = flags |
		(cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS);
	break;
    case CKA_TRUST_CLIENT_AUTH:
	dbTrust.sslFlags = flags | (cert->trust->sslFlags & 
				(CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CA));
	break;
    case CKA_TRUST_SERVER_AUTH:
	dbTrust.sslFlags = flags | (cert->trust->sslFlags & 
			(CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CLIENT_CA));
	break;
    default:
	return CKR_ATTRIBUTE_READ_ONLY;
    }

    rv = nsslowcert_ChangeCertTrust(to->obj.slot->certDB,cert,&dbTrust);
    if (rv != SECSuccess) {
	return CKR_DEVICE_ERROR;
    }
    return CKR_OK;
}

static CK_RV
pk11_forceTokenAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type, 
						void *value, unsigned int len)
{
    PK11Attribute *attribute;
    PK11TokenObject *to = pk11_narrowToTokenObject(object);
    CK_RV crv = CKR_ATTRIBUTE_READ_ONLY;

    PORT_Assert(to);
    if (to == NULL) {
	return CKR_DEVICE_ERROR;
    }

    /* if we are just setting it to the value we already have,
     * allow it to happen. Let label setting go through so
     * we have the opportunity to repair any database corruption. */
    attribute=pk11_FindAttribute(object,type);
    if ((type != CKA_LABEL) && (attribute->attrib.ulValueLen == len) &&
	PORT_Memcmp(attribute->attrib.pValue,value,len) == 0) {
	pk11_FreeAttribute(attribute);
	return CKR_OK;
    }

    switch (object->objclass) {
    case CKO_CERTIFICATE:
	/* change NICKNAME, EMAIL,  */
	crv = pk11_SetCertAttribute(to,type,value,len);
	break;
    case CKO_NETSCAPE_CRL:
	/* change URL */
	break;
    case CKO_NETSCAPE_TRUST:
	crv = pk11_SetTrustAttribute(to,type,value,len);
	break;
    case CKO_PRIVATE_KEY:
    case CKO_SECRET_KEY:
	crv = pk11_SetPrivateKeyAttribute(to,type,value,len);
	break;
    }
    pk11_FreeAttribute(attribute);
    return crv;
}
	
/*
 * force an attribute to a spaecif value.
 */
CK_RV
pk11_forceAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type, void *value,
						unsigned int len)
{
    PK11Attribute *attribute;
    void *att_val = NULL;
    PRBool freeData = PR_FALSE;

    if (pk11_isToken(object->handle)) {
	return pk11_forceTokenAttribute(object,type,value,len);
    }
    attribute=pk11_FindAttribute(object,type);
    if (attribute == NULL) return pk11_AddAttributeType(object,type,value,len);


    if (value) {
#ifdef PKCS11_STATIC_ATTRIBUTES
        if (len <= ATTR_SPACE) {
	    att_val = attribute->space;
	} else {
	    att_val = PORT_Alloc(len);
	    freeData = PR_TRUE;
	}
#else
#ifdef PKCS11_REF_COUNT_ATTRIBUTES
	att_val = PORT_Alloc(len);
	freeData = PR_TRUE;
#else
	att_val = PORT_ArenaAlloc(object->arena,len);
#endif /* PKCS11_REF_COUNT_ATTRIBUTES */
#endif /* PKCS11_STATIC_ATTRIBUTES */
	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;
    }
    pk11_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 *
pk11_getString(PK11Object *object,CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;
    char *label = NULL;

    attribute=pk11_FindAttribute(object,type);
    if (attribute == NULL) return NULL;

    if (attribute->attrib.pValue != NULL) {
	label = (char *) PORT_Alloc(attribute->attrib.ulValueLen+1);
	if (label == NULL) {
    	    pk11_FreeAttribute(attribute);
	    return NULL;
	}

	PORT_Memcpy(label,attribute->attrib.pValue,
						attribute->attrib.ulValueLen);
	label[attribute->attrib.ulValueLen] = 0;
    }
    pk11_FreeAttribute(attribute);
    return label;
}

/*
 * decode when a particular attribute may be modified
 * 	PK11_NEVER: This attribute must be set at object creation time and
 *  can never be modified.
 *	PK11_ONCOPY: This attribute may be modified only when you copy the
 *  object.
 *	PK11_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from
 *  CK_FALSE to CK_TRUE.
 *	PK11_ALWAYS: This attribute can always be modified.
 * Some attributes vary their modification type based on the class of the 
 *   object.
 */
PK11ModifyType
pk11_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
{
    /* if we don't know about it, user user defined, always allow modify */
    PK11ModifyType mtype = PK11_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_SUBPRIME:
    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_NETSCAPE_DB:
	mtype = PK11_NEVER;
	break;

    /* ONCOPY */
    case CKA_TOKEN:
    case CKA_PRIVATE:
    case CKA_MODIFIABLE:
	mtype = PK11_ONCOPY;
	break;

    /* SENSITIVE */
    case CKA_SENSITIVE:
    case CKA_EXTRACTABLE:
	mtype = PK11_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 = PK11_ALWAYS;
	break;

    /* DEPENDS ON CLASS */
    case CKA_VALUE:
	mtype = (inClass == CKO_DATA) ? PK11_ALWAYS : PK11_NEVER;
	break;

    case CKA_SUBJECT:
	mtype = (inClass == CKO_CERTIFICATE) ? PK11_NEVER : PK11_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
pk11_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
pk11_Attribute2SecItem(PLArenaPool *arena,SECItem *item,PK11Object *object,
					CK_ATTRIBUTE_TYPE type)
{
    int len;
    PK11Attribute *attribute;

    attribute = pk11_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) {
	pk11_FreeAttribute(attribute);
	return CKR_HOST_MEMORY;
    }
    item->len = len;
    PORT_Memcpy(item->data,attribute->attrib.pValue, len);
    pk11_FreeAttribute(attribute);
    return CKR_OK;
}

CK_RV
pk11_GetULongAttribute(PK11Object *object, CK_ATTRIBUTE_TYPE type,
							 CK_ULONG *longData)
{
    PK11Attribute *attribute;

    attribute = pk11_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;
    pk11_FreeAttribute(attribute);
    return CKR_OK;
}

void
pk11_DeleteAttributeType(PK11Object *object,CK_ATTRIBUTE_TYPE type)
{
    PK11Attribute *attribute;
    attribute = pk11_FindAttribute(object, type);
    if (attribute == NULL) return ;
    pk11_DeleteAttribute(object,attribute);
    pk11_FreeAttribute(attribute);
}

CK_RV
pk11_AddAttributeType(PK11Object *object,CK_ATTRIBUTE_TYPE type,void *valPtr,
							CK_ULONG length)
{
    PK11Attribute *attribute;
    attribute = pk11_NewAttribute(object,type,valPtr,length);
    if (attribute == NULL) { return CKR_HOST_MEMORY; }
    pk11_AddAttribute(object,attribute);
    return CKR_OK;
}

/*
 * ******************** Object Utilities *******************************
 */

static SECStatus
pk11_deleteTokenKeyByHandle(PK11Slot *slot, CK_OBJECT_HANDLE handle)
{
   SECItem *item;
   PRBool rem;

   item = (SECItem *)PL_HashTableLookup(slot->tokenHashTable, (void *)handle);
   rem = PL_HashTableRemove(slot->tokenHashTable,(void *)handle) ;
   if (rem && item) {
	SECITEM_FreeItem(item,PR_TRUE);
   }
   return rem ? SECSuccess : SECFailure;
}

static SECStatus
pk11_addTokenKeyByHandle(PK11Slot *slot, CK_OBJECT_HANDLE handle, SECItem *key)
{
    PLHashEntry *entry;
    SECItem *item;

    item = SECITEM_DupItem(key);
    if (item == NULL) {
	return SECFailure;
    }
    entry = PL_HashTableAdd(slot->tokenHashTable,(void *)handle,item);
    if (entry == NULL) {
	SECITEM_FreeItem(item,PR_TRUE);
	return SECFailure;
    }
    return SECSuccess;
}

static SECItem *
pk11_lookupTokenKeyByHandle(PK11Slot *slot, CK_OBJECT_HANDLE handle)
{
    return (SECItem *)PL_HashTableLookup(slot->tokenHashTable, (void *)handle);
}

/*
 * use the refLock. This operations should be very rare, so the added
 * contention on the ref lock should be lower than the overhead of adding
 * a new lock. We use separate functions for this just in case I'm wrong.
 */
static void
pk11_tokenKeyLock(PK11Slot *slot) {
    PK11_USE_THREADS(PZ_Lock(slot->objectLock);)
}

static void
pk11_tokenKeyUnlock(PK11Slot *slot) {
    PK11_USE_THREADS(PZ_Unlock(slot->objectLock);)
}


/* allocation hooks that allow us to recycle old object structures */
static PK11ObjectFreeList sessionObjectList = { NULL, NULL, 0 };
static PK11ObjectFreeList tokenObjectList = { NULL, NULL, 0 };

PK11Object *
pk11_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace, 
     PK11ObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject)
{
    PK11Object *object;
    int size = 0;

    if (!optimizeSpace) {
	if (list->lock == NULL) {
	    list->lock = PZ_NewLock(nssILockObject);
	}

	PK11_USE_THREADS(PZ_Lock(list->lock));
	object = list->head;
	if (object) {
	    list->head = object->next;
	    list->count--;
	}    	
	PK11_USE_THREADS(PZ_Unlock(list->lock));
	if (object) {
	    object->next = object->prev = NULL;
            *hasLocks = PR_TRUE;
	    return object;
	}
    }
    size = isSessionObject ? sizeof(PK11SessionObject) 
		+ hashSize *sizeof(PK11Attribute *) : sizeof(PK11TokenObject);

    object  = (PK11Object*)PORT_ZAlloc(size);
    if (isSessionObject) {
	((PK11SessionObject *)object)->hashSize = hashSize;
    }
    *hasLocks = PR_FALSE;
    return object;
}

static void
pk11_PutObjectToList(PK11Object *object, PK11ObjectFreeList *list,
						PRBool isSessionObject) {

    /* the code below is equivalent to :
     *     optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE;
     * just faster.
     */
    PRBool optimizeSpace = isSessionObject && 
				((PK11SessionObject *)object)->optimizeSpace; 
    if (!optimizeSpace && (list->count < MAX_OBJECT_LIST_SIZE)) {
	if (list->lock == NULL) {
	    list->lock = PZ_NewLock(nssILockObject);
	}
	PK11_USE_THREADS(PZ_Lock(list->lock));
	object->next = list->head;
	list->head = object;
	list->count++;
	PK11_USE_THREADS(PZ_Unlock(list->lock));
	return;
    }
    if (isSessionObject) {
	PK11SessionObject *so = (PK11SessionObject *)object;
	PK11_USE_THREADS(PZ_DestroyLock(so->attributeLock);)
	so->attributeLock = NULL;
    }
    PK11_USE_THREADS(PZ_DestroyLock(object->refLock);)
    object->refLock = NULL;
    PORT_Free(object);
}

static PK11Object *
pk11_freeObjectData(PK11Object *object) {
   PK11Object *next = object->next;

   PORT_Free(object);
   return next;
}
   
static void
pk11_CleanupFreeList(PK11ObjectFreeList *list, PRBool isSessionList)
{
    PK11Object *object;

    if (!list->lock) {
	return;
    }
    PK11_USE_THREADS(PZ_Lock(list->lock));
    for (object= list->head; object != NULL; 
					object = pk11_freeObjectData(object)) {
#ifdef PKCS11_USE_THREADS
	PZ_DestroyLock(object->refLock);
	if (isSessionList) {
	    PZ_DestroyLock(((PK11SessionObject *)object)->attributeLock);
	}
#endif
    }
    list->count = 0;
    list->head = NULL;
    PK11_USE_THREADS(PZ_Unlock(list->lock));
    PK11_USE_THREADS(PZ_DestroyLock(list->lock));
    list->lock = NULL;
}

void
pk11_CleanupFreeLists(void)
{
    pk11_CleanupFreeList(&sessionObjectList, PR_TRUE);
    pk11_CleanupFreeList(&tokenObjectList, PR_FALSE);
}


/*
 * Create a new object
 */
PK11Object *
pk11_NewObject(PK11Slot *slot)
{
    PK11Object *object;
    PK11SessionObject *sessObject;
    PRBool hasLocks = PR_FALSE;
    unsigned int i;
    unsigned int hashSize = 0;

    hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE :
				TIME_ATTRIBUTE_HASH_SIZE;

#ifdef PKCS11_STATIC_ATTRIBUTES
    object = pk11_GetObjectFromList(&hasLocks, slot->optimizeSpace,
				&sessionObjectList,  hashSize, PR_TRUE);
    if (object == NULL) {
	return NULL;
    }
    sessObject = (PK11SessionObject *)object;
    sessObject->nextAttr = 0;

    for (i=0; i < MAX_OBJS_ATTRS; i++) {
	sessObject->attrList[i].attrib.pValue = NULL;
	sessObject->attrList[i].freeData = PR_FALSE;
    }
#else
    PRArenaPool *arena;

    arena = PORT_NewArena(2048);
    if (arena == NULL) return NULL;

    object = (PK11Object*)PORT_ArenaAlloc(arena,sizeof(PK11SessionObject)
		+hashSize * sizeof(PK11Attribute *));
    if (object == NULL) {
	PORT_FreeArena(arena,PR_FALSE);
	return NULL;
    }
    object->arena = arena;

    sessObject = (PK11SessionObject *)object;
    sessObject->hashSize = hashSize;
#endif
    sessObject->optimizeSpace = slot->optimizeSpace;

    object->handle = 0;
    object->next = object->prev = NULL;
    object->slot = slot;
    
    object->refCount = 1;
    sessObject->sessionList.next = NULL;
    sessObject->sessionList.prev = NULL;
    sessObject->sessionList.parent = object;
    sessObject->session = NULL;
    sessObject->wasDerived = PR_FALSE;
#ifdef PKCS11_USE_THREADS
    if (!hasLocks) object->refLock = PZ_NewLock(nssILockRefLock);
    if (object->refLock == NULL) {
#ifdef PKCS11_STATIC_ATTRIBUTES
	PORT_Free(object);
#else
	PORT_FreeArena(arena,PR_FALSE);
#endif
	return NULL;
    }
    if (!hasLocks) sessObject->attributeLock = PZ_NewLock(nssILockAttribute);
    if (sessObject->attributeLock == NULL) {
	PK11_USE_THREADS(PZ_DestroyLock(object->refLock);)
#ifdef PKCS11_STATIC_ATTRIBUTES
	PORT_Free(object);
#else
	PORT_FreeArena(arena,PR_FALSE);
#endif
	return NULL;
    }
#else
    sessObject->attributeLock = NULL;
    object->refLock = NULL;
#endif
    for (i=0; i < sessObject->hashSize; i++) {
	sessObject->head[i] = NULL;
    }
    object->objectInfo = NULL;
    object->infoFree = NULL;
    return object;
}

static CK_RV
pk11_DestroySessionObjectData(PK11SessionObject *so)
{
#if defined(PKCS11_STATIC_ATTRIBUTES) || defined(PKCS11_REF_COUNT_ATTRIBUTES)
	int i;
#endif

#ifdef PKCS11_STATIC_ATTRIBUTES
	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;
	    }
	}
#endif

#ifdef PKCS11_REF_COUNT_ATTRIBUTES
	/* clean out the attributes */
	/* since no one is referencing us, it's safe to walk the chain
	 * without a lock */
	for (i=0; i < so->hashSize; i++) {
	    PK11Attribute *ap,*next;
	    for (ap = so->head[i]; ap != NULL; ap = next) {
		next = ap->next;
		/* paranoia */
		ap->next = ap->prev = NULL;
		pk11_FreeAttribute(ap);
	    }
	    so->head[i] = NULL;
	}
#endif
/*	PK11_USE_THREADS(PZ_DestroyLock(so->attributeLock));*/
	return CKR_OK;
}

/*
 * free all the data associated with an object. Object reference count must
 * be 'zero'.
 */
static CK_RV
pk11_DestroyObject(PK11Object *object)
{
    CK_RV crv = CKR_OK;
    PK11SessionObject *so = pk11_narrowToSessionObject(object);
    PK11TokenObject *to = pk11_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) {
	pk11_DestroySessionObjectData(so);
    }
    if (object->objectInfo) {
	(*object->infoFree)(object->objectInfo);
	object->objectInfo = NULL;
	object->infoFree = NULL;
    }
#ifdef PKCS11_STATIC_ATTRIBUTES
    if (so) {
	pk11_PutObjectToList(object,&sessionObjectList,PR_TRUE);
    } else {
	pk11_PutObjectToList(object,&tokenObjectList,PR_FALSE);
    }
#else
    if (object->refLock) {
        PK11_USE_THREADS(PZ_DestroyLock(object->refLock);)
    }
    arena = object->arena;
    PORT_FreeArena(arena,PR_FALSE);
#endif
    return crv;
}

void
pk11_ReferenceObject(PK11Object *object)
{
    PK11_USE_THREADS(PZ_Lock(object->refLock);)
    object->refCount++;
    PK11_USE_THREADS(PZ_Unlock(object->refLock);)
}

static PK11Object *
pk11_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, PK11Slot *slot)
{
    PK11Object *object;
    PRUint32 index = pk11_hash(handle, slot->tokObjHashSize);

    if (pk11_isToken(handle)) {
	return pk11_NewTokenObject(slot, NULL, handle);
    }

    PK11_USE_THREADS(PZ_Lock(slot->objectLock);)
    pk11queue_find2(object, handle, index, slot->tokObjects);
    if (object) {
	pk11_ReferenceObject(object);
    }
    PK11_USE_THREADS(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.
 */
PK11Object *
pk11_ObjectFromHandle(CK_OBJECT_HANDLE handle, PK11Session *session)
{
    PK11Slot *slot = pk11_SlotFromSession(session);

    return pk11_ObjectFromHandleOnSlot(handle,slot);
}


/*
 * release a reference to an object handle
 */
PK11FreeStatus
pk11_FreeObject(PK11Object *object)
{
    PRBool destroy = PR_FALSE;
    CK_RV crv;

    PK11_USE_THREADS(PZ_Lock(object->refLock);)
    if (object->refCount == 1) destroy = PR_TRUE;
    object->refCount--;
    PK11_USE_THREADS(PZ_Unlock(object->refLock);)

    if (destroy) {
	crv = pk11_DestroyObject(object);
	if (crv != CKR_OK) {
	   return PK11_DestroyFailure;
	} 
	return PK11_Destroyed;
    }
    return PK11_Busy;
}
 
/*
 * add an object to a slot and session queue. These two functions
 * adopt the object.
 */
void
pk11_AddSlotObject(PK11Slot *slot, PK11Object *object)
{
    PRUint32 index = pk11_hash(object->handle, slot->tokObjHashSize);
    pk11queue_init_element(object);
    PK11_USE_THREADS(PZ_Lock(slot->objectLock);)
    pk11queue_add2(object, object->handle, index, slot->tokObjects);
    PK11_USE_THREADS(PZ_Unlock(slot->objectLock);)
}

void
pk11_AddObject(PK11Session *session, PK11Object *object)
{
    PK11Slot *slot = pk11_SlotFromSession(session);
    PK11SessionObject *so = pk11_narrowToSessionObject(object);

    if (so) {
	PK11_USE_THREADS(PZ_Lock(session->objectLock);)
	pk11queue_add(&so->sessionList,0,session->objects,0);
	so->session = session;
	PK11_USE_THREADS(PZ_Unlock(session->objectLock);)
    }
    pk11_AddSlotObject(slot,object);
    pk11_ReferenceObject(object);
} 

/*
 * add an object to a slot andsession queue
 */
CK_RV
pk11_DeleteObject(PK11Session *session, PK11Object *object)
{
    PK11Slot *slot = pk11_SlotFromSession(session);
    PK11SessionObject *so = pk11_narrowToSessionObject(object);
    PK11TokenObject *to = pk11_narrowToTokenObject(object);
    CK_RV crv = CKR_OK;
    SECStatus rv;
    NSSLOWCERTCertificate *cert;
    NSSLOWCERTCertTrust tmptrust;
    PRBool isKrl;
    PRUint32 index = pk11_hash(object->handle, slot->tokObjHashSize);

  /* Handle Token case */
    if (so && so->session) {
	PK11Session *session = so->session;
	PK11_USE_THREADS(PZ_Lock(session->objectLock);)
	pk11queue_delete(&so->sessionList,0,session->objects,0);
	PK11_USE_THREADS(PZ_Unlock(session->objectLock);)
	PK11_USE_THREADS(PZ_Lock(slot->objectLock);)
	pk11queue_delete2(object, object->handle, index, slot->tokObjects);
	PK11_USE_THREADS(PZ_Unlock(slot->objectLock);)
	pk11queue_clear_deleted_element(object);
	pk11_FreeObject(object); /* reduce it's reference count */
    } else {
	PORT_Assert(to);
	/* remove the objects from the real data base */
	switch (object->handle & PK11_TOKEN_TYPE_MASK) {
	case PK11_TOKEN_TYPE_PRIV:
	case PK11_TOKEN_TYPE_KEY:
	    /* KEYID is the public KEY for DSA and DH, and the MODULUS for
	     *  RSA */
	    PORT_Assert(slot->keyDB);
	    rv = nsslowkey_DeleteKey(slot->keyDB, &to->dbKey);
	    if (rv != SECSuccess) crv= CKR_DEVICE_ERROR;
	    break;
	case PK11_TOKEN_TYPE_PUB:
	    break; /* public keys only exist at the behest of the priv key */
	case PK11_TOKEN_TYPE_CERT:
	    cert = nsslowcert_FindCertByKey(slot->certDB,&to->dbKey);
	    if (cert == NULL) {
		crv = CKR_DEVICE_ERROR;
		break;
	    }
	    rv = nsslowcert_DeletePermCertificate(cert);
	    if (rv != SECSuccess) crv = CKR_DEVICE_ERROR;
	    nsslowcert_DestroyCertificate(cert);
	    break;
	case PK11_TOKEN_TYPE_CRL:
	    isKrl = (PRBool) (object->handle == PK11_TOKEN_KRL_HANDLE);
	    rv = nsslowcert_DeletePermCRL(slot->certDB,&to->dbKey,isKrl);
	    if (rv == SECFailure) crv = CKR_DEVICE_ERROR;
	    break;
	case PK11_TOKEN_TYPE_TRUST:
	    cert = nsslowcert_FindCertByKey(slot->certDB,&to->dbKey);
	    if (cert == NULL) {
		crv = CKR_DEVICE_ERROR;
		break;
	    }
	    tmptrust = *cert->trust;
	    tmptrust.sslFlags &= CERTDB_PRESERVE_TRUST_BITS;
	    tmptrust.emailFlags &= CERTDB_PRESERVE_TRUST_BITS;
	    tmptrust.objectSigningFlags &= CERTDB_PRESERVE_TRUST_BITS;
	    tmptrust.sslFlags |= CERTDB_TRUSTED_UNKNOWN;
	    tmptrust.emailFlags |= CERTDB_TRUSTED_UNKNOWN;
	    tmptrust.objectSigningFlags |= CERTDB_TRUSTED_UNKNOWN;
	    rv = nsslowcert_ChangeCertTrust(slot->certDB,cert,&tmptrust);
	    if (rv != SECSuccess) crv = CKR_DEVICE_ERROR;
	    nsslowcert_DestroyCertificate(cert);
	    break;
	default:
	    break;
	}
	pk11_tokenKeyLock(object->slot);
	pk11_deleteTokenKeyByHandle(object->slot,object->handle);
	pk11_tokenKeyUnlock(object->slot);
    } 
    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
pk11_CopyObject(PK11Object *destObject,PK11Object *srcObject)
{
    PK11Attribute *attribute;
    PK11SessionObject *src_so = pk11_narrowToSessionObject(srcObject);
    unsigned int i;

    if (src_so == NULL) {
	return CKR_DEVICE_ERROR; /* can't copy token objects yet */
    }

    PK11_USE_THREADS(PZ_Lock(src_so->attributeLock);)
    for(i=0; i < src_so->hashSize; i++) {
	attribute = src_so->head[i];
	do {
	    if (attribute) {
		if (!pk11_hasAttribute(destObject,attribute->handle)) {
		    /* we need to copy the attribute since each attribute
		     * only has one set of link list pointers */
		    PK11Attribute *newAttribute = pk11_NewAttribute(
			  destObject,pk11_attr_expand(&attribute->attrib));
		    if (newAttribute == NULL) {
			PK11_USE_THREADS(PZ_Unlock(src_so->attributeLock);)
			return CKR_HOST_MEMORY;
		    }
		    pk11_AddAttribute(destObject,newAttribute);
		}
		attribute=attribute->next;
	    }
	} while (attribute != NULL);
    }
    PK11_USE_THREADS(PZ_Unlock(src_so->attributeLock);)
    return CKR_OK;
}

/*
 * ******************** Search Utilities *******************************
 */

/* add an object to a search list */
CK_RV
AddToList(PK11ObjectListElement **list,PK11Object *object)
{
     PK11ObjectListElement *newElem = 
	(PK11ObjectListElement *)PORT_Alloc(sizeof(PK11ObjectListElement));

     if (newElem == NULL) return CKR_HOST_MEMORY;

     newElem->next = *list;
     newElem->object = object;
     pk11_ReferenceObject(object);

    *list = newElem;
    return CKR_OK;
}


/* return true if the object matches the template */
PRBool
pk11_objectMatch(PK11Object *object,CK_ATTRIBUTE_PTR theTemplate,int count)
{
    int i;

    for (i=0; i < count; i++) {
	PK11Attribute *attribute = pk11_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) {
        	pk11_FreeAttribute(attribute);
		continue;
	    }
	}
        pk11_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
pk11_searchObjectList(PK11SearchResults *search,PK11Object **head, 
	unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate, 
						int count, PRBool isLoggedIn)
{
    unsigned int i;
    PK11Object *object;
    CK_RV crv = CKR_OK;

    for(i=0; i < size; i++) {
        /* We need to hold the lock to copy a consistant version of
         * the linked list. */
        PK11_USE_THREADS(PZ_Lock(lock);)
	for (object = head[i]; object != NULL; object= object->next) {
	    if (pk11_objectMatch(object,theTemplate,count)) {
		/* don't return objects that aren't yet visible */
		if ((!isLoggedIn) && pk11_isTrue(object,CKA_PRIVATE)) continue;
		pk11_addHandle(search,object->handle);
	    }
	}
        PK11_USE_THREADS(PZ_Unlock(lock);)
    }
    return crv;
}

/*
 * free a single list element. Return the Next object in the list.
 */
PK11ObjectListElement *
pk11_FreeObjectListElement(PK11ObjectListElement *objectList)
{
    PK11ObjectListElement *ol = objectList->next;

    pk11_FreeObject(objectList->object);
    PORT_Free(objectList);
    return ol;
}

/* free an entire object list */
void
pk11_FreeObjectList(PK11ObjectListElement *objectList)
{
    PK11ObjectListElement *ol;

    for (ol= objectList; ol != NULL; ol = pk11_FreeObjectListElement(ol)) {}
}

/*
 * free a search structure
 */
void
pk11_FreeSearch(PK11SearchResults *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
pk11_update_state(PK11Slot *slot,PK11Session *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
pk11_update_all_states(PK11Slot *slot)
{
    unsigned int i;
    PK11Session *session;

    for (i=0; i < slot->sessHashSize; i++) {
	PK11_USE_THREADS(PZ_Lock(PK11_SESSION_LOCK(slot,i));)
	for (session = slot->head[i]; session; session = session->next) {
	    pk11_update_state(slot,session);
	}
	PK11_USE_THREADS(PZ_Unlock(PK11_SESSION_LOCK(slot,i));)
    }
}

/*
 * context are cipher and digest contexts that are associated with a session
 */
void
pk11_FreeContext(PK11SessionContext *context)
{
    if (context->cipherInfo) {
	(*context->destroy)(context->cipherInfo,PR_TRUE);
    }
    if (context->hashInfo) {
	(*context->hashdestroy)(context->hashInfo,PR_TRUE);
    }
    if (context->key) {
	pk11_FreeObject(context->key);
	context->key = NULL;
    }
    PORT_Free(context);
}

/*
 * create a new nession. NOTE: The session handle is not set, and the
 * session is not added to the slot's session queue.
 */
PK11Session *
pk11_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication,
							     CK_FLAGS flags)
{
    PK11Session *session;
    PK11Slot *slot = pk11_SlotFromID(slotID);

    if (slot == NULL) return NULL;

    session = (PK11Session*)PORT_Alloc(sizeof(PK11Session));
    if (session == NULL) return NULL;

    session->next = session->prev = NULL;
    session->refCount = 1;
    session->enc_context = NULL;
    session->hash_context = NULL;
    session->sign_context = NULL;
    session->search = NULL;
    session->objectIDCount = 1;
#ifdef PKCS11_USE_THREADS
    session->objectLock = PZ_NewLock(nssILockObject);
    if (session->objectLock == NULL) {
	PORT_Free(session);
	return NULL;
    }
#else
    session->objectLock = NULL;
#endif
    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;
    pk11_update_state(slot,session);
    return session;
}


/* free all the data associated with a session. */
static void
pk11_DestroySession(PK11Session *session)
{
    PK11ObjectList *op,*next;
    PORT_Assert(session->refCount == 0);

    /* 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;
	pk11_DeleteObject(session,op->parent);
    }
    PK11_USE_THREADS(PZ_DestroyLock(session->objectLock);)
    if (session->enc_context) {
	pk11_FreeContext(session->enc_context);
    }
    if (session->hash_context) {
	pk11_FreeContext(session->hash_context);
    }
    if (session->sign_context) {
	pk11_FreeContext(session->sign_context);
    }
    if (session->search) {
	pk11_FreeSearch(session->search);
    }
    PORT_Free(session);
}


/*
 * look up a session structure from a session handle
 * generate a reference to it.
 */
PK11Session *
pk11_SessionFromHandle(CK_SESSION_HANDLE handle)
{
    PK11Slot	*slot = pk11_SlotFromSessionHandle(handle);
    PK11Session *session;

    PK11_USE_THREADS(PZ_Lock(PK11_SESSION_LOCK(slot,handle));)
    pk11queue_find(session,handle,slot->head,slot->sessHashSize);
    if (session) session->refCount++;
    PK11_USE_THREADS(PZ_Unlock(PK11_SESSION_LOCK(slot,handle));)

    return (session);
}

/*
 * release a reference to a session handle
 */
void
pk11_FreeSession(PK11Session *session)
{
    PRBool destroy = PR_FALSE;
    PK11_USE_THREADS(PK11Slot *slot = pk11_SlotFromSession(session);)

    PK11_USE_THREADS(PZ_Lock(PK11_SESSION_LOCK(slot,session->handle));)
    if (session->refCount == 1) destroy = PR_TRUE;
    session->refCount--;
    PK11_USE_THREADS(PZ_Unlock(PK11_SESSION_LOCK(slot,session->handle));)

    if (destroy) pk11_DestroySession(session);
}
/*
 * handle Token Object stuff
 */
static void
pk11_XORHash(unsigned char *key, unsigned char *dbkey, int len)
{
   int i;

   PORT_Memset(key, 0, 4);

   for (i=0; i < len-4; i += 4) {
	key[0] ^= dbkey[i];
	key[1] ^= dbkey[i+1];
	key[2] ^= dbkey[i+2];
	key[3] ^= dbkey[i+3];
   }
}

/* Make a token handle for an object and record it so we can find it again */
CK_OBJECT_HANDLE
pk11_mkHandle(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE class)
{
    unsigned char hashBuf[4];
    CK_OBJECT_HANDLE handle;
    SECItem *key;

    handle = class;
    /* there is only one KRL, use a fixed handle for it */
    if (handle != PK11_TOKEN_KRL_HANDLE) {
	pk11_XORHash(hashBuf,dbKey->data,dbKey->len);
	handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) | 
					(hashBuf[2] << 8)  | hashBuf[3];
	handle = PK11_TOKEN_MAGIC | class | 
			(handle & ~(PK11_TOKEN_TYPE_MASK|PK11_TOKEN_MASK));
	/* we have a CRL who's handle has randomly matched the reserved KRL
	 * handle, increment it */
	if (handle == PK11_TOKEN_KRL_HANDLE) {
	    handle++;
	}
    }

    pk11_tokenKeyLock(slot);
    while ((key = pk11_lookupTokenKeyByHandle(slot,handle)) != NULL) {
	if (SECITEM_ItemsAreEqual(key,dbKey)) {
    	   pk11_tokenKeyUnlock(slot);
	   return handle;
	}
	handle++;
    }
    pk11_addTokenKeyByHandle(slot,handle,dbKey);
    pk11_tokenKeyUnlock(slot);
    return handle;
}

PRBool
pk11_poisonHandle(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE class)
{
    unsigned char hashBuf[4];
    CK_OBJECT_HANDLE handle;
    SECItem *key;

    handle = class;
    /* there is only one KRL, use a fixed handle for it */
    if (handle != PK11_TOKEN_KRL_HANDLE) {
	pk11_XORHash(hashBuf,dbKey->data,dbKey->len);
	handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) | 
					(hashBuf[2] << 8)  | hashBuf[3];
	handle = PK11_TOKEN_MAGIC | class | 
			(handle & ~(PK11_TOKEN_TYPE_MASK|PK11_TOKEN_MASK));
	/* we have a CRL who's handle has randomly matched the reserved KRL
	 * handle, increment it */
	if (handle == PK11_TOKEN_KRL_HANDLE) {
	    handle++;
	}
    }
    pk11_tokenKeyLock(slot);
    while ((key = pk11_lookupTokenKeyByHandle(slot,handle)) != NULL) {
	if (SECITEM_ItemsAreEqual(key,dbKey)) {
	   key->data[0] ^= 0x80;
    	   pk11_tokenKeyUnlock(slot);
	   return PR_TRUE;
	}
	handle++;
    }
    pk11_tokenKeyUnlock(slot);
    return PR_FALSE;
}

void
pk11_addHandle(PK11SearchResults *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 const CK_OBJECT_HANDLE pk11_classArray[] = {
    0, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY, CKO_SECRET_KEY,
    CKO_NETSCAPE_TRUST, CKO_NETSCAPE_CRL, CKO_NETSCAPE_SMIME,
     CKO_CERTIFICATE };

#define handleToClass(handle) \
    pk11_classArray[((handle & PK11_TOKEN_TYPE_MASK))>>28]

PK11Object *
pk11_NewTokenObject(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle)
{
    PK11Object *object = NULL;
    PK11TokenObject *tokObject = NULL;
    PRBool hasLocks = PR_FALSE;
    SECStatus rv;

#ifdef PKCS11_STATIC_ATTRIBUTES
    object = pk11_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList,  0,
							PR_FALSE);
    if (object == NULL) {
	return NULL;
    }
#else
    PRArenaPool *arena;

    arena = PORT_NewArena(2048);
    if (arena == NULL) return NULL;

    object = (PK11Object*)PORT_ArenaZAlloc(arena,sizeof(PK11TokenObject));
    if (object == NULL) {
	PORT_FreeArena(arena,PR_FALSE);
	return NULL;
    }
    object->arena = arena;
#endif
    tokObject = (PK11TokenObject *) object;

    object->objclass = handleToClass(handle);
    object->handle = handle;
    object->slot = slot;
    object->objectInfo = NULL;
    object->infoFree = NULL;
    if (dbKey == NULL) {
	pk11_tokenKeyLock(slot);
	dbKey = pk11_lookupTokenKeyByHandle(slot,handle);
	if (dbKey == NULL) {
    	    pk11_tokenKeyUnlock(slot);
	    goto loser;
	}
	rv = SECITEM_CopyItem(NULL,&tokObject->dbKey,dbKey);
	pk11_tokenKeyUnlock(slot);
    } else {
	rv = SECITEM_CopyItem(NULL,&tokObject->dbKey,dbKey);
    }
    if (rv != SECSuccess) {
	goto loser;
    }
#ifdef PKCS11_USE_THREADS
    if (!hasLocks) {
	object->refLock = PZ_NewLock(nssILockRefLock);
    }
    if (object->refLock == NULL) {
	goto loser;
    }
#endif
    object->refCount = 1;

    return object;
loser:
    if (object) {
	(void) pk11_DestroyObject(object);
    }
    return NULL;

}

PRBool
pk11_tokenMatch(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE class,
					CK_ATTRIBUTE_PTR theTemplate,int count)
{
    PK11Object *object;
    PRBool ret;

    object = pk11_NewTokenObject(slot,dbKey,PK11_TOKEN_MASK|class);
    if (object == NULL) {
	return PR_FALSE;
    }

    ret = pk11_objectMatch(object,theTemplate,count);
    pk11_FreeObject(object);
    return ret;
}

PK11TokenObject *
pk11_convertSessionToToken(PK11Object *obj)
{
    SECItem *key;
    PK11SessionObject *so = (PK11SessionObject *)obj;
    PK11TokenObject *to = pk11_narrowToTokenObject(obj);
    SECStatus rv;

    pk11_DestroySessionObjectData(so);
    PK11_USE_THREADS(PZ_DestroyLock(so->attributeLock));
    if (to == NULL) {
	return NULL;
    }
    pk11_tokenKeyLock(so->obj.slot);
    key = pk11_lookupTokenKeyByHandle(so->obj.slot,so->obj.handle);
    if (key == NULL) {
    	pk11_tokenKeyUnlock(so->obj.slot);
	return NULL;
    }
    rv = SECITEM_CopyItem(NULL,&to->dbKey,key);
    pk11_tokenKeyUnlock(so->obj.slot);
    if (rv == SECFailure) {
	return NULL;
    }

    return to;

}

PK11SessionObject *
pk11_narrowToSessionObject(PK11Object *obj)
{
    return !pk11_isToken(obj->handle) ? (PK11SessionObject *)obj : NULL;
}

PK11TokenObject *
pk11_narrowToTokenObject(PK11Object *obj)
{
    return pk11_isToken(obj->handle) ? (PK11TokenObject *)obj : NULL;
}

