Blob Blame History Raw
diff -up ./src/coolkey/coolkey.cpp.coolkey-update ./src/coolkey/coolkey.cpp
--- ./src/coolkey/coolkey.cpp.coolkey-update	2016-01-19 17:48:49.064182244 -0800
+++ ./src/coolkey/coolkey.cpp	2016-01-19 17:50:46.711214704 -0800
@@ -34,7 +34,6 @@
 #include "cky_base.h"
 #include "params.h"
 
-#define NULL 0
 
 /* static module data --------------------------------  */
 
@@ -70,11 +69,19 @@ typedef struct {
 /**********************************************************************
  ************************** MECHANISM TABLE ***************************
  **********************************************************************/
-static MechInfo
-mechanismList[] = {
+
+static const MechInfo
+rsaMechanismList[] = {
     {CKM_RSA_PKCS, { 1024, 4096, CKF_HW | CKF_SIGN | CKF_DECRYPT } }
 };
-static unsigned int numMechanisms = sizeof(mechanismList)/sizeof(MechInfo);
+
+static const MechInfo
+ecMechanismList[] = {
+    {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDSA_SHA1, {256, 521, CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} }
+};
+
+unsigned int numRSAMechanisms = sizeof(rsaMechanismList)/sizeof(MechInfo);
+unsigned int numECMechanisms = sizeof(ecMechanismList)/sizeof(MechInfo);
 
 /* ------------------------------------------------------------ */
 
@@ -166,7 +173,6 @@ NOTSUPPORTED(C_GenerateKey, (CK_SESSION_
 NOTSUPPORTED(C_GenerateKeyPair, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_ATTRIBUTE_PTR,CK_ULONG,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR,CK_OBJECT_HANDLE_PTR))
 NOTSUPPORTED(C_WrapKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_OBJECT_HANDLE,CK_BYTE_PTR,CK_ULONG_PTR))
 NOTSUPPORTED(C_UnwrapKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_BYTE_PTR,CK_ULONG,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR))
-NOTSUPPORTED(C_DeriveKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR))
 NOTSUPPORTED(C_GetFunctionStatus, (CK_SESSION_HANDLE))
 NOTSUPPORTED(C_CancelFunction, (CK_SESSION_HANDLE))
 
@@ -200,6 +206,10 @@ SUPPORTED(C_SeedRandom, seedRandom,
 SUPPORTED(C_GenerateRandom, generateRandom,
   (CK_SESSION_HANDLE hSession ,CK_BYTE_PTR data,CK_ULONG dataLen),
   (hSession, data, dataLen))
+SUPPORTED(C_DeriveKey,derive,
+  (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+  CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey ),
+  (hSession, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey))
 
 /* non-specialized functions supported with the slot directly */
 
@@ -249,7 +259,7 @@ C_Initialize(CK_VOID_PTR pInitArgs)
 	log = new DummyLog();
     }
     log->log("Initialize called, hello %d\n", 5);
-    CKY_SetName("coolkey");
+    CKY_SetName((char *) "coolkey");
     slotList = new SlotList(log);
     initialized = TRUE;
     return CKR_OK;
@@ -347,6 +357,11 @@ CK_RV
 C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList,
     CK_ULONG_PTR pulCount)
 {
+
+    const MechInfo *mechanismList = NULL;
+    unsigned int numMechanisms = 0;
+
+
     if( ! initialized ) {
         return CKR_CRYPTOKI_NOT_INITIALIZED;
     }
@@ -359,11 +374,21 @@ C_GetMechanismList(CK_SLOT_ID slotID, CK
         }
 
         slotList->validateSlotID(slotID);
-        if( ! slotList->getSlot(
-            slotIDToIndex(slotID))->isTokenPresent() ) {
+
+        Slot *slot = slotList->getSlot(slotIDToIndex(slotID));
+
+        if( ! slot ||  ! slot->isTokenPresent() ) {
             return CKR_TOKEN_NOT_PRESENT;
         }
 
+        if ( slot->getIsECC()) {
+            mechanismList = ecMechanismList;
+            numMechanisms = numECMechanisms;
+        } else {
+            mechanismList = rsaMechanismList;
+            numMechanisms = numRSAMechanisms;
+        }
+  
         if( pMechanismList != NULL ) {
             if( *pulCount < numMechanisms ) {
                 rv = CKR_BUFFER_TOO_SMALL;
@@ -390,19 +415,36 @@ CK_RV
 C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
     CK_MECHANISM_INFO_PTR pInfo)
 {
+    const MechInfo *mechanismList = NULL;
+    unsigned int numMechanisms = 0;
+
     if( ! initialized ) {
         return CKR_CRYPTOKI_NOT_INITIALIZED;
     }
+
+
     try {
         log->log("C_GetMechanismInfo called\n");
         if( pInfo == NULL ) {
             throw PKCS11Exception(CKR_ARGUMENTS_BAD);
         }
         slotList->validateSlotID(slotID);
-        if( ! slotList->getSlot(slotIDToIndex(slotID))->isTokenPresent() ) {
+
+
+        Slot *slot = slotList->getSlot(slotIDToIndex(slotID));
+
+        if( ! slot ||  ! slot->isTokenPresent() ) {
             return CKR_TOKEN_NOT_PRESENT;
         }
 
+        if ( slot->getIsECC()) {
+            mechanismList = ecMechanismList;
+            numMechanisms = numECMechanisms;
+        } else {
+            mechanismList = rsaMechanismList;
+            numMechanisms = numRSAMechanisms;
+        }
+
         for(unsigned int i=0; i < numMechanisms; ++i ) {
             if( mechanismList[i].mech == type ) {
                 *pInfo = mechanismList[i].info;
@@ -557,13 +599,10 @@ C_Login(CK_SESSION_HANDLE hSession, CK_U
     }
     try {
         log->log("C_Login called\n");
-        if( userType != CKU_USER ) {
-            throw PKCS11Exception(CKR_USER_TYPE_INVALID);
-        }
         if( pPin == NULL ) {
             throw PKCS11Exception(CKR_ARGUMENTS_BAD);
         }
-        slotList->login(hSession, pPin, ulPinLen);
+        slotList->login(hSession, userType, pPin, ulPinLen);
         return CKR_OK;
     } catch(PKCS11Exception &e) {
         e.log(log);
diff -up ./src/coolkey/machdep.cpp.coolkey-update ./src/coolkey/machdep.cpp
--- ./src/coolkey/machdep.cpp.coolkey-update	2016-01-19 17:51:35.553813017 -0800
+++ ./src/coolkey/machdep.cpp	2016-01-19 17:51:59.607615196 -0800
@@ -368,6 +368,7 @@ SHMem::initSegment(const char *name, int
 #ifdef FULL_CLEANUP
 	    flock(shmemData->fd, LOCK_UN);
 #endif
+	    free(buf);
 	    delete shmemData;
 	    return NULL;
 	}
diff -up ./src/coolkey/object.cpp.coolkey-update ./src/coolkey/object.cpp
--- ./src/coolkey/object.cpp.coolkey-update	2016-01-19 17:52:36.313313325 -0800
+++ ./src/coolkey/object.cpp	2016-01-19 17:52:50.959192876 -0800
@@ -19,18 +19,80 @@
 
 #include "mypkcs11.h"
 #include "PKCS11Exception.h"
-#include "object.h"
 #include <algorithm>
 #include <string.h>
+#include "object.h"
 
 using std::find_if;
 
+const CKYByte rsaOID[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D, 0x01, 0x01,0x1};
+const CKYByte eccOID[] = {0x2a,0x86,0x48,0xce,0x3d,0x02,0x01};
+
+#ifdef DEBUG
+void dump(const char *label, const CKYBuffer *buf)
+{
+    CKYSize i;
+    CKYSize size = CKYBuffer_Size(buf);
+#define ROW_LENGTH 60
+    char string[ROW_LENGTH+1];
+    char *bp = &string[0];
+    CKYByte c;
+
+    printf("%s size=%d\n", label, (int)size);
+
+    for (i=0; i < size; i++) {
+        if (i && ((i % (ROW_LENGTH)) == 0) ) {
+            *bp = 0;
+            printf(" %s\n",string);
+            bp = &string[0];
+        }
+        c = CKYBuffer_GetChar(buf, i);
+        printf("%02x ",c);
+        *bp++ =  (c < ' ') ? '.' : ((c & 0x80) ? '*' : c);
+    }
+    *bp = 0;
+    for (i= (i % (ROW_LENGTH)); i && (i < ROW_LENGTH); i++) {
+        printf("   ");
+    }
+    printf(" %s\n",string);
+    fflush(stdout);
+}
+
+void dumpData(const char *label, const CKYByte *buf, CKYSize size)
+{
+    CKYSize i;
+#define ROW_LENGTH 16
+    char string[ROW_LENGTH+1];
+    char *bp = &string[0];
+    CKYByte c;
+
+    printf("%s size=%d:\n",label, (int)size);
+
+    for (i=0; i < size; i++) {
+        if (i && ((i % (ROW_LENGTH)) == 0) ) {
+            *bp = 0;
+            printf(" %s\n",string);
+            bp = &string[0];
+        }
+        c = buf[i];
+        printf("%02x ",c);
+        *bp++ =  (c < ' ') ? '.' : ((c & 0x80) ? '*' : c);
+    }
+    *bp = 0;
+    for (i= (i % (ROW_LENGTH)); i && (i < ROW_LENGTH); i++) {
+        printf("   ");
+    }
+    printf(" %s\n",string);
+    fflush(stdout);
+}
+#endif
+
 
 bool AttributeMatch::operator()(const PKCS11Attribute& cmp) 
 {
     return (attr->type == cmp.getType()) &&
-	CKYBuffer_DataIsEqual(cmp.getValue(), 
-			(const CKYByte *)attr->pValue, attr->ulValueLen);
+        CKYBuffer_DataIsEqual(cmp.getValue(), 
+                        (const CKYByte *)attr->pValue, attr->ulValueLen);
 }
 
 class AttributeTypeMatch
@@ -45,16 +107,23 @@ class AttributeTypeMatch
 };
 
 PKCS11Object::PKCS11Object(unsigned long muscleObjID_,CK_OBJECT_HANDLE handle_)
-    : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL)
+    : muscleObjID(muscleObjID_), handle(handle_), label(NULL), keySize(0),
+                   user(CKU_USER), name(NULL), keyType(unknown),
+		   keyRef(PK15_INVALID_KEY_REF)
 { 
     CKYBuffer_InitEmpty(&pubKey);
+    CKYBuffer_InitEmpty(&authId);
+    CKYBuffer_InitEmpty(&pinAuthId);
 }
 
 PKCS11Object::PKCS11Object(unsigned long muscleObjID_, const CKYBuffer *data,
     CK_OBJECT_HANDLE handle_) :  muscleObjID(muscleObjID_), handle(handle_),
-			label(NULL), name(NULL)
+                   label(NULL), keySize(0), user(CKU_USER), name(NULL), 
+		   keyType(unknown), keyRef(PK15_INVALID_KEY_REF) 
 {
     CKYBuffer_InitEmpty(&pubKey);
+    CKYBuffer_InitEmpty(&authId);
+    CKYBuffer_InitEmpty(&pinAuthId);
 
     CKYByte type = CKYBuffer_GetChar(data,0);
     // verify object ID is what we think it is
@@ -63,9 +132,98 @@ PKCS11Object::PKCS11Object(unsigned long
             "PKCS #11 actual object id does not match stated id");
     }
     if (type == 0) {
-	parseOldObject(data);
+        parseOldObject(data);
     } else if (type == 1) {
-	parseNewObject(data);
+        parseNewObject(data);
+    }
+}
+
+SecretKey::SecretKey(unsigned long muscleObjID_, CK_OBJECT_HANDLE handle_, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount)
+     : PKCS11Object(muscleObjID_, handle_)
+{
+    static CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+    static CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+    static CK_BBOOL value = 0x1;
+
+    if ( secretKeyBuffer == NULL)
+        return;
+
+    /* Rifle through the input template */
+
+    CK_ATTRIBUTE_TYPE type;
+    CK_ATTRIBUTE attr;
+    CK_ULONG valueLength = 0;
+
+    for(int i = 0; i <  (int) ulAttributeCount; i++) {
+       attr = pTemplate[i];
+       type =  attr.type;
+
+       if ( type == CKA_VALUE_LEN) {
+           //CK_ULONG ulValueLen = attr.ulValueLen;
+           valueLength = *((CK_ULONG *)attr.pValue);
+       } else {
+
+           CKYBuffer val;
+           CKYBuffer_InitFromData(&val,(const CK_BYTE *) attr.pValue, attr.ulValueLen);
+           setAttribute( type, &val);
+           CKYBuffer_FreeData(&val);
+       }
+    }
+
+    adjustToKeyValueLength( secretKeyBuffer, valueLength ); 
+
+    /* Fall backs. */
+
+    if(!attributeExists(CKA_CLASS))
+        setAttributeULong(CKA_CLASS, objClass);
+
+    if(!attributeExists(CKA_KEY_TYPE))
+        setAttributeULong(CKA_KEY_TYPE, keyType);
+
+    if(!attributeExists(CKA_TOKEN))
+        setAttributeBool(CKA_TOKEN, value);
+      
+    if(!attributeExists(CKA_DERIVE)) 
+        setAttributeBool(CKA_DERIVE, value);
+
+    /* Actual value */
+    setAttribute(CKA_VALUE, secretKeyBuffer);
+
+}
+
+void SecretKey::adjustToKeyValueLength(CKYBuffer * secretKeyBuffer,CK_ULONG valueLength)
+{
+    const CK_LONG MAX_DIFF = 200; /* Put some bounds on this value */
+
+    if ( !secretKeyBuffer ) {
+        return;
+    }
+
+    CKYBuffer scratch;
+    CK_ULONG actual_length = CKYBuffer_Size(secretKeyBuffer);
+
+    CK_LONG diff = 0;
+    diff = (CK_LONG) valueLength - actual_length;
+
+    if ( diff == 0 ) {
+        return;
+    }
+
+    if ( diff > 0 && diff < MAX_DIFF ) { /*check for silly values */
+        /* prepend with zeroes */
+        CKYBuffer_InitFromLen(&scratch, diff);
+        CKYBuffer_AppendCopy(&scratch, secretKeyBuffer);
+
+        CKYBuffer_FreeData(secretKeyBuffer);
+        CKYBuffer_InitFromCopy(secretKeyBuffer, &scratch);
+        CKYBuffer_FreeData(&scratch);
+
+    } else if (diff < 0 ) {
+        /* truncate most significant bytes */
+        CKYBuffer_InitFromData(&scratch, CKYBuffer_Data(secretKeyBuffer)-diff, valueLength);
+        CKYBuffer_FreeData(secretKeyBuffer);
+        CKYBuffer_InitFromCopy(secretKeyBuffer, &scratch);
+        CKYBuffer_FreeData(&scratch);
     }
 }
 
@@ -95,29 +253,29 @@ PKCS11Object::parseOldObject(const CKYBu
         attrib.setType(CKYBuffer_GetLong(data, idx));
         idx += 4;
         unsigned int attrLen = CKYBuffer_GetShort(data, idx);
-		idx += 2;
+                idx += 2;
         if( attrLen > CKYBuffer_Size(data) 
-			|| (idx + attrLen > CKYBuffer_Size(data)) ) {
+                        || (idx + attrLen > CKYBuffer_Size(data)) ) {
             throw PKCS11Exception(CKR_DEVICE_ERROR,
                 "Invalid attribute length %d\n", attrLen);
         }
-	/* these two types are ints, read them back from 
-	 * the card in host order */
-	if ((attrib.getType() == CKA_CLASS) || 
-	    (attrib.getType() == CKA_CERTIFICATE_TYPE) ||
-	    (attrib.getType() == CKA_KEY_TYPE)) {
-	    /* ulongs are 4 bytes on the token, even if they are 8 or
-	     * more in the pkcs11 module */
-	    if (attrLen != 4) {
+        /* these two types are ints, read them back from 
+         * the card in host order */
+        if ((attrib.getType() == CKA_CLASS) || 
+            (attrib.getType() == CKA_CERTIFICATE_TYPE) ||
+            (attrib.getType() == CKA_KEY_TYPE)) {
+            /* ulongs are 4 bytes on the token, even if they are 8 or
+             * more in the pkcs11 module */
+            if (attrLen != 4) {
                 throw PKCS11Exception(CKR_DEVICE_ERROR,
                 "Invalid attribute length %d\n", attrLen);
-	    }
-	    CK_ULONG value = makeLEUInt(data,idx);
+            }
+            CK_ULONG value = makeLEUInt(data,idx);
 
-	    attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG));
-	} else {
-	    attrib.setValue(CKYBuffer_Data(data)+idx, attrLen);
-	}
+            attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG));
+        } else {
+            attrib.setValue(CKYBuffer_Data(data)+idx, attrLen);
+        }
         idx += attrLen;
         attributes.push_back(attrib);
     }
@@ -177,33 +335,33 @@ PKCS11Object::expandAttributes(unsigned
     unsigned long i;
 
     if (!attributeExists(CKA_ID)) {
-	PKCS11Attribute attrib;
-	attrib.setType(CKA_ID);
-	attrib.setValue(&cka_id, 1);
+        PKCS11Attribute attrib;
+        attrib.setType(CKA_ID);
+        attrib.setValue(&cka_id, 1);
         attributes.push_back(attrib);
     }
     /* unpack the class */
     if (!attributeExists(CKA_CLASS)) {
-	PKCS11Attribute attrib;
-	attrib.setType(CKA_CLASS);
-	attrib.setValue((CKYByte *)&objectType, sizeof(CK_ULONG));
+        PKCS11Attribute attrib;
+        attrib.setType(CKA_CLASS);
+        attrib.setValue((CKYByte *)&objectType, sizeof(CK_ULONG));
         attributes.push_back(attrib);
     }
 
     /* unpack the boolean flags. Note, the default mask is based on
      * the class specified in fixedAttrs, not on the real class */
     for (i=1; i < sizeof(unsigned long)*8; i++) {
-	unsigned long iMask = 1<< i;
-	if ((mask & iMask) == 0) {
-	   continue;
-	}
-	if (attributeExists(boolType[i])) {
-	    continue;
-	}
-	PKCS11Attribute attrib;
-	CKYByte bVal = (fixedAttrs & iMask) != 0;
-	attrib.setType(boolType[i]);
-	attrib.setValue(&bVal, 1);
+        unsigned long iMask = 1<< i;
+        if ((mask & iMask) == 0) {
+           continue;
+        }
+        if (attributeExists(boolType[i])) {
+            continue;
+        }
+        PKCS11Attribute attrib;
+        CKYByte bVal = (fixedAttrs & iMask) != 0;
+        attrib.setType(boolType[i]);
+        attrib.setValue(&bVal, 1);
         attributes.push_back(attrib);
     }
 }
@@ -224,40 +382,40 @@ PKCS11Object::parseNewObject(const CKYBu
     // load up the explicit attributes first
     for (j=0, offset = 11; j < attributeCount && offset < size; j++) {
         PKCS11Attribute attrib;
-	CKYByte attributeDataType = CKYBuffer_GetChar(data, offset+4);
-	unsigned int attrLen = 0;
+        CKYByte attributeDataType = CKYBuffer_GetChar(data, offset+4);
+        unsigned int attrLen = 0;
         attrib.setType(CKYBuffer_GetLong(data, offset));
         offset += 5;
 
-	switch(attributeDataType) {
-	case DATATYPE_STRING:
-	    attrLen = CKYBuffer_GetShort(data, offset);
-	    offset += 2;
+        switch(attributeDataType) {
+        case DATATYPE_STRING:
+            attrLen = CKYBuffer_GetShort(data, offset);
+            offset += 2;
             if (attrLen > CKYBuffer_Size(data) 
-			|| (offset + attrLen > CKYBuffer_Size(data)) ) {
-            	throw PKCS11Exception(CKR_DEVICE_ERROR,
-            	    "Invalid attribute length %d\n", attrLen);
+                        || (offset + attrLen > CKYBuffer_Size(data)) ) {
+                    throw PKCS11Exception(CKR_DEVICE_ERROR,
+                        "Invalid attribute length %d\n", attrLen);
              }
-	    attrib.setValue(CKYBuffer_Data(data)+offset, attrLen);
-	    break;
-	case DATATYPE_BOOL_FALSE:
-	case DATATYPE_BOOL_TRUE:
-	    {
-		CKYByte bval = attributeDataType & 1;
-		attrib.setValue(&bval, 1);
-	    }
-	    break;
-	case DATATYPE_INTEGER:
-	    {
-		CK_ULONG value = CKYBuffer_GetLong(data, offset);
-		attrLen = 4;
-		attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG));
-	    }
-	    break;
-	default:
-	    throw PKCS11Exception(CKR_DEVICE_ERROR, 
-		"Invalid attribute Data Type %d\n", attributeDataType);
-	}
+            attrib.setValue(CKYBuffer_Data(data)+offset, attrLen);
+            break;
+        case DATATYPE_BOOL_FALSE:
+        case DATATYPE_BOOL_TRUE:
+            {
+                CKYByte bval = attributeDataType & 1;
+                attrib.setValue(&bval, 1);
+            }
+            break;
+        case DATATYPE_INTEGER:
+            {
+                CK_ULONG value = CKYBuffer_GetLong(data, offset);
+                attrLen = 4;
+                attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG));
+            }
+            break;
+        default:
+            throw PKCS11Exception(CKR_DEVICE_ERROR, 
+                "Invalid attribute Data Type %d\n", attributeDataType);
+        }
         offset += attrLen;
         attributes.push_back(attrib);
     }
@@ -276,7 +434,7 @@ static const CK_ATTRIBUTE    rdr_templat
 
 bool
 PKCS11Object::matchesTemplate(const CK_ATTRIBUTE_PTR pTemplate, 
-						CK_ULONG ulCount)
+                                                CK_ULONG ulCount)
     const
 {
     unsigned int i;
@@ -285,10 +443,10 @@ PKCS11Object::matchesTemplate(const CK_A
 
 #if defined( NSS_HIDE_NONSTANDARD_OBJECTS )
     if (!ulCount) {
-	// exclude MOZ reader objects from searches for all objects.
-	// To find an MOZ reader object, one must search for it by 
-	// some matching attribute, such as class.
-	iterator iter = find_if(attributes.begin(), attributes.end(),
+        // exclude MOZ reader objects from searches for all objects.
+        // To find an MOZ reader object, one must search for it by 
+        // some matching attribute, such as class.
+        iterator iter = find_if(attributes.begin(), attributes.end(),
                                 AttributeMatch(&rdr_template[0]));
         return (iter == attributes.end()) ? true : false;
     }
@@ -325,7 +483,7 @@ PKCS11Object::getAttribute(CK_ATTRIBUTE_
             AttributeTypeMatch(type));
 
     if( iter == attributes.end() ) {
-	return NULL;
+        return NULL;
     }
     return iter->getValue();
 }
@@ -349,8 +507,9 @@ PKCS11Object::getAttributeValue(CK_ATTRI
         if( iter == attributes.end() ) {
             // no attribute of this type
             attrTypeInvalid = true;
-            log->log("GetAttributeValue: invalid type 0x%08x on object %x\n",
-                pTemplate[i].type, muscleObjID);
+            if ( log )
+                log->log("GetAttributeValue: invalid type 0x%08x on object %x\n",
+                    pTemplate[i].type, muscleObjID);
             pTemplate[i].ulValueLen = (CK_ULONG)-1;
             continue;
         }
@@ -371,7 +530,7 @@ PKCS11Object::getAttributeValue(CK_ATTRI
         // the buffer is large enough. return the value and set the exact
         // length.
         memcpy(pTemplate[i].pValue, CKYBuffer_Data(iter->getValue()), 
-					CKYBuffer_Size(iter->getValue()));
+                                        CKYBuffer_Size(iter->getValue()));
         pTemplate[i].ulValueLen = CKYBuffer_Size(iter->getValue());
     }
 
@@ -406,14 +565,14 @@ PKCS11Object::getLabel()
 
     // none found 
     if( iter == attributes.end() ) {
-	return "";
+        return "";
     }
 
     int size = CKYBuffer_Size(iter->getValue());
 
     label = new char [ size + 1 ];
     if (!label) {
-	return "";
+        return "";
     }
     memcpy(label, CKYBuffer_Data(iter->getValue()), size);
     label[size] = 0;
@@ -431,13 +590,13 @@ PKCS11Object::getClass()
 
     // none found */
     if( iter == attributes.end() ) {
-	return (CK_OBJECT_CLASS) -1;
+        return (CK_OBJECT_CLASS) -1;
     }
 
     int size = CKYBuffer_Size(iter->getValue());
 
     if (size != sizeof(objClass)) {
-	return (CK_OBJECT_CLASS) -1;
+        return (CK_OBJECT_CLASS) -1;
     }
 
     memcpy(&objClass, CKYBuffer_Data(iter->getValue()), size);
@@ -453,13 +612,28 @@ PKCS11Object::setAttribute(CK_ATTRIBUTE_
     iter = find_if(attributes.begin(), attributes.end(),
         AttributeTypeMatch(type));
     if( iter != attributes.end() )  {
-	iter->setValue( CKYBuffer_Data(value), CKYBuffer_Size(value));
+        iter->setValue( CKYBuffer_Data(value), CKYBuffer_Size(value));
     } else {
         attributes.push_back(PKCS11Attribute(type, value));
     }
 }
 
 void
+PKCS11Object::setAttribute(CK_ATTRIBUTE_TYPE type, const CKYByte *data,
+			CKYSize size)
+{
+    AttributeIter iter;  
+
+    iter = find_if(attributes.begin(), attributes.end(),
+        AttributeTypeMatch(type));
+    if( iter != attributes.end() )  {
+        iter->setValue( data, size);
+    } else {
+        attributes.push_back(PKCS11Attribute(type, data, size));
+    }
+}
+
+void
 PKCS11Object::setAttribute(CK_ATTRIBUTE_TYPE type, const char *string)
 {
     CKYBuffer buf;
@@ -491,7 +665,7 @@ PKCS11Object::setAttributeULong(CK_ATTRI
 
 typedef struct {
     const CKYByte*data;
-    unsigned int len;
+    CKYSize len;
 } CCItem;
 
 typedef enum {
@@ -499,15 +673,21 @@ typedef enum {
     SECFailure=1
 } SECStatus;
 
-static const CKYByte*
-dataStart(const CKYByte *buf, unsigned int length,
-                        unsigned int *data_length, bool includeTag) {
+const CKYByte*
+dataStart(const CKYByte *buf, CKYSize length,
+                        CKYSize *data_length, bool includeTag) {
     unsigned char tag;
     unsigned int used_length= 0;
 
+    *data_length = 0; /* make sure data_length is zero on failure */
+
     if(!buf) {
         return NULL;
     }
+    /* there must be at least 2 bytes */
+    if (length < 2) {
+	return NULL;
+    }
 
     tag = buf[used_length++];
 
@@ -521,15 +701,22 @@ dataStart(const CKYByte *buf, unsigned i
     if (*data_length&0x80) {
         int  len_count = *data_length & 0x7f;
 
+	if (len_count+used_length > length) {
+	    return NULL;
+	}
+
         *data_length = 0;
 
         while (len_count-- > 0) {
             *data_length = (*data_length << 8) | buf[used_length++];
         }
     }
+    /* paranoia, can't happen */
+    if (length < used_length) {
+	return NULL;
+    }
 
     if (*data_length > (length-used_length) ) {
-        *data_length = length-used_length;
         return NULL;
     }
     if (includeTag) *data_length += used_length;
@@ -538,27 +725,169 @@ dataStart(const CKYByte *buf, unsigned i
 }
 
 static const CKYByte *
-unwrapBitString(const CKYByte *buf, unsigned int len, unsigned int *retLen)
+unwrapBitString(const CKYByte *buf, CKYSize len, CKYSize *retLen)
 {
     /* for RSA, bit string always has byte number of bits */
     if (buf[0] != 0) {
-	return NULL;
+        return NULL;
     }
     if (len < 1) {
-	return NULL;
+        return NULL;
     }
     *retLen = len -1;
     return buf+1;
 }
 
 static SECStatus
-GetKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length,
+GetECKeyFieldItems(const CKYByte *spki_data, CKYSize spki_length,
+        CCItem *point, CCItem *params)
+{
+    const CKYByte *buf = spki_data;
+    CKYSize buf_length = spki_length;
+    const CKYByte *algid;
+    CKYSize algidlen;
+    const CKYByte *dummy;
+    CKYSize dummylen;
+
+    if (!point || !params || !buf)
+        return SECFailure;
+
+    point->data = NULL;
+    point->len = 0;
+    params->data = NULL;
+    params->len = 0;
+
+    /* unwrap the algorithm id */
+    dummy = dataStart(buf,buf_length,&dummylen,false);
+    if (dummy == NULL) return SECFailure;
+    buf_length -= (dummy-buf) + dummylen;
+    buf = dummy + dummylen;
+    /* unwrpped value is in dummy */
+    algid = dummy;
+    algidlen = dummylen;
+    /* skip past algid oid */
+    dummy = dataStart(algid, algidlen, &dummylen, false);
+    if (dummy == NULL) return SECFailure;
+    algidlen -= (dummy-algid) + dummylen;
+    algid = dummy + dummylen;
+    params->data = algid;
+    params->len = algidlen;
+
+       /* unwrap the public key info */
+    buf = dataStart(buf,buf_length,&buf_length,false);
+    if (buf == NULL) return SECFailure;
+    buf = unwrapBitString(buf,buf_length,&buf_length);
+    if (buf == NULL) return SECFailure;
+
+    point->data = buf;
+    point->len = buf_length;
+
+    if(point->data == NULL) return SECFailure;
+
+    return SECSuccess;
+}
+
+static bool
+GetKeyOIDMatches(const CKYByte *spki_data, unsigned int length, const CKYByte *oid_data)
+{
+    bool ret = TRUE;
+
+    if( spki_data == NULL || oid_data == NULL) {
+        return FALSE;
+    }
+
+    for ( int i = 0 ; i < (int) length ; i++) {
+        if (spki_data[i] != oid_data[i]) {
+            ret = FALSE;
+            break;
+        }
+            
+    }
+
+    return ret;
+}
+
+static SECStatus
+GetKeyAlgorithmId(const CKYByte *spki_data, CKYSize spki_length,
+       CCItem *algorithmId)
+{
+
+    const CKYByte *buf = spki_data;
+    CKYSize buf_length = spki_length;
+
+    if ( algorithmId == NULL) return SECFailure;
+
+    /* objtain the algorithm id */
+    algorithmId->data = dataStart(buf,buf_length,&algorithmId->len,false);
+    if (algorithmId->data == NULL) return SECFailure;
+
+    return SECSuccess;
+
+}
+
+static PKCS11Object::KeyType
+GetKeyTypeFromSPKI(const CKYBuffer *key)
+{
+    CCItem algIdItem;
+    SECStatus ret = GetKeyAlgorithmId(CKYBuffer_Data(key), 
+                                      CKYBuffer_Size(key),&algIdItem);
+    PKCS11Object::KeyType foundType = PKCS11Object::unknown;
+
+    if ( ret != SECSuccess ) {
+        throw PKCS11Exception(CKR_FUNCTION_FAILED,
+            "Failed to decode key algorithm ID.");
+    }
+
+    CKYSize length = 0;
+    const CKYByte *keyData = NULL;
+
+    /* Get actual oid buffer */
+
+    keyData = dataStart(algIdItem.data,algIdItem.len,&length, false);
+    if (keyData == NULL) {
+        throw PKCS11Exception(CKR_FUNCTION_FAILED,
+            "Failed to decode key algorithm ID.");
+    }
+
+    bool match = FALSE;
+    
+    /* Check for outrageous length */
+
+    if ( length <= 3 || length >= algIdItem.len) {
+        throw PKCS11Exception(CKR_FUNCTION_FAILED,
+            "Failed to decode key algorithm ID.");
+    }
+    /* check for RSA */
+ 
+    match = GetKeyOIDMatches(keyData, length, rsaOID);
+   
+    if ( match == TRUE ) {
+       foundType = PKCS11Object::rsa;
+    } else { 
+      /* check for ECC */
+       match = GetKeyOIDMatches(keyData, length, eccOID);
+
+       if ( match == TRUE ) {
+         foundType = PKCS11Object::ecc;
+       }
+
+    }
+
+    if ( foundType == PKCS11Object::unknown) {
+        throw PKCS11Exception(CKR_FUNCTION_FAILED,
+            "Failed to decode key algorithm ID.");
+    }
+    return foundType;
+}
+
+static SECStatus
+GetKeyFieldItems(const CKYByte *spki_data,CKYSize spki_length,
         CCItem *modulus, CCItem *exponent)
 {
     const CKYByte *buf = spki_data;
-    unsigned int buf_length = spki_length;
+    CKYSize buf_length = spki_length;
     const CKYByte*dummy;
-    unsigned int dummylen;
+    CKYSize dummylen;
 
     /* skip past the algorithm id */
     dummy = dataStart(buf,buf_length,&dummylen,false);
@@ -596,7 +925,7 @@ GetKeyFields(const CKYBuffer *spki, CKYB
     CCItem modulusItem, exponentItem;
 
     rv = GetKeyFieldItems(CKYBuffer_Data(spki), CKYBuffer_Size(spki), 
-	&modulusItem, &exponentItem);
+        &modulusItem, &exponentItem);
 
     if( rv != SECSuccess ) {
         throw PKCS11Exception(CKR_FUNCTION_FAILED,
@@ -607,6 +936,29 @@ GetKeyFields(const CKYBuffer *spki, CKYB
     CKYBuffer_Replace(exponent, 0, exponentItem.data, exponentItem.len);
 }
 
+static void
+GetECKeyFields(const CKYBuffer *spki, CKYBuffer *point, CKYBuffer *params)
+{
+    SECStatus rv;
+    CCItem pointItem, paramsItem;
+
+    if (spki == NULL || point == NULL || params == NULL) {
+        throw PKCS11Exception(CKR_FUNCTION_FAILED,
+             "Failed to decode certificate Subject Public KeyInfo!");
+    }
+    
+    rv = GetECKeyFieldItems(CKYBuffer_Data(spki), CKYBuffer_Size(spki),
+        &pointItem, &paramsItem);
+
+    if( rv != SECSuccess ) {
+        throw PKCS11Exception(CKR_FUNCTION_FAILED,
+            "Failed to decode certificate Subject Public Key Info!");
+    }
+
+    CKYBuffer_Replace(point, 0, pointItem.data, pointItem.len);
+    CKYBuffer_Replace(params, 0, paramsItem.data, paramsItem.len);
+}
+
 Key::Key(unsigned long muscleObjID, const CKYBuffer *data,
     CK_OBJECT_HANDLE handle) : PKCS11Object(muscleObjID, data, handle)
 {
@@ -616,67 +968,113 @@ Key::Key(unsigned long muscleObjID, cons
     CKYBuffer_InitEmpty(&empty);
 
     if ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY)) {
-	/* only CKK_RSA is supported */
-	setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
+        //we may know already what type of key this is.
+        if (attributeExists(CKA_KEY_TYPE)) {
+            CK_ULONG type = 0;
+            CK_ATTRIBUTE aTemplate = {CKA_KEY_TYPE, &type, sizeof(CK_ULONG)};
+    
+            getAttributeValue(&aTemplate, 1, NULL);
+
+            if (type == 0x3) {
+                setKeyType(ecc);
+                setAttributeULong(CKA_KEY_TYPE, CKK_EC);
+            } else {  
+                setKeyType(rsa);
+                setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
+            }
+        } else {
+           /* default to rsa */
+           setKeyType(rsa);
+           setAttributeULong(CKA_KEY_TYPE, CKK_RSA); 
+        }
+
+    // Could be RSA or ECC
     } else if (objClass == CKO_SECRET_KEY) {
-	if (!attributeExists(CKA_LABEL)) {
-	    setAttribute(CKA_LABEL, &empty);
-	}
-	if (!attributeExists(CKA_KEY_TYPE)) {
-	    /* default to DES3 */
-	    setAttributeULong(CKA_KEY_TYPE, CKK_DES3);
-	}
+        if (!attributeExists(CKA_LABEL)) {
+            setAttribute(CKA_LABEL, &empty);
+        }
+        if (!attributeExists(CKA_KEY_TYPE)) {
+            /* default to DES3 */
+            setAttributeULong(CKA_KEY_TYPE, CKK_DES3);
+        }
     }
     if (!attributeExists(CKA_START_DATE)) {
-	setAttribute(CKA_START_DATE, &empty);
+        setAttribute(CKA_START_DATE, &empty);
     }
     if (!attributeExists(CKA_END_DATE)) {
-	setAttribute(CKA_END_DATE, &empty);
+        setAttribute(CKA_END_DATE, &empty);
     }
 }
 
 void
-Key::completeKey(const PKCS11Object &cert)
+PKCS11Object::completeKey(const PKCS11Object &cert)
 {
     // infer key attributes from cert
     bool modulusExists, exponentExists;
-    CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus);
-    CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent);
+    bool pointExists, paramsExists;
+
+    PKCS11Object::KeyType keyType;
+    const CKYBuffer *key = cert.getPubKey();
 
     if (!attributeExists(CKA_LABEL)) {
-	setAttribute(CKA_LABEL, cert.getAttribute(CKA_LABEL));
+        setAttribute(CKA_LABEL, cert.getAttribute(CKA_LABEL));
     }
+
+    CKYBuffer param1; CKYBuffer_InitEmpty(&param1);
+    CKYBuffer param2; CKYBuffer_InitEmpty(&param2);
     try {
- 	modulusExists = attributeExists(CKA_MODULUS);
-	exponentExists = attributeExists(CKA_PUBLIC_EXPONENT);
-	if (!modulusExists || !exponentExists) {
-	    const CKYBuffer *key = cert.getPubKey();
-	    GetKeyFields(key, &modulus, &exponent);
-	    if (!modulusExists) {
-		setAttribute(CKA_MODULUS, &modulus);
-	    }
-	    if (!exponentExists) {
-		setAttribute(CKA_PUBLIC_EXPONENT, &exponent);
-	    }
-	}
+        keyType = GetKeyTypeFromSPKI(key);
+        setKeyType(keyType);
+
+        switch (keyType) {
+        case rsa:
+            modulusExists = attributeExists(CKA_MODULUS);
+            exponentExists = attributeExists(CKA_PUBLIC_EXPONENT);
+            if (!modulusExists || !exponentExists) {
+                GetKeyFields(key, &param1, &param2);
+                if (!modulusExists) {
+                        setAttribute(CKA_MODULUS, &param1);
+                }
+                if (!exponentExists) {
+                      setAttribute(CKA_PUBLIC_EXPONENT, &param2);
+                }
+            }
+            break;
+        case ecc:
+            pointExists = attributeExists(CKA_EC_POINT);
+            paramsExists = attributeExists(CKA_EC_PARAMS);
+
+            if (!pointExists || !paramsExists) {
+                GetECKeyFields(key, &param1, &param2);
+                if (!pointExists) {
+                   setAttribute(CKA_EC_POINT, &param1);
+                }
+                if (!paramsExists) {
+                    setAttribute(CKA_EC_PARAMS, &param2);
+                }
+            }
+            break;
+        default:
+            break;
+        }
     } catch (PKCS11Exception &e) {
-	CKYBuffer_FreeData(&modulus);
-	CKYBuffer_FreeData(&exponent);
-	throw e;
+        CKYBuffer_FreeData(&param1);
+        CKYBuffer_FreeData(&param2);
+        throw e;
     }
-    CKYBuffer_FreeData(&modulus);
-    CKYBuffer_FreeData(&exponent);
+    CKYBuffer_FreeData(&param1);
+    CKYBuffer_FreeData(&param2);
 }
 
 static SECStatus
-GetCertFieldItems(const CKYByte *dercert,unsigned int cert_length,
+GetCertFieldItems(const CKYByte *dercert, CKYSize cert_length,
         CCItem *issuer, CCItem *serial, CCItem *derSN, CCItem *subject,
         CCItem *valid, CCItem *subjkey)
 {
     const CKYByte *buf;
-    unsigned int buf_length;
+    CKYSize buf_length;
     const CKYByte*dummy;
-    unsigned int dummylen;
+    CKYSize dummylen;
 
     /* get past the signature wrap */
     buf = dataStart(dercert,cert_length,&buf_length, false);
@@ -737,14 +1135,14 @@ GetCertFieldItems(const CKYByte *dercert
 
 static void
 GetCertFields(const CKYBuffer *derCert, CKYBuffer *derSerial, 
-	    CKYBuffer *derSubject, CKYBuffer *derIssuer, CKYBuffer *subjectKey)
+            CKYBuffer *derSubject, CKYBuffer *derIssuer, CKYBuffer *subjectKey)
 {
     SECStatus rv;
     CCItem issuerItem, serialItem, derSerialItem, subjectItem,
         validityItem, subjectKeyItem;
 
     rv = GetCertFieldItems(CKYBuffer_Data(derCert), CKYBuffer_Size(derCert), 
-	&issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem,
+        &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem,
         &subjectKeyItem);
 
     if( rv != SECSuccess ) {
@@ -769,50 +1167,50 @@ Cert::Cert(unsigned long muscleObjID, co
     CK_ULONG certTypeValue = CKC_X_509;
 
     CKYBuffer_InitFromData(&certType, (CKYByte *)&certTypeValue, 
-						sizeof(certTypeValue));
+                                                sizeof(certTypeValue));
     CKYBuffer_Resize(&pubKey,0);
 
     try {
- 	setAttribute(CKA_CERTIFICATE_TYPE, &certType);
+         setAttribute(CKA_CERTIFICATE_TYPE, &certType);
 
-	if (!attributeExists(CKA_VALUE)) {
-	    if (derCert) {
-		 setAttribute(CKA_VALUE, derCert);
-	    } else  {
-		throw PKCS11Exception(CKR_DEVICE_ERROR, 
-		    "Missing certificate data from token");
-	    }
-	}
+        if (!attributeExists(CKA_VALUE)) {
+            if (derCert) {
+                 setAttribute(CKA_VALUE, derCert);
+            } else  {
+                throw PKCS11Exception(CKR_DEVICE_ERROR, 
+                    "Missing certificate data from token");
+            }
+        }
 
-	if (!derCert) {
-	    derCert = getAttribute(CKA_VALUE);
-	    if (!derCert) {
-		// paranoia, should never happen since we verify the
-		// attribute exists above
-		throw PKCS11Exception(CKR_DEVICE_ERROR, 
-		     "Missing certificate data from token");
-	    }
-	}
+        if (!derCert) {
+            derCert = getAttribute(CKA_VALUE);
+            if (!derCert) {
+                // paranoia, should never happen since we verify the
+                // attribute exists above
+                throw PKCS11Exception(CKR_DEVICE_ERROR, 
+                     "Missing certificate data from token");
+            }
+        }
 
-	// infer cert attributes
+        // infer cert attributes
 
-	GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey);
+        GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey);
 
-	if (!attributeExists(CKA_SERIAL_NUMBER)) {
-	    setAttribute(CKA_SERIAL_NUMBER, &derSerial);
-	}
-	if (!attributeExists(CKA_SUBJECT)) {
-	    setAttribute(CKA_SUBJECT, &derSubject);
-	}
-	if (!attributeExists(CKA_ISSUER)) {
-	    setAttribute(CKA_ISSUER, &derIssuer);
-	}
+        if (!attributeExists(CKA_SERIAL_NUMBER)) {
+            setAttribute(CKA_SERIAL_NUMBER, &derSerial);
+        }
+        if (!attributeExists(CKA_SUBJECT)) {
+            setAttribute(CKA_SUBJECT, &derSubject);
+        }
+        if (!attributeExists(CKA_ISSUER)) {
+            setAttribute(CKA_ISSUER, &derIssuer);
+        }
    } catch (PKCS11Exception &e) {
-	CKYBuffer_FreeData(&certType);
-	CKYBuffer_FreeData(&derSerial);
-	CKYBuffer_FreeData(&derSubject);
-	CKYBuffer_FreeData(&derIssuer);
-	throw e;
+        CKYBuffer_FreeData(&certType);
+        CKYBuffer_FreeData(&derSerial);
+        CKYBuffer_FreeData(&derSubject);
+        CKYBuffer_FreeData(&derIssuer);
+        throw e;
     }
     CKYBuffer_FreeData(&certType);
     CKYBuffer_FreeData(&derSerial);
@@ -822,7 +1220,7 @@ Cert::Cert(unsigned long muscleObjID, co
 
 Reader::Reader(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, 
     const char *reader, const CKYBuffer *cardATR, bool isCoolkey) : 
-	PKCS11Object(muscleObjID, handle)
+        PKCS11Object(muscleObjID, handle)
 {
     setAttributeULong(CKA_CLASS, CKO_MOZ_READER);
     setAttribute(CKA_LABEL, reader);
@@ -833,9 +1231,10 @@ Reader::Reader(unsigned long muscleObjID
     setAttribute(CKA_MOZ_ATR, cardATR);
 }
 
+
 CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert) : 
-	PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16,
-			 instance | 0x400)
+        PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16,
+                         instance | 0x400)
 {
     CKYBuffer id;
     CKYBuffer empty;
@@ -844,7 +1243,7 @@ CACPrivKey::CACPrivKey(CKYByte instance,
     /* So we know what the key is supposed to be used for based on
      * the instance */
     if (instance == 2) {
-	decrypt = TRUE;
+        decrypt = TRUE;
     }
 
     CKYBuffer_InitEmpty(&empty);
@@ -863,33 +1262,52 @@ CACPrivKey::CACPrivKey(CKYByte instance,
     setAttributeBool(CKA_LOCAL, TRUE);
     setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
 
-    setAttributeBool(CKA_DECRYPT, decrypt);
     setAttributeBool(CKA_SIGN, !decrypt);
     setAttributeBool(CKA_SIGN_RECOVER, !decrypt);
     setAttributeBool(CKA_UNWRAP, FALSE);
     setAttributeBool(CKA_SENSITIVE, TRUE);
     setAttributeBool(CKA_EXTRACTABLE, FALSE);
 
-    CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus);
-    CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent);
+    CKYBuffer param1; CKYBuffer_InitEmpty(&param1);
+    CKYBuffer param2; CKYBuffer_InitEmpty(&param2);
 
     try {
-	const CKYBuffer *key = cert.getPubKey();
-	GetKeyFields(key, &modulus, &exponent);
-	setAttribute(CKA_MODULUS, &modulus);
-	setAttribute(CKA_PUBLIC_EXPONENT, &exponent);
-    } catch (PKCS11Exception &e) {
-	CKYBuffer_FreeData(&modulus);
-	CKYBuffer_FreeData(&exponent);
-	throw e;
-    }
-    CKYBuffer_FreeData(&modulus);
-    CKYBuffer_FreeData(&exponent);
+        const CKYBuffer *key = cert.getPubKey();
+        keyType = GetKeyTypeFromSPKI(key);
+        setKeyType(keyType);
+
+        switch (keyType) {
+        case rsa:
+            GetKeyFields(key, &param1, &param2);
+            setAttribute(CKA_MODULUS, &param1);
+            setAttribute(CKA_PUBLIC_EXPONENT, &param2);
+	    setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
+	    setAttributeBool(CKA_DECRYPT, decrypt);
+	    setAttributeBool(CKA_DERIVE, FALSE);
+            break;
+        case ecc:
+            GetECKeyFields(key, &param1, &param2);
+            setAttribute(CKA_EC_POINT, &param1);
+            setAttribute(CKA_EC_PARAMS, &param2);
+	    setAttributeULong(CKA_KEY_TYPE, CKK_EC);
+	    setAttributeBool(CKA_DECRYPT, FALSE);
+	    setAttributeBool(CKA_DERIVE, decrypt);
+            break;
+        default:
+            break;
+        }
+     } catch (PKCS11Exception &e) {
+        CKYBuffer_FreeData(&param1);
+        CKYBuffer_FreeData(&param2);
+        throw e;
+     }
+     CKYBuffer_FreeData(&param1);
+     CKYBuffer_FreeData(&param2);
 }
 
 CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert) : 
-	PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16,
-		       instance | 0x500)
+        PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16,
+                       instance | 0x500)
 {
     CKYBuffer id;
     CKYBuffer empty;
@@ -898,7 +1316,7 @@ CACPubKey::CACPubKey(CKYByte instance, c
     /* So we know what the key is supposed to be used for based on
      * the instance */
     if (instance == 2) {
-	encrypt = TRUE;
+        encrypt = TRUE;
     }
 
     CKYBuffer_InitEmpty(&empty);
@@ -915,83 +1333,102 @@ CACPubKey::CACPubKey(CKYByte instance, c
     setAttribute(CKA_END_DATE, &empty);
     setAttributeBool(CKA_DERIVE, FALSE);
     setAttributeBool(CKA_LOCAL, TRUE);
-    setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
 
     setAttributeBool(CKA_ENCRYPT, encrypt);
     setAttributeBool(CKA_VERIFY, !encrypt);
     setAttributeBool(CKA_VERIFY_RECOVER, !encrypt);
     setAttributeBool(CKA_WRAP, FALSE);
 
-    CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus);
-    CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent);
+    CKYBuffer param1; CKYBuffer_InitEmpty(&param1);
+    CKYBuffer param2; CKYBuffer_InitEmpty(&param2);
 
     try {
-	const CKYBuffer *key = cert.getPubKey();
-	GetKeyFields(key, &modulus, &exponent);
-	setAttribute(CKA_MODULUS, &modulus);
-	setAttribute(CKA_PUBLIC_EXPONENT, &exponent);
-    } catch (PKCS11Exception &e) {
-	CKYBuffer_FreeData(&modulus);
-	CKYBuffer_FreeData(&exponent);
-	throw e;
-    }
-    CKYBuffer_FreeData(&modulus);
-    CKYBuffer_FreeData(&exponent);
+        const CKYBuffer *key = cert.getPubKey();
+        keyType = GetKeyTypeFromSPKI(key);
+        setKeyType(keyType);
+
+        switch (keyType) {
+        case rsa:
+            GetKeyFields(key, &param1, &param2);
+            setAttribute(CKA_MODULUS, &param1);
+            setAttribute(CKA_PUBLIC_EXPONENT, &param2);
+	    setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
+            break;
+        case ecc:
+            GetECKeyFields(key, &param1, &param2);
+            setAttribute(CKA_EC_POINT, &param1);
+            setAttribute(CKA_EC_PARAMS, &param2);
+	    setAttributeULong(CKA_KEY_TYPE, CKK_EC);
+            break;
+        default:
+            break;
+        }
+     } catch (PKCS11Exception &e) {
+        CKYBuffer_FreeData(&param1);
+        CKYBuffer_FreeData(&param2);
+        throw e;
+     }
+     CKYBuffer_FreeData(&param1);
+     CKYBuffer_FreeData(&param2);
 }
 
 static const char *CAC_Label[] = {
-	"CAC ID Certificate",
-	"CAC Email Signature Certificate",
-	"CAC Email Encryption Certificate",
+        "CAC ID Certificate",
+        "CAC Email Signature Certificate",
+        "CAC Email Encryption Certificate",
 };
 
 static const unsigned char CN_DATA[] = { 0x55, 0x4, 0x3 };
 const unsigned int CN_LENGTH = sizeof(CN_DATA);
 
 static SECStatus
-GetCN(const CKYByte *dn, unsigned int dn_length, CCItem *cn)
+GetCN(const CKYByte *dn, CKYSize dn_length, CCItem *cn)
 {
     const CKYByte *buf;
-    unsigned int buf_length;
+    CKYSize buf_length;
 
     /* unwrap the sequence */
     buf = dataStart(dn,dn_length,&buf_length, false);
     if (buf == NULL) return SECFailure;
 
     while (buf_length) {
-	const CKYByte *name;
-	unsigned int name_length;
-	const CKYByte *oid;
-	unsigned int oid_length;
-
-	/* unwrap the set */
-	name = dataStart(buf, buf_length, &name_length, false);
+        const CKYByte *name;
+        CKYSize name_length;
+        const CKYByte *oid;
+        CKYSize oid_length;
+
+        /* unwrap the set */
+        name = dataStart(buf, buf_length, &name_length, false);
+	if (name == NULL) return SECFailure;
 
         /* advance to next set */
-	buf_length -= (name-buf) + name_length;
-	buf = name + name_length; 
+        buf_length -= (name-buf) + name_length;
+        buf = name + name_length; 
 
-	/* unwrap the Sequence */
-	name = dataStart(name, name_length, &name_length, false);
+        /* unwrap the Sequence */
+        name = dataStart(name, name_length, &name_length, false);
+	if (name == NULL) return SECFailure;
 
         /* unwrap the oid */
-	oid = dataStart(name, name_length, &oid_length, false);
+        oid = dataStart(name, name_length, &oid_length, false);
+	if (oid == NULL) return SECFailure;
 
-	/* test the oid */
-	if (oid_length != CN_LENGTH) {
-	    continue;
-	}
-	if (memcmp(oid, CN_DATA, CN_LENGTH) != 0) {
-	    continue;
-	}
-
-	/* advance to CN */
-	name_length -= (oid-name) + oid_length;
-	name = oid + oid_length;
+        /* test the oid */
+        if (oid_length != CN_LENGTH) {
+            continue;
+        }
+        if (memcmp(oid, CN_DATA, CN_LENGTH) != 0) {
+            continue;
+        }
 
-	/* unwrap the CN */
-	cn->data = dataStart(name, name_length, &cn->len, false);
-	return SECSuccess;
+        /* advance to CN */
+        name_length -= (oid-name) + oid_length;
+        name = oid + oid_length;
+
+        /* unwrap the CN */
+        cn->data = dataStart(name, name_length, &cn->len, false);
+	if (cn->data == NULL) return SECFailure;
+        return SECSuccess;
     }
     return SECFailure;
 }
@@ -1006,11 +1443,11 @@ GetUserName(const CKYBuffer *dn)
     rv = GetCN(CKYBuffer_Data(dn), CKYBuffer_Size(dn) , &cn);
 
     if( rv != SECSuccess ) {
-	return NULL;
+        return NULL;
     }
     string = new char [ cn.len + 1 ];
     if (string == NULL) {
-	return NULL;
+        return NULL;
     }
     memcpy(string, cn.data, cn.len);
     string[cn.len] = 0;
@@ -1018,18 +1455,11 @@ GetUserName(const CKYBuffer *dn)
 }
 
 CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert) : 
-	PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, 
-			instance | 0x600)
+        PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, 
+                        instance | 0x600)
 {
     CKYBuffer id;
     CKYBuffer empty;
-    CK_BBOOL decrypt = FALSE;
-
-    /* So we know what the key is supposed to be used for based on
-     * the instance */
-    if (instance == 2) {
-	decrypt = TRUE;
-    }
 
     CKYBuffer_InitEmpty(&empty);
     setAttributeULong(CKA_CLASS, CKO_CERTIFICATE);
@@ -1050,19 +1480,19 @@ CACCert::CACCert(CKYByte instance, const
     CKYBuffer_Resize(&pubKey,0);
 
     try {
-	setAttribute(CKA_VALUE, derCert);
-	// infer cert attributes
+        setAttribute(CKA_VALUE, derCert);
+        // infer cert attributes
 
-	GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey);
+        GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey);
 
-	setAttribute(CKA_SERIAL_NUMBER, &derSerial);
-	setAttribute(CKA_SUBJECT, &derSubject);
-	setAttribute(CKA_ISSUER, &derIssuer);
+        setAttribute(CKA_SERIAL_NUMBER, &derSerial);
+        setAttribute(CKA_SUBJECT, &derSubject);
+        setAttribute(CKA_ISSUER, &derIssuer);
    } catch (PKCS11Exception &e) {
-	CKYBuffer_FreeData(&derSerial);
-	CKYBuffer_FreeData(&derSubject);
-	CKYBuffer_FreeData(&derIssuer);
-	throw e;
+        CKYBuffer_FreeData(&derSerial);
+        CKYBuffer_FreeData(&derSubject);
+        CKYBuffer_FreeData(&derIssuer);
+        throw e;
     }
 
     name = GetUserName(&derSubject); /* adopt */
@@ -1070,3 +1500,1248 @@ CACCert::CACCert(CKYByte instance, const
     CKYBuffer_FreeData(&derSubject);
     CKYBuffer_FreeData(&derIssuer);
 }
+
+static const CKYByte rev[] = {
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+unsigned long GetBits(const CKYByte *entry, CKYSize entrySize,
+				unsigned int numBits, unsigned int numBytes)
+{
+   unsigned long bits = 0;
+   unsigned long bitFlag = 0;
+   unsigned int i;
+
+   /* size of zero is valid for no bits */
+   if (entrySize <= 1) {
+	return 0;
+   }
+   entrySize--;
+   entry++;
+
+   /* if we are longer than and unsigned, just bail now */
+   if (entrySize > sizeof (unsigned long)) {
+	bitFlag = BROKEN_FLAG;
+	entrySize = sizeof(unsigned long);
+   }
+   /* turn the flags into an int */
+   for (i=0; i < entrySize; i++) {
+	CKYByte c = rev[entry[i]];
+	bits  = bits | (((unsigned long)c) << (i*8));
+   }
+   return bits | bitFlag;
+}
+
+
+/*
+ * parse the path object.
+ * Caller has already unwrapped the outer ASN1Sequence
+ */
+CKYStatus PK15ObjectPath::setObjectPath(const CKYByte *current, CKYSize size)
+{
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    unsigned int i;
+    CKYStatus status;
+
+
+    if ((current == NULL) || (current[0] != ASN1_OCTET_STRING)) {
+	return CKYINVALIDDATA;
+    }
+    /* entry */
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    tagSize = entry - current;
+    current += entrySize + tagSize;
+    if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+    size -= (entrySize +tagSize);
+    status = CKYBuffer_Replace(&path, 0, entry, entrySize);
+    if (status != CKYSUCCESS) {
+	return status;
+    }
+
+    /* index */
+    if ((size != 0) && current[0] ==  ASN1_INTEGER) { 
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDDATA; }
+	tagSize = entry - current;
+	current += entrySize + tagSize;
+	if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+	size -= (entrySize +tagSize);
+	if (entrySize > 5) { return CKYINVALIDDATA; }
+	for (index = 0, i=0; i < entrySize; i++) {
+	    index = (index << 8) + (unsigned int) entry[i];
+	}
+    }
+
+    /* length */
+    if ((size != 0) && ((current[0]|ASN1_CONSTRUCTED) ==  ASN1_CHOICE_0)) { 
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDDATA; }
+	tagSize = entry - current;
+	current += entrySize + tagSize;
+	if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+	size -= (entrySize +tagSize);
+	if (entrySize > 5) { return CKYINVALIDDATA; }
+	for (length = 0, i=0; i < entrySize; i++) {
+	    length = (length << 8) + (unsigned int) entry[i];
+	}
+    }
+    return CKYSUCCESS;
+}
+
+static unsigned int pK15GetTag(PK15ObjectType type) {
+     switch (type) { case PK15PvKey: case PK15PuKey: return 'k'<<24;
+		     case PK15Cert: return 'c' << 24; default: break; }
+     return 'v';
+}
+
+
+PK15Object::PK15Object(CKYByte inst, PK15ObjectType type, 
+	const CKYByte *der, CKYSize derSize) 
+	: PKCS11Object(pK15GetTag(type) | ((inst+'0') << 16), 0xa000 | inst)
+{
+    CKYStatus status;
+
+    instance = inst;
+    p15Type =  type;
+    CKYBuffer_InitEmpty(&authId);
+    CKYBuffer_InitEmpty(&pinAuthId);
+    state = PK15StateInit;
+    pinInfo.pinFlags = 0;
+    pinInfo.pinType = P15PinUTF8;
+    pinInfo.minLength = 4;
+    pinInfo.storedLength = 0;
+    pinInfo.maxLength = 0;
+    pinInfo.pinRef = 0;
+    pinInfo.padChar = 0xff;
+
+    status = completeObject(der, derSize);
+    if (status != CKYSUCCESS) {
+	state = PK15StateInit; /* don't try to fetch any more if we failed */
+    }
+}
+
+/* returns true if there is more work to do... */
+CKYStatus 
+PK15Object::completeObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYByte objectTag;
+    CKYStatus status;
+    CKYBitFlags bits;
+
+    switch (state) {
+    case PK15StateInit:
+    case PK15StateNeedObject:
+	break;
+    case PK15StateNeedRawPublicKey:
+	return  completeRawPublicKey(current, currentSize);
+    case PK15StateNeedRawCertificate:
+	return  completeRawCertificate(current, currentSize);
+    case PK15StateComplete:
+	return CKYSUCCESS;
+    }
+
+    if (current == NULL) { return CKYINVALIDARGS; }
+
+    objectTag = current[0];
+
+    setAttributeBool(CKA_TOKEN, TRUE);
+
+    /* set type specific attributes */
+    switch (p15Type) {
+    case PK15Cert:
+	setAttributeULong(CKA_CLASS, CKO_CERTIFICATE);
+    	setAttributeULong(CKA_CERTIFICATE_TYPE, CKC_X_509);
+	if (objectTag != PK15X509CertType) {
+	    return CKYUNSUPPORTED;
+	}
+	break;
+    case PK15PvKey:
+	setAttributeULong(CKA_CLASS, CKO_PRIVATE_KEY);
+	goto set_key_type;
+    case PK15PuKey:
+	setAttributeULong(CKA_CLASS, CKO_PUBLIC_KEY);
+set_key_type:
+	switch (objectTag) {
+	case PK15RSAKeyType:
+	    keyType = rsa;
+	    setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
+	    break;
+	case PK15ECCKeyType:
+	    keyType = ecc;
+	    setAttributeULong(CKA_KEY_TYPE, CKK_EC);
+	    break;
+	case PK15DSAKeyType:
+	case PK15DHKeyType:
+	default:
+	    return CKYUNSUPPORTED;
+	}
+	break;
+    case PK15AuthObj:
+	setAttributeULong(CKA_CLASS, CKO_DATA);
+	break;
+    default:
+	return CKYUNSUPPORTED;
+    }
+
+    /* unwrap the object */	
+    current = dataStart(current, currentSize, &currentSize, false);
+    if (current == NULL) { return CKYINVALIDDATA; }
+
+    /*
+     * parse the Common Attributes 
+     *     label UTF8_STRING
+     *     flags BIT_STRING (optional)
+     *     authid OCTET_STRING (optional)
+     */
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) 
+	{ return CKYINVALIDDATA; }
+    /* unwrap */
+    commonAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* point current to the next section (cass attributes)  */
+    tagSize = commonAttributes - current;
+    current += commonSize + tagSize;
+    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+    currentSize -= (commonSize +tagSize);
+
+    /* get the CKA_LABEL */
+    if (commonAttributes[0] != ASN1_UTF8_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAttributes;
+    commonAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    setAttribute(CKA_LABEL, entry, entrySize);
+
+    /* parse optional flags */
+    bits = BROKEN_FLAG;
+    if (commonAttributes[0] == ASN1_BIT_STRING) {
+	entry = dataStart(commonAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAttributes;
+	commonAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	bits = GetBits(entry,entrySize,2,1);
+    }
+
+    if (commonAttributes[0] == ASN1_OCTET_STRING) {
+	entry = dataStart(commonAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAttributes;
+	commonAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	status = CKYBuffer_Replace(&authId, 0, entry, entrySize);
+	if (status != CKYSUCCESS) {
+	   return status;
+	}
+    }
+
+    if (bits & BROKEN_FLAG) {
+	bits = defaultCommonBits();
+    }
+    setAttributeBool(CKA_PRIVATE, 
+		(bits & P15FlagsPrivate) ? TRUE: FALSE);
+    setAttributeBool(CKA_MODIFIABLE, FALSE); /* our token is ReadOnly, so the
+					      * object is never modifiable for
+					      * us */
+    /* future common attributes here */
+
+    /*
+     *  Parse Class variables
+     *
+     */
+    switch (p15Type) {
+    case PK15Cert:
+	status = completeCertObject(current,currentSize);
+	break;
+    case PK15PuKey:
+    case PK15PvKey:
+	status = completeKeyObject(current,currentSize);
+	break;
+    case PK15AuthObj:
+	status = completeAuthObject(current, currentSize);
+	break;
+    }
+    return status;
+}
+
+
+CKYStatus 
+PK15Object::completeCertObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonCertAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    CKYByte valueTag;
+
+    CKYBuffer_InitEmpty(&empty);
+
+    /*
+     * parse the Common Cert Attributes 
+     *     id OCTET_STRING
+     *     authority BOOLEAN DEFAULT FALSE
+     *     requestId BIT_STRING (optional)
+     *     thumbprint [0] PKS15OOBCertHash (optional)
+     */
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) 
+		{ return CKYINVALIDARGS; }
+    /* unwrap */
+    commonCertAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonCertAttributes == NULL) { return CKYINVALIDDATA; }
+    /* point current to the next section (type attributes)  */
+    tagSize = commonCertAttributes - current;
+    current += commonSize + tagSize;
+    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+    currentSize -= (commonSize +tagSize);
+
+    /* get the id */
+    if (commonCertAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonCertAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonCertAttributes;
+    commonCertAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    setAttribute(CKA_ID, entry, entrySize);
+
+
+    /* skip authority (currently unused) */
+    /* skip requestID */
+    /* skip thumbprint */
+    /* future common cert attributes here */
+
+    /* certs have not subclass attributes  ASN1_CHOICE_0 */
+
+    /* handle the X509 type attributes */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDDATA; }
+    /* unwrap */
+    commonCertAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonCertAttributes == NULL) { return CKYINVALIDDATA; }
+   
+    /*
+     * PCKS11X504CertificateAttributes
+     *     value   SEQUENCE or CHOICE_0
+     *     ... don't care about the rest.
+     */
+    valueTag = commonCertAttributes[0];
+    /* unwrapp */
+    entry = dataStart(commonCertAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    if (valueTag == ASN1_SEQUENCE) {
+    	entry = dataStart(entry, entrySize, &entrySize, false);
+    	if (entry == NULL) { return CKYINVALIDDATA; }
+	/* if we have a path, the actual object is in another file,
+	 * tell the caller to get it and come back here */
+	status = objectPath.setObjectPath(entry, entrySize);
+	state = PK15StateNeedRawCertificate;
+        return status;
+    }
+    if (valueTag != ASN1_CHOICE_0) {
+	return CKYINVALIDDATA;
+    }
+    return  completeRawCertificate(entry, entrySize);
+}
+
+CKYStatus 
+PK15Object::completeAuthObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonAuthAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+
+    CKYBuffer_InitEmpty(&empty);
+
+    if (current == NULL) { return CKYINVALIDARGS; }
+    /* common Auth attributes */
+    if (current[0] == ASN1_SEQUENCE) {
+         /* unwrap */
+        commonAuthAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+	if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+	tagSize = commonAuthAttributes - current;
+	current += commonSize + tagSize;
+	if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+	currentSize -= (commonSize + tagSize);
+	if (commonAuthAttributes[0] != ASN1_OCTET_STRING) {
+	    return CKYINVALIDDATA;
+	}
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	status = CKYBuffer_Replace(&pinAuthId, 0, entry, entrySize);
+	if (status != CKYSUCCESS) {
+	   return status;
+	}
+	
+    }
+    /* auth specific values */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDARGS; }
+    /* unwrap */
+    commonAuthAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+    tagSize = commonAuthAttributes - current;
+    current += commonSize + tagSize;
+    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+    currentSize -= (commonSize + tagSize);
+    /*
+     * parse the Pin Auth Attributes 
+     *     pinFlags  BIT_STRING
+     *     pinType   ENUMERATED (bcd, ascii-numeric, utf8)
+     *     minLength INTEGER
+     *     storedLength INTEGER
+     *     maxlength INTEGER (optional)
+     *     pinReference CHOICE_0 (optional)
+     *     padChar OCTET_STRING (optional)
+     *     lastPinChange GENERALIZED_TIME (optional)
+     *     path PKCS15Path (optional)
+     */
+    if (commonAuthAttributes[0] != ASN1_SEQUENCE) { return CKYINVALIDARGS; }
+    commonAuthAttributes = dataStart(commonAuthAttributes, 
+					commonSize, &commonSize, false);
+    if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* parse pin flags */
+    if (commonAuthAttributes[0] != ASN1_BIT_STRING) { return CKYINVALIDDATA; }
+
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    pinInfo.pinFlags = GetBits(entry,entrySize,9,2);
+
+
+    /* parse PinType */
+    if (commonAuthAttributes[0] != ASN1_ENUMERATED) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    /* turn entry into an int */
+    if (entrySize > 1) { return CKYINVALIDARGS; }
+    pinInfo.pinType = (P15PinType) *entry;
+
+    /* parse minLength */
+    if (commonAuthAttributes[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    if (entrySize > 1) { return CKYINVALIDARGS; }
+    pinInfo.minLength = *entry;
+
+    /* parse storedLength */
+    if (commonAuthAttributes[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    if (entrySize > 1) { return CKYINVALIDARGS; }
+    pinInfo.storedLength = *entry;
+
+    /* parse maxLength (optional) */
+    if (commonAuthAttributes[0] == ASN1_INTEGER) { 
+	unsigned long maxPin;
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > sizeof (maxPin)) { return CKYINVALIDARGS; }
+	maxPin = 0; 
+	CKYSize i;
+	for (i=0; i < entrySize; i++) {
+	    maxPin = (maxPin << 8) | entry[i];
+	}
+	pinInfo.maxLength = maxPin;
+    }
+
+    /* parse pin ref  (optional) */
+    if ((commonAuthAttributes[0]|ASN1_CONSTRUCTED) == ASN1_CHOICE_0)  {
+	CKYByte pinRef;
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 2) { return CKYINVALIDARGS; }
+	if (entrySize == 2) {
+	    if (*entry != 0) { return CKYINVALIDARGS; }
+	    pinRef = entry[1];
+	} else pinRef = entry[0];
+	pinInfo.pinRef = pinRef;
+    }
+
+    /* parse padChar */
+    if (commonAuthAttributes[0] == ASN1_OCTET_STRING) { 
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 1) { return CKYINVALIDARGS; }
+	pinInfo.padChar = *entry;
+    }
+
+    /* skip lastPinChange */
+    if (commonAuthAttributes[0] == ASN1_GENERALIZED_TIME) { 
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+    }
+    /* parse path */
+    if (commonAuthAttributes[0] == ASN1_SEQUENCE) { 
+	entry = dataStart(commonAuthAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	/* if we have a path, the actual object is in another file,
+	 * tell the caller to get it and come back here */
+	status = objectPath.setObjectPath(entry, entrySize);
+	if (status != CKYSUCCESS) { return status; }
+    }
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+CKYStatus
+PK15Object::completeKeyObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonKeyAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    unsigned long bits;
+    /*bool native; */
+
+    CKYBuffer_InitEmpty(&empty);
+    /*
+     * parse the Common Key Attributes 
+     *     id OCTET_STRING
+     *     usageFlags BIT_STRING 
+     *     native BOOLEAN DEFAULT TRUE
+     *     accessFlags BIT_STRING (optional)
+     *     keyReference OCTET_STRING (optional)
+     *     startDate GENERALIZED_TIME (optional)
+     *     endDate [0] GENERALIZED_TYPE (optional)
+     */
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) 
+		{ return CKYINVALIDARGS; }
+    /* unwrap */
+    commonKeyAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonKeyAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* point current to the next section (sublcass attributes)  */
+    tagSize = commonKeyAttributes - current;
+    current += commonSize + tagSize;
+    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+    currentSize -= (commonSize + tagSize);
+
+    /* get the id */
+    if (commonKeyAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonKeyAttributes;
+    commonKeyAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    setAttribute(CKA_ID, entry, entrySize);
+
+    /* parse flags */
+    if (commonKeyAttributes[0] != ASN1_BIT_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonKeyAttributes;
+    commonKeyAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    bits = GetBits(entry,entrySize,10,2);
+    if (bits & BROKEN_FLAG) {
+	bits = defaultUsageBits();
+    }
+    setAttributeBool(CKA_ENCRYPT,
+			(bits & P15UsageEncrypt)          ? TRUE : FALSE);
+    setAttributeBool(CKA_DECRYPT,
+			(bits & P15UsageDecrypt)          ? TRUE : FALSE);
+    setAttributeBool(CKA_SIGN,
+			(bits & P15UsageSign)             ? TRUE : FALSE);
+    setAttributeBool(CKA_SIGN_RECOVER,
+			(bits & P15UsageSignRecover)      ? TRUE : FALSE);
+    setAttributeBool(CKA_WRAP,
+			(bits & P15UsageWrap)             ? TRUE : FALSE);
+    setAttributeBool(CKA_UNWRAP,
+			(bits & P15UsageUnwrap)           ? TRUE : FALSE);
+    setAttributeBool(CKA_VERIFY,
+			(bits & P15UsageVerify)           ? TRUE : FALSE);
+    setAttributeBool(CKA_VERIFY_RECOVER,
+			(bits & P15UsageVerifyRecover)    ? TRUE : FALSE);
+    setAttributeBool(CKA_DERIVE,
+			(bits & P15UsageDerive)           ? TRUE : FALSE);
+    /* no CKA value for P15UsageNonRepudiation */
+    if (bits & P15UsageNonRepudiation) {
+	/* set signing and sign recover. Non-repudiation keys are automatically
+         * signing keys */
+	setAttributeBool(CKA_SIGN, TRUE);
+	if (keyType == rsa) {
+	    setAttributeBool(CKA_SIGN_RECOVER, TRUE);
+	}
+    }
+
+    /* parse native (currently unused) */
+    /*native=true; */
+    if (commonKeyAttributes[0] == ASN1_BOOLEAN) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	/*if ((entrySize == 1) && (entry[0] == 0)) {
+	    native = false;
+	} */
+    }
+    /* parse access flags */
+    bits = BROKEN_FLAG;
+    if (commonKeyAttributes[0] == ASN1_BIT_STRING) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	bits = GetBits(entry,entrySize,4,1);
+    }
+    if (bits & BROKEN_FLAG) {
+	bits = defaultAccessBits();
+    }
+    setAttributeBool(CKA_SENSITIVE,  
+			(bits & P15AccessSensitive)       ? TRUE : FALSE);
+    setAttributeBool(CKA_EXTRACTABLE,
+			(bits & P15AccessExtractable)     ? TRUE : FALSE);
+    setAttributeBool(CKA_ALWAYS_SENSITIVE, 
+			(bits & P15AccessAlwaysSenstive)  ? TRUE : FALSE);
+    setAttributeBool(CKA_NEVER_EXTRACTABLE,
+			(bits & P15AccessNeverExtractable)? TRUE : FALSE);
+    setAttributeBool(CKA_LOCAL,      
+			(bits & P15AccessLocal)           ? TRUE : FALSE);
+
+    /* parse the key reference */
+    keyRef = PK15_INVALID_KEY_REF; /* invalid keyRef */
+    if (commonKeyAttributes[0] == ASN1_INTEGER) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize == 1) {
+	    keyRef = entry[0];
+	} else if ((entrySize == 2) && (entry[0] == 0)) {
+	    keyRef = entry[1];
+	}
+    }
+    setAttribute(CKA_START_DATE, &empty);
+    if (commonKeyAttributes[0] == ASN1_GENERALIZED_TIME) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	setAttribute(CKA_START_DATE,entry, entrySize);
+    }
+    setAttribute(CKA_END_DATE, &empty);
+    if (commonKeyAttributes[0] == ASN1_CHOICE_0) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	setAttribute(CKA_END_DATE,entry, entrySize);
+    }
+    /* future common key attributes here */
+
+    /*
+     *  Parse Class variables
+     *
+     */
+    switch (p15Type) {
+    case PK15PuKey:
+	status = completePubKeyObject(current,currentSize);
+	break;
+    case PK15PvKey:
+	status = completePrivKeyObject(current,currentSize);
+	break;
+    default:
+	status=CKYLIBFAIL; /* shouldn't happen */
+	break;
+    }
+    return status;
+}
+
+CKYStatus PK15Object::completePrivKeyObject(const CKYByte *current,
+							CKYSize currentSize)
+{
+    const CKYByte *commonPrivKeyAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    unsigned int modulusSize;
+    unsigned int i;
+
+    CKYBuffer_InitEmpty(&empty);
+    if (current == NULL) { return CKYINVALIDARGS; }
+
+    /* optional subclass = CommonPrivateKeyAttributes */
+    if (current[0] == ASN1_CHOICE_0) {
+	/*
+         * PKCS15CommonPrivateKeyAttributes
+         *
+         * subjectName   SEQUENCE optional
+         * keyIdentifiers CHOICE 0  optional
+         */
+	/* unwrap */
+	commonPrivKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+	if (commonPrivKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	/* point current to the next section (type attributes)  */
+	tagSize = commonPrivKeyAttributes - current;
+	current += commonSize + tagSize;
+	if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+	currentSize -= (commonSize +tagSize);
+
+ 	/* subjectName */
+	if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPrivKeyAttributes, commonSize, 
+							&entrySize, false);
+	    if (entry == NULL) { return CKYINVALIDARGS; }
+	    tagSize = entry - commonPrivKeyAttributes;
+	    commonPrivKeyAttributes += entrySize + tagSize;
+	    commonSize -= (entrySize +tagSize);
+	    setAttribute(CKA_SUBJECT, entry, entrySize);
+	}
+
+	/* keyIdentfiers */
+	/* future CommonPrivateKeyAttributes here */
+    }
+
+    
+    /* Type attributes (either PKCS15RSAPrivateKeyAttributes or 
+     * PKCS15ECCPrivateKeyAttributes) -- Not Optional */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDDATA; }
+    /*
+     *    PKCS15RSAPrivateKeyAttributes
+     *        value PKCS15ObjectValue
+     *        modulusLength INTEGER
+     *        keyInfo SEQUENCE optional
+     *    PKCS15ECCPrivateKeyAttributes
+     *        value PKCS15ObjectValue
+     *        keyInfo SEQUENCE optional
+     */
+    /* unwrap */
+    commonPrivKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+    if (commonPrivKeyAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* value */
+     /* don't support direct private key objects */
+    if (commonPrivKeyAttributes[0] == ASN1_CHOICE_0) { return CKYUNSUPPORTED;  }
+    if (commonPrivKeyAttributes[0] != ASN1_SEQUENCE) { return CKYINVALIDDATA; }
+    commonPrivKeyAttributes = dataStart(commonPrivKeyAttributes, commonSize, &commonSize, false);
+    if (commonPrivKeyAttributes == NULL) { return CKYINVALIDARGS; }
+    entry = dataStart(commonPrivKeyAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonPrivKeyAttributes;
+    commonPrivKeyAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    /* if we have a path, the actual object is in another file,
+     * tell the caller to get it and come back here */
+    status = objectPath.setObjectPath(entry, entrySize);
+    if (status != CKYSUCCESS) { return status; }
+
+    /* parse modulus size */
+    if ((keyType == rsa) && commonPrivKeyAttributes[0] == ASN1_INTEGER) {
+	entry = dataStart(commonPrivKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonPrivKeyAttributes;
+	commonPrivKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 4) {
+	   return CKYINVALIDDATA;
+	}
+	for (modulusSize = 0, i=0; i < entrySize; i++) {
+	   modulusSize = (modulusSize << 8) + entry[i];
+	}
+	setKeySize(modulusSize);
+    }
+
+    if (keyType == rsa) {
+	state = PK15StateComplete;
+	return CKYSUCCESS; /* we're done with RSA */
+    }
+
+    /* parse keyinfo  at this point all we are after is the EC_PARAM*/
+    if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+	/* unwrap */
+	commonPrivKeyAttributes = dataStart(commonPrivKeyAttributes, 
+					commonSize, &commonSize, true);
+	if (commonPrivKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPrivKeyAttributes, commonSize, 
+							&entrySize, true);
+	    if (entry == NULL) { return CKYINVALIDDATA; }
+	    setAttribute(CKA_EC_PARAMS, entry, entrySize);
+	}
+    }
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+CKYStatus 
+PK15Object::completePubKeyObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonPubKeyAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    unsigned int modulusSize;
+    unsigned int i;
+
+    CKYBuffer_InitEmpty(&empty);
+    if (current == NULL) { return CKYINVALIDDATA; }
+
+    /* optional subclass = CommonPublicKeyAttributes */
+    if (current[0] == ASN1_CHOICE_0) {
+	/*
+         * PKCS15CommonPublicKeyAttributes
+         *
+         * subjectName   SEQUENCE optional
+         * keyIdentifiers CHOICE 0  optional
+         */
+	/* unwrap */
+	commonPubKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+	if (commonPubKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	/* point current to the next section (type attributes)  */
+	tagSize = commonPubKeyAttributes - current;
+	current += commonSize + tagSize;
+	if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+	currentSize -= (commonSize + tagSize);
+
+ 	/* subjectName */
+	if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	    if (entry == NULL) { return CKYINVALIDARGS; }
+	    tagSize = entry - commonPubKeyAttributes;
+	    commonPubKeyAttributes += entrySize + tagSize;
+	    commonSize -= (entrySize +tagSize);
+	    setAttribute(CKA_SUBJECT, entry, entrySize);
+	}
+	/* future CommonPublicKeyAttributes here */
+    }
+
+    
+    /* Type attributes (either PKCS15RSAPublicKeyAttributes or 
+     * PKCS15ECCPublicKeyAttributes) -- Not Optional */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDDATA; }
+    /*
+     *    PKCS15RSAPublicKeyAttributes
+     *        value PKCS15ObjectValue
+     *        modulusLength INTEGER
+     *        keyInfo SEQUENCE optional
+     *    PKCS15ECCPublicKeyAttributes
+     *        value PKCS15ObjectValue
+     *        keyInfo SEQUENCE optional
+     */
+    /* unwrap */
+    commonPubKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+    if (commonPubKeyAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* value */
+    if (commonPubKeyAttributes[0] == ASN1_CHOICE_0) { 
+    	entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	status = completeRawPublicKey(entry, entrySize);
+	if (status != CKYSUCCESS) { return status; }
+    } else if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) { 
+	entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonPubKeyAttributes;
+	commonPubKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	/* if we have a path, the actual object is in another file,
+	 * tell the caller to get it and come back here */
+	status = objectPath.setObjectPath(entry, entrySize);
+	if (status != CKYSUCCESS) { return status; }
+	state = PK15StateNeedRawPublicKey;
+    }
+
+    /* parse modulus size */
+    if ((keyType == rsa) && commonPubKeyAttributes[0] == ASN1_INTEGER) {
+	entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonPubKeyAttributes;
+	commonPubKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 4) {
+	   return CKYINVALIDDATA;
+	}
+	for (modulusSize = 0, i=0; i < entrySize; i++) {
+	   modulusSize = (modulusSize << 8) + entry[i];
+	}
+	setKeySize(modulusSize);
+    }
+
+    if (keyType == rsa) {
+	return CKYSUCCESS; /* we're done with RSA */
+    }
+
+    /* parse keyinfo  at this point all we are after is the EC_PARAM*/
+    if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+	/* unwrap */
+	commonPubKeyAttributes = dataStart(commonPubKeyAttributes, 
+					commonSize, &commonSize, true);
+	if (commonPubKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, true);
+	    if (entry == NULL) { return CKYINVALIDDATA; } 
+	    setAttribute(CKA_EC_PARAMS, entry, entrySize);
+	}
+    }
+    return CKYSUCCESS;
+
+}
+
+CKYStatus 
+PK15Object::completeRawCertificate(const CKYByte *derCert, CKYSize derCertSize)
+{
+    SECStatus rv;
+    CCItem issuerItem, serialItem, derSerialItem, subjectItem,
+        validityItem, subjectKeyItem;
+    const char *certLabel;
+
+    setAttribute(CKA_VALUE, derCert, derCertSize);
+    rv = GetCertFieldItems(derCert, derCertSize, 
+        &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem,
+        &subjectKeyItem);
+    if (rv != SECSuccess) {
+	return CKYINVALIDDATA;
+    }
+    setAttribute(CKA_SERIAL_NUMBER, derSerialItem.data, derSerialItem.len);
+    setAttribute(CKA_SUBJECT, subjectItem.data, subjectItem.len);
+    setAttribute(CKA_ISSUER, issuerItem.data, issuerItem.len);
+    CKYBuffer_Replace(&pubKey, 0, subjectKeyItem.data, subjectKeyItem.len);
+    /* if we didn't get a label, set one based on the CN */
+    certLabel = getLabel();
+    if ((certLabel == NULL) || (*certLabel == 0)) {
+	CKYBuffer subject;
+	char *newLabel;
+	CKYBuffer_InitFromData(&subject, subjectItem.data, subjectItem.len);
+	newLabel = GetUserName(&subject);
+	if (newLabel) {
+	    setAttribute(CKA_LABEL, (CKYByte *)newLabel, 
+					(CKYSize) strlen(newLabel)-1);
+	    delete [] newLabel;
+	}
+	CKYBuffer_FreeData(&subject);
+    }
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+CKYStatus 
+PK15Object::completeRawPublicKey(const CKYByte *current, CKYSize size)
+{
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) {
+	return CKYINVALIDDATA;
+    }
+    /* unwrap*/
+    current = dataStart(current, size, &size, false);
+    if (current == NULL) { return CKYINVALIDDATA; }
+
+    /* modulus */
+    if (current[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    tagSize = entry - current;
+    current += entrySize + tagSize;
+    if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+    size -= (entrySize +tagSize);
+    if ((entry[0] == 0) && (entrySize > 1)) {
+	entry++; entrySize--;
+    }
+    setAttribute(CKA_MODULUS, entry, entrySize);
+
+    /* exponent */
+    if (current[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    tagSize = entry - current;
+    current += entrySize + tagSize;
+    if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+    size -= (entrySize + tagSize);
+    if ((entry[0] == 0) && (entrySize > 1)) {
+	entry++; entrySize--;
+    }
+    setAttribute(CKA_PUBLIC_EXPONENT, entry, entrySize);
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+DEREncodedSignature::DEREncodedSignature(const CKYBuffer *derSig)
+{
+
+    CKYBuffer_InitEmpty(&derEncodedSignature);
+    CKYBuffer_InitFromCopy(&derEncodedSignature, derSig);
+}
+
+DEREncodedSignature::~DEREncodedSignature()
+{
+    CKYBuffer_FreeData(&derEncodedSignature);
+}
+
+int DEREncodedSignature::getRawSignature(CKYBuffer *rawSig, 
+					  unsigned int keySize)
+{
+    const CKYByte *buf = NULL;
+
+    if (rawSig == NULL) {
+        return -1;
+    }
+
+    if (CKYBuffer_Size(&derEncodedSignature) == 0) {
+        return -1;
+    }
+
+    CKYBuffer_Zero(rawSig);
+
+    CKYSize seq_length = 0;
+    CKYSize expected_sig_len = ( (keySize + 7) / 8 ) * 2 ;
+    CKYSize expected_piece_size = expected_sig_len / 2 ;
+
+    /* unwrap the sequence */
+    buf = dataStart(CKYBuffer_Data(&derEncodedSignature), CKYBuffer_Size(&derEncodedSignature),&seq_length, false);
+
+    if (buf == NULL) return -1;
+
+    // unwrap first multi byte integer
+   
+    CKYSize int_length = 0;
+    const CKYByte *int1Buf = NULL;
+    const CKYByte *int2Buf = NULL;
+
+    int1Buf = dataStart(buf, seq_length, &int_length, false );
+
+    if (int1Buf == NULL) return -1;
+    //advance to next entry
+
+    if (int_length > expected_piece_size) {
+
+      unsigned int diff = int_length - expected_piece_size ;
+
+      /* Make sure we are chopping off zeroes 
+         Otherwise give up. */
+
+      for (int i = 0 ; i < (int) diff ; i++) {
+          if ( int1Buf[i] != 0) 
+              return -1;
+      }
+
+      int_length -= diff;
+      int1Buf += diff;
+
+    }
+
+    seq_length -= (int1Buf -buf) + int_length;
+    buf = int1Buf +  int_length;
+
+    // unwrap second multi byte integer
+
+    CKYSize second_int_length = 0;
+
+    int2Buf = dataStart(buf, seq_length, &second_int_length, false);
+
+    if (int2Buf == NULL) return -1;
+
+
+    if (second_int_length > expected_piece_size) {
+        unsigned int diff = second_int_length - expected_piece_size ;
+
+        /* Make sure we are chopping off zeroes 
+           Otherwise give up. */
+
+        for (int i = 0 ;  i < (int)  diff ; i++) {
+            if ( int2Buf[i] != 0) 
+                return -1;
+        }
+      
+        second_int_length -= diff;
+        int2Buf += diff;
+    }
+
+    CKYBuffer_AppendData(rawSig, int1Buf, int_length);
+    CKYBuffer_AppendData(rawSig, int2Buf, second_int_length);
+
+    return CKYSUCCESS;
+}
+
+DEREncodedTokenInfo::DEREncodedTokenInfo(CKYBuffer *derTokenInfo)
+{
+    const CKYByte *current = CKYBuffer_Data(derTokenInfo);
+    const CKYByte *entry;
+    CKYSize size = CKYBuffer_Size(derTokenInfo);
+    CKYSize entrySize;
+    CKYSize tagSize;
+    /* set token name, etc */
+
+    version = -1;
+    CKYBuffer_InitEmpty(&serialNumber);
+    manufacturer = NULL;
+    tokenName = NULL;
+
+    if (current[0] != ASN1_SEQUENCE) {
+	return; /* just use the defaults */
+    }
+    /* unwrap */
+    current = dataStart(current, size, &size, false);
+    if (current == NULL) return;
+
+    /* parse the version */
+    if (current[0] != ASN1_INTEGER) { return; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) return;
+    tagSize = entry - current;
+    current += tagSize + entrySize;
+    if (size < tagSize + entrySize) return;
+    size -= tagSize + entrySize;
+    if (entrySize < 1) {
+	version = *entry;
+    }
+
+    /* get the serial number */
+    if (current[0] != ASN1_OCTET_STRING) { return ; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) return;
+    tagSize = entry - current;
+    current += tagSize + entrySize;
+    size -= tagSize + entrySize;
+    CKYBuffer_Replace(&serialNumber, 0, entry, entrySize);
+    /* should we fake the cuid here? */
+
+    /* get the optional manufacture ID */
+    if (current[0] == ASN1_UTF8_STRING) {
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) return;
+	tagSize = entry - current;
+	current += tagSize + entrySize;
+	size -= tagSize + entrySize;
+	manufacturer = (char *)malloc(entrySize+1);
+	if (manufacturer) {
+	    memcpy(manufacturer, entry, entrySize);
+	    manufacturer[entrySize] = 0;
+	}
+    }
+
+    /* get the optional token name */
+    /* most choices are constructed, 
+     * but this one isn't explicity add the flag */
+    if ((current[0]|ASN1_CONSTRUCTED) == ASN1_CHOICE_0) {
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) return;
+	tagSize = entry - current;
+	current += tagSize + entrySize;
+	size -= tagSize + entrySize;
+	tokenName = (char *)malloc(entrySize+1);
+	if (tokenName) {
+	    memcpy(tokenName, entry, entrySize);
+	    tokenName[entrySize] = 0;
+	}
+    }
+
+    /* parsing flags */
+#ifdef notdef
+    /* we arn't using this right now, keep it for future reference */
+    if (current[0] == ASN1_BIT_STRING) {
+    /* recordinfo parsing would go here */
+	unsigned long bits;
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) return;
+	tagSize = entry - current;
+	current += tagSize + entrySize;
+	size -= tagSize + entrySize;
+	bits = GetBits(entry, entrySize,8,2);
+    }
+#endif
+    return;
+}
+
diff -up ./src/coolkey/object.h.coolkey-update ./src/coolkey/object.h
--- ./src/coolkey/object.h.coolkey-update	2016-01-19 17:53:12.934012153 -0800
+++ ./src/coolkey/object.h	2016-01-19 17:53:22.057937117 -0800
@@ -27,6 +27,33 @@
 
 using std::list;
 
+/*
+ * Sigh PKCS 15 is heavily ASN.1...
+ */
+const CKYByte  ASN1_BOOLEAN           = 0x01;
+const CKYByte  ASN1_INTEGER           = 0x02;
+const CKYByte  ASN1_BIT_STRING        = 0x03;
+const CKYByte  ASN1_OCTET_STRING      = 0x04;
+const CKYByte  ASN1_ENUMERATED        = 0x0a;
+const CKYByte  ASN1_UTF8_STRING       = 0x0c;
+const CKYByte  ASN1_GENERALIZED_TIME  = 0x18;
+const CKYByte  ASN1_CONSTRUCTED       = 0x20;
+const CKYByte  ASN1_SEQUENCE          = 0x30;
+const CKYByte  ASN1_CHOICE_0          = 0xa0;
+const CKYByte  ASN1_CHOICE_1          = 0xa1;
+const CKYByte  ASN1_CHOICE_2          = 0xa2;
+const CKYByte  ASN1_CHOICE_3          = 0xa3;
+
+const CKYBitFlags BROKEN_FLAG = 0x80000000;
+const unsigned int PK11_INVALID_KEY_REF = -1;
+
+const CKYByte PK15X509CertType = ASN1_SEQUENCE;
+const CKYByte PK15RSAKeyType   = ASN1_SEQUENCE;
+const CKYByte PK15ECCKeyType   = ASN1_CHOICE_0;
+const CKYByte PK15DHKeyType    = ASN1_CHOICE_1;
+const CKYByte PK15DSAKeyType   = ASN1_CHOICE_2;
+const CKYByte PK15KEAKeyType   = ASN1_CHOICE_3;
+
 class PKCS11Attribute {
   private:
     CK_ATTRIBUTE_TYPE type;
@@ -49,14 +76,40 @@ class PKCS11Attribute {
 				CKYBuffer_Size(&cpy.value));
 	return *this;
     }
-    PKCS11Attribute() { CKYBuffer_InitEmpty(&value); }
+    PKCS11Attribute() : type(0){ CKYBuffer_InitEmpty(&value); }
     PKCS11Attribute(CK_ATTRIBUTE_TYPE type_, const CKYBuffer *value_)
         : type(type_) { CKYBuffer_InitFromCopy(&value, value_); }
+    PKCS11Attribute(CK_ATTRIBUTE_TYPE type_, const CKYByte *data_,
+	 CKYSize size_) : type(type_) 
+		{ CKYBuffer_InitFromData(&value, data_, size_); }
     ~PKCS11Attribute() { CKYBuffer_FreeData(&value); }
 };
 
+class PK15ObjectPath {
+  private:
+    CKYBuffer path;
+    CKYOffset index;
+    CKYSize   length;
+   public:
+    PK15ObjectPath() : index(0), length(0) { CKYBuffer_InitEmpty(&path); }
+    PK15ObjectPath(const PK15ObjectPath &cpy) : 
+		index(cpy.index), length(cpy.length) 
+		{ CKYBuffer_InitFromCopy(&path, &cpy.path); }
+    ~PK15ObjectPath() { CKYBuffer_FreeData(&path); }
+    const CKYBuffer *getPath() const { return &path; }
+    CKYOffset getIndex() const { return index; }
+    CKYSize getLength() const { return length; }
+    CKYStatus setObjectPath(const CKYByte *entry, CKYSize size);
+};
+
+
 class PKCS11Object {
   public:
+    enum KeyType {
+        rsa,
+        ecc,
+        unknown
+    };
 
     typedef list<PKCS11Attribute> AttributeList;
     typedef AttributeList::iterator AttributeIter;
@@ -67,6 +120,8 @@ class PKCS11Object {
     unsigned long muscleObjID;
     CK_OBJECT_HANDLE handle;
     char *label;
+    unsigned int keySize;
+    CK_USER_TYPE user;
 
     void parseOldObject(const CKYBuffer *data);
     void parseNewObject(const CKYBuffer *data);
@@ -75,19 +130,39 @@ class PKCS11Object {
     PKCS11Object &operator=(PKCS11Object &cpy) { return *this; } //Disallow
 
   protected :
-    CKYBuffer pubKey; 
     char *name;
+    KeyType keyType;
+    unsigned int keyRef;
+    CKYBuffer pubKey;
+    CKYBuffer authId;
+    CKYBuffer pinAuthId;
+    PK15ObjectPath objectPath;
 
   public:
     PKCS11Object(unsigned long muscleObjID, CK_OBJECT_HANDLE handle);
     PKCS11Object(unsigned long muscleObjID, const CKYBuffer *data,
         CK_OBJECT_HANDLE handle);
-    ~PKCS11Object() { delete [] label; delete [] name; CKYBuffer_FreeData(&pubKey); }
+    virtual ~PKCS11Object() { delete [] label; delete [] name; 
+		CKYBuffer_FreeData(&pubKey); CKYBuffer_FreeData(&authId);
+		CKYBuffer_FreeData(&pinAuthId); attributes.clear(); }
 
     PKCS11Object(const PKCS11Object& cpy) :
         attributes(cpy.attributes), muscleObjID(cpy.muscleObjID),
-        handle(cpy.handle), label(NULL),  name(NULL) { 
-			CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey); }
+        handle(cpy.handle), label(NULL),  keySize(cpy.keySize), 
+	user(cpy.user), name(NULL), keyType(cpy.keyType), keyRef(cpy.keyRef),
+	objectPath(cpy.objectPath) { 
+			/* label is just a cached value, don't need 
+  			 * to copy it. */
+			if (cpy.name != NULL) {
+			    int len = strlen(cpy.name);
+			    name= new char [len+1];
+			    if (name) {
+				memcpy(name,cpy.name,len+1);
+			    }
+			}
+			CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey);
+			CKYBuffer_InitFromCopy(&authId,&cpy.authId);
+			CKYBuffer_InitFromCopy(&pinAuthId,&cpy.pinAuthId); }
 
 
     unsigned long getMuscleObjID() const { return muscleObjID; }
@@ -100,6 +175,8 @@ class PKCS11Object {
 
     void setAttribute(CK_ATTRIBUTE_TYPE type, const CKYBuffer *value);
     void setAttribute(CK_ATTRIBUTE_TYPE type, const char *);
+    void setAttribute(CK_ATTRIBUTE_TYPE type, const CKYByte *data, 
+							CKYSize size);
     /* bools and ulongs are too close, don't abuse function overloading
      * for these cases */
     void setAttributeBool(CK_ATTRIBUTE_TYPE type, CK_BBOOL);
@@ -116,14 +193,22 @@ class PKCS11Object {
     const CKYBuffer *getPubKey(void) const {
 	return &pubKey;
     }
+
+    KeyType getKeyType(void) const { return keyType;}
+    unsigned int getKeySize(void) const { return keySize; }
+    unsigned int getKeyRef(void) const { return keyRef; }
+    CK_USER_TYPE getUser(void) const { return user; }
+    void setKeyType(KeyType theType) { keyType = theType; }
+    void setKeySize(unsigned int keySize_) { keySize = keySize_; }
+    const CKYBuffer *getAuthId(void) const { return &authId; }
+    const CKYBuffer *getPinAuthId(void) const { return &pinAuthId; }
+    const PK15ObjectPath &getObjectPath() const { return objectPath; }
+    void completeKey(const PKCS11Object &cert);
 };
 
 class Key : public PKCS11Object {
-
   public:
     Key(unsigned long muscleObjID, const CKYBuffer *data, CK_OBJECT_HANDLE handle);
-    void completeKey(const PKCS11Object &cert);
-	
 };
 
 class Cert : public PKCS11Object {
@@ -147,12 +232,124 @@ class CACCert : public PKCS11Object {
     CACCert(CKYByte instance, const CKYBuffer *derCert);
 };
 
+typedef enum { PK15StateInit, PK15StateNeedObject, 
+	PK15StateNeedRawPublicKey,PK15StateNeedRawCertificate, 
+	PK15StateComplete } PK15State;
+
+typedef enum {PK15PvKey, PK15PuKey, PK15Cert, PK15AuthObj} PK15ObjectType;
+const unsigned int PK15_INVALID_KEY_REF = -1;
+
+class PK15Object : public PKCS11Object {
+  private:
+    CKYByte	instance;
+    PK15ObjectType p15Type;
+    PK15State state;
+    P15PinInfo pinInfo;
+
+    CKYStatus completeCertObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completeAuthObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completeKeyObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completePrivKeyObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completePubKeyObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completeRawPublicKey(const CKYByte *buf, CKYSize size);
+    CKYStatus completeRawCertificate(const CKYByte *buf, CKYSize size);
+   
+    CKYBitFlags defaultCommonBits() {
+	return ((p15Type == PK15PvKey) && (CKYBuffer_Size(&authId) != 0)) ?
+		P15FlagsPrivate : 0;
+    }
+    CKYBitFlags defaultUsageBits() {
+	CKYBitFlags sign, recover, encrypt;
+	switch (p15Type) {
+	case PK15PuKey:
+	    sign = P15UsageVerify; recover = P15UsageVerifyRecover;
+	    encrypt = P15UsageEncrypt;
+	    break;
+	case PK15PvKey:
+	    sign = P15UsageSign; recover = P15UsageSignRecover;
+	    encrypt = P15UsageDecrypt;
+	    break;
+	default:
+	    sign = 0; recover = 0; encrypt = 0;
+	    break;
+	}
+	switch(keyType) {
+	case rsa:
+	    return sign | recover | encrypt;
+	case ecc:
+	    return sign | P15UsageDerive;
+	default:
+	    break;
+	}
+	return 0;
+    }
+    CKYBitFlags defaultAccessBits() {
+	switch (p15Type) {
+	case PK15PuKey:
+		return P15AccessExtractable | P15AccessLocal;
+	case PK15PvKey:
+		return P15AccessSensitive | P15AccessLocal;
+	default:
+		break;
+	}
+	return 0;
+    }
+    CKYBitFlags defaultPinBits() {
+	return ((p15Type == PK15AuthObj) ?  P15PinInitialized : 0);
+    }
+		
+  public:
+    PK15Object(CKYByte inst, PK15ObjectType type, 
+					const CKYByte *derObject, CKYSize size);
+    CKYStatus completeObject(const CKYByte *data, CKYSize size);
+    PK15State getState(void) const { return state; }
+    bool isSO(void) const { return 
+		(pinInfo.pinFlags & P15PinSOPin) ? true : false; }
+    bool isLocal(void) const { return 
+			(pinInfo.pinFlags & P15PinLocal) ? true : false; }
+    const P15PinInfo *getPinInfo(void) const { return &pinInfo; }
+};
+
 class Reader : public PKCS11Object {
   public:
     Reader(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, 
 		const char *reader, const CKYBuffer *cardATR, bool isCoolkey);
 };
 
+class SecretKey : public PKCS11Object {
+    public: 
+      SecretKey(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount);
+    private:
+      void adjustToKeyValueLength(CKYBuffer * secretKeyBuffer,CK_ULONG valueLength);
+
+};
+
+class DEREncodedSignature  {
+
+  protected :
+    CKYBuffer derEncodedSignature;
+  public:
+    DEREncodedSignature(const CKYBuffer *derSig);
+    ~DEREncodedSignature();
+    int getRawSignature(CKYBuffer *rawSig, unsigned int keySize);
+
+};
+
+class DEREncodedTokenInfo {
+public:
+   int   version;
+   CKYBuffer serialNumber;
+   char *manufacturer;
+   char *tokenName;
+   public :
+   DEREncodedTokenInfo(CKYBuffer *derTokenInfo);
+   ~DEREncodedTokenInfo() { 
+	CKYBuffer_FreeData(&serialNumber);
+	free(manufacturer);
+	free(tokenName);
+    }
+};
+
 class AttributeMatch {
 
   private:
@@ -175,6 +372,9 @@ makeLEUInt(const CKYBuffer *buf, unsigne
             (b[offset+0] <<  0) ;
 }
 
+const CKYByte* dataStart(const CKYByte *buf, CKYSize length,
+                        CKYSize *data_length, bool includeTag);
+
 // fixed object ID constants 
 #define READER_ID 0x72300000 /* 'r0\0\0' */
 #define COMBINED_ID 0x7a300000 /* 'z0\0\0' */
diff -up ./src/coolkey/pkcs11t.h.coolkey-update ./src/coolkey/pkcs11t.h
--- ./src/coolkey/pkcs11t.h.coolkey-update	2016-01-19 17:55:02.900107782 -0800
+++ ./src/coolkey/pkcs11t.h	2016-01-19 17:55:16.040999709 -0800
@@ -274,6 +274,7 @@ typedef CK_ULONG          CK_USER_TYPE;
 #define CKU_SO    0
 /* Normal user */
 #define CKU_USER  1
+#define CKU_CONTEXT_SPECIFIC 2
 
 
 /* CK_STATE enumerates the session states */
@@ -492,6 +493,9 @@ typedef CK_ULONG          CK_ATTRIBUTE_T
 #define CKA_RESET_ON_INIT      0x00000301
 #define CKA_HAS_RESET          0x00000302
 
+/* new for v2.20 */
+#define CKA_ALWAYS_AUTHENTICATE  0x00000202
+
 #define CKA_VENDOR_DEFINED     0x80000000
 
 
@@ -1351,4 +1355,41 @@ typedef struct CK_PKCS5_PBKD2_PARAMS {
 
 typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
 
+/* The following EC Key Derivation Functions are defined */
+
+#define CKD_NULL                 0x00000001
+
+#define CKD_SHA1_KDF             0x00000002
+
+/* CK_ECDH1_DERIVE_PARAMS is new for v2.11. */
+
+ typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/*  
+ *    CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ *    
+ *    CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ *   
+ *    where each party contributes one key pair.
+ *   
+ */
+
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+
+  CK_EC_KDF_TYPE kdf;
+
+  CK_ULONG ulSharedDataLen;
+
+  CK_BYTE_PTR pSharedData;
+
+  CK_ULONG ulPublicDataLen;
+
+  CK_BYTE_PTR pPublicData;
+
+} CK_ECDH1_DERIVE_PARAMS;
+
+
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
 #endif
diff -up ./src/coolkey/slot.cpp.coolkey-update ./src/coolkey/slot.cpp
--- ./src/coolkey/slot.cpp.coolkey-update	2016-01-19 17:55:37.409823970 -0800
+++ ./src/coolkey/slot.cpp	2016-01-19 17:55:56.591666216 -0800
@@ -54,6 +54,39 @@ const CKYByte ATR2[] =
 {  0x3B, 0x6F, 0x00, 0xFF, 0x52, 0x53, 0x41, 0x53, 0x65, 0x63, 0x75, 0x72,
    0x49, 0x44, 0x28, 0x52, 0x29, 0x31, 0x30 };
 
+/* PKCS #15 AID */
+const CKYByte P15AID[] =
+{ 0xa0, 0, 0,  0, 0x63, 'P', 'K', 'C', 'S', '-', '1', '5'};
+
+
+
+/* ECC curve information
+ *    Provide information for the limited set of curves supported by our smart card(s).
+ *    
+ */
+
+typedef struct curveBytes2Name {
+    const CKYByte * bytes;
+    const char *curveName;
+    unsigned int length;
+
+} CurveBytes2Name;
+
+/* First byte is length of oid byte array. */
+
+const CKYByte nistp256[] = { 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07};
+const CKYByte nistp384[] = { 0x5, 0x2b, 0x81, 0x04, 0x00, 0x22 };
+const CKYByte nistp521[] = { 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 };
+
+const int numECCurves = 3;
+
+static CurveBytes2Name curveBytesNamePair[] =
+{
+ { nistp256, "nistp256", 256 },
+ { nistp384, "nistp384", 384 },
+ { nistp521, "nistp521", 521 }
+};
+
 SlotList::SlotList(Log *log_) : log(log_)
 {
     // initialize things to NULL so we can recover from an exception
@@ -136,7 +169,11 @@ SlotList::updateSlotList()
 	    throw PKCS11Exception(CKR_HOST_MEMORY);
 	memset(newSlots, 0, numReaders*sizeof(Slot*));
 
-        memcpy(newSlots, slots, sizeof(slots[0]) * numSlots);
+        /* keep coverity happy, even though slot == NULL implies that
+	 * numSlots == 0 */
+	if (slots) { 
+            memcpy(newSlots, slots, sizeof(slots[0]) * numSlots);
+	}
 
 	for (unsigned int i=numSlots; i < numReaders; i++) {
 	    newSlots[i] = new
@@ -237,32 +274,19 @@ SlotList::updateReaderList()
 
     CKYStatus status = CKYCardContext_ListReaders(context, &readerNames);
     if ( status != CKYSUCCESS ) {
-	throw PKCS11Exception(CKR_GENERAL_ERROR,
+	/* if the service is stopped, treat it as if we have no readers */
+ 	if ((CKYCardContext_GetLastError(context) != SCARD_E_NO_SERVICE) && 
+	    (CKYCardContext_GetLastError(context) != SCARD_E_SERVICE_STOPPED)) {
+	    throw PKCS11Exception(CKR_GENERAL_ERROR,
                 "Failed to list readers: 0x%x\n", 
 				CKYCardContext_GetLastError(context));
+	}
     }
 
-    if (!readerStates) {
+    if (readerStates == NULL && readerNames != NULL) {
 	/* fresh Reader State list, just create it */
 	readerStates = CKYReader_CreateArray(readerNames, (CKYSize *)&numReaders);
 
-	/* if we have no readers, make sure we have at least one to keep things
-	 * happy */
-	if (readerStates == NULL &&
-			 CKYReaderNameList_GetCount(readerNames) == 0) {
-	    readerStates = (SCARD_READERSTATE *)
-				malloc(sizeof(SCARD_READERSTATE));
-	    if (readerStates) {
-		CKYReader_Init(readerStates);
-		status = CKYReader_SetReaderName(readerStates, "E-Gate 0 0");
-		if (status != CKYSUCCESS) {
- 		    CKYReader_DestroyArray(readerStates, 1);
-		    readerStates = NULL;
-		} else {
-		    numReaders = 1;
-		}
-	    }
-	}
 	CKYReaderNameList_Destroy(readerNames);
 	        
 	if (readerStates == NULL) {
@@ -272,6 +296,16 @@ SlotList::updateReaderList()
 	return;
     }
 
+    if (readerStates == NULL) {
+	/* if we didn't have any readers before and we did get new names, 
+	 * that is handled above. If we didn't have any readers before, and
+	 * we didn't get any names, there is nothing to update. blow out now.
+	 * This more efficient and makes coverity happy (since coverity doesn't
+	 * know numReaders and readerStates are linked). */
+	return;
+    }
+
+
     /* it would be tempting at this point just to see if we have more readers
      * then specified previously. The problem with this is it is possible that
      * some readers have been deleted, so the only way to tell if we have
@@ -286,18 +320,26 @@ SlotList::updateReaderList()
 
     const char *curReaderName = NULL;
     unsigned long knownState = 0;
-    for(int ri = 0 ; ri < numReaders; ri ++)  {
+    for(unsigned int ri = 0 ; ri < numReaders; ri ++)  {
         knownState = CKYReader_GetKnownState(&readerStates[ri]);
-
+ 
         curReaderName =  CKYReader_GetReaderName(&readerStates[ri]); 
-        if(readerNameExistsInList(curReaderName,&readerNames)) {
-            CKYReader_SetKnownState(&readerStates[ri], knownState & ~SCARD_STATE_IGNORE); 
+        if(readerNames && readerNameExistsInList(curReaderName,&readerNames)) {
+            CKYReader_SetKnownState(&readerStates[ri], 
+		 knownState & ~SCARD_STATE_IGNORE); 
         } else {
-            if (!(knownState & SCARD_STATE_UNAVAILABLE))
-                CKYReader_SetKnownState(&readerStates[ri], knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED);
-        }
+	    if (!(knownState & SCARD_STATE_UNAVAILABLE))
+		CKYReader_SetKnownState(&readerStates[ri], 
+		 knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED);
+	}
     } 
 
+    if (readerNames == NULL) {
+        /* OK we've marked everything unavailable, we clearly
+	 * aren't adding any readers, so we can blow out here */
+	return;
+    }
+
     const char *newReadersData[MAX_READER_DELTA];
     const char **newReaders = &newReadersData[0];
     unsigned int newReaderCount = 0;
@@ -368,18 +410,29 @@ SlotList::updateReaderList()
 
 Slot::Slot(const char *readerName_, Log *log_, CKYCardContext* context_)
     : log(log_), readerName(NULL), personName(NULL), manufacturer(NULL),
+	tokenManufacturer(NULL),
 	slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), 
 	isVersion1Key(false), needLogin(false), fullTokenName(false), 
-	mCoolkey(false), mOldCAC(false),
+	mCoolkey(false), mOldCAC(false), mCACLocalLogin(false),
+	pivContainer(-1), pivKey(-1), mECC(false), p15aid(0), p15odfAddr(0),
+	p15tokenInfoAddr(0), p15Instance(0),
 #ifdef USE_SHMEM
 	shmem(readerName_),
 #endif
 	sessionHandleCounter(1), objectHandleCounter(1)
 {
+  int i; 
+
+  for (i=0; i < MAX_AUTH_USERS; i++) {
+    auth[i]=NULL;
+  }
 
   tokenFWVersion.major = 0;
   tokenFWVersion.minor = 0;
-
+  CKYBuffer_InitFromData(&p15AID, P15AID, sizeof(P15AID));
+  CKYBuffer_InitEmpty(&p15tokenInfo);
+  CKYBuffer_InitEmpty(&p15odf);
+  CKYBuffer_InitEmpty(&p15serialNumber);
 
   try {
     conn = CKYCardConnection_Create(context);
@@ -395,6 +448,8 @@ Slot::Slot(const char *readerName_, Log
     loggedIn = false;
     pinCache.invalidate();
     pinCache.clearPin();
+    contextPinCache.invalidate();
+    contextPinCache.clearPin();
     //readSlotInfo();
     manufacturer = strdup("Unknown");
     if (!manufacturer) {
@@ -477,12 +532,23 @@ Slot::~Slot()
     if (manufacturer) {
 	free(manufacturer);
     }
+    if (tokenManufacturer) {
+	free(tokenManufacturer);
+    }
     CKYBuffer_FreeData(&nonce);
     CKYBuffer_FreeData(&cardATR);
     CKYBuffer_FreeData(&mCUID);
+    CKYBuffer_FreeData(&p15AID);
+    CKYBuffer_FreeData(&p15odf);
+    CKYBuffer_FreeData(&p15tokenInfo);
+    CKYBuffer_FreeData(&p15serialNumber);
     for (int i=0; i < MAX_CERT_SLOTS; i++) {
 	CKYBuffer_FreeData(&cardAID[i]);
     }
+    for (int i=0; i < MAX_AUTH_USERS; i++) {
+	if (auth[i]) delete auth[i];
+	auth[i]=NULL;
+    }
 }
 
 template <class C>
@@ -573,6 +639,36 @@ SlotList::getSlotList(CK_BBOOL tokenPres
     return rv;
 }
 
+bool
+Slot::getPIVLoginType(void)
+{
+    CKYStatus status;
+    CKYISOStatus apduRC;
+    CKYBuffer buffer;
+    bool local = true;
+
+    CKYBuffer_InitEmpty(&buffer);
+
+    /* get the discovery object */
+    status = PIVApplet_GetCertificate(conn, &buffer, 0x7e, &apduRC);
+    if (status != CKYSUCCESS) {
+	/* Discovery object optional, PIV defaults to local */
+	goto done;
+    }
+    /* techically we probably should parse out the TLVs, but the PIV
+     * specifies exactly what they should be, so we know exactly which
+     * byte to look at */
+    if ((CKYBuffer_Size(&buffer) >= 20) && 
+			(CKYBuffer_GetChar(&buffer,17) == 0x60)) {
+	/* This tells us we should use global login for this piv card */
+	local = false;
+    }
+done:
+    CKYBuffer_FreeData(&buffer);
+    return local;
+}
+
+
 void
 Slot::connectToToken()
 {
@@ -587,6 +683,7 @@ Slot::connectToToken()
     if( ! CKYCardConnection_IsConnected(conn) ) {
         int i = 0;
     //for cranky readers try again a few more times
+	status = CKYSCARDERR;
         while( i++ < 5 && status != CKYSUCCESS )
         {
             status = CKYCardConnection_Connect(conn, readerName);
@@ -672,12 +769,40 @@ Slot::connectToToken()
     // see if the applet is selectable
 
     log->log("time connnect: Begin transaction %d ms\n", OSTimeNow() - time);
+    status = PIVApplet_Select(conn, NULL);
+    if (status == CKYSUCCESS) {
+	 /* CARD is a PIV card */
+	 state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+	 isVersion1Key = 0;
+	 needLogin = true;
+         mCoolkey = 0;
+	 mOldCAC = 0;
+	 mCACLocalLogin = getPIVLoginType();
+	return;
+    } 
     status = CKYApplet_SelectCoolKeyManager(conn, NULL);
     if (status != CKYSUCCESS) {
         log->log("CoolKey Select failed 0x%x\n", status);
 	status = getCACAid();
 	if (status != CKYSUCCESS) {
-	    goto loser;
+	    log->log("CAC Select failed 0x%x\n", status);
+	    status = getP15Params();
+	    if (status != CKYSUCCESS) {
+	    	if (status == CKYSCARDERR) {
+		    log->log("Card Failure 0x%x\n",
+				CKYCardConnection_GetLastError(conn));
+		    disconnect();
+	    	}
+	        /* CARD is unknown */
+	        return;
+	    } 
+	    /* enable PCKS 15 */
+	    state |= P15_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+	    isVersion1Key = 0;
+	    needLogin = false; /* get it from token info */
+            mCoolkey = 0;
+	    mCACLocalLogin = false;
+	    return;
 	}
 	state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
 	/* skip the read of the cuid. We really don't need it and,
@@ -685,17 +810,9 @@ Slot::connectToToken()
          * other apps may be running now, so resetting the cac is a bit
          * unfriendly */
 	isVersion1Key = 0;
-	needLogin = 1;
+	needLogin = true;
         mCoolkey = 0;
-	return;
-
-loser:
-        log->log("CAC Select failed 0x%x\n", status);
-	if (status == CKYSCARDERR) {
-	    log->log("CAC Card Failure 0x%x\n", 
-			CKYCardConnection_GetLastError(conn));
-	    disconnect();
-	}
+	mCACLocalLogin = false;
 	return;
     }
     mCoolkey = 1;
@@ -762,8 +879,10 @@ Slot::invalidateLogin(bool hard)
 	}
     } else {
 	loggedIn = false;
+	pinCache.invalidate();
+	contextPinCache.invalidate();
+	contextPinCache.clearPin();
 	if (hard) {
-	    pinCache.invalidate();
 	    pinCache.clearPin();
 	}
     }
@@ -873,6 +992,414 @@ done:
     return status;
 }
 
+CKYStatus Slot::getP15Params()
+{
+    CKYStatus status = CKYSCARDERR;
+    int i;
+    CKYISOStatus apduRC;
+
+    /* read the EF(DIR) */  
+    status = CACApplet_SelectFile(conn, 0x2f00, &apduRC);
+    if (status == CKYSUCCESS) {
+	CKYBuffer record;
+
+	CKYBuffer_InitEmpty(&record);
+	/* dump it out */
+        for (i=1; i < 255; i++) {
+	    status = P15Applet_ReadRecord(conn, i, 0, P15_READ_P1, 255,
+			&record, &apduRC);
+	    if (status != CKYSUCCESS) {
+		log->log("EF(DIR) Read Record %d failed 0x%x apduRC=0x%x\n",
+							 i, status, apduRC);
+		break;
+	    }
+	}
+	CKYBuffer_FreeData(&record);
+	return CKYSCARDERR; /* don't yet support EF(DIR)*/
+
+    } else {
+	log->log("EF(DIR) Select failed 0x%x apduRC=0x%0x\n", status, apduRC);
+	p15aid = 0; /* use the default */
+	p15odfAddr=0x5031;
+	p15tokenInfoAddr=0x5032;
+    }
+   
+    status = CKYApplet_SelectFile(conn, &p15AID, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("DF(PKCS-15) select failed 0x%x apduRC=0x%0x\n", status,
+				apduRC);
+	return status;
+    }
+    status = P15Applet_SelectFile(conn, p15tokenInfoAddr, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("EF(TokenInfo) select failed 0x%x apduRC=0x%0x\n", status, 
+				apduRC);
+	return status;
+    }
+    /* dump it out */
+    CKYBuffer_Resize(&p15tokenInfo, 0);
+    status = P15Applet_ReadBinary(conn, 0, 0, 0, 0, &p15tokenInfo, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("EF(TokenInfo) Read binary failed 0x%x apduRC=0x%x\n", status,
+				 apduRC);
+	    return status;
+    }
+    status = P15Applet_SelectFile(conn, p15odfAddr, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("EF(ODF) select failed 0x%x apduRC=0x%0x\n", status,
+				apduRC);
+	return status;
+    }
+
+    CKYBuffer_Resize(&p15odf, 0);
+    status = P15Applet_ReadBinary(conn, 0, 0, 0, 0, &p15odf, &apduRC);
+    if (status != CKYSUCCESS) {
+	    log->log("EF(ODF) Read binary failed 0x%x apduRC=0x%x\n", status,
+				 apduRC);
+	    return status; 
+    }
+
+    return CKYSUCCESS;
+}
+
+CKYStatus
+Slot::readFromPath(const PK15ObjectPath &obj, CKYBuffer *file)
+{
+    CKYStatus status;
+    CKYISOStatus apduRC;
+    CKYSize bufSize;
+    CKYOffset index = obj.getIndex();
+    CKYSize length = obj.getLength();
+
+    CKYBuffer_Resize(file, 0);
+    status = selectPath(obj.getPath(), &apduRC);
+    if (status != CKYSUCCESS) {
+	return status;
+    }
+    status = P15Applet_ReadBinary(conn, index, 0, 0, 
+			(length >= 256)?0:length, file, &apduRC);
+    if (status != CKYSUCCESS) {
+	return status;
+    }
+
+    /* if we asked for a specific length and got it, or we asked for 
+     * an indeterminate length and got less than 256 bytes, then we 
+     * got everything. */
+    bufSize = CKYBuffer_Size(file);
+    if ((length && (bufSize >= length)) || ((length == 0) && (bufSize < 256))) {
+	/* we've already got it all */
+	return status;
+    }
+    if (bufSize < 0x82) {
+	/* make sure we have enough bytes to handle the worst case ANS.1
+         * mistake */
+	return CKYINVALIDDATA;
+    }
+    
+    if (length == 0) {
+	/* we don't yet know how long the length is, use the ASN.1 parser to
+         * find out. We lie to dataStart about actual size so that it won't
+         * fail since we know we don't have the whole buffer yet.*/
+	(void) dataStart(CKYBuffer_Data(file), 65535, &length, true);
+    }
+    if (length > 65535) {
+	return CKYINVALIDDATA;
+    }
+    while ((bufSize =CKYBuffer_Size(file)) < length) {
+	CKYSize tmpLength = length - bufSize;
+
+	if (tmpLength >= 256) tmpLength = 0;
+
+	status = P15Applet_ReadBinary(conn, (unsigned short)(index+bufSize),
+			 0, 0, tmpLength, file, &apduRC);
+	if (status != CKYSUCCESS) {
+	    return status;
+	}
+    }
+	
+    return CKYSUCCESS;
+}
+
+void
+Slot::parseEF_TokenInfo(void)
+{
+    DEREncodedTokenInfo derTokenInfo(&p15tokenInfo);
+    const CKYBuffer *serial=&derTokenInfo.serialNumber;
+
+    if (derTokenInfo.version >= 0) {
+	tokenFWVersion.major = derTokenInfo.version;
+	tokenFWVersion.minor = 0;
+    }
+
+    if (CKYSize(serial) != 0) {
+	CKYBuffer_Replace(&p15serialNumber, 0, CKYBuffer_Data(serial),
+						CKYBuffer_Size(serial));
+    }
+
+    if (derTokenInfo.manufacturer) {
+	if (tokenManufacturer) {
+	    free(tokenManufacturer);
+	    tokenManufacturer = NULL;
+	}
+	tokenManufacturer = derTokenInfo.manufacturer;
+	derTokenInfo.manufacturer = NULL; /* adopted */
+    }
+
+    if (derTokenInfo.tokenName) {
+	if (personName) {
+	    free(personName);
+	    personName = NULL;
+	}
+	personName = derTokenInfo.tokenName;
+	derTokenInfo.tokenName = NULL; /* adopted */
+	fullTokenName = true;
+    }
+    return;
+}
+
+void
+Slot::parseEF_ODF(void)
+{
+    const CKYByte *current = CKYBuffer_Data(&p15odf);
+    CKYSize size = CKYBuffer_Size(&p15odf);
+    CKYBuffer files;
+
+    CKYBuffer_InitEmpty(&files);
+
+    while (size > 0) {
+	const CKYByte *entry;
+	CKYSize entrySize;
+	CKYSize tagSize;
+	CKYByte type, type1;
+	PK15ObjectPath objPath;
+	bool skip;
+
+	type = current[0];
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) { break; }
+	tagSize = entry-current;
+	current += entrySize + tagSize;
+	size -= (entrySize + tagSize);
+
+	/* skip those entries we aren't going to parse */
+	skip = false;
+	switch (type) {
+	case 0xa2: skip=true; break; /* skip EF(PuKDF-trusted) */
+	case 0xa3: skip=true; break; /* skip EF(SKDF) */
+	case 0xa7: skip=true; break; /* skip EF(DODF) */
+	default: skip=true; break;
+	case 0xa0:        /* EF(PvKDF) */
+	case 0xa1:        /* EF(PuKDF) */
+	case 0xa4:        /* EF(CDF) */
+	case 0xa5:        /* EF(CDF-trusted) */
+	case 0xa6:        /* EF(CDF-useful) */
+	case 0xa8: break; /* EF(AODF) */
+ 	}
+	if (skip) continue;
+
+	type1 = entry[0];
+	/* unwrap */
+	entry = dataStart(entry, entrySize, &entrySize, false);
+	if (entry == NULL) continue;
+	if (type1 == ASN1_SEQUENCE) {
+	    objPath.setObjectPath(entry, entrySize);
+	    CKYBuffer_Resize(&files, 0);
+	    readFromPath(objPath, &files);
+	    entry = CKYBuffer_Data(&files);
+	    entrySize = CKYBuffer_Size(&files);
+	} else if (type1 != ASN1_CHOICE_0) {
+	    continue;
+	}
+	
+	switch (type) {
+	case 0xa0: parseEF_Directory(entry, entrySize, PK15PvKey); break;
+	case 0xa1: parseEF_Directory(entry, entrySize, PK15PuKey); break;
+	case 0xa4: parseEF_Directory(entry, entrySize, PK15Cert); break;
+	case 0xa5: parseEF_Directory(entry, entrySize, PK15Cert); break;
+	case 0xa6: parseEF_Directory(entry, entrySize, PK15Cert); break;
+	case 0xa8: parseEF_Directory(entry, entrySize, PK15AuthObj); break;
+	default: break;
+	}
+    }
+    CKYBuffer_FreeData(&files);
+    return;
+}
+
+
+class ObjectCertCKAIDMatch {
+  private:
+    const CKYBuffer *cka_id;
+  public:
+    ObjectCertCKAIDMatch(const CKYBuffer *cka_id_) : cka_id(cka_id_) {}
+    bool operator()(const PKCS11Object& obj) {
+	const CKYBuffer *id;
+        const CKYBuffer *objClass;
+	CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+	objClass = obj.getAttribute(CKA_CLASS);
+        if (objClass == NULL || !CKYBuffer_DataIsEqual(objClass, 
+				(CKYByte *)&certClass, sizeof(certClass))) {
+	    return false;
+        }
+ 	id = obj.getAttribute(CKA_ID);
+        return (id != NULL && CKYBuffer_IsEqual(id,cka_id)) ? true : false;
+    }
+};
+
+class ObjectKeyCKAIDMatch {
+  private:
+    const CKYBuffer *cka_id;
+  public:
+    ObjectKeyCKAIDMatch(const CKYBuffer *cka_id_) : cka_id(cka_id_) {}
+    bool operator()(const PKCS11Object& obj) {
+	const CKYBuffer *id;
+        const CKYBuffer *objClass;
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	objClass = obj.getAttribute(CKA_CLASS);
+        if (objClass == NULL || !CKYBuffer_DataIsEqual(objClass, 
+				(CKYByte *)&keyClass, sizeof(keyClass))) {
+	    return false;
+        }
+ 	id = obj.getAttribute(CKA_ID);
+        return (id != NULL && CKYBuffer_IsEqual(id,cka_id)) ? true : false;
+    }
+};
+
+CKYStatus
+Slot::parseEF_Directory(const CKYByte *current, 
+					CKYSize size, PK15ObjectType type)
+{
+    CKYBuffer file;
+    CKYBuffer_InitEmpty(&file);
+    CKYStatus status;
+
+
+    while (size > 0) {
+	const CKYByte *entry;
+	CKYSize entrySize;
+
+	if (current[0] != ASN1_SEQUENCE) {
+	    /* no more */
+	    break;
+	}
+
+	entry = dataStart(current, size, &entrySize, true);
+	if (entry == NULL) { break; }
+	current += entrySize;
+	size -= entrySize;
+
+	do {
+	    PK15Object obj(PK15Instance(), type, entry, entrySize);
+
+	    /* if state failed, then there is something wrong with this
+	     * der, skip this object */
+	    if (obj.getState() == PK15StateInit) {
+		break;
+	    }
+	    status = CKYSUCCESS;
+	    while (obj.getState() != PK15StateComplete) {
+	        CKYBuffer_Resize(&file, 0);
+	        readFromPath(obj.getObjectPath(), &file);
+		status = obj.completeObject(CKYBuffer_Data(&file), 
+						CKYBuffer_Size(&file));
+		if (status != CKYSUCCESS) {
+		    break;
+		}
+	    }
+	    if (status != CKYSUCCESS) {
+		break;
+	    }
+	    assert(obj.getState() == PK15StateComplete);
+	    /* handle type specific per object fixups */
+	    switch (type) {
+	    case PK15AuthObj:
+	    /* if we're an auth object, squirrel us away for future use */
+		if (obj.isSO()) {
+		    if (auth[CKU_SO] != 0) {
+			auth[CKU_SO] = new PK15Object(obj);
+		    }
+		} else if (auth[CKU_USER] == NULL) {
+		    auth[CKU_USER] = new PK15Object(obj);
+		} else if (auth[CKU_CONTEXT_SPECIFIC] == NULL) {
+		    ObjectIter iter;
+		    const CKYBuffer *authid = obj.getPinAuthId();
+
+		    /* these should put on the individual keys */
+		    auth[CKU_CONTEXT_SPECIFIC] = new PK15Object(obj);
+
+		    for( iter = tokenObjects.begin(); 
+			 iter != tokenObjects.end(); ++iter) {
+			if( CKYBuffer_IsEqual(iter->getAuthId(),authid)) {
+			    iter->setAttributeBool(CKA_ALWAYS_AUTHENTICATE,
+						   TRUE);
+			}
+		    }
+		}
+		/* drop unkown */
+		break;
+	    case PK15PvKey:
+		/* does the cert already exist? */
+		{
+		    ObjectConstIter iter;
+		    const CKYBuffer *id;
+
+		    id = obj.getAttribute(CKA_ID);
+		    if ((!id) || (CKYBuffer_Size(id) != 1)) {
+			break;
+		    }
+		    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
+                        ObjectCertCKAIDMatch(id));
+			
+		    if ( iter != tokenObjects.end() ) {
+			obj.completeKey(*iter);
+		    }
+		}
+		break;
+	    case PK15Cert:
+		/* does a corresponding key already exist? */
+		{
+		    ObjectIter iter;
+		    const CKYBuffer *id;
+
+		    id = obj.getAttribute(CKA_ID);
+		    if ((!id) || (CKYBuffer_Size(id) != 1)) {
+			break;
+		    }
+		    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
+                        ObjectKeyCKAIDMatch(id));
+			
+		    if ( iter != tokenObjects.end() ) {
+			iter->completeKey(obj);
+		    }
+		}
+		break;
+	    case PK15PuKey:
+		break;
+	    }
+    	    tokenObjects.push_back(obj);
+  	} while ( false );
+    }
+    CKYBuffer_FreeData(&file);
+    return CKYSUCCESS;
+}
+
+
+CKYStatus
+Slot::selectPath(const CKYBuffer *path, CKYISOStatus  *apduRC)
+{
+    CKYSize size = CKYBuffer_Size(path);
+    CKYStatus status = CKYINVALIDARGS;
+    CKYOffset pos;
+
+    for (pos=0; pos < size; pos +=2) {
+	unsigned short ef = CKYBuffer_GetShort(path, pos);
+	status = P15Applet_SelectFile(conn, ef, apduRC);
+	if (status != CKYSUCCESS) {
+		break;
+	}
+    }
+    return status;
+}
+
 void
 Slot::refreshTokenState()
 {
@@ -1054,8 +1581,20 @@ void
 Slot::makeSerialString(char *serialNumber, int maxSize,
 						 const unsigned char *cuid)
 {
+    CKYSize ssize = CKYBuffer_Size(&p15serialNumber);
     memset(serialNumber, ' ', maxSize);
 
+    if (ssize != 0) {
+	CKYSize i;
+	ssize = MIN((CKYSize)maxSize/2, ssize);
+	for (i=0; i < ssize; i++) {
+	   CKYByte c = CKYBuffer_GetChar(&p15serialNumber, i);
+	   serialNumber[2*i] = hex((c >> 4) & 0xf);
+	   serialNumber[2*i+1] = hex(c & 0xf);
+	}
+    }
+	    
+
     // otherwise we use the eepromSerialNumber as a hex value 
     if (cuid) {
          makeCUIDString(serialNumber, maxSize, cuid);
@@ -1126,7 +1665,8 @@ struct _manList {
 static const struct _manList  manList[] = {
         { 0x4090, "Axalto" },
         { 0x2050, "Oberthur" },
-        { 0x4780, "RSA" }
+        { 0x4780, "RSA" },
+        { 0x534e, "SafeNet" }
 };
 
 static int manListSize = sizeof(manList)/sizeof(manList[0]);
@@ -1135,8 +1675,15 @@ void
 Slot::makeManufacturerString(char *man, int maxSize, const unsigned char *cuid)
 {
     char *cp = man;
+    int manLen;
     memset(man, ' ', maxSize);
 
+    if (tokenManufacturer) {
+	manLen = strlen(tokenManufacturer);
+	memcpy(man, tokenManufacturer, MIN(manLen, maxSize));
+        // UTF8 Truncate fixup! don't drop halfway through a UTF8 character 
+        return;
+    }
     if (!cuid) {
 	return;
     }
@@ -1202,6 +1749,7 @@ Slot::getTokenInfo(CK_TOKEN_INFO_PTR pTo
 
 
     return CKR_OK;
+
 }
 
 void
@@ -1222,7 +1770,16 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
     bool found = FALSE;
     CKYStatus status;
     SCARD_READERSTATE *myReaderStates = NULL;
+    static SCARD_READERSTATE pnp = { 0 };
     unsigned int myNumReaders = 0;
+
+    readerListLock.getLock();
+    if (pnp.szReader == 0) {
+	    CKYReader_Init(&pnp);
+	    pnp.szReader = "\\\\?PnP?\\Notification";
+    }
+    readerListLock.releaseLock();
+
 #ifndef notdef
     do {
 	readerListLock.getLock();
@@ -1241,11 +1798,13 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
 	 * from that set to return
 	 */
 	for (i=0; i < numReaders; i++) {
-	    unsigned long knownState = CKYReader_GetKnownState(&readerStates[i]);
+	    unsigned long knownState = 
+				CKYReader_GetKnownState(&readerStates[i]);
 
 	    if ((knownState & SCARD_STATE_UNAVAILABLE) &&
 		(knownState & SCARD_STATE_CHANGED)) {
-		CKYReader_SetKnownState(&readerStates[i], knownState & ~SCARD_STATE_CHANGED);
+		CKYReader_SetKnownState(&readerStates[i], 
+				knownState & ~SCARD_STATE_CHANGED);
 		readerListLock.releaseLock();
 		*slotp = slotIndexToID(i);
 		found = TRUE;
@@ -1262,31 +1821,48 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
 	    break;
 	}
 
-	if (myNumReaders != numReaders) {
+	if (myNumReaders != numReaders + 1) {
 	    if (myReaderStates) {
 		delete [] myReaderStates;
 	    } 
-	    myReaderStates = new SCARD_READERSTATE [numReaders];
+	    myReaderStates = new SCARD_READERSTATE [numReaders + 1];
+            myNumReaders = numReaders + 1;
 	}
-	memcpy(myReaderStates, readerStates, 
-				sizeof(SCARD_READERSTATE)*numReaders);
-	myNumReaders = numReaders;
+
+	memcpy(myReaderStates, readerStates,
+				sizeof(SCARD_READERSTATE) * numReaders);
+	memcpy(&myReaderStates[numReaders], &pnp, sizeof(pnp));
 	readerListLock.releaseLock();
 	status = CKYCardContext_WaitForStatusChange(context,
-				 myReaderStates, myNumReaders, timeout);
+			 myReaderStates, myNumReaders, timeout);
 	if (status == CKYSUCCESS) {
-	    for (i=0; i < myNumReaders; i++) {
-		SCARD_READERSTATE *rsp = &myReaderStates[i];
-	        unsigned long eventState = CKYReader_GetEventState(rsp);
+            unsigned long eventState;
+	    for (i=0; i < myNumReaders - 1; i++) {
+		eventState = CKYReader_GetEventState(&myReaderStates[i]);
 		if (eventState & SCARD_STATE_CHANGED) {
 		    readerListLock.getLock();
-		    CKYReader_SetKnownState(&readerStates[i], eventState & ~SCARD_STATE_CHANGED);
+		    CKYReader_SetKnownState(&readerStates[i], 
+				eventState & ~SCARD_STATE_CHANGED);
 		    readerListLock.releaseLock();
 		    *slotp = slotIndexToID(i);
 		    found = TRUE;
 		    break;
 		}
 	    }
+            /* No real need to check for an additional card, we already update 
+	     * the list when we iterate. */
+	    if (!found) {
+		eventState = CKYReader_GetEventState(
+					&myReaderStates[myNumReaders-1]);
+                if (eventState & SCARD_STATE_CHANGED) {
+		    readerListLock.getLock();
+		    CKYReader_SetKnownState(&pnp, 
+				eventState & ~SCARD_STATE_CHANGED);
+		    readerListLock.releaseLock();
+                    log->log("Reader insertion/removal detected\n");
+		    continue; /* get the update */
+		}
+            }
 	}
 
         if (found || (flag == CKF_DONT_BLOCK) || shuttingDown) {
@@ -1294,21 +1870,21 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
         }
 
         #ifndef WIN32
-        if (status != CKYSUCCESS) {
-
-            if ( (CKYCardContext_GetLastError(context) ==
-                                        SCARD_E_READER_UNAVAILABLE) ||
-                (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT))
-            {
-                OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY);
-            }
-
-
-        }
+        /* pcsc-lite needs to make progress or something */
+	if (status != CKYSUCCESS) {
+	    if ((CKYCardContext_GetLastError(context) ==
+						 SCARD_E_READER_UNAVAILABLE) ||
+	       (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) {
+		OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY);
+	    }
+	}
         #endif
     } while ((status == CKYSUCCESS) ||
        (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT) ||
-        ( CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE));
+       (CKYCardContext_GetLastError(context) == SCARD_E_UNKNOWN_READER) ||
+       (CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE) ||
+       (CKYCardContext_GetLastError(context) == SCARD_E_NO_SERVICE) ||
+       (CKYCardContext_GetLastError(context) == SCARD_E_SERVICE_STOPPED) );
 #else
     do {
 	OSSleep(100);
@@ -1345,6 +1921,7 @@ Slot::handleConnectionError()
       case SCARD_W_REMOVED_CARD:
         ckrv = CKR_DEVICE_REMOVED;
         break;
+      
       default:
         ckrv = CKR_DEVICE_ERROR;
         break;
@@ -1404,13 +1981,46 @@ Slot::selectApplet()
 }
 
 void
-Slot::selectCACApplet(CKYByte instance)
+Slot::selectCACApplet(CKYByte instance, bool doDisconnect)
 {
     CKYStatus status;
+    /* PIV containers and keys by instance */
+    static const int container[] = {
+	0x5fc105, 0x5fc10a, 0x5fc10b, 0x5fc101,
+	0x5fc10d, 0x5fc10e, 0x5fc10f, 0x5fc110, 
+	0x5fc111, 0x5fc112, 0x5fc113, 0x5fc114, 
+	0x5fc115, 0x5fc116, 0x5fc117, 0x5fc118, 
+	0x5fc119, 0x5fc11a, 0x5fc11b, 0x5fc11c, 
+	0x5fc11d, 0x5fc11e, 0x5fc11f, 0x5fc120
+    };
+    static const int keyRef[] = {
+	0x9a,     0x9c,     0x9d,     0x9e,
+	0x82,     0x83,     0x84,     0x85,
+	0x86,     0x87,     0x88,     0x89,
+	0x8a,     0x8b,     0x8c,     0x8d,
+	0x8e,     0x8f,     0x90,     0x91,
+	0x92,     0x93,     0x94,     0x95
+    };
+
+    if (state & PIV_CARD) {
+        status = PIVApplet_Select(conn, NULL);
+	if (status == CKYSCARDERR) handleConnectionError();
+	if (status != CKYSUCCESS) {
+	    if (doDisconnect) {
+	        disconnect();
+	    }
+	    throw PKCS11Exception(CKR_DEVICE_REMOVED);
+	}
+	pivContainer = container[instance];
+	pivKey = keyRef[instance];
+	return;
+    }
     CKYBuffer *aid = &cardAID[instance];
 
     if (CKYBuffer_Size(aid) == 0) {
-        disconnect();
+	if (doDisconnect) {
+	    disconnect();
+	}
         throw PKCS11Exception(CKR_DEVICE_REMOVED);
 	return;
     }
@@ -1419,7 +2029,9 @@ Slot::selectCACApplet(CKYByte instance)
     if ( status == CKYSCARDERR ) handleConnectionError();
     if ( status != CKYSUCCESS) {
         // could not select applet: this just means it's not there
-        disconnect();
+	if (doDisconnect) {
+	    disconnect();
+	}
         throw PKCS11Exception(CKR_DEVICE_REMOVED);
     }
     if (mOldCAC) {
@@ -1428,7 +2040,9 @@ Slot::selectCACApplet(CKYByte instance)
     status = CACApplet_SelectFile(conn, cardEF[instance], NULL);
     if ( status == CKYSCARDERR ) handleConnectionError();
     if ( status != CKYSUCCESS) {
-        disconnect();
+	if (doDisconnect) {
+	    disconnect();
+	}
         throw PKCS11Exception(CKR_DEVICE_REMOVED);
     }
 }
@@ -1488,26 +2102,6 @@ class KeyNumMatch {
     }
 };
 
-class ObjectCertCKAIDMatch {
-  private:
-    CKYByte cka_id;
-  public:
-    ObjectCertCKAIDMatch(CKYByte cka_id_) : cka_id(cka_id_) {}
-    bool operator()(const PKCS11Object& obj) {
-	const CKYBuffer *id;
-        const CKYBuffer *objClass;
-	CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
-	objClass = obj.getAttribute(CKA_CLASS);
-        if (objClass == NULL || !CKYBuffer_DataIsEqual(objClass, 
-				(CKYByte *)&certClass, sizeof(certClass))) {
-	    return false;
-        }
- 	id = obj.getAttribute(CKA_ID);
-        return (id != NULL && CKYBuffer_DataIsEqual(id,&cka_id, 1))
-						 ? true : false;
-    }
-};
-
 CK_OBJECT_HANDLE
 Slot::generateUnusedObjectHandle()
 {
@@ -1521,6 +2115,29 @@ Slot::generateUnusedObjectHandle()
     return handle;
 }
 
+/* Create a short lived Secret Key for ECC key derive. */
+PKCS11Object *
+Slot::createSecretKeyObject(CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount)
+{
+
+    if (secretKeyBuffer == NULL ) {
+        throw PKCS11Exception(CKR_DEVICE_ERROR,
+                        "Can't create secret key object for ECC.");
+    }
+
+    unsigned long muscleID = 0xfff;
+    PKCS11Object *secret =  new SecretKey(muscleID,  handle, secretKeyBuffer, pTemplate, ulAttributeCount);
+
+    if (secret == NULL) {
+        throw PKCS11Exception(CKR_DEVICE_ERROR,
+                        "Can't create secret key object for ECC.");
+    }
+
+    tokenObjects.push_back(*secret);
+    
+    return secret;
+}
+
 void
 Slot::addKeyObject(list<PKCS11Object>& objectList, const ListObjectInfo& info,
     CK_OBJECT_HANDLE handle, bool isCombined)
@@ -1530,24 +2147,32 @@ Slot::addKeyObject(list<PKCS11Object>& o
     CK_OBJECT_CLASS objClass = keyObj.getClass();
     const CKYBuffer *id;
 
-
     if (isCombined &&
-	   ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY))) {
-	id = keyObj.getAttribute(CKA_ID);
-	if ((!id) || (CKYBuffer_Size(id) != 1)) {
-	    throw PKCS11Exception(CKR_DEVICE_ERROR,
-			"Missing or invalid CKA_ID value");
-	}
-	iter = find_if(objectList.begin(), objectList.end(),
-			ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0)));
-	if ( iter == objectList.end() ) {
+           ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY))) {
+        id = keyObj.getAttribute(CKA_ID);
+        if ((!id) || (CKYBuffer_Size(id) != 1)) {
+            throw PKCS11Exception(CKR_DEVICE_ERROR,
+                        "Missing or invalid CKA_ID value");
+        }
+        iter = find_if(objectList.begin(), objectList.end(),
+                        ObjectCertCKAIDMatch(id));
+        if ( iter == objectList.end() ) {
             // We failed to find a cert with a matching CKA_ID. This
             // can happen if the cert is not present on the token, or
             // the der encoded cert stored on the token was corrupted.
-	    throw PKCS11Exception(CKR_DEVICE_ERROR,
-			"Failed to find cert with matching CKA_ID value");
-	}
-	keyObj.completeKey(*iter);
+                throw PKCS11Exception(CKR_DEVICE_ERROR,
+                                         "Failed to find cert with matching CKA_ID value");
+        }
+        keyObj.completeKey(*iter);
+
+        /* For now this is how we determine what type of key.
+           Also for now, allow only one or the other */
+        if ( keyObj.getKeyType() == PKCS11Object::ecc) {
+            mECC = true;
+        } else {
+            mECC = false;
+        }
+       
     }
     objectList.push_back(keyObj);
 
@@ -1577,10 +2202,16 @@ Slot::addCertObject(list<PKCS11Object>&
 void
 Slot::unloadObjects()
 {
+    mECC = false;
     tokenObjects.clear();
     free(personName);
     personName = NULL;
     fullTokenName = false;
+    if (tokenManufacturer) {
+	free(tokenManufacturer);
+	tokenManufacturer = NULL;
+    }
+    CKYBuffer_Resize(&p15serialNumber,0);
 }
 
 #ifdef USE_SHMEM
@@ -1970,7 +2601,7 @@ Slot::readCUID(void)
     // shared memory is protected by our transaction call on the card
     //
     CKYStatus status;
-    if (state & CAC_CARD) {
+    if (state & GOV_CARD) {
 	status = CACApplet_SelectCardManager(conn, NULL);
     } else {
 	status = CKYApplet_SelectCardManager(conn, NULL);
@@ -2203,6 +2834,50 @@ Slot::fetchCombinedObjects(const CKYBuff
     return objInfoList;
 }
 
+typedef enum {
+	BER_UNWRAP,
+	BER_NEXT
+} BERop;
+
+static CKYStatus
+berProcess(CKYBuffer *buf, int matchTag, CKYBuffer *target, BERop type)
+{
+    unsigned char tag;
+    unsigned int used_length= 0;
+    unsigned int data_length;
+
+    tag = CKYBuffer_GetChar(buf,used_length++);
+
+    /* blow out when we come to the end */
+    if (matchTag && tag != matchTag) {
+        return CKYLIBFAIL;
+    }
+
+    data_length = CKYBuffer_GetChar(buf,used_length++);
+
+    if (data_length & 0x80) {
+        int  len_count = data_length & 0x7f;
+
+        data_length = 0;
+
+        while (len_count-- > 0) {
+            data_length = (data_length << 8) | 
+				CKYBuffer_GetChar(buf,used_length++);
+        }
+    }
+
+    if (data_length > (CKYBuffer_Size(buf)-used_length) ) {
+        return CKYLIBFAIL;
+    }
+
+    if (type == BER_UNWRAP) {
+        return CKYBuffer_AppendBuffer(target, buf, used_length, data_length);
+    }
+    return CKYBuffer_AppendBuffer(target, buf, used_length+data_length,
+		CKYBuffer_Size(buf)-(used_length+data_length));
+}
+
+
 CKYStatus
 Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, 
 			      bool throwException)
@@ -2211,16 +2886,60 @@ Slot::readCACCertificateFirst(CKYBuffer
     CKYISOStatus apduRC;
     *nextSize = 0;
 
+    if (state & PIV_CARD) {
+	CKYBuffer pivData;
+	CKYBuffer certInfo;
+
+	CKYBuffer_InitEmpty(&pivData);
+	CKYBuffer_InitEmpty(&certInfo);
+	CKYBuffer_Resize(cert, 0);
+	status = PIVApplet_GetCertificate(conn, cert, pivContainer, &apduRC);
+	if (throwException && (status != CKYSUCCESS)) {
+	    handleConnectionError();
+	}
+	/* actually, on success, we need to parse the certificate and find the
+	 * propper tag */
+	if (status == CKYSUCCESS) {
+	    status = berProcess(cert, 0x53, &pivData, BER_UNWRAP);
+	    CKYBuffer_Resize(cert, 0);
+	    CKYBuffer_AppendChar(cert,0);
+	    do {
+		CKYByte tag = CKYBuffer_GetChar(&pivData,0);
+		if (tag == CAC_TAG_CERTIFICATE) {
+		    status = berProcess(&pivData, CAC_TAG_CERTIFICATE, 
+					cert, BER_UNWRAP);
+		}
+		if (tag == CAC_TAG_CERTINFO) {
+		    CKYBuffer_Resize(&certInfo, 0);
+		    status = berProcess(&pivData, CAC_TAG_CERTINFO, 
+					&certInfo, BER_UNWRAP);
+		    if (CKYBuffer_Size(&certInfo) == 1) {
+			CKYBuffer_SetChar(cert,0,
+					CKYBuffer_GetChar(&certInfo,0));
+		    }
+		}
+		if (status == CKYSUCCESS) {
+		    CKYBuffer_Resize(&certInfo, 0);
+		    status = berProcess(&pivData, 0, &certInfo, BER_NEXT);
+		    if (status == CKYSUCCESS) {
+			CKYBuffer_Resize(&pivData,0);
+			status = CKYBuffer_AppendCopy(&pivData,&certInfo);
+		    }
+		}
+	    } while ((status == CKYSUCCESS) && (CKYBuffer_Size(&pivData) != 0));
+	    CKYBuffer_FreeData(&pivData);
+	    CKYBuffer_FreeData(&certInfo);
+	}
+	
+	return status;
+    }
+
     if (mOldCAC) {
 	/* get the first 100 bytes of the cert */
 	status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC);
 	if (throwException && (status != CKYSUCCESS)) {
 	    handleConnectionError();
 	}
-        
-        if(CKYBuffer_Size(cert) == 0) {
-            handleConnectionError();
-        }
 	return status;
     }
 
@@ -2233,6 +2952,7 @@ Slot::readCACCertificateFirst(CKYBuffer
     CKYBuffer_InitEmpty(&tBuf);
     CKYBuffer_InitEmpty(&vBuf);
     CKYBuffer_Resize(cert, 0);
+    CKYBuffer_AppendChar(cert,0);
 
     /* handle the new CAC card read */
     /* read the TLV */
@@ -2258,11 +2978,12 @@ Slot::readCACCertificateFirst(CKYBuffer
 	    length = CKYBuffer_GetShortLE(&tBuf, toffset);
 	    toffset +=2;
 	}
-	if (tag != CAC_TAG_CERTIFICATE) {
-	    continue;
+	if (tag == CAC_TAG_CERTIFICATE) {
+	    CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length);
+	}
+	if (tag == CAC_TAG_CERTINFO) {
+	    CKYBuffer_SetChar(cert,0,CKYBuffer_GetChar(&vBuf,voffset));
 	}
-	CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length);
-	break;
     }
     status = CKYSUCCESS;
 
@@ -2272,6 +2993,191 @@ done:
     return status;
 }
 
+
+const static unsigned long crc_table[] = {
+0x00000000,0x77073096,0xee0e612c,0x990951ba,
+0x076dc419,0x706af48f,0xe963a535,0x9e6495a3,
+0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,
+0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,
+0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,
+0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,
+0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,
+0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,
+0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172,
+0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,
+0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,
+0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,
+0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,
+0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f,
+0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,
+0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,
+0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a,
+0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433,
+0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,
+0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01,
+0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,
+0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,
+0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c,
+0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,
+0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,
+0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,
+0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0,
+0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,
+0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,
+0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,
+0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,
+0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,
+0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a,
+0xead54739,0x9dd277af,0x04db2615,0x73dc1683,
+0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8,
+0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,
+0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,
+0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,
+0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc,
+0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,
+0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,
+0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b,
+0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,
+0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79,
+0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,
+0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,
+0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,
+0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,
+0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a,
+0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,
+0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38,
+0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,
+0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e,
+0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,
+0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,
+0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,
+0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,
+0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,
+0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,
+0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,
+0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,
+0xbad03605,0xcdd70693,0x54de5729,0x23d967bf,
+0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94,
+0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d
+};
+
+static unsigned long 
+calc_crc32(const unsigned char *buf, int len)
+{
+    unsigned long crc = 0xffffffff;
+    int i;
+
+    for (i=0; i < len; i++) {
+	unsigned char crc_low = crc & 0xff;
+	unsigned long crc_high = crc >> 8;
+	crc = crc_table[crc_low ^ buf[i]] ^ crc_high;
+    }
+    return crc ^ 0xffffffff;
+}
+
+/*
+ * decompress, handles both gzip and zlib trailers
+ * it also automatically allocates the output buffer and expands it as 
+ * necessary.
+ */
+static int 
+decompress(CKYBuffer *out, 
+			CKYBuffer *in, CKYOffset offset, CKYSize len)
+{
+    int zret;
+    CKYStatus status;
+    z_stream stream;
+    int chunk = len *2;
+    int outlen = 0;
+    
+
+    /* allocate inflate state */
+    stream.zalloc = Z_NULL;
+    stream.zfree = Z_NULL;
+    stream.opaque = Z_NULL;
+    stream.avail_in = 0;
+    stream.next_in = Z_NULL;
+    zret = inflateInit(&stream);
+    if (zret != Z_OK)
+        return zret;
+
+    status = CKYBuffer_Reserve(out, outlen);
+    if (status != CKYSUCCESS) {
+	return Z_MEM_ERROR;
+    }
+
+    stream.avail_in = len;
+    stream.next_in =  (Bytef *)(CKYBuffer_Data(in) + offset);
+
+    do {
+	CKYBuffer_Resize(out, outlen + chunk);
+ 	stream.avail_out = chunk;
+
+	stream.next_out = (Bytef *)CKYBuffer_Data(out)+ outlen;
+
+	zret= inflate(&stream, Z_NO_FLUSH);
+
+	/* we need the length early so it can be used in error processing */
+	outlen += chunk - stream.avail_out;
+
+	/* proccess the error codes */
+	switch (zret) {
+	case Z_DATA_ERROR:
+	    /* a DATA error can occur on either corrupted data, or on gzip.
+	     * data. This is because gzip uses CRC32 and zlib used ADLER32
+	     * checksums. We need to check to see if this failure is do to
+	     * a gzip header. */
+	    /* 1) a gzip header includes 4 extra bytes containing the length
+	     * of the gziped data. This means there must be 4 more bytes
+	     * in our input buffer that have not been processed */
+	    if (stream.avail_in != 4) {
+		break; /* not a gzip header */
+	    }
+	    /* The last 4 bytes of a gzip header include the uncompressed length
+	     * modulo 2^32. Make sure the actual uncompressed length matches
+	     * the header. */
+	    if ((outlen  & 0xffffffffL)
+				!= CKYBuffer_GetLongLE(in, offset+len-4)) {
+		break; /* didn't decode the full length */
+	    }
+	    /* At this point it''s pretty likely we have a gzip trailer. Verify
+	     * the crc32 values to make sure there hasn't been any corruption.
+	     */
+	    if (calc_crc32(CKYBuffer_Data(out), outlen) != 
+				CKYBuffer_GetLongLE(in,offset+len-8)) {
+		break; /* CRC didn't match */
+	    }
+ 	    /* This was valid gzip data, and we've successfully uncompressed
+	     * it. We're now done. */
+	    zret=Z_STREAM_END;
+	    break;
+	case Z_NEED_DICT:
+	    /* if we need the dict, it wasn't in the data, 
+	     * so it's a data error */
+	    zret = Z_DATA_ERROR;
+	    break;
+	case Z_OK:
+	    /* Z_OK means we need more data, expand the buffer and go again.
+	     * if we don't need more buffer space, then the input must have
+	     * been truncated, that's a data error */
+	    if (stream.avail_out != 0) {
+		zret = Z_DATA_ERROR;
+	    }
+	    break;
+ 	}
+    } while (zret == Z_OK);
+
+    /* cleanup */
+    if (zret == Z_STREAM_END) {
+	zret = Z_OK;
+	CKYBuffer_Resize(out, outlen);
+    } else {
+	CKYBuffer_Resize(out, 0);
+    }
+    (void)inflateEnd(&stream);
+    return zret;
+}
+
 /*
  * only necessary for old CAC cards. New CAC cards have to read the
  * whole cert in anyway above....
@@ -2304,7 +3210,7 @@ Slot::loadCACCert(CKYByte instance)
     // catch the applet selection errors if they don't
     //
     try {
-        selectCACApplet(instance);
+        selectCACApplet(instance, false);
     } catch(PKCS11Exception& e) {
 	// all CAC's must have instance '0', throw the error it
 	// they don't.
@@ -2322,6 +3228,10 @@ Slot::loadCACCert(CKYByte instance)
 
     if (instance == 0) {
 	readCACCertificateFirst(&rawCert, &nextSize, true);
+
+        if(CKYBuffer_Size(&rawCert) <= 1) {
+             handleConnectionError();
+        }
 	log->log("CAC Cert %d: fetch CAC Cert:  %d ms\n", 
 						instance, OSTimeNow() - time);
     }
@@ -2364,7 +3274,7 @@ Slot::loadCACCert(CKYByte instance)
 	} else {
 	    status = readCACCertificateFirst(&rawCert, &nextSize, false);
 	
-	    if (status != CKYSUCCESS) {
+	    if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) {
 		/* CAC only requires the Certificate in pki '0' */
 		/* if pki '1' or '2' are empty, treat it as a non-fatal error*/
 		if (instance == 2) {
@@ -2393,31 +3303,61 @@ Slot::loadCACCert(CKYByte instance)
 
     log->log("CAC Cert %d: Cert has been read:  %d ms\n",
 						instance, OSTimeNow() - time);
-    if (!mOldCAC || CKYBuffer_GetChar(&rawCert,0) == 1) {
-	CKYSize guessFinalSize = CKYBuffer_Size(&rawCert);
-	CKYSize certSize = 0;
-	CKYOffset offset = mOldCAC ? 1 : 0;
+    /* new CACs, and old CACs with the high one bit are compressed, 
+     * uncompress them */
+    if ((CKYBuffer_GetChar(&rawCert,0) & 0x3) == 1) {
+	CKYOffset offset = 1;
 	int zret = Z_MEM_ERROR;
 
-	do {
-	    guessFinalSize *= 2;
-	    status = CKYBuffer_Resize(&cert, guessFinalSize);
-	    if (status != CKYSUCCESS) {
-		    break;
+	/* process the GZIP header if present */
+	/* header_id = 0x1f, 0x8b. CM=8. If we ever support something other
+	 * than CM=8, we need to change the zlib header below. Currently both
+	 * gzip and zlib only support CM=8 (DEFLATE) compression */
+	if ((CKYBuffer_GetChar(&rawCert,1) == 0x1f) &&
+	    (CKYBuffer_GetChar(&rawCert,2) == 0x8b) &&
+	    (CKYBuffer_GetChar(&rawCert,3) == 8)) {
+	    CKYByte flags = CKYBuffer_GetChar(&rawCert,4);
+	    /* this has a gzip header, not raw data. */
+	    offset += 10; /* base size of the gzip header */
+	    if (flags & 4) { /* FEXTRA */
+		CKYSize len = CKYBuffer_GetShortLE(&rawCert,offset);
+		offset += len;
+	    }
+	    if (flags & 8) { /* FNAME */
+		while (CKYBuffer_GetChar(&rawCert,offset) != 0) {
+		    offset++;
+		}
+		offset++;
+	    }
+	    if (flags & 0x10) { /* FComment */
+		while (CKYBuffer_GetChar(&rawCert,offset) != 0) {
+		    offset++;
+		}
+		offset++;
+	    }
+	    if (flags & 2) { /* FHCRC */
+		offset += 2;
 	    }
-	    certSize = guessFinalSize;
-	    zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize,
-			CKYBuffer_Data(&rawCert)+offset, 
-			CKYBuffer_Size(&rawCert)-offset);
-	} while (zret == Z_BUF_ERROR);
+	    offset -= 2;
+
+	    /* add zlib header, so libz will be happy */
+	    /* CINFO=7, CM=8, LEVEL=2, DICTFLAG=0, FCHECK= 1c */
+	    /* NOTE: the zlib will fail when procssing the trailer. this is
+	     * ok because decompress automatically notices the failure and
+	     * and checks the gzip trailer. */
+	    CKYBuffer_SetChar(&rawCert, offset, 0x78);
+	    CKYBuffer_SetChar(&rawCert, offset+1, 0x9c);
+	}
+	/* uncompress. This expands cert as necessary. */
+	zret = decompress(&cert, &rawCert, offset, 
+					CKYBuffer_Size(&rawCert)-offset);
 
 	if (zret != Z_OK) {
 	    CKYBuffer_FreeData(&rawCert);
 	    CKYBuffer_FreeData(&cert);
 	    throw PKCS11Exception(CKR_DEVICE_ERROR, 
-				"Corrupted compressed CAC Cert");
+				"Corrupted compressed CAC/PIV Cert");
 	}
-	CKYBuffer_Resize(&cert,certSize);
     } else {
 	CKYBuffer_InitFromBuffer(&cert,&rawCert,1,CKYBuffer_Size(&rawCert)-1);
     }
@@ -2431,6 +3371,9 @@ Slot::loadCACCert(CKYByte instance)
     tokenObjects.push_back(privKey);
     tokenObjects.push_back(pubKey);
     tokenObjects.push_back(certObj);
+    if ( pubKey.getKeyType() == PKCS11Object::ecc) {
+	mECC = 1;
+    }
 
     if (personName == NULL) {
 	const char *name = certObj.getName();
@@ -2459,7 +3402,7 @@ Slot::loadObjects()
     list<ListObjectInfo> objInfoList;
     std::list<ListObjectInfo>::iterator iter;
 
-    if (state & CAC_CARD) {
+    if (state & GOV_CARD) {
 	loadCACCert(0);
 	loadCACCert(1);
 	loadCACCert(2);
@@ -2467,6 +3410,17 @@ Slot::loadObjects()
 	loadReaderObject();
 	return;
     }
+    if (state & P15_CARD) {
+	parseEF_TokenInfo();
+	parseEF_ODF();
+	if (auth[CKU_USER] != NULL) {
+	    /* set need login */
+	    needLogin = true;
+	}
+	status = trans.end();
+	loadReaderObject();
+	return;
+    }
 
     selectApplet();
     log->log("time load object: Select Applet (again) %d ms\n",
@@ -2634,15 +3588,15 @@ Slot::getSessionInfo(SessionHandleSuffix
 }
 
 void
-SlotList::login(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin,
-    CK_ULONG ulPinLen)
+SlotList::login(CK_SESSION_HANDLE hSession, CK_USER_TYPE user,
+    CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
 {
     CK_SLOT_ID slotID;
     SessionHandleSuffix suffix;
 
     decomposeSessionHandle(hSession, slotID, suffix);
 
-    slots[slotIDToIndex(slotID)]->login(suffix, pPin, ulPinLen);
+    slots[slotIDToIndex(slotID)]->login(suffix, user, pPin, ulPinLen);
 }
 
 void
@@ -2692,8 +3646,8 @@ Slot::isLoggedIn()
 }
 
 void
-Slot::login(SessionHandleSuffix handleSuffix, CK_UTF8CHAR_PTR pPin,
-    CK_ULONG ulPinLen)
+Slot::login(SessionHandleSuffix handleSuffix, CK_USER_TYPE user,
+    CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
 {
     refreshTokenState();
 
@@ -2702,9 +3656,23 @@ Slot::login(SessionHandleSuffix handleSu
             "Slot::login\n", (unsigned long) handleSuffix);
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
+    /* only support CKU_USER for CAC, PIV, and coolkey... CKU_USER and
+     * CKU_CONTEX_SPECIFIC for P15Card */
+    if (user != CKU_USER) {
+	if ((user != CKU_CONTEXT_SPECIFIC) || ((state & P15_CARD) == 0)) {
+	    throw PKCS11Exception(CKR_USER_TYPE_INVALID);
+	}
+    }
+
 
     if (!isVersion1Key) {
-	pinCache.set((const char *)pPin, ulPinLen);
+	if (user == CKU_USER) {
+	    pinCache.invalidate();
+	    pinCache.set((const char *)pPin, ulPinLen);
+	} else { 
+	    contextPinCache.invalidate();
+	    contextPinCache.set((const char *)pPin, ulPinLen);
+	}
     } else if (nonceValid) {
 	throw PKCS11Exception(CKR_USER_ALREADY_LOGGED_IN);
     }
@@ -2713,19 +3681,85 @@ Slot::login(SessionHandleSuffix handleSu
     CKYStatus status = trans.begin(conn);
     if(status != CKYSUCCESS ) handleConnectionError();
 
-    if (state & CAC_CARD) {
-	selectCACApplet(0);
-    } else {
+    if (state & GOV_CARD) {
+	selectCACApplet(0, true);
+    } else if ((state & P15_CARD)== 0) {
+	/* p15 does the select in attemptLogin */
 	selectApplet();
     }
 
     if (isVersion1Key) {
-	attemptLogin((const char *)pPin);
-    } else if (state & CAC_CARD) {
+	attemptCoolKeyLogin((const char *)pPin);
+    } else  {
+	attemptLogin(user, false);
+    }
+}
+
+void
+Slot::attemptLogin(CK_USER_TYPE user, bool flushPin) {
+    if (state & GOV_CARD) {
 	attemptCACLogin();
+    } else if (state & P15_CARD) {
+	attemptP15Login(user);
     } else {
 	oldAttemptLogin();
     }
+    if (flushPin && (user == CKU_CONTEXT_SPECIFIC)) {
+	contextPinCache.clearPin();
+    }
+}
+void dump(const char *label, const CKYBuffer *buf);
+
+void
+Slot::attemptP15Login(CK_USER_TYPE user)
+{
+    PinCache *pinCachePtr  = userPinCache(user);
+
+    if (user == CKU_USER) {
+	loggedIn = false;
+    }
+    pinCachePtr->invalidate();
+
+    CKYStatus status;
+    CKYISOStatus result;
+
+    if ((user >= MAX_AUTH_USERS) || (auth[user] == NULL)) {
+	throw PKCS11Exception(CKR_USER_TYPE_INVALID,
+			"No PKCS #15 auth object for user %d\n", user);
+    }
+
+    status = selectPath(auth[user]->getObjectPath().getPath(), &result);
+    if( status == CKYSCARDERR )  {
+	handleConnectionError();
+    }
+    if (status != CKYSUCCESS) {
+	throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet select return 0x%04x",
+								result);
+    }
+
+    status = P15Applet_VerifyPIN(conn, 
+		(const char *)CKYBuffer_Data(pinCachePtr->get()), 
+		auth[user]->getPinInfo(), &result);
+    if( status == CKYSCARDERR ) {
+	handleConnectionError();
+    }
+    switch( result ) {
+      case CKYISO_SUCCESS:
+        break;
+      case 0x6983:
+	pinCachePtr->clearPin();
+        throw PKCS11Exception(CKR_PIN_LOCKED);
+      default:
+	pinCachePtr->clearPin();
+	if ((result & 0xff00) == 0x6300) {
+            throw PKCS11Exception(CKR_PIN_INCORRECT);
+	}
+        throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet returned 0x%04x", 
+								result);
+    }
+    pinCachePtr->validate();
+    if (user == CKU_USER)
+	loggedIn = true;
 }
 
 void
@@ -2738,7 +3772,8 @@ Slot::attemptCACLogin()
     CKYISOStatus result;
 
     status = CACApplet_VerifyPIN(conn, 
-		(const char *)CKYBuffer_Data(pinCache.get()), &result);
+		(const char *)CKYBuffer_Data(pinCache.get()), 
+		mCACLocalLogin, &result);
     if( status == CKYSCARDERR ) {
 	handleConnectionError();
     }
@@ -2746,8 +3781,10 @@ Slot::attemptCACLogin()
       case CKYISO_SUCCESS:
         break;
       case 0x6981:
+	pinCache.clearPin();
         throw PKCS11Exception(CKR_PIN_LOCKED);
       default:
+	pinCache.clearPin();
 	if ((result & 0xff00) == 0x6300) {
             throw PKCS11Exception(CKR_PIN_INCORRECT);
 	}
@@ -2759,6 +3796,7 @@ Slot::attemptCACLogin()
     loggedIn = true;
 }
 
+
 void
 Slot::oldAttemptLogin()
 {
@@ -2776,10 +3814,13 @@ Slot::oldAttemptLogin()
       case CKYISO_SUCCESS:
         break;
       case CKYISO_AUTH_FAILED:
+	pinCache.clearPin();
         throw PKCS11Exception(CKR_PIN_INCORRECT);
       case CKYISO_IDENTITY_BLOCKED:
+	pinCache.clearPin();
         throw PKCS11Exception(CKR_PIN_LOCKED);
       default:
+	pinCache.clearPin();
         throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet returned 0x%04x", 
 								result);
     }
@@ -2790,7 +3831,7 @@ Slot::oldAttemptLogin()
 
 // should already be in a transaction, and applet selected
 void
-Slot::attemptLogin(const char *pin)
+Slot::attemptCoolKeyLogin(const char *pin)
 {
     CKYStatus status;
     CKYISOStatus result;
@@ -2866,7 +3907,7 @@ Slot::logout(SessionHandleSuffix suffix)
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
 
-    if (state & CAC_CARD) {
+    if (state & (GOV_CARD|P15_CARD)) {
 	CACLogout();
 	return;
     }
@@ -2993,7 +4034,7 @@ Slot::getAttributeValue(SessionHandleSuf
     ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
         ObjectHandleMatch(hObject));
 
-    if( iter == tokenObjects.end() ) {
+    if ( iter == tokenObjects.end()) {
         throw PKCS11Exception(CKR_OBJECT_HANDLE_INVALID);
     }
 
@@ -3077,6 +4118,21 @@ SlotList::generateRandom(CK_SESSION_HAND
 }
 
 void
+SlotList::derive(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+        CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, 
+        CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey)
+{
+
+    CK_SLOT_ID slotID;
+    SessionHandleSuffix suffix;
+
+    decomposeSessionHandle(hSession, slotID, suffix);
+
+    slots[slotIDToIndex(slotID)]->derive(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey);
+
+}
+
+void
 Slot::ensureValidSession(SessionHandleSuffix suffix)
 {
     if( ! isValidSession(suffix) ) {
@@ -3090,24 +4146,36 @@ Slot::ensureValidSession(SessionHandleSu
 // from 0-9.
 //
 CKYByte
-Slot::objectHandleToKeyNum(CK_OBJECT_HANDLE hKey)
+Slot::objectToKeyNum(const PKCS11Object *key)
+{
+    unsigned long id = key->getMuscleObjID();
+
+    if( getObjectClass(id) != 'k' ) {
+        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+    }
+    unsigned short keyNum = getObjectIndex(id);
+    if( keyNum > 9 ) {
+        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+    }
+    return keyNum & 0xFF;
+}
+
+PKCS11Object *
+Slot::getKeyFromHandle(CK_OBJECT_HANDLE hKey)
 {
     ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
         ObjectHandleMatch(hKey));
+    PKCS11Object &obj = (PKCS11Object &)*iter;
 
     if( iter == tokenObjects.end() ) {
-        // no such object
-        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
     }
 
     if( getObjectClass(iter->getMuscleObjID()) != 'k' ) {
         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
     }
-    unsigned short keyNum = getObjectIndex(iter->getMuscleObjID());
-    if( keyNum > 9 ) {
-        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
-    }
-    return keyNum & 0xFF;
+
+    return &obj;
 }
 
 void
@@ -3119,7 +4187,8 @@ Slot::signInit(SessionHandleSuffix suffi
     if( session == sessions.end() ) {
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
-    session->signatureState.initialize(objectHandleToKeyNum(hKey));
+
+    session->signatureState.initialize(getKeyFromHandle(hKey));
 }
 
 void
@@ -3131,7 +4200,8 @@ Slot::decryptInit(SessionHandleSuffix su
     if( session == sessions.end() ) {
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
-    session->decryptionState.initialize(objectHandleToKeyNum(hKey));
+
+    session->decryptionState.initialize(getKeyFromHandle(hKey));
 }
 
 /**
@@ -3240,67 +4310,183 @@ stripRSAPadding(CKYBuffer *stripped, con
     }
 }
 
-class RSASignatureParams : public CryptParams {
+class ECCKeyAgreementParams : public CryptParams {
   public:
-    RSASignatureParams(unsigned int keysize) : CryptParams(keysize) { }
+    ECCKeyAgreementParams(unsigned int keysize) : CryptParams(keysize) { }
 
-    CKYByte getDirection() const { return CKY_DIR_ENCRYPT; }
+    CKYByte getDirection() const { return CKY_DIR_NONE;}
 
     CryptOpState& getOpState(Session& session) const {
-        return session.signatureState;
+        return session.keyAgreementState;
     }
 
     void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const {
-        // RSA_NO_PAD requires RSA PKCS #1 Type 1 padding
-  	CKYStatus status = CKYBuffer_Resize(paddedInput,getKeySize()/8);
-	if (status != CKYSUCCESS) {
-	    throw PKCS11Exception(CKR_HOST_MEMORY);
-	}
-        padRSAType1(unpaddedInput, paddedInput);
         return;
     }
 
     void
     unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const {
-        // no need to unpad ciphertext
-	CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(paddedOutput),
-					CKYBuffer_Size(paddedOutput));
-	
+        return;
     }
+
 };
 
-class RSADecryptParams: public CryptParams {
+class SignatureParams : public CryptParams {
   public:
-    RSADecryptParams(unsigned int keysize) : CryptParams(keysize) { }
+    SignatureParams(unsigned int keysize) : CryptParams(keysize) { }
 
-    CKYByte getDirection() const { return CKY_DIR_DECRYPT; }
+    CKYByte getDirection() const { return CKY_DIR_NONE; }
 
     CryptOpState& getOpState(Session& session) const {
-        return session.decryptionState;
+        return session.signatureState;
     }
 
     void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const {
-        // no need to unpad ciphertext
-	CKYBuffer_Replace(paddedInput, 0, CKYBuffer_Data(unpaddedInput),
-					CKYBuffer_Size(unpaddedInput));
+        return;
     }
 
     void
     unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const {
-        // strip off PKCS #1 padding
-        stripRSAPadding( unpaddedOutput, paddedOutput );
-	return;
+        return;
     }
+
 };
 
-void
-Slot::sign(SessionHandleSuffix suffix, CK_BYTE_PTR pData,
-        CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
-        CK_ULONG_PTR pulSignatureLen)
-{
-    RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE);
-    cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen,
-        params);
+  
+
+class ECCSignatureParams : public CryptParams {
+  public:
+    ECCSignatureParams(unsigned int keysize) : CryptParams(keysize) { }
+
+    CKYByte getDirection() const { return CKY_DIR_NONE; }
+
+    CryptOpState& getOpState(Session& session) const {
+        return session.signatureState;
+    }
+
+    void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const {
+        return;
+    }
+
+    void
+    unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const {
+        /* Here we will unpack the DER encoding of the signature */
+  
+        if ( unpaddedOutput == NULL || paddedOutput == NULL) {
+            throw PKCS11Exception(CKR_ARGUMENTS_BAD);
+        }
+
+        CKYBuffer rawSignature;
+        CKYBuffer_InitEmpty(&rawSignature); 
+
+        DEREncodedSignature sig(paddedOutput);
+
+        int rv = sig.getRawSignature(&rawSignature, getKeySize() ); 
+   
+        if (rv == CKYSUCCESS) { 
+            CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(&rawSignature),
+                             CKYBuffer_Size(&rawSignature));
+        } else {
+            throw PKCS11Exception(CKR_DEVICE_ERROR);
+        }
+
+        CKYBuffer_FreeData(&rawSignature);
+
+    }
+
+};
+
+
+class RSASignatureParams : public CryptParams {
+  public:
+    RSASignatureParams(unsigned int keysize) : CryptParams(keysize) { }
+
+    CKYByte getDirection() const { return CKY_DIR_ENCRYPT; }
+
+    CryptOpState& getOpState(Session& session) const {
+        return session.signatureState;
+    }
+
+    void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const {
+        // RSA_NO_PAD requires RSA PKCS #1 Type 1 padding
+  	CKYStatus status = CKYBuffer_Resize(paddedInput,getKeySize()/8);
+	if (status != CKYSUCCESS) {
+	    throw PKCS11Exception(CKR_HOST_MEMORY);
+	}
+        padRSAType1(unpaddedInput, paddedInput);
+        return;
+    }
+
+    void
+    unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const {
+        // no need to unpad ciphertext
+	CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(paddedOutput),
+					CKYBuffer_Size(paddedOutput));
+	
+    }
+};
+
+class RSADecryptParams: public CryptParams {
+  public:
+    RSADecryptParams(unsigned int keysize) : CryptParams(keysize) { }
+
+    CKYByte getDirection() const { return CKY_DIR_DECRYPT; }
+
+    CryptOpState& getOpState(Session& session) const {
+        return session.decryptionState;
+    }
+
+    void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const {
+        // no need to unpad ciphertext
+	CKYBuffer_Replace(paddedInput, 0, CKYBuffer_Data(unpaddedInput),
+					CKYBuffer_Size(unpaddedInput));
+    }
+
+    void
+    unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const {
+        // strip off PKCS #1 padding
+        stripRSAPadding( unpaddedOutput, paddedOutput );
+	return;
+    }
+};
+
+void
+Slot::sign(SessionHandleSuffix suffix, CK_BYTE_PTR pData,
+        CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+        CK_ULONG_PTR pulSignatureLen)
+{
+
+    refreshTokenState();
+    SessionIter session = findSession(suffix);
+    if( session == sessions.end() ) {
+        throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+    }
+
+    if (!isVersion1Key && ! isLoggedIn() ) {
+        throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN);
+    }
+
+    /* Create a default one just to get the sigState */
+    SignatureParams dummyParams(CryptParams::DEFAULT_KEY_SIZE);
+
+    CryptOpState sigState = dummyParams.getOpState(*session);
+
+    PKCS11Object::KeyType keyType = sigState.key->getKeyType();
+   
+    if ( keyType == PKCS11Object::unknown) {
+        throw PKCS11Exception(CKR_DATA_INVALID);
+    } 
+
+    if( keyType == Key::ecc ) {
+        ECCSignatureParams params(CryptParams::ECC_DEFAULT_KEY_SIZE);
+        signECC(suffix, pData, ulDataLen, pSignature, pulSignatureLen,
+            params);
+
+    } else if (keyType == Key::rsa) {
+        RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE);
+        cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen,
+            params);
+    }
 }
 
 void
@@ -3332,11 +4518,11 @@ Slot::cryptRSA(SessionHandleSuffix suffi
     }
     CryptOpState& opState = params.getOpState(*session);
     CKYBuffer *result = &opState.result;
-    CKYByte keyNum = opState.keyNum;
+    PKCS11Object  *key = opState.key;
 
-    unsigned int keySize = getKeySize(keyNum);
+    unsigned int keySize = getRSAKeySize(key);
 
-    if(keySize != CryptParams::DEFAULT_KEY_SIZE)
+    if (keySize != CryptParams::DEFAULT_KEY_SIZE)
         params.setKeySize(keySize);
 
     if( CKYBuffer_Size(result) == 0 ) {
@@ -3358,7 +4544,8 @@ Slot::cryptRSA(SessionHandleSuffix suffi
   	}
 	try {
 	    params.padInput(&inputPad, &input);
-            performRSAOp(&output, &inputPad, keyNum, params.getDirection());
+            performRSAOp(&output, &inputPad, params.getKeySize(), key, 
+							params.getDirection());
 	    params.unpadOutput(result, &output);
 	    CKYBuffer_FreeData(&input);
 	    CKYBuffer_FreeData(&inputPad);
@@ -3395,35 +4582,216 @@ Slot::getNonce()
     return &nonce;
 }
 
+void Slot::signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
+        CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
+        CK_ULONG_PTR pulOutputLen, CryptParams& params)
+{
+
+    if( pulOutputLen == NULL ) {
+        throw PKCS11Exception(CKR_DATA_INVALID,
+            "output length is NULL");
+    }
+
+    refreshTokenState();
+    SessionIter session = findSession(suffix);
+    if( session == sessions.end() ) {
+        throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+    }
+    /* version 1 keys may not need login. We catch the error
+       on the operation. The token will not allow us to sign with
+       a protected key unless we are logged in.
+       can be removed when version 0 support is depricated.
+    */
+
+    if (!isVersion1Key && ! isLoggedIn() ) {
+        throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN);
+    }
+    CryptOpState& opState = params.getOpState(*session);
+    CKYBuffer *result = &opState.result;
+    PKCS11Object  *key = opState.key;
+
+    unsigned int keySize = getECCKeySize(key);
+
+    if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE)
+        params.setKeySize(keySize);
+
+    if( CKYBuffer_Size(result) == 0 ) {
+	unsigned int maxSize = params.getKeySize()/8;
+
+        if( pInput == NULL || ulInputLen == 0) {
+            throw PKCS11Exception(CKR_DATA_LEN_RANGE);
+        }
+	if (ulInputLen > maxSize) {
+	    //pInput += ulInputLen - maxSize;
+	    ulInputLen = maxSize;
+	}
+
+        CKYBuffer input;
+        CKYBuffer output;
+        CKYBuffer_InitEmpty(&output);
+        CKYStatus status = CKYBuffer_InitFromData(&input, pInput, ulInputLen);
+
+        if (status != CKYSUCCESS) {
+            CKYBuffer_FreeData(&output);
+            throw PKCS11Exception(CKR_HOST_MEMORY);
+        }
+        try {
+            performECCSignature(&output, &input, params.getKeySize(), key);
+            params.unpadOutput(result, &output);
+            CKYBuffer_FreeData(&input);
+            CKYBuffer_FreeData(&output);
+        } catch(PKCS11Exception& e) {
+            CKYBuffer_FreeData(&input);
+            CKYBuffer_FreeData(&output);
+            throw(e);
+        }
+    }
+
+    if( pOutput != NULL ) {
+        if( *pulOutputLen < CKYBuffer_Size(result) ) {
+            *pulOutputLen = CKYBuffer_Size(result);
+            throw PKCS11Exception(CKR_BUFFER_TOO_SMALL);
+        }
+        memcpy(pOutput, CKYBuffer_Data(result), CKYBuffer_Size(result));
+    }
+    *pulOutputLen = CKYBuffer_Size(result);
+}
+
 void
-Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, 
-					CKYByte keyNum, CKYByte direction)
+Slot::selectKey(const PKCS11Object *key, bool retry)
 {
-    //
-    // establish a transaction
-    //
+    /* P15 cards need to be reselected on retry because P15 must select
+     * on authentication. PIV, CAC and Coolkeys do not */
+    if (retry && ((state & GOV_CARD) || ((state & P15_CARD) == 0))) {
+	return;
+    }
+    if (state & GOV_CARD) {
+	selectCACApplet(objectToKeyNum(key), true);
+    } else if (state & P15_CARD) {
+	selectPath(key->getObjectPath().getPath(), NULL);
+    } else {
+	selectApplet();
+    }
+    return;
+}
+
+void
+Slot::performECCSignature(CKYBuffer *output, const CKYBuffer *input, 
+			unsigned int keySize, const PKCS11Object *key)
+{
+
+    /* establish a transaction */
     Transaction trans;
     CKYStatus status = trans.begin(conn);
     if( status != CKYSUCCESS ) handleConnectionError();
 
-    //
-    // select the applet
-    //
-    if (state & CAC_CARD) {
-	selectCACApplet(keyNum);
+    if (!mECC) {
+        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+    }
+
+    CKYISOStatus result;
+    bool loginAttempted = false;
+
+retry:
+    selectKey(key, loginAttempted);
+
+    if (state & PIV_CARD) {
+        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, 
+					input, output, &result);
+    } else if (state & CAC_CARD) {
+        status = CACApplet_SignDecrypt(conn, input, output, &result);
+    } else if (state & P15_CARD) {
+	status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
+				CKY_DIR_ENCRYPT, input, output, &result); 
+	
     } else {
-	selectApplet();
+        status = CKYApplet_ComputeECCSignature(conn, objectToKeyNum(key), 					input, NULL, output, getNonce(), &result);
+    }
+    /* map the ISO not logged in code to the coolkey one */
+    if ((result == CKYISO_CONDITION_NOT_SATISFIED) ||
+		(result == CKYISO_SECURITY_NOT_SATISFIED)) {
+	result = (CKYStatus) CKYISO_UNAUTHORIZED;
+    }
+
+    if (status != CKYSUCCESS) {
+        if ( status == CKYSCARDERR ) {
+            handleConnectionError();
+        }
+
+        if (result == CKYISO_DATA_INVALID) {
+            throw PKCS11Exception(CKR_DATA_INVALID);
+        }
+        /* keys could be logged out in the middle by someone else,
+         *  reauthenticate... coolkey version 1 bypasses this issue by
+         *  allowing multiple applications separate login states at once.
+         */
+        if (!isVersion1Key && !loginAttempted  &&
+				userPinCache(key->getUser())->isValid() &&
+                    (result == CKYISO_UNAUTHORIZED)) {
+            /* try to reauthenticate  */
+	    try {
+		attemptLogin(key->getUser(),true);
+            } catch(PKCS11Exception& ) {
+                /* attemptLogin can throw things like CKR_PIN_INCORRECT
+                  that don't make sense from a crypto operation. This is
+                  a result of pin caching. We will reformat any login
+                  exception to a CKR_DEVICE_ERROR.
+                */
+                throw PKCS11Exception(CKR_DEVICE_ERROR);
+            }
+            loginAttempted = true;
+            goto retry; /* easier to understand than a while loop in this case. */
+        }
+        throw PKCS11Exception( result == CKYISO_UNAUTHORIZED ?
+                 CKR_USER_NOT_LOGGED_IN : CKR_DEVICE_ERROR);
+
     }
 
+}
+
+
+void
+Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, 
+		unsigned int keySize, const PKCS11Object *key, 
+		CKYByte direction)
+{
+    if ( mECC ) {
+        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+    }
+
+    //
+    // establish a transaction
+    //
+    Transaction trans;
+    CKYStatus status = trans.begin(conn);
+    if( status != CKYSUCCESS ) handleConnectionError();
     CKYISOStatus result;
-    int loginAttempted = 0;
+    bool loginAttempted = false;
+
 retry:
-    if (state & CAC_CARD) {
+    selectKey(key, loginAttempted);
+
+
+    if (state & PIV_CARD) {
+        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, 
+				input, output, &result);
+    } else if (state & CAC_CARD) {
         status = CACApplet_SignDecrypt(conn, input, output, &result);
+    } else if (state & P15_CARD) {
+	status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
+				direction, input, output, &result);
     } else {
-        status = CKYApplet_ComputeCrypt(conn, keyNum, CKY_RSA_NO_PAD, direction,
-		input, NULL, output, getNonce(), &result);
+        status = CKYApplet_ComputeCrypt(conn, objectToKeyNum(key), 
+		CKY_RSA_NO_PAD, direction, input, NULL, output, 
+		getNonce(), &result);
     } 
+
+    /* map the ISO not logged in code to the coolkey one */
+    if ((result == CKYISO_CONDITION_NOT_SATISFIED) ||
+	 (result == CKYISO_SECURITY_NOT_SATISFIED)) {
+	result = CKYISO_UNAUTHORIZED;
+    }
+
     if (status != CKYSUCCESS) {
 	if ( status == CKYSCARDERR ) {
 	    handleConnectionError();
@@ -3435,10 +4803,11 @@ retry:
 	// reauthenticate... This code can go away when we depricate.
         // version0 applets.
 	if (!isVersion1Key && !loginAttempted  && 
+				userPinCache(key->getUser())->isValid() &&
 					(result == CKYISO_UNAUTHORIZED)) {
 	    // try to reauthenticate 
 	    try {
-		oldAttemptLogin();
+		attemptLogin(key->getUser(), true);
 	    } catch(PKCS11Exception& ) {
 		// attemptLogin can throw things like CKR_PIN_INCORRECT
 		// that don't make sense from a crypto operation. This is
@@ -3458,7 +4827,7 @@ void
 Slot::seedRandom(SessionHandleSuffix suffix, CK_BYTE_PTR pData,
         CK_ULONG ulDataLen)
 {
-    if (state & CAC_CARD) {
+    if (state & (GOV_CARD|P15_CARD)) {
 	/* should throw unsupported */
 	throw PKCS11Exception(CKR_DEVICE_ERROR);
     }
@@ -3510,7 +4879,7 @@ void
 Slot::generateRandom(SessionHandleSuffix suffix, const CK_BYTE_PTR pData,
         CK_ULONG ulDataLen)
 {
-    if (state & CAC_CARD) {
+    if (state & (GOV_CARD|P15_CARD)) {
 	/* should throw unsupported */
 	throw PKCS11Exception(CKR_DEVICE_ERROR);
     }
@@ -3544,33 +4913,249 @@ Slot::generateRandom(SessionHandleSuffix
 
 #define MAX_NUM_KEYS 8
 unsigned int
-Slot::getKeySize(CKYByte keyNum)
+Slot::getRSAKeySize(PKCS11Object *key)
 {
     unsigned int keySize = CryptParams::DEFAULT_KEY_SIZE;
     int modSize = 0;
 
-    if(keyNum >= MAX_NUM_KEYS) {
-        return keySize;
-    }
-
-    ObjectConstIter iter;
-    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
-        KeyNumMatch(keyNum,*this));
-
-    if( iter == tokenObjects.end() ) {
-        return keySize;
+    modSize = key->getKeySize();
+    if (modSize != 0) {
+	return modSize;
     }
 
-    CKYBuffer const *modulus = iter->getAttribute(CKA_MODULUS);
+    CKYBuffer const *modulus = key->getAttribute(CKA_MODULUS);
 
     if(modulus) {
         modSize = CKYBuffer_Size(modulus);
         if(CKYBuffer_GetChar(modulus,0) == 0x0) {
             modSize--;
         }
-        if(modSize > 0)
+        if(modSize > 0) {
             keySize = modSize * 8;
+	    key->setKeySize(keySize);
+	}
     }
 
     return keySize;
 }
+
+unsigned int
+Slot::getECCKeySize(PKCS11Object *key)
+{
+    unsigned int keySize = CryptParams::ECC_DEFAULT_KEY_SIZE;
+    unsigned int objKeySize = 0;
+
+    objKeySize = key->getKeySize();
+    if (objKeySize != 0) {
+	return objKeySize;
+    }
+
+    CKYBuffer const *eccParams = key->getAttribute(CKA_EC_PARAMS);
+
+    if (eccParams == NULL) {
+        return keySize;
+    }
+
+    /* Extract the oid from the params */
+
+    CKYByte ecParamsLen = CKYBuffer_GetChar(eccParams, 1);
+
+    if ( ecParamsLen == 0 ) {
+        return keySize;
+    }
+
+/* Now compare against the limited known list of oid byte info */
+
+    unsigned int oidByteLen =  0;
+
+    CKYByte curByte = 0;
+
+    for (int i = 0 ; i < numECCurves ; i++ ) {
+
+        oidByteLen = curveBytesNamePair[i].bytes[0];
+
+        if ( oidByteLen !=  (unsigned int ) ecParamsLen ) {
+            continue;
+        }
+
+        int match = 1;
+        for ( int j = 0 ; j < ecParamsLen ; j++ ) {
+            curByte = CKYBuffer_GetChar(eccParams, 2 + j );
+            if ( curveBytesNamePair[i].bytes[ j + 1 ] != curByte ) {
+                match = 0;
+                break;
+            }
+        }
+
+        if ( match == 1 ) {
+            keySize =  curveBytesNamePair[i].length;
+	    key->setKeySize(keySize);
+            return keySize;
+        }
+
+    }
+
+    return keySize;
+}
+
+void
+Slot::derive(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism,
+        CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, 
+        CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey)
+{
+
+    log->log("Inside of Slot::Derive! \n");
+
+    ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE);
+    SessionIter session = findSession(suffix);
+
+    session->keyAgreementState.initialize(getKeyFromHandle(hBaseKey));
+    deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, 
+		phKey, params);
+
+}
+
+void Slot::deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism,
+       CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params)
+{
+    if (pMechanism == NULL ) {
+        throw PKCS11Exception(CKR_ARGUMENTS_BAD);
+    }
+
+    CK_ECDH1_DERIVE_PARAMS *mechParams      = NULL;
+
+    mechParams = (CK_ECDH1_DERIVE_PARAMS*) pMechanism->pParameter;
+
+    if (mechParams == NULL || mechParams->kdf != CKD_NULL ) {
+        throw PKCS11Exception(CKR_ARGUMENTS_BAD);
+    }
+
+    refreshTokenState();
+    SessionIter session = findSession(suffix);
+    if( session == sessions.end() ) {
+        throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+    }
+
+     /* version 1 keys may not need login. We catch the error
+      on the operation. The token will not allow us to sign with
+      a protected key unless we are logged in.
+      can be removed when version 0 support is depricated. */
+
+    if (!isVersion1Key && ! isLoggedIn() ) {
+        throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN);
+    }
+
+    CryptOpState& opState = params.getOpState(*session);
+    CKYBuffer *result = &opState.result;
+
+    unsigned int keySize = getECCKeySize(opState.key);
+
+    if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE)
+        params.setKeySize(keySize);
+
+    CK_MECHANISM_TYPE deriveMech = pMechanism->mechanism;
+
+    CK_ULONG otherPublicLen = mechParams->ulPublicDataLen;
+    CK_BYTE_PTR    otherPublicData = mechParams->pPublicData;
+
+    CKYBuffer secretKeyBuffer;
+    CKYBuffer_InitEmpty(&secretKeyBuffer);
+    CKYBuffer publicDataBuffer;
+    CKYStatus status = CKYBuffer_InitFromData(&publicDataBuffer,otherPublicData, otherPublicLen);
+
+    if (status != CKYSUCCESS) {
+        CKYBuffer_FreeData(&secretKeyBuffer);
+        throw PKCS11Exception(CKR_HOST_MEMORY);
+    }
+
+    PKCS11Object *secret = NULL;
+    *phKey = 0;
+
+    if( CKYBuffer_Size(result) == 0 ) {
+        try {
+            performECCKeyAgreement(deriveMech, &publicDataBuffer, 
+			&secretKeyBuffer, opState.key, params.getKeySize());
+            CK_OBJECT_HANDLE keyObjectHandle = generateUnusedObjectHandle();
+            secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, 
+						pTemplate, ulAttributeCount);
+        } catch(PKCS11Exception& e) {
+            CKYBuffer_FreeData(&secretKeyBuffer);
+            CKYBuffer_FreeData(&publicDataBuffer);
+            throw(e);
+        }
+   }
+
+   CKYBuffer_FreeData(&secretKeyBuffer);
+   CKYBuffer_FreeData(&publicDataBuffer);
+
+   if ( secret ) {
+       *phKey = secret->getHandle();
+        delete secret;
+   }
+}
+
+void
+Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
+	CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, 
+	const PKCS11Object *key, unsigned int keySize)
+{
+    if (!mECC) {
+       throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+    }
+
+    Transaction trans;
+    CKYStatus status = trans.begin(conn);
+    if( status != CKYSUCCESS ) handleConnectionError();
+
+    CKYISOStatus result;
+    bool loginAttempted = false;
+
+retry:
+    selectKey(key, loginAttempted);
+
+    if (state & PIV_CARD) {
+        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, 
+			publicDataBuffer, secretKeyBuffer, &result);
+    } else if (state & CAC_CARD) {
+        status = CACApplet_SignDecrypt(conn, publicDataBuffer, 
+			secretKeyBuffer, &result);
+    } else if (state & P15_CARD) {
+	/*status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
+				publicDataBuffer, secretKeyBuffer, &result); */
+	throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+    } else {
+    	status = CKYApplet_ComputeECCKeyAgreement(conn, objectToKeyNum(key), 
+			publicDataBuffer , NULL, secretKeyBuffer, 
+			getNonce(), &result);
+    }
+    /* map the ISO not logged in code to the coolkey one */
+    if ((result == CKYISO_CONDITION_NOT_SATISFIED) ||
+		(result == CKYISO_SECURITY_NOT_SATISFIED)) {
+	result = (CKYStatus) CKYISO_UNAUTHORIZED;
+    }
+
+    if (status != CKYSUCCESS) {
+        if ( status == CKYSCARDERR ) {
+            handleConnectionError();
+        }
+
+        if (result == CKYISO_DATA_INVALID) {
+            throw PKCS11Exception(CKR_DATA_INVALID);
+        }
+        if (!isVersion1Key && !loginAttempted  &&
+				userPinCache(key->getUser())->isValid() &&
+            				(result == CKYISO_UNAUTHORIZED)) {
+	    try {
+		attemptLogin(key->getUser(), true);
+	    } catch(PKCS11Exception& ) {
+              throw PKCS11Exception(CKR_DEVICE_ERROR);
+	    }
+	    loginAttempted = true;
+	    goto retry;
+        }
+       
+        throw PKCS11Exception( result == CKYISO_UNAUTHORIZED ?
+                               CKR_USER_NOT_LOGGED_IN : CKR_DEVICE_ERROR);
+
+    } 
+}
diff -up ./src/coolkey/slot.h.coolkey-update ./src/coolkey/slot.h
--- ./src/coolkey/slot.h.coolkey-update	2016-01-19 17:56:25.705426782 -0800
+++ ./src/coolkey/slot.h	2016-01-19 17:56:35.438346737 -0800
@@ -183,7 +183,7 @@ struct PinCache {
 	CKYBuffer_Replace(&cachedPin, 0, (const CKYByte *)newPin, pinLen);
 	CKYBuffer_AppendChar(&cachedPin, 0);
     }
-    void clearPin() { CKYBuffer_Zero(&cachedPin); }
+    void clearPin() { CKYBuffer_Zero(&cachedPin); valid = false; }
     void invalidate() { valid = false; }
     void validate() { valid = true; }
     const CKYBuffer *get() const { return &cachedPin; }
@@ -209,26 +209,26 @@ class CryptOpState {
   public:
     enum State { NOT_INITIALIZED, IN_PROCESS, FINALIZED };
     State state;
-    CKYByte keyNum;
     CKYBuffer result;
+    PKCS11Object *key;
 
-    CryptOpState() : state(NOT_INITIALIZED), keyNum(0) 
+    CryptOpState() : state(NOT_INITIALIZED), key(NULL)
 				{ CKYBuffer_InitEmpty(&result); }
     CryptOpState(const CryptOpState &cpy) : 
-				state(cpy.state), keyNum(cpy.keyNum) { 
+				state(cpy.state), key(cpy.key) { 
 	CKYBuffer_InitFromCopy(&result, &cpy.result);
     }
     CryptOpState &operator=(const CryptOpState &cpy) {
 	state = cpy.state,
-	keyNum = cpy.keyNum;
+        key = cpy.key;
 	CKYBuffer_Replace(&result, 0, CKYBuffer_Data(&cpy.result),
 				CKYBuffer_Size(&cpy.result));
 	return *this;
     }
     ~CryptOpState() { CKYBuffer_FreeData(&result); }
-    void initialize(CKYByte keyNum) {
+    void initialize(PKCS11Object *theKey) {
         state = IN_PROCESS;
-        this->keyNum = keyNum;
+        this->key = theKey;
         CKYBuffer_Resize(&result, 0);
     }
 };
@@ -258,6 +258,7 @@ class Session {
 
     CryptOpState signatureState;
     CryptOpState decryptionState;
+    CryptOpState keyAgreementState;
 };
 
 typedef list<Session> SessionList;
@@ -267,12 +268,11 @@ typedef SessionList::const_iterator Sess
 class CryptParams {
   private:
     unsigned int keySize; // in bits
-  protected:
-    unsigned int getKeySize() const { return keySize; }
   public:
     // set the actual key size obtained from the card
     void setKeySize(unsigned int newKeySize) { keySize = newKeySize; }
-    enum { DEFAULT_KEY_SIZE = 1024 };
+    unsigned int getKeySize() const { return keySize; }
+    enum { DEFAULT_KEY_SIZE = 1024, ECC_DEFAULT_KEY_SIZE=256 };
 
 
     CryptParams(unsigned int keySize_) : keySize(keySize_) { }
@@ -295,6 +295,7 @@ class CryptParams {
 };
 
 #define MAX_CERT_SLOTS 3
+#define MAX_AUTH_USERS 3
 class Slot {
 
   public:
@@ -304,17 +305,22 @@ class Slot {
         ATR_MATCH = 0x04,
         APPLET_SELECTABLE = 0x08,
         APPLET_PERSONALIZED = 0x10,
-        CAC_CARD = 0x20
+        CAC_CARD = 0x20,
+        PIV_CARD = 0x40,
+	P15_CARD = 0x80
     };
     enum {
 	NONCE_SIZE = 8
     };
 
+    static const SlotState GOV_CARD = (SlotState)(CAC_CARD|PIV_CARD);
+
   private:
     Log *log;
     char *readerName;
     char *personName;
     char *manufacturer;
+    char *tokenManufacturer;
     //char *model;
     CK_VERSION hwVersion;
     CK_VERSION tokenFWVersion;
@@ -323,6 +329,7 @@ class Slot {
     CKYCardConnection* conn;
     unsigned long state; // = UNKNOWN
     PinCache pinCache;
+    PinCache contextPinCache;
     bool loggedIn;
     bool reverify;
     bool nonceValid;
@@ -339,7 +346,18 @@ class Slot {
     bool fullTokenName;
     bool mCoolkey;
     bool mOldCAC;
-
+    bool mCACLocalLogin;
+    int pivContainer;
+    int pivKey;
+    bool mECC;
+    unsigned short p15aid;
+    unsigned short p15odfAddr;
+    unsigned short p15tokenInfoAddr;
+    unsigned int p15Instance;
+    CKYBuffer p15AID;
+    CKYBuffer p15tokenInfo;
+    CKYBuffer p15odf;
+    CKYBuffer p15serialNumber;
     //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 };
 
 #ifdef USE_SHMEM
@@ -358,6 +376,7 @@ class Slot {
 
     void closeAllSessions();
     SessionHandleSuffix generateNewSession(Session::Type type);
+    PK15Object *auth[MAX_AUTH_USERS];
 
     bool cardStateMayHaveChanged();
     void connectToToken();
@@ -386,6 +405,7 @@ class Slot {
     const CKYBuffer *getATR();
     bool isLoggedIn();
     bool needLoggedIn();
+    bool getPIVLoginType();
     void testNonce();
 
     void addKeyObject(list<PKCS11Object>& objectList,
@@ -395,6 +415,7 @@ class Slot {
 	const CKYBuffer *derCert, CK_OBJECT_HANDLE handle);
     void addObject(list<PKCS11Object>& objectList,
         const ListObjectInfo& info, CK_OBJECT_HANDLE handle);
+    PKCS11Object *createSecretKeyObject(CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer,CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount);
 
     void ensureValidSession(SessionHandleSuffix suffix);
 
@@ -407,37 +428,63 @@ class Slot {
                               bool throwException);
     CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize);
 
+    CKYStatus getP15Params();
     void selectApplet();
-    void selectCACApplet(CKYByte instance);
+    void selectCACApplet(CKYByte instance,bool do_disconnect);
+    void selectKey(const PKCS11Object *key, bool retry);
+    CKYStatus selectPath(const CKYBuffer *path, CKYISOStatus *adpurc);
+    CKYStatus readFromPath(const PK15ObjectPath &obj, CKYBuffer *file);
     void unloadObjects();
     void loadCACObjects();
     void loadCACCert(CKYByte instance);
     void loadObjects();
     void loadReaderObject();
 
-    void attemptLogin(const char *pin);
+    void attemptCoolKeyLogin(const char *pin);
+    void attemptLogin(CK_USER_TYPE user, bool flushPin);
+    void attemptP15Login(CK_USER_TYPE user);
     void attemptCACLogin();
     void oldAttemptLogin();
     void oldLogout(void);
     void CACLogout(void);
+    PinCache *userPinCache(CK_USER_TYPE user) {
+	return ((user == CKU_CONTEXT_SPECIFIC) && (state & P15_CARD)) ?
+		&contextPinCache : &pinCache; }
+
+    void parseEF_ODF(void);
+    void parseEF_TokenInfo(void);
+    CKYStatus parseEF_Directory(const CKYByte *data, CKYSize size, 
+				PK15ObjectType type);
+    unsigned int PK15Instance(void) { return p15Instance++; }
 
     void readMuscleObject(CKYBuffer *obj, unsigned long objID, 
 							unsigned int objSize);
 
     void performSignature(CKYBuffer *sig, const CKYBuffer *unpaddedInput, 
-							CKYByte keyNum);
-    void performDecryption(CKYBuffer *data, const CKYBuffer *input, CKYByte keyNum);
+						const PKCS11Object *key);
+    void performDecryption(CKYBuffer *data, const CKYBuffer *input, 
+						const PKCS11Object *key);
 
     void cryptRSA(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
         CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
         CK_ULONG_PTR pulOutputLen, CryptParams& params);
 
-    void performRSAOp(CKYBuffer *out, const CKYBuffer *input, CKYByte keyNum, 
-							     CKYByte direction);
+    void performRSAOp(CKYBuffer *out, const CKYBuffer *input, 
+	unsigned int keySize, const PKCS11Object *key, CKYByte direction);
+
+    void signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
+        CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
+        CK_ULONG_PTR pulOutputLen, CryptParams& params);
+
+    void performECCSignature(CKYBuffer *out, const CKYBuffer *input, 
+				unsigned int keySize, const PKCS11Object *key);
+    void performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
+        CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, 
+	const PKCS11Object *key, 	unsigned int keySize);
 
     void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu);
 
-    CKYByte objectHandleToKeyNum(CK_OBJECT_HANDLE hKey);
+    CKYByte objectToKeyNum(const PKCS11Object *key);
     Slot(const Slot &cpy)
 #ifdef USE_SHMEM
 	: shmem(readerName)
@@ -469,7 +516,10 @@ class Slot {
     }
 
     // actually get the size of a key in bits from the card
-    unsigned int getKeySize(CKYByte keyNum);
+    unsigned int getRSAKeySize(PKCS11Object *key);
+    unsigned int getECCKeySize(PKCS11Object *key);
+
+    PKCS11Object *getKeyFromHandle(CK_OBJECT_HANDLE hKey);
 
     SessionHandleSuffix openSession(Session::Type type);
     void closeSession(SessionHandleSuffix handleSuffix);
@@ -479,8 +529,8 @@ class Slot {
     void getSessionInfo(SessionHandleSuffix handleSuffix,
         CK_SESSION_INFO_PTR pInfo);
 
-    void login(SessionHandleSuffix handleSuffix, CK_UTF8CHAR_PTR pPin,
-        CK_ULONG ulPinLen);
+    void login(SessionHandleSuffix handleSuffix, CK_USER_TYPE user,
+	CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);
 
     void logout(SessionHandleSuffix suffix);
 
@@ -511,6 +561,16 @@ class Slot {
 	CK_ULONG len);
     void generateRandom(SessionHandleSuffix suffix, CK_BYTE_PTR data,
 	CK_ULONG len);
+
+    void derive(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism,
+        CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_PTR pTemplate, 
+        CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey);
+
+    void deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism,
+       CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, 
+       CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params);
+
+    bool getIsECC() { return mECC; }
 };
 
 class SlotList {
@@ -569,8 +629,8 @@ class SlotList {
     void getSessionInfo(CK_SESSION_HANDLE sessionHandle,
         CK_SESSION_INFO_PTR pInfo);
 
-    void login(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin,
-        CK_ULONG ulPinLen);
+    void login(CK_SESSION_HANDLE hSession, CK_USER_TYPE user,
+	CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);
 
     void logout(CK_SESSION_HANDLE hSession);
 
@@ -604,6 +664,10 @@ class SlotList {
     void seedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
         CK_ULONG ulDataLen);
 
+    void derive(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+        CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_PTR pTemplate, 
+        CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey);
+
 
 };
 #endif
diff -up ./src/install/pk11install.c.coolkey-update ./src/install/pk11install.c
--- ./src/install/pk11install.c.coolkey-update	2016-01-19 18:20:42.299464185 -0800
+++ ./src/install/pk11install.c	2016-01-19 18:18:52.766363310 -0800
@@ -171,7 +171,7 @@ int dirListCount = sizeof(dirList)/sizeo
 static void
 usage(char *prog)
 {
-    fprintf(stderr,"usage: %s [-u][-v] [-p path] module\n", prog);
+    fprintf(stderr,"usage: %s [-u][-v][-s][-l] [-p path] module\n", prog);
     return;
 }
 
@@ -181,9 +181,9 @@ usage(char *prog)
 
 #define CONFIG_TAG "configDir="
 int
-installPKCS11(char *dirPath, InstType type, char *module)
+installPKCS11(char *dirPath, char *dbType, InstType type, char *module)
 {
-    char *paramString = (char *)malloc(strlen(dirPath)+sizeof(CONFIG_TAG)+3);
+    char *paramString = (char *)malloc(strlen(dbType)+strlen(dirPath)+sizeof(CONFIG_TAG)+3);
     char *cp;
     char **rc;
 
@@ -191,7 +191,7 @@ installPKCS11(char *dirPath, InstType ty
 	PINST_SET_ERROR(ERROR_NOT_ENOUGH_MEMORY);
 	return 0;
     }
-    sprintf(paramString,CONFIG_TAG"\"%s\" ",dirPath);
+    sprintf(paramString,CONFIG_TAG"\"%s%s\" ",dbType,dirPath);
 
     /* translate all the \'s to /'s */
     for (cp=paramString; *cp; cp++) {
@@ -214,7 +214,7 @@ installPKCS11(char *dirPath, InstType ty
 
 
 int
-installAllPKCS11(char *dirPath, char *search, char *tail,
+installAllPKCS11(char *dirPath, char *dbType, char *search, char *tail,
 					InstType type, char *module)
 {
     char *searchString;
@@ -282,9 +282,9 @@ installAllPKCS11(char *dirPath, char *se
 
 	myPath=PINST_FULLPATH(tempPath,path);
 	if (tail) {
-	    installAllPKCS11(myPath, tail, NULL, type, module);
+	    installAllPKCS11(myPath, dbType, tail, NULL, type, module);
 	} else {
-	    installPKCS11(myPath, type, module);
+	    installPKCS11(myPath, dbType, type, module);
 	}
     } while (PINST_NEXT(iter, fileData));
     free(tempPath);
@@ -309,6 +309,7 @@ int main(int argc, char **argv)
     int i;
     InstType type = Install;
     char * path = NULL;
+    char *dbType = "";
 #ifdef WIN32
     BOOL brc;
     HKEY regKey;
@@ -333,6 +334,12 @@ int main(int argc, char **argv)
 	    case 'v':
 		verbose = 1;
 		break;
+	    case 'l':
+		dbType = "dbm:";
+		break;
+	    case 's':
+		dbType = "sql:";
+		break;
 	    case 'p':
 		path = *argv++;
 		if (path == NULL) {
@@ -359,7 +366,7 @@ int main(int argc, char **argv)
     }
 
     if (path) {
-	installAllPKCS11(path, "", NULL, type, module);
+	installAllPKCS11(path, dbType, "", NULL, type, module);
 	return 0;
     }
 
@@ -444,7 +451,7 @@ int main(int argc, char **argv)
 	if (!dirPath) {
 	    continue;
 	}
-	installAllPKCS11(dirPath, dirList[i].search, dirList[i].tail, 
+	installAllPKCS11(dirPath, dbType, dirList[i].search, dirList[i].tail, 
 								type, module);
     }
 
diff -up ./src/libckyapplet/cky_applet.c.coolkey-update ./src/libckyapplet/cky_applet.c
--- ./src/libckyapplet/cky_applet.c.coolkey-update	2016-01-19 18:01:32.031909257 -0800
+++ ./src/libckyapplet/cky_applet.c	2016-01-19 18:01:45.612797732 -0800
@@ -19,6 +19,7 @@
 
 #include <stdio.h>
 #include "cky_applet.h"
+#include <string.h>
 
 #define MIN(x, y) ((x) < (y) ? (x) : (y))
 
@@ -51,6 +52,12 @@ CACAppletFactory_SelectFile(CKYAPDU *apd
 }
 
 CKYStatus
+P15AppletFactory_SelectFile(CKYAPDU *apdu, const void *param)
+{
+    return CKYAPDUFactory_SelectFile(apdu, 0, 0, (const CKYBuffer *)param);
+}
+
+CKYStatus
 CKYAppletFactory_SelectCardManager(CKYAPDU *apdu, const void *param)
 {
     return CKYAPDUFactory_SelectCardManager(apdu);
@@ -103,6 +110,22 @@ CKYAppletFactory_ComputeCryptOneStep(CKY
 }
 
 CKYStatus
+CKYAppletFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, const void *param)
+{
+    const CKYAppletArgComputeECCSignature *ccs=(const CKYAppletArgComputeECCSignature *)param;
+    return CKYAPDUFactory_ComputeECCSignatureOneStep(apdu, ccs->keyNumber,
+                        ccs->location, ccs->data, ccs->sig);
+}
+
+CKYStatus
+CKYAppletFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, const void *param)
+{
+
+    const CKYAppletArgComputeECCKeyAgreement *ccs=(const CKYAppletArgComputeECCKeyAgreement *)param;
+    return CKYAPDUFactory_ComputeECCKeyAgreementOneStep(apdu, ccs->keyNumber, ccs->location, ccs->publicValue, ccs->secretKey);
+}
+
+CKYStatus
 CKYAppletFactory_CreatePIN(CKYAPDU *apdu, const void *param)
 {
     const CKYAppletArgCreatePIN *cps = (const CKYAppletArgCreatePIN *)param;
@@ -245,10 +268,18 @@ CACAppletFactory_SignDecryptFinal(CKYAPD
 }
 
 CKYStatus
-CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
+PIVAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param)
+{
+    const PIVAppletArgSignDecrypt *psd = (const PIVAppletArgSignDecrypt *)param;
+    return PIVAPDUFactory_SignDecrypt(apdu, psd->chain, psd->alg, psd->key, 
+					psd->len, psd->buf);
+}
+
+CKYStatus
+P15AppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
 {
-    const char *pin=(const char *)param;
-    return CACAPDUFactory_VerifyPIN(apdu, pin);
+    const P15AppletArgVerifyPIN *vps = (const P15AppletArgVerifyPIN *)param;
+    return P15APDUFactory_VerifyPIN(apdu, vps->pinRef, vps->pinVal);
 }
 
 CKYStatus
@@ -259,6 +290,13 @@ CACAppletFactory_GetCertificate(CKYAPDU
 }
 
 CKYStatus
+PIVAppletFactory_GetCertificate(CKYAPDU *apdu, const void *param)
+{
+    CKYBuffer *tag  =(CKYBuffer*)param;
+    return PIVAPDUFactory_GetData(apdu, tag, 0);
+}
+
+CKYStatus
 CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param)
 {
     const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param;
@@ -285,6 +323,39 @@ CKYAppletFactory_LogoutAllV0(CKYAPDU *ap
    return CKYAPDU_SetSendData(apdu, data, sizeof(data));
 }
 
+CKYStatus
+P15AppletFactory_ReadRecord(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgReadRecord *rrs = (const P15AppletArgReadRecord *)param;
+    return P15APDUFactory_ReadRecord(apdu, rrs->record,
+					rrs->short_ef, rrs->flags, rrs->size);
+}
+
+CKYStatus
+P15AppletFactory_ReadBinary(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgReadBinary *res = (const P15AppletArgReadBinary *)param;
+    return P15APDUFactory_ReadBinary(apdu, res->offset,
+					res->short_ef, res->flags, res->size);
+}
+
+CKYStatus
+P15AppletFactory_ManageSecurityEnvironment(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgManageSecurityEnvironment *mse = 
+		(const P15AppletArgManageSecurityEnvironment *)param;
+    return P15APDUFactory_ManageSecurityEnvironment(apdu, mse->p1, 
+						mse->p2, mse->keyRef);
+}
+
+CKYStatus
+P15AppletFactory_PerformSecurityOperation(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgPerformSecurityOperation *pso = 
+		(const P15AppletArgPerformSecurityOperation *)param;
+    return P15APDUFactory_PerformSecurityOperation(apdu, pso->dir, pso->chain,
+						   pso->retLen, pso->data);
+}
 /*****************************************************************
  *
  * Generic Fill routines used by several calls in common
@@ -325,6 +396,7 @@ CKYAppletFill_AppendBuffer(const CKYBuff
 						CKYBuffer_Size(response) -2);
 }
 
+
 CKYStatus
 CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param)
 {
@@ -725,6 +797,32 @@ CKYApplet_ComputeCryptProcess(CKYCardCon
 	&ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC);
 }
 
+/* computeECCValue returns data in the form :
+ *            len: short
+ *            data: byte[len]
+ * This fill routine returns A buffer with a copy of data and a length of len */
+static CKYStatus
+ckyAppletFill_ComputeECCValueFinal(const CKYBuffer *response,
+                                                CKYSize size, void *param)
+{
+    CKYBuffer *cbuf = (CKYBuffer *)param;
+    CKYSize respSize = CKYBuffer_Size(response);
+    CKYSize dataLen;
+
+    if (cbuf == 0) {
+        return CKYSUCCESS; /* app didn't want the result */
+    }
+    /* data response code + length code */
+    if (respSize < 4) {
+        return CKYAPDUFAIL;
+    }
+    dataLen = CKYBuffer_GetShort(response, 0);
+    if (dataLen > (respSize-4)) {
+        return CKYAPDUFAIL;
+    }
+    return CKYBuffer_Replace(cbuf, 0, CKYBuffer_Data(response)+2, dataLen);
+}
+
 /* computeCrypt returns data in the form :
  * 		len: short
  * 		data: byte[len]
@@ -872,6 +970,74 @@ fail:
     return ret;
 }
 
+CKYStatus
+CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber,
+    const CKYBuffer *publicValue, CKYBuffer *sharedSecret,
+    CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC)
+{
+    CKYStatus ret = CKYAPDUFAIL;
+    CKYAppletArgComputeECCKeyAgreement ccd;
+    CKYBuffer    empty;
+    CKYISOStatus status;
+    /* Routine creates a sym key, should easily fit in one apdu */
+
+    CKYBuffer_InitEmpty(&empty);
+    ccd.keyNumber = keyNumber;
+    ccd.location  = CKY_DL_APDU;
+
+    if (!apduRC)
+        apduRC = &status;
+
+    if (ccd.location == CKY_DL_APDU) {
+        ccd.publicValue = publicValue;
+        ccd.secretKey  = sharedSecret;
+        ret =   CKYApplet_HandleAPDU(conn,
+                            CKYAppletFactory_ComputeECCKeyAgreementOneStep, &ccd, nonce,
+                            CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal,
+                            result, apduRC);
+        if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) {
+            return ret;
+        }
+    } 
+
+    return ret;
+}
+
+CKYStatus
+CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber,
+    const CKYBuffer *data, CKYBuffer *sig,
+    CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC)
+{
+    CKYStatus ret = CKYAPDUFAIL;
+    CKYAppletArgComputeECCSignature ccd;
+    CKYBuffer    empty;
+    CKYISOStatus status;
+
+    CKYBuffer_InitEmpty(&empty);
+    ccd.keyNumber = keyNumber;
+
+    /* Assume APDU, the signature can only get so big with our key sizes, ~ 130 for 521 bit key. */
+    ccd.location  = CKY_DL_APDU;
+
+    if (!apduRC)
+        apduRC = &status;
+
+    if (ccd.location == CKY_DL_APDU) {
+        ccd.data = data;
+        ccd.sig  = sig;
+        ret =   CKYApplet_HandleAPDU(conn,
+                            CKYAppletFactory_ComputeECCSignatureOneStep, &ccd, nonce,
+                            CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal,
+                            result, apduRC);
+        if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) {
+            return ret;
+        }
+
+    } 
+
+    return ret;
+}
+
 /*
  * do a CAC Sign/Decrypt
  */
@@ -916,32 +1082,23 @@ done:
     return ret;
 }
 
+const P15PinInfo CACPinInfo = 
+   { P15PinInitialized|P15PinNeedsPadding, P15PinUTF8, 0, 8, 8, 0, 0xff };
+const P15PinInfo PIVPinInfo = 
+   { P15PinLocal|P15PinInitialized|P15PinNeedsPadding, 
+					   P15PinUTF8, 0, 8, 8, 0, 0xff };
 /*
  * do a CAC VerifyPIN
  */
 CKYStatus
-CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local,
 		    CKYISOStatus *apduRC)
 {
-    CKYStatus ret;
-    CKYISOStatus status;
-    if (apduRC == NULL) {
-	apduRC = &status;
-    }
-
-    ret = CKYApplet_HandleAPDU(conn, 
-			    CACAppletFactory_VerifyPIN, pin, NULL, 
-			    0, CKYAppletFill_Null, 
-			    NULL, apduRC);
-    /* it's unfortunate that the same code that means 'more data to follow' for
-     * GetCertificate also means, auth failure, you only have N more attempts
-     * left in the verify PIN call */
-    if ((*apduRC & CKYISO_MORE_MASK) == CKYISO_MORE) {
-	ret = CKYAPDUFAIL;
-    }
-    return ret;
+    return P15Applet_VerifyPIN(conn, pin, 
+				local ? &PIVPinInfo: &CACPinInfo, apduRC);
 }
 
+
 /*
  * Get a CAC Certificate 
  */
@@ -1028,6 +1185,217 @@ CACApplet_ReadFile(CKYCardConnection *co
     return ret;
 }
 
+/*
+ *  Select a EF
+ */
+CKYStatus
+P15Applet_SelectFile(CKYCardConnection *conn, unsigned short ef,
+						 CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer efBuf;
+    CKYBuffer_InitEmpty(&efBuf);
+    CKYBuffer_AppendShort(&efBuf, ef);
+    ret = CKYApplet_HandleAPDU(conn, P15AppletFactory_SelectFile, &efBuf,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+    CKYBuffer_FreeData(&efBuf);
+    return ret;
+}
+
+CKYStatus
+P15Applet_SelectRootFile(CKYCardConnection *conn, unsigned short ef,
+						 CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer efBuf;
+    CKYBuffer_InitEmpty(&efBuf);
+    CKYBuffer_AppendShort(&efBuf, ef);
+    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &efBuf,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+    CKYBuffer_FreeData(&efBuf);
+    return ret;
+}
+
+CKYStatus
+P15Applet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+			const P15PinInfo *pinInfo, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYISOStatus status;
+    CKYSize size;
+    CKYBuffer encodedPin;
+    P15AppletArgVerifyPIN vps;
+
+    CKYBuffer_InitEmpty(&encodedPin);
+
+    if (apduRC == NULL) {
+	apduRC = &status;
+    }
+
+    size = strlen(pin);
+    if (pinInfo->pinFlags & P15PinNeedsPadding) {
+	if (size > pinInfo->storedLength) {
+	    size = pinInfo->storedLength;
+	}
+	ret=CKYBuffer_Reserve(&encodedPin, pinInfo->storedLength);
+	if (ret != CKYSUCCESS) { goto fail; }
+    }
+    /* This is where we would do upcase processing for the case insensitive 
+     * flag. It's also where we would do mapping for bcd pins */
+    ret = CKYBuffer_Replace(&encodedPin, 0, (const CKYByte *)pin, size);
+    if (ret != CKYSUCCESS) { goto fail; }
+    if (pinInfo->pinFlags & P15PinNeedsPadding) {
+	int i; 
+	int padSize = pinInfo->storedLength - size;
+	for (i=0; i < padSize; i++) {
+	    CKYBuffer_AppendChar(&encodedPin, pinInfo->padChar);
+	}
+    }
+
+    vps.pinRef = pinInfo->pinRef | 
+     ((pinInfo->pinFlags & P15PinLocal) ? ISO_LOGIN_LOCAL : ISO_LOGIN_GLOBAL);
+    vps.pinVal = &encodedPin;
+    ret = CKYApplet_HandleAPDU(conn, P15AppletFactory_VerifyPIN, &vps, NULL, 
+			    0, CKYAppletFill_Null, 
+			    NULL, apduRC);
+    /* it's unfortunate that the same code that means 'more data to follow' for
+     * GetCertificate also means, auth failure, you only have N more attempts
+     * left in the verify PIN call */
+    if ((*apduRC & CKYISO_MORE_MASK) == CKYISO_MORE) {
+	ret = CKYAPDUFAIL;
+    }
+fail:
+    CKYBuffer_FreeData(&encodedPin);
+    return ret;
+}
+
+
+/*
+ * Read Record
+ */
+CKYStatus
+P15Applet_ReadRecord(CKYCardConnection *conn, CKYByte record, CKYByte short_ef,
+		CKYByte flags, CKYByte size, CKYBuffer *data, CKYISOStatus *apduRC)
+{
+    P15AppletArgReadRecord rrd;
+
+    rrd.record = record;
+    rrd.short_ef = short_ef;
+    rrd.flags = flags;
+    rrd.size = size;
+    return CKYApplet_HandleAPDU(conn, P15AppletFactory_ReadRecord, &rrd, NULL,
+	CKY_SIZE_UNKNOWN, CKYAppletFill_ReplaceBuffer, data, apduRC);
+}
+
+static CKYStatus
+P15Applet_ManageSecurityEnvironment(CKYCardConnection *conn, CKYByte key,
+				 CKYByte direction, CKYByte p1, 
+				 CKYISOStatus *apduRC)
+{
+    P15AppletArgManageSecurityEnvironment mse;
+
+    mse.p1 = p1; /* this appears to be where most cards disagree */
+    mse.p2 = (direction == CKY_DIR_DECRYPT) ? ISO_MSE_KEA : ISO_MSE_SIGN;
+    mse.keyRef = key; /* should be CKYBuffer in the future? */
+    return CKYApplet_HandleAPDU(conn, 
+		P15AppletFactory_ManageSecurityEnvironment, &mse, NULL,
+		CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+}
+
+CKYStatus
+P15Applet_SignDecrypt(CKYCardConnection *conn, CKYByte key, 
+		unsigned int keySize, CKYByte direction, 
+		const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    P15AppletArgPerformSecurityOperation pso;
+    CKYSize dataSize = CKYBuffer_Size(data);
+    CKYOffset offset = 0;
+    CKYBuffer tmp;
+    int length = dataSize;
+    int appendLength = length;
+    int hasPad = 0;
+
+    /* Hack, lie and say we are always doing encipherment */
+    direction = CKY_DIR_DECRYPT;
+    CKYBuffer_Resize(result,0);
+    /*
+     * first set the security environment
+     */
+    ret = P15Applet_ManageSecurityEnvironment(conn, key, direction, 
+			ISO_MSE_SET|ISO_MSE_QUAL_COMPUTE, apduRC);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+
+    CKYBuffer_InitEmpty(&tmp);
+
+    pso.data = &tmp;
+    pso.dir = direction;
+    if (direction == CKY_DIR_DECRYPT) {
+	length++;
+	CKYBuffer_AppendChar(&tmp, 0x00); /* pad byte */
+	hasPad = 1;
+    }
+    if (CKYCardConnection_GetProtocol(conn) == SCARD_PROTOCOL_T0) {
+	ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE);
+	if (ret != CKYSUCCESS) {
+	    goto done;
+	}
+	for(offset = 0; length > CKY_MAX_WRITE_CHUNK_SIZE; 
+					hasPad = 0,
+					offset += CKY_MAX_WRITE_CHUNK_SIZE, 
+					length -= CKY_MAX_WRITE_CHUNK_SIZE) {
+	    pso.chain = 1;
+	    pso.retLen = 0;
+	    CKYBuffer_AppendBuffer(&tmp, data, offset, 
+	     hasPad ? (CKY_MAX_WRITE_CHUNK_SIZE-1) : CKY_MAX_WRITE_CHUNK_SIZE);
+            ret = CKYApplet_HandleAPDU(conn, 
+			P15AppletFactory_PerformSecurityOperation, &pso, NULL, 
+			CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+	    if (ret != CKYSUCCESS) {
+		goto done;
+	    }
+	    CKYBuffer_Resize(&tmp, 0);
+	}
+	appendLength = length;
+    } else {
+	ret = CKYBuffer_Reserve(&tmp, length);
+	if (ret != CKYSUCCESS) {
+	    goto done;
+	}
+    }
+    CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength);
+    pso.chain = 0;
+    pso.retLen = dataSize;
+
+    ret = CKYApplet_HandleAPDU(conn, 
+		P15AppletFactory_PerformSecurityOperation, &pso, NULL, 
+	CKY_SIZE_UNKNOWN, CKYAppletFill_ReplaceBuffer, result, apduRC);
+
+done:
+    CKYBuffer_FreeData(&tmp);
+    return ret;
+}
+
+/*
+ * Read Binary
+ */
+CKYStatus
+P15Applet_ReadBinary(CKYCardConnection *conn, unsigned short offset,
+			CKYByte short_ef, CKYByte flags, CKYByte size, 
+			CKYBuffer *data, CKYISOStatus *apduRC)
+{
+    P15AppletArgReadBinary red;
+
+    red.offset = offset;
+    red.short_ef = short_ef;
+    red.flags = flags;
+    red.size = size;
+    return CKYApplet_HandleAPDU(conn, P15AppletFactory_ReadBinary, &red, NULL,
+	CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, data, apduRC);
+}
+
 CKYStatus 
 CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, 
 			CKYSize *nextSize, CKYISOStatus *apduRC)
@@ -1078,6 +1446,278 @@ CACApplet_GetCertificateAppend(CKYCardCo
     return ret;
 }
 
+/* Select the PIV applet */
+static CKYByte pivAid[] = {0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 
+			   0x10, 0x00};
+CKYStatus
+PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer PIV_Applet_AID,return_AID;
+    
+    CKYBuffer_InitEmpty(&return_AID);
+    CKYBuffer_InitFromData(&PIV_Applet_AID, pivAid, sizeof(pivAid));
+    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, 
+		 &PIV_Applet_AID,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, 
+		 &return_AID, apduRC);
+    /* Some cards return OK, but don't switch to our applet */
+    /* PIV has a well defined return for it's select, check to see if we have
+     * a PIV card here */
+    if (CKYBuffer_GetChar(&return_AID,0) != 0x61) {
+	/* not an application property template, so not a PIV. We could
+	 * check that the aid tag (0x4f) and theallocation authority tag (0x79)
+	 * are present, but what we are really avoiding is broken cards that
+	 * lie about being able to switch to a particular applet, so the first
+	 * tag should be sufficient */
+	ret = CKYAPDUFAIL; /* what we should have gotten */
+    }
+    CKYBuffer_FreeData(&PIV_Applet_AID);
+    CKYBuffer_FreeData(&return_AID);
+    return ret;
+}
+
+/*
+ * Get a PIV Certificate 
+ */
+CKYStatus
+PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, int tag,
+		    CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYISOStatus status;
+    CKYBuffer tagBuf;
+
+    CKYBuffer_InitEmpty(&tagBuf);
+    CKYBuffer_Reserve(&tagBuf,4); /* can be up to 4 bytes */
+
+    CKYBuffer_Resize(cert,0);
+    if (apduRC == NULL) {
+	apduRC = &status;
+    }
+    if (tag >= 0x01000000) {
+	ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 24) & 0xff);
+        if (ret != CKYSUCCESS) { goto loser; }
+    }
+    if (tag >= 0x010000) {
+	ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 16) & 0xff);
+        if (ret != CKYSUCCESS) { goto loser; }
+    }
+    if (tag >= 0x0100) {
+	ret =CKYBuffer_AppendChar(&tagBuf, (tag >> 8) & 0xff);
+        if (ret != CKYSUCCESS) { goto loser; }
+    }
+    ret = CKYBuffer_AppendChar(&tagBuf, tag  & 0xff);
+    if (ret != CKYSUCCESS) { goto loser; }
+	
+
+    ret = CKYApplet_HandleAPDU(conn, 
+			    PIVAppletFactory_GetCertificate, &tagBuf, NULL, 
+			    CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, cert,
+			    apduRC);
+loser:
+    CKYBuffer_FreeData(&tagBuf);
+
+    return ret;
+}
+
+
+/*
+ * record the next ber tag and length. NOTE: this is a state machine.
+ * we can handle the case where we are passed the data just one byte
+ * at a time.
+ */
+static CKYStatus
+pivUnwrap(const CKYBuffer *buf, CKYOffset *offset, 
+		 CKYSize *dataSize, PIVUnwrapState *unwrap)
+{
+    if (unwrap->tag == 0) {
+	unwrap->tag = CKYBuffer_GetChar(buf, *offset);
+	if (unwrap->tag == 0) unwrap->tag = 0xff;
+	(*offset)++;
+	(*dataSize)--;
+    }
+    if (*dataSize == 0) {
+	return CKYSUCCESS;
+    }
+    if (unwrap->length_bytes != 0) {
+	int len;
+	if (unwrap->length_bytes == -1) {
+	    len = CKYBuffer_GetChar(buf, *offset);
+	    unwrap->length_bytes = 0;
+	    unwrap->length = len;
+	    (*offset)++;
+	    (*dataSize)--;
+	    if (len & 0x80) {
+		unwrap->length = 0;
+		unwrap->length_bytes = len & 0x7f;
+	    }
+	}
+	while ((*dataSize != 0) && (unwrap->length_bytes != 0)) {
+		len = CKYBuffer_GetChar(buf, *offset);
+		(*offset) ++;
+		(*dataSize) --;
+		unwrap->length = ((unwrap->length) << 8 | len);
+		unwrap->length_bytes--;
+	}
+    }
+    return CKYSUCCESS;
+}
+
+/*
+ * Remove the BER wrapping first...
+ */
+static CKYStatus
+pivAppletFill_AppendUnwrapBuffer(const CKYBuffer *response, 
+				 CKYSize size, void *param)
+{
+    PIVAppletRespSignDecrypt *prsd = (PIVAppletRespSignDecrypt *)param;
+    CKYBuffer *buf = prsd->buf;
+    CKYSize dataSize = CKYBuffer_Size(response);
+    CKYOffset offset = 0;
+
+    if (dataSize <= 2) {
+	return CKYSUCCESS;
+    }
+    dataSize -= 2;
+    /* remove the first tag */
+    (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_1);
+    if (dataSize == 0) {
+	return CKYSUCCESS;
+    }
+    /* remove the second tag */
+    (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_2);
+    if (dataSize == 0) {
+	return CKYSUCCESS;
+    }
+    /* the rest is real data */
+    return CKYBuffer_AppendData(buf, CKYBuffer_Data(response) + offset, 
+						dataSize);
+}
+
+static CKYStatus
+piv_wrapEncodeLength(CKYBuffer *buf, int length, int ber_len)
+{
+    if (ber_len== 1) {
+	CKYBuffer_AppendChar(buf,length);
+    } else {
+	ber_len--;
+	CKYBuffer_AppendChar(buf,0x80+ber_len);
+	while(ber_len--) {
+	    CKYBuffer_AppendChar(buf,(length >> (8*ber_len)) & 0xff);
+ 	}
+    }
+    return CKYSUCCESS;
+}
+/*
+ * do a PIV Sign/Decrypt
+ */
+CKYStatus
+PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, unsigned int keySize, int derive,
+		const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYSize dataSize = CKYBuffer_Size(data);
+    CKYSize outputSize = keySize;
+    CKYOffset offset = 0;
+    CKYBuffer tmp;
+    CKYByte  alg;
+    int ber_len_1;
+    int ber_len_2;
+    int length;
+    PIVAppletArgSignDecrypt pasd; 
+    PIVAppletRespSignDecrypt prsd; 
+
+    /* PIV only defines RSA 1024 and 2048, ECC 256 and ECC 384!!! */
+    if (keySize == 128) { /* 1024 bit == 128 bytes */
+	ber_len_2 = 2;
+	ber_len_1 = 2;
+	alg = 0x6;
+    } else if (keySize == 256) { /* 2048 bits == 256 bytes */
+	ber_len_2 = 3;
+	ber_len_1 = 3;
+	alg = 0x7;
+    } else if (keySize == 32) {  /* 256 bits = 32 bytes */
+	ber_len_2 = 1;
+	ber_len_1 = 1;
+	alg = 0x11;
+	if (!derive) outputSize = keySize*2;
+    } else if (keySize == 48) {  /* 384 bits = 48 bytes */
+	ber_len_2 = 1;
+	ber_len_1 = 1;
+	alg = 0x14;
+	if (!derive) outputSize = keySize*2;
+    } else {
+	return CKYINVALIDARGS; 
+    }
+
+    CKYBuffer_InitEmpty(&tmp);
+    ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE);
+    if (ret != CKYSUCCESS) {
+	goto done;
+    }
+    CKYBuffer_AppendChar(&tmp,0x7c);
+    piv_wrapEncodeLength(&tmp,dataSize + ber_len_2 + 3,ber_len_1);
+    CKYBuffer_AppendChar(&tmp,0x82);
+    CKYBuffer_AppendChar(&tmp,0x0);
+    CKYBuffer_AppendChar(&tmp, derive ? 0x85 : 0x81);
+    piv_wrapEncodeLength(&tmp,dataSize,ber_len_2);
+
+    /* now length == header length from here to the end*/
+    length = CKYBuffer_Size(&tmp);
+
+    if (length + dataSize > CKY_MAX_WRITE_CHUNK_SIZE) {
+	CKYBuffer_AppendBuffer(&tmp, data, 0, CKY_MAX_WRITE_CHUNK_SIZE-length);
+    } else {
+	CKYBuffer_AppendBuffer(&tmp, data, 0, dataSize);
+    }
+
+    prsd.tag_1.tag = 0;
+    prsd.tag_1.length_bytes = -1;
+    prsd.tag_1.length = 0;
+    prsd.tag_2.tag = 0;
+    prsd.tag_2.length_bytes = -1;
+    prsd.tag_2.length = 0;
+    prsd.buf = result;
+    pasd.alg = alg;
+    pasd.key = key;
+    pasd.buf = &tmp;
+
+    CKYBuffer_Resize(result,0);
+    for(offset = -length; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; ) {
+	pasd.chain = 1;
+	pasd.len = 0;
+        ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, 
+			    &pasd, NULL, CKY_SIZE_UNKNOWN, 
+			    pivAppletFill_AppendUnwrapBuffer, 
+			    &prsd, apduRC);
+	if (ret != CKYSUCCESS) {
+	    goto done;
+	}
+	CKYBuffer_Resize(&tmp,0);
+	/* increment before we append the next tmp buffer */
+	offset += CKY_MAX_WRITE_CHUNK_SIZE;
+	CKYBuffer_AppendBuffer(&tmp, data, offset,
+			MIN(dataSize-offset, CKY_MAX_WRITE_CHUNK_SIZE));
+    }
+
+    pasd.chain = 0;
+    pasd.len = outputSize;
+
+    ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, 
+			    &pasd, NULL, CKY_SIZE_UNKNOWN, 
+			    pivAppletFill_AppendUnwrapBuffer, 
+			    &prsd, apduRC);
+
+    if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != outputSize)) {
+	/* RSA returns the same data size as input, didn't happen, so
+	 * something is wrong. */
+    }
+
+done:
+    CKYBuffer_FreeData(&tmp);
+    return ret;
+}
 
 /*
  * PIN cluster
@@ -1223,6 +1863,7 @@ CKYApplet_ReadObjectFull(CKYCardConnecti
     return ret;
 }
 
+
 /*
  * Write Object
  * This makes multiple APDU calls to write the entire object.
diff -up ./src/libckyapplet/cky_applet.h.coolkey-update ./src/libckyapplet/cky_applet.h
--- ./src/libckyapplet/cky_applet.h.coolkey-update	2016-01-19 18:02:04.200645090 -0800
+++ ./src/libckyapplet/cky_applet.h	2016-01-19 18:02:11.013589143 -0800
@@ -43,6 +43,8 @@ typedef unsigned short CKYISOStatus; /*
 #define CKYISO_MORE_MASK	    0xff00  /* More data mask */
 #define CKYISO_MORE		    0x6300  /* More data available */
 #define CKYISO_DATA_INVALID	    0x6984
+#define CKYISO_CONDITION_NOT_SATISFIED 0x6985  /* AKA not logged in (CAC)*/
+#define CKYISO_SECURITY_NOT_SATISFIED  0x6982  /* AKA not logged in (PIV)*/
 /* Applet Defined Return codes */
 #define CKYISO_NO_MEMORY_LEFT        0x9c01  /* There have been memory 
                                              * problems on the card */
@@ -78,6 +80,7 @@ typedef unsigned short CKYISOStatus; /*
 
 #define CAC_TAG_CARDURL			0xf3
 #define CAC_TAG_CERTIFICATE		0x70
+#define CAC_TAG_CERTINFO		0x71
 #define CAC_TLV_APP_PKI			0x04
 
 /*
@@ -201,6 +204,7 @@ typedef struct _CKYAppletArgReadObject {
     CKYByte         size;
 } CKYAppletArgReadObject;
 
+
 typedef struct _CKYAppletArgWriteObject {
     unsigned long objectID;
     CKYOffset     offset;
@@ -218,12 +222,80 @@ typedef struct _CKYAppletArgComputeCrypt
     const CKYBuffer *sig;
 } CKYAppletArgComputeCrypt;
 
+typedef struct _CKYAppletArgComputeECCSignature {
+    CKYByte   keyNumber;
+    CKYByte   location;
+    const CKYBuffer *data;
+    const CKYBuffer *sig;
+} CKYAppletArgComputeECCSignature;
+
+typedef struct _CKYAppletArgComputeECCKeyAgreement {
+    CKYByte keyNumber;
+    CKYByte location;
+    const CKYBuffer *publicValue;
+    const CKYBuffer *secretKey;
+} CKYAppletArgComputeECCKeyAgreement;
+
+
 typedef struct _CACAppletArgReadFile {
     CKYByte   type;
     CKYByte   count;
     unsigned short offset;
 } CACAppletArgReadFile;
 
+typedef struct _PIVAppletArgSignDecrypt {
+     CKYByte	alg;   
+     CKYByte	key;   
+     CKYByte	chain;   
+     CKYSize	len;   
+     CKYBuffer  *buf;
+} PIVAppletArgSignDecrypt;
+
+typedef struct _pivUnwrapState {
+     CKYByte	tag;
+     CKYByte	length;
+     int	length_bytes;
+} PIVUnwrapState;
+
+typedef struct _PIVAppletRespSignDecrypt {
+     PIVUnwrapState tag_1;
+     PIVUnwrapState tag_2;
+     CKYBuffer  *buf;
+} PIVAppletRespSignDecrypt;
+
+typedef struct _P15AppletArgReadRecord {
+    CKYByte	    record;
+    CKYByte         short_ef;
+    CKYByte         flags;
+    CKYByte         size;
+} P15AppletArgReadRecord;
+
+typedef struct _P15AppletArgReadBinary {
+    unsigned short    offset;
+    CKYByte         short_ef;
+    CKYByte         flags;
+    CKYByte         size;
+} P15AppletArgReadBinary;
+
+typedef struct _P15AppletArgVerifyPIN {
+    const  CKYBuffer *pinVal;
+    CKYByte pinRef;
+} P15AppletArgVerifyPIN;
+
+typedef struct _P15AppletArgManageSecurityEnvironment {
+    CKYByte p1;
+    CKYByte p2;
+    CKYByte keyRef;
+}
+ P15AppletArgManageSecurityEnvironment;
+
+typedef struct _P15AppletArgPerformSecurityOperation {
+    CKYByte dir;
+    int     chain;
+    CKYSize retLen;
+    const CKYBuffer *data;
+} P15AppletArgPerformSecurityOperation;
+
 /* fills in an APDU from a structure -- form of all the generic factories*/
 typedef CKYStatus (*CKYAppletFactory)(CKYAPDU *apdu, const void *param);
 /* fills in an a structure from a response -- form of all the fill structures*/
@@ -335,7 +407,6 @@ CKYStatus CKYAppletFill_AppendBuffer(con
 /* Single value fills: Byte, Short, & Long */
 /* param == CKYByte * */
 CKYStatus CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param);
-/* param == CKYByte * */
 CKYStatus CKYAppletFill_Short(const CKYBuffer *response, CKYSize size, void *param);
 CKYStatus CKYAppletFill_Long(const CKYBuffer *response, CKYSize size, void *param);
 
@@ -361,7 +432,7 @@ CKYBool CKYApplet_VerifyResponse(const C
  *   Sends the ADPU to the card through the connection conn.
  *   Checks that the response was valid (returning the responce code in apduRC.
  *   Formats the response data into fillArg with fillFunc
- * nonce and apduRC can be NULL (no nonce is added, not status returned 
+ * nonce and apduRC can be NULL (no nonce is added, no status returned 
  * legal values for afArg are depened on afFunc.
  * legal values for fillArg are depened on fillFunc.
  */
@@ -377,7 +448,7 @@ CKYStatus CKYApplet_HandleAPDU(CKYCardCo
  *   into function calls, with input and output parameters.
  *   The application is still responsible for 
  *      1) creating a connection to the card, 
- *      2) Getting a tranaction long,  then
+ *      2) Getting a transaction lock,  then
  *      3) selecting  the appropriate applet (or Card manager). 
  *   Except for those calls that have been noted, the appropriate applet 
  *   is the CoolKey applet.
@@ -477,6 +548,7 @@ CKYStatus CACApplet_ReadFile(CKYCardConn
 CKYStatus CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef,
 			     CKYISOStatus *apduRC);
 
+
 /* must happen with PKI applet selected */
 CKYStatus CACApplet_SignDecrypt(CKYCardConnection *conn, const CKYBuffer *data,
 		CKYBuffer *result, CKYISOStatus *apduRC);
@@ -490,8 +562,37 @@ CKYStatus CACApplet_GetCertificateAppend
 				   CKYISOStatus *apduRC);
 
 /*CKYStatus CACApplet_GetProperties(); */
-CKYStatus CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin,
-				   CKYISOStatus *apduRC);
+CKYStatus CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+				int local, CKYISOStatus *apduRC);
+
+/* Select a PIV applet  */
+CKYStatus PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC);
+
+CKYStatus PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert,
+				   int tag, CKYISOStatus *apduRC);
+CKYStatus PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key,
+				   unsigned int keySize, int derive,
+                                   const CKYBuffer *data, CKYBuffer *result, 
+                                   CKYISOStatus *apduRC);
+
+/* PKCS Commands 15 */
+CKYStatus P15Applet_SelectFile(CKYCardConnection *conn, unsigned short ef,
+			     CKYISOStatus *apduRC);
+CKYStatus P15Applet_SelectRootFile(CKYCardConnection *conn, unsigned short ef,
+			     CKYISOStatus *apduRC);
+CKYStatus P15Applet_ReadRecord(CKYCardConnection *conn, CKYByte record, 
+		CKYByte short_ef, CKYByte flags, CKYByte size, CKYBuffer *data, 
+		CKYISOStatus *apduRC);
+CKYStatus P15Applet_ReadBinary(CKYCardConnection *conn, unsigned short offset, 
+		CKYByte short_ef, CKYByte flags, CKYByte size, CKYBuffer *data, 
+		CKYISOStatus *apduRC);
+CKYStatus P15Applet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+			const P15PinInfo *pinInfo, CKYISOStatus *apduRC);
+
+CKYStatus P15Applet_SignDecrypt(CKYCardConnection *conn, CKYByte key,
+				   unsigned int keySize, CKYByte direction,
+                                   const CKYBuffer *data, CKYBuffer *result, 
+                                   CKYISOStatus *apduRC);
 
 /*
  * There are 3 read commands:
@@ -553,6 +654,18 @@ CKYStatus CKYApplet_GetIssuerInfo(CKYCar
 CKYStatus CKYApplet_GetBuiltinACL(CKYCardConnection *conn,
 	 	CKYAppletRespGetBuiltinACL *gba, CKYISOStatus *apduRC);
 
+/** ECC commands
+ * *                                                  */
+
+CKYStatus CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber,
+    const CKYBuffer *data, CKYBuffer *sig,
+    CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC);
+
+CKYStatus
+CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber,
+    const CKYBuffer *publicValue, CKYBuffer *sharedSecret,
+    CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC);
+
 
 /*
  * deprecates 0.x functions
diff -up ./src/libckyapplet/cky_base.c.coolkey-update ./src/libckyapplet/cky_base.c
--- ./src/libckyapplet/cky_base.c.coolkey-update	2016-01-19 18:02:38.270365312 -0800
+++ ./src/libckyapplet/cky_base.c	2016-01-19 18:02:51.042260431 -0800
@@ -41,6 +41,7 @@ ckyBuffer_initBuffer(CKYBuffer *buf)
     buf->data = NULL;
     buf->size = 0;
     buf->len = 0;
+    buf->reserved = NULL; /* make coverity happy */
 } 
 
 /*
@@ -573,6 +574,7 @@ CKYAPDU_Init(CKYAPDU *apdu)
     assert(sizeof(CKYAPDU) == sizeof(CKYAPDUPublic));
 #endif
    ckyBuffer_initBuffer(&apdu->apduBuf);
+   apdu->reserved = NULL;
    return CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_MIN_LEN);
 }
 
@@ -583,6 +585,7 @@ CKYAPDU_InitFromData(CKYAPDU *apdu, cons
     assert(sizeof(CKYAPDU) == sizeof(CKYAPDUPublic));
 #endif
     ckyBuffer_initBuffer(&apdu->apduBuf);
+    apdu->reserved = NULL;
     if (len > CKYAPDU_MAX_DATA_LEN) {
 	return CKYDATATOOLONG;
     }
@@ -648,21 +651,38 @@ CKYStatus
 CKYAPDU_SetSendData(CKYAPDU *apdu, const CKYByte *data, CKYSize len)
 {
     CKYStatus ret;
+    CKYOffset offset = 0;
 
-    if (len > CKYAPDU_MAX_DATA_LEN) {
-	return CKYDATATOOLONG;
-    }
+    /* Encode with T1 if necessary */
 
-    ret = CKYBuffer_Resize(&apdu->apduBuf, len + CKYAPDU_HEADER_LEN);
-    if (ret != CKYSUCCESS) {
-	return ret;
+    if (len < CKYAPDU_MAX_DATA_LEN) {
+	offset = 0;
+        ret = CKYBuffer_Resize(&apdu->apduBuf, len+offset+CKYAPDU_HEADER_LEN);
+	if (ret != CKYSUCCESS ) {
+	    return ret;
+	}
+    	ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) len);
+    } else if (len < CKYAPDU_MAX_T1_DATA_LEN) {
+	offset = 2;
+        ret = CKYBuffer_Resize(&apdu->apduBuf, len+offset+CKYAPDU_HEADER_LEN);
+	if (ret != CKYSUCCESS ) {
+	    return ret;
+	}
+    	ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) 0);
+	if (ret != CKYSUCCESS) {
+	    return ret;
+	}
+        ret = CKYBuffer_SetShort(&apdu->apduBuf,CKY_LC_OFFSET+1,
+							(unsigned short)len);
+    } else {
+	return CKYDATATOOLONG;
     }
-    ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET,
-				len == CKYAPDU_MAX_DATA_LEN ? 0: (CKYByte) len);
+	
     if (ret != CKYSUCCESS) {
 	return ret;
     }
-    return CKYBuffer_Replace(&apdu->apduBuf, CKYAPDU_HEADER_LEN, data, len);
+    return CKYBuffer_Replace(&apdu->apduBuf, 
+				CKYAPDU_HEADER_LEN + offset , data, len);
 }
 
 CKYStatus
@@ -682,15 +702,15 @@ CKYAPDU_AppendSendData(CKYAPDU *apdu, co
     }
 
     dataLen = CKYBuffer_Size(&apdu->apduBuf) + len - CKYAPDU_HEADER_LEN;
-    if (dataLen > CKYAPDU_MAX_DATA_LEN) {
+    /* only handles T0 encoding, not T1 encoding */
+    if (dataLen >= CKYAPDU_MAX_DATA_LEN) {
 	return CKYDATATOOLONG;
     }
     ret = CKYBuffer_AppendData(&apdu->apduBuf, data, len);
     if (ret != CKYSUCCESS) {
 	return ret;
     }
-    return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET,
-			dataLen == CKYAPDU_MAX_DATA_LEN ? 0 : (CKYByte) dataLen);
+    return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) dataLen);
 }
 
 CKYStatus
@@ -710,8 +730,104 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY
     return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, recvlen);
 }
 
+CKYStatus
+CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen)
+{
+    CKYStatus ret;
+
+    if (recvlen <= CKYAPDU_MAX_DATA_LEN) {
+	return CKYAPDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff));
+    }
+    ret = CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_HEADER_LEN+2);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, 0);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    return CKYBuffer_SetShort(&apdu->apduBuf, CKY_LE_OFFSET+1, recvlen);
+}
+
+CKYStatus
+CKYAPDU_SetReceiveLength(CKYAPDU *apdu, CKYSize recvlen)
+{
+    if (recvlen <= CKYAPDU_MAX_T1_DATA_LEN) {
+	return CKYAPDU_SetShortReceiveLen(apdu, (unsigned short) 
+						(recvlen & 0xffff));
+    }
+    return CKYDATATOOLONG;
+}
+
+/*
+ *  Append Le, If Le=0, treat it as 256 (CKYAPD_MAX_DATA_LEN)
+ */
+CKYStatus
+CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen)
+{
+    /* If we already have a data buffer, make sure that we aren't already
+     * using T1 encoding */
+    if (CKYBuffer_Size(&apdu->apduBuf) > CKYAPDU_MIN_LEN) {
+	if (CKYBuffer_GetChar(&apdu->apduBuf, CKY_LC_OFFSET) == 0) {
+	    /* we are using T1 encoding, use AppendShort*/
+	    return CKYBuffer_AppendShort(&apdu->apduBuf, 
+		recvlen ? (unsigned short) recvlen: CKYAPDU_MAX_DATA_LEN);
+	}
+    }
+    return CKYBuffer_AppendChar(&apdu->apduBuf, recvlen);
+}
+
+/*
+ * Append a short Le. If Le be encoded with just T0, do so. If Le=0 treat
+ * it as 65536 (CKYAPDU_MAX_T1_DATA_LEN)
+ */
+CKYStatus
+CKYAPDU_AppendShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen)
+{
+    CKYStatus ret;
+    /* If we already have a data buffer, it's encoding affects ours */
+    if (CKYBuffer_Size(&apdu->apduBuf) > CKYAPDU_MIN_LEN) {
+	/* CKY_LC_OFFSET == 0 means T1, otherwise it's T0 */
+	if (CKYBuffer_GetChar(&apdu->apduBuf, CKY_LC_OFFSET) != 0) {
+	    /* remember 0 is 65536 here */
+	    if ((recvlen == 0) || (recvlen > CKYAPDU_MAX_DATA_LEN)) {
+		/* we can't a encode T1 receive length if we already have a
+ 		 * T0 encoded buffer data */
+		return CKYDATATOOLONG;
+	    }
+	    /* T0 encoding */
+	    return CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)recvlen&0xff);
+	}
+	/* T1 encoding */
+	return CKYBuffer_AppendShort(&apdu->apduBuf, recvlen);
+    }
+    /* if length fits in a bit and we aren't forced into T1 encoding, use
+     * T0 */
+    if ((recvlen != 0) && (recvlen <= CKYAPDU_MAX_DATA_LEN)) {
+	return CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)recvlen&0xff);
+    }
+    /* write the T1 encoding marker */
+    ret = CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)0);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    /* T1 encoded length */
+    return CKYBuffer_AppendShort(&apdu->apduBuf, recvlen);
+}
+
+CKYStatus
+CKYAPDU_AppendReceiveLength(CKYAPDU *apdu, CKYSize recvlen)
+{
+    if (recvlen > CKYAPDU_MAX_T1_DATA_LEN) {
+	return CKYDATATOOLONG;
+    }
+    return CKYAPDU_AppendShortReceiveLen(apdu, 
+					(unsigned short)(recvlen & 0xffff));
+}
+
+
 void
-CKY_SetName(char *p)
+CKY_SetName(const char *p)
 {
 }
     
diff -up ./src/libckyapplet/cky_base.h.coolkey-update ./src/libckyapplet/cky_base.h
--- ./src/libckyapplet/cky_base.h.coolkey-update	2016-01-19 18:03:10.571100060 -0800
+++ ./src/libckyapplet/cky_base.h	2016-01-19 18:03:23.866990876 -0800
@@ -32,6 +32,8 @@ typedef unsigned char CKYByte;
 /* Bool type */
 typedef unsigned char CKYBool;
 
+typedef unsigned long CKYBitFlags;
+
 #define CKYBUFFER_PUBLIC \
     unsigned long reserved1;\
     unsigned long reserved2;\
@@ -93,6 +95,8 @@ typedef enum {
 			 * (command) sent. ADPUIOStatus has more info on
 			 * why the APDU failed */
     CKYINVALIDARGS,      /* Caller passed in bad args */
+    CKYINVALIDDATA,	/* Data supplied was invalid */
+    CKYUNSUPPORTED,	/* Requested Operation or feature is not supported */
 } CKYStatus;
 
 /*
@@ -107,12 +111,56 @@ typedef enum {
 #define CKY_LE_OFFSET	4
 
 #define CKYAPDU_MAX_DATA_LEN	256
+#define CKYAPDU_MAX_T1_DATA_LEN	65536
 #define CKYAPDU_MIN_LEN		4
 #define CKYAPDU_HEADER_LEN	5
 #define CKYAPDU_MAX_LEN		(CKYAPDU_HEADER_LEN+CKYAPDU_MAX_DATA_LEN)
 #define CKY_MAX_ATR_LEN		32
 #define CKY_OUTRAGEOUS_MALLOC_SIZE (1024*1024)
 
+#define P15FlagsPrivate           0x00000001
+#define P15FlagsModifiable        0x00000002
+
+#define P15UsageEncrypt           0x00000001
+#define P15UsageDecrypt           0x00000002
+#define P15UsageSign              0x00000004
+#define P15UsageSignRecover       0x00000008
+#define P15UsageWrap              0x00000010
+#define P15UsageUnwrap            0x00000020
+#define P15UsageVerify            0x00000040
+#define P15UsageVerifyRecover     0x00000080
+#define P15UsageDerive            0x00000100
+#define P15UsageNonRepudiation    0x00000200
+
+#define P15AccessSensitive        0x00000001
+#define P15AccessExtractable      0x00000002
+#define P15AccessAlwaysSenstive   0x00000004
+#define P15AccessNeverExtractable 0x00000008
+#define P15AccessLocal            0x00000010
+
+#define P15PinCaseSensitive       0x00000001
+#define P15PinLocal               0x00000002
+#define P15PinChangeDisabled      0x00000004
+#define P15PinUnblockDisabled     0x00000008
+#define P15PinInitialized         0x00000010
+#define P15PinNeedsPadding        0x00000020
+#define P15PinUnblockingPin       0x00000040
+#define P15PinSOPin               0x00000080
+#define P15PinDisableAllowed      0x00000100
+
+typedef enum {P15PinBCD=0, P15PinASCIINum=1, P15PinUTF8=2} P15PinType;
+
+typedef struct _P15PinInfo {
+    CKYBitFlags   pinFlags;
+    P15PinType    pinType;
+    CKYByte       minLength;
+    CKYByte       storedLength;
+    unsigned long maxLength;
+    CKYByte       pinRef;
+    CKYByte       padChar;
+} P15PinInfo;
+     
+
 /*
  * allow direct inclusion in C++ files 
  */
@@ -278,9 +326,14 @@ CKYStatus CKYAPDU_AppendSendDataBuffer(C
 /* set Le in the APDU header to the amount of bytes expected to be
  * returned. */
 CKYStatus CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKYByte recvlen);
+CKYStatus CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen);
+CKYStatus CKYAPDU_SetReceiveLength(CKYAPDU *apdu, CKYSize recvlen);
+CKYStatus CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen);
+CKYStatus CKYAPDU_AppendShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen);
+CKYStatus CKYAPDU_AppendReceiveLength(CKYAPDU *apdu, CKYSize recvlen);
 
 /* set the parent loadmodule name */
-void CKY_SetName(char *name);
+void CKY_SetName(const char *name);
 
 CKY_END_PROTOS
     
diff -up ./src/libckyapplet/cky_card.c.coolkey-update ./src/libckyapplet/cky_card.c
--- ./src/libckyapplet/cky_card.c.coolkey-update	2016-01-19 18:03:46.210807391 -0800
+++ ./src/libckyapplet/cky_card.c	2016-01-19 18:03:59.217700579 -0800
@@ -27,6 +27,7 @@
 
 #ifndef WINAPI
 #define WINAPI
+typedef SCARD_READERSTATE *LPSCARD_READERSTATE;
 #endif
 
 #ifndef SCARD_E_NO_READERS_AVAILABLE
@@ -843,6 +844,11 @@ CKYCardContext_WaitForStatusChange(CKYCa
     rv = ctx->scard->SCardGetStatusChange(ctx->context, timeout, 
 							readers, readerCount);
     if (rv != SCARD_S_SUCCESS) {
+	if ((rv == SCARD_E_NO_SERVICE) || (rv == SCARD_E_SERVICE_STOPPED)) {
+	    /* if we were stopped, don't reuse the old context, 
+	     * pcsc-lite hangs */
+	    ckyCardContext_release(ctx); 
+	} 
 	ctx->lastError = rv;
 	return CKYSCARDERR;
     }
@@ -904,6 +910,7 @@ ckyCardConnection_init(CKYCardConnection
     conn->protocol = SCARD_PROTOCOL_T0;
 }
 
+
 CKYCardConnection *
 CKYCardConnection_Create(const CKYCardContext *ctx)
 {
@@ -978,6 +985,12 @@ CKYCardConnection_IsConnected(const CKYC
     return (conn->cardHandle != 0);
 }
 
+unsigned long
+CKYCardConnection_GetProtocol(const CKYCardConnection *conn)
+{
+    return conn->protocol;
+}
+
 CKYStatus 
 ckyCardConnection_reconnectRaw(CKYCardConnection *conn, unsigned long init)
 {
@@ -990,6 +1003,7 @@ ckyCardConnection_reconnectRaw(CKYCardCo
 	conn->lastError = rv;
 	return CKYSCARDERR;
     }
+    conn->protocol = protocol;
     return CKYSUCCESS;
 }
 
@@ -1071,25 +1085,39 @@ CKYCardConnection_ExchangeAPDU(CKYCardCo
 							CKYBuffer *response)
 {
     CKYStatus ret;
+    CKYBuffer getResponse;
+    CKYSize size = 0;
 
     ret = CKYCardConnection_TransmitAPDU(conn, apdu, response);
     if (ret != CKYSUCCESS) {
 	return ret;
     }
+    CKYBuffer_InitEmpty(&getResponse);
 
-    if (CKYBuffer_Size(response) == 2 && CKYBuffer_GetChar(response,0) == 0x61) {
+    /* automatically handle the response data protocol */
+    while ((ret == CKYSUCCESS) &&
+	   (size = CKYBuffer_Size(response)) >= 2 &&
+	   (CKYBuffer_GetChar(response,size-2) == 0x61)) {
 	/* get the response */
 	CKYAPDU getResponseAPDU;
 
+	CKYBuffer_Zero(&getResponse);
 	CKYAPDU_Init(&getResponseAPDU);
 	CKYAPDU_SetCLA(&getResponseAPDU, 0x00);
 	CKYAPDU_SetINS(&getResponseAPDU, 0xc0);
 	CKYAPDU_SetP1(&getResponseAPDU, 0x00);
 	CKYAPDU_SetP2(&getResponseAPDU, 0x00);
-	CKYAPDU_SetReceiveLen(&getResponseAPDU, CKYBuffer_GetChar(response,1));
-	ret = CKYCardConnection_TransmitAPDU(conn, &getResponseAPDU, response);
+	CKYAPDU_SetReceiveLen(&getResponseAPDU, 
+					CKYBuffer_GetChar(response,size-1));
+	ret = CKYCardConnection_TransmitAPDU(conn, &getResponseAPDU,
+					&getResponse);
 	CKYAPDU_FreeData(&getResponseAPDU);
+	if ((ret == CKYSUCCESS) && (CKYBuffer_Size(&getResponse) >= 2)) {
+	    CKYBuffer_Resize(response, size-2);
+	    CKYBuffer_AppendCopy(response,&getResponse);
+	}
     }
+    CKYBuffer_FreeData(&getResponse);
     return ret;
 }
 
diff -up ./src/libckyapplet/cky_card.h.coolkey-update ./src/libckyapplet/cky_card.h
--- ./src/libckyapplet/cky_card.h.coolkey-update	2016-01-19 18:04:19.315535537 -0800
+++ ./src/libckyapplet/cky_card.h	2016-01-19 18:04:28.384461063 -0800
@@ -116,6 +116,7 @@ CKYStatus CKYCardConnection_ExchangeAPDU
 CKYStatus CKYCardConnection_Connect(CKYCardConnection *connection, 
 					const char *readerName);
 CKYStatus CKYCardConnection_Disconnect(CKYCardConnection *connection);
+unsigned long CKYCardConnection_GetProtocol(const CKYCardConnection *conn);
 CKYBool CKYCardConnection_IsConnected(const CKYCardConnection *connection);
 CKYStatus CKYCardConnection_Reconnect(CKYCardConnection *connection);
 CKYStatus CKYCardConnection_GetStatus(CKYCardConnection *connection,
diff -up ./src/libckyapplet/cky_factory.c.coolkey-update ./src/libckyapplet/cky_factory.c
--- ./src/libckyapplet/cky_factory.c.coolkey-update	2016-01-19 18:04:51.976267329 -0800
+++ ./src/libckyapplet/cky_factory.c	2016-01-19 18:05:03.518172548 -0800
@@ -29,7 +29,7 @@ CKYAPDUFactory_SelectFile(CKYAPDU *apdu,
 			  const CKYBuffer *AID)
 {
     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
-    CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE);
+    CKYAPDU_SetINS(apdu, ISO_INS_SELECT_FILE);
     CKYAPDU_SetP1(apdu, p1);
     CKYAPDU_SetP2(apdu, p2);
     return CKYAPDU_SetSendDataBuffer(apdu, AID);
@@ -40,7 +40,7 @@ CKYAPDUFactory_SelectCardManager(CKYAPDU
 {
     CKYByte c = 0;
     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
-    CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE);
+    CKYAPDU_SetINS(apdu, ISO_INS_SELECT_FILE);
     CKYAPDU_SetP1(apdu, 0x04);
     CKYAPDU_SetP2(apdu, 0x00);
     /* I can't find the documentation for this, but if you pass an empty
@@ -57,12 +57,11 @@ CKYStatus
 CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu)
 {
     CKYAPDU_SetCLA(apdu, CKY_CLASS_GLOBAL_PLATFORM);
-    CKYAPDU_SetINS(apdu, CKY_INS_GET_DATA);
+    CKYAPDU_SetINS(apdu, ISO_INS_GET_DATA);
     CKYAPDU_SetP1(apdu, 0x9f);
     CKYAPDU_SetP2(apdu, 0x7f);
     return CKYAPDU_SetReceiveLen(apdu, CKY_SIZE_GET_CPLCDATA);
 }
-
 /*
  * applet commands must be issued with the appplet selected.
  */
@@ -184,6 +183,49 @@ fail:
 }
 
 CKYStatus
+CKYAPDUFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, CKYByte keyNumber,
+                             CKYByte location,
+                            const CKYBuffer *publicData, const CKYBuffer *secretKey)
+{
+    CKYStatus ret      = CKYINVALIDARGS;
+    CKYSize   len;
+    CKYBuffer buf;
+
+    if (!publicData)
+        return ret;
+
+    if (!(len = CKYBuffer_Size(publicData)))
+        return ret;
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY);
+    CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_ECC_KEY_AGREEMENT);
+    CKYAPDU_SetP1(apdu, keyNumber);
+    CKYAPDU_SetP2(apdu, CKY_CIPHER_ONE_STEP);
+
+    CKYBuffer_InitEmpty(&buf);
+
+    ret = CKYBuffer_Reserve(&buf, 3);
+
+    if (ret == CKYSUCCESS)
+        ret = CKYBuffer_AppendChar(&buf, location);
+    if (ret == CKYSUCCESS)
+        ret = CKYBuffer_AppendShort(&buf, (unsigned short)len);
+    if (ret == CKYSUCCESS)
+        ret = CKYAPDU_SetSendDataBuffer(apdu, &buf);
+    if (ret == CKYSUCCESS)
+        ret = CKYAPDU_AppendSendDataBuffer(apdu, publicData);
+    if (ret == CKYSUCCESS && secretKey && 0 < (len = CKYBuffer_Size(secretKey))) {
+        CKYBuffer_Resize(&buf,2);
+        CKYBuffer_SetShort(&buf, 0, (unsigned short)len);
+        ret = CKYAPDU_AppendSendDataBuffer(apdu, &buf);
+        if (ret == CKYSUCCESS)
+            ret = CKYAPDU_AppendSendDataBuffer(apdu, secretKey);
+    }
+    CKYBuffer_FreeData(&buf);
+    return ret;
+}
+
+CKYStatus
 CKYAPDUFactory_ComputeCryptOneStep(CKYAPDU *apdu, CKYByte keyNumber, CKYByte mode,
 				CKYByte direction, CKYByte location,
 				const CKYBuffer *idata, const CKYBuffer *sig)
@@ -386,6 +428,49 @@ fail:
 }
 
 CKYStatus
+CKYAPDUFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, CKYByte keyNumber,
+                             CKYByte location,
+                            const CKYBuffer *idata, const CKYBuffer *sig)
+{
+    CKYStatus ret      = CKYINVALIDARGS;
+    CKYSize   len;
+    CKYBuffer buf;
+
+    if (!idata)
+        return ret;
+
+    if (!(len = CKYBuffer_Size(idata)) && location != CKY_DL_OBJECT)
+        return ret;
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY);
+    CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_ECC_SIGNATURE);
+    CKYAPDU_SetP1(apdu, keyNumber);
+    CKYAPDU_SetP2(apdu, CKY_CIPHER_ONE_STEP);
+
+    CKYBuffer_InitEmpty(&buf);
+
+    ret = CKYBuffer_Reserve(&buf, 3);
+
+    if (ret == CKYSUCCESS)
+        ret = CKYBuffer_AppendChar(&buf, location);
+    if (ret == CKYSUCCESS)
+        ret = CKYBuffer_AppendShort(&buf, (unsigned short)len);
+    if (ret == CKYSUCCESS)
+        ret = CKYAPDU_SetSendDataBuffer(apdu, &buf);
+    if (ret == CKYSUCCESS)
+        ret = CKYAPDU_AppendSendDataBuffer(apdu, idata);
+    if (ret == CKYSUCCESS && sig && 0 < (len = CKYBuffer_Size(sig))) {
+        CKYBuffer_Resize(&buf,2);
+        CKYBuffer_SetShort(&buf, 0, (unsigned short)len);
+        ret = CKYAPDU_AppendSendDataBuffer(apdu, &buf);
+        if (ret == CKYSUCCESS)
+            ret = CKYAPDU_AppendSendDataBuffer(apdu, sig);
+    }
+    CKYBuffer_FreeData(&buf);
+    return ret;
+}
+
+CKYStatus
 CKYAPDUFactory_ReadObject(CKYAPDU *apdu, unsigned long objectID, 
 						CKYOffset offset, CKYByte size)
 {
@@ -634,32 +719,167 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap
 }
 
 CKYStatus
-CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin)
+PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, 
+			   CKYByte key, int len, const CKYBuffer *data)
+{
+    CKYStatus ret;
+    CKYAPDU_SetCLA(apdu, chain ? CKY_CLASS_ISO7816_CHAIN :
+				  CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, PIV_INS_GEN_AUTHENTICATE);
+    CKYAPDU_SetP1(apdu, alg);
+    CKYAPDU_SetP2(apdu, key);
+    ret =  CKYAPDU_SetSendDataBuffer(apdu, data);
+    if (ret == CKYSUCCESS && chain == 0 && len != 0) {
+	if (len >= 256) len = 0;
+	ret = CKYAPDU_AppendReceiveLen(apdu, len);
+    }
+    return ret;
+}
+
+CKYStatus
+PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, CKYByte count)
+{
+    CKYStatus ret;
+    CKYBuffer buf;
+    CKYByte objectSize;
+
+    CKYBuffer_InitEmpty(&buf);
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, 0xcb);
+    CKYAPDU_SetP1(apdu, 0x3f);
+    CKYAPDU_SetP2(apdu, 0xff);
+
+    objectSize = CKYBuffer_Size(object);
+
+    ret = CKYBuffer_Reserve(&buf, 2+objectSize);
+    if (ret != CKYSUCCESS) {
+	    goto fail;
+    }
+    ret = CKYBuffer_AppendChar(&buf, 0x5c);
+    if (ret != CKYSUCCESS) {
+	    goto fail;
+    }
+    ret = CKYBuffer_AppendChar(&buf, objectSize);
+    if (ret != CKYSUCCESS) {
+	    goto fail;
+    } 
+    ret = CKYBuffer_AppendCopy(&buf, object);
+    if (ret != CKYSUCCESS) {
+	    goto fail;
+    } 
+    ret = CKYAPDU_SetSendDataBuffer(apdu, &buf);
+    if (ret != CKYSUCCESS) {
+	    goto fail;
+    } 
+    ret = CKYAPDU_AppendReceiveLen(apdu, count);
+fail:
+    CKYBuffer_FreeData(&buf);
+    return ret;
+}
+
+CKYStatus
+P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const CKYBuffer *pin)
 {
     CKYStatus ret;
-    CKYSize size;
 
     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
     CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN);
     CKYAPDU_SetP1(apdu, 0x00);
-    CKYAPDU_SetP2(apdu, 0x00);
+    CKYAPDU_SetP2(apdu, keyRef);
     /* no pin, send an empty buffer */
-    if (!pin) {
+    if (CKYBuffer_Size(pin) == 0) {
     	return CKYAPDU_SetReceiveLen(apdu, 0);
     }
 
     /* all CAC pins are 8 bytes exactly. If to long, truncate it */
-    size = strlen(pin);
-    if (size > 8) {
-	size = 8;
-    }
-    ret = CKYAPDU_SetSendData(apdu, (unsigned char *) pin, size);
-    /* if too short, pad it */
-    if ((ret == CKYSUCCESS) && (size < 8)) {
-	static const unsigned char pad[]= { 0xff , 0xff, 0xff ,0xff, 
-				   0xff, 0xff, 0xff, 0xff };
-	return CKYAPDU_AppendSendData(apdu, pad, 8-size);
-    }
+    ret = CKYAPDU_SetSendDataBuffer(apdu, pin);
     return ret;
 
 }
+
+CKYStatus
+P15APDUFactory_ReadRecord(CKYAPDU *apdu, CKYByte record, CKYByte short_ef, 
+					CKYByte flags, CKYByte count)
+{
+    CKYByte control;
+
+    control = (short_ef << 3) & 0xf8;
+    control |= flags & 0x07;
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_READ_RECORD);
+    CKYAPDU_SetP1(apdu, record);
+    CKYAPDU_SetP2(apdu, control);
+    return CKYAPDU_SetReceiveLen(apdu, count);
+}
+
+CKYStatus
+P15APDUFactory_ReadBinary(CKYAPDU *apdu, unsigned short offset, 
+			CKYByte short_ef, CKYByte flags, CKYByte count)
+{
+    CKYByte p1 = 0,p2 = 0;
+    unsigned short max_offset = 0;
+
+    if (flags & P15_USE_SHORT_EF) {
+	max_offset = 0xff;
+	p1 = P15_USE_SHORT_EF | (short_ef & 0x7);
+	p2 = offset & 0xff;
+    } else {
+	max_offset = 0x7fff;
+	p1 = (offset >> 8) & 0x7f;
+	p2 = offset & 0xff;
+    }
+    if (offset > max_offset) {
+	return CKYINVALIDARGS;
+    }
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_READ_BINARY);
+    CKYAPDU_SetP1(apdu, p1);
+    CKYAPDU_SetP2(apdu, p2);
+    return CKYAPDU_SetReceiveLen(apdu, count);
+}
+
+CKYStatus
+P15APDUFactory_ManageSecurityEnvironment(CKYAPDU *apdu, CKYByte p1, CKYByte p2,
+					CKYByte keyRef)
+{
+    CKYByte param[3];
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_MANAGE_SECURITY_ENVIRONMENT);
+    CKYAPDU_SetP1(apdu, p1);
+    CKYAPDU_SetP2(apdu, p2);
+    param[0] = 0x83;
+    param[1] = 1;
+    param[2] = keyRef;
+    return CKYAPDU_SetSendData(apdu, param, sizeof param);
+}
+
+CKYStatus
+P15APDUFactory_PerformSecurityOperation(CKYAPDU *apdu, CKYByte dir,
+			int chain, CKYSize retLen, const CKYBuffer *data)
+{
+    CKYByte p1,p2;
+    CKYStatus ret;
+
+    CKYAPDU_SetCLA(apdu, chain ? CKY_CLASS_ISO7816_CHAIN :
+				  CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_PERFORM_SECURITY_OPERATION);
+    if (dir == CKY_DIR_DECRYPT) {
+	p1 = ISO_PSO_DECRYPT_P1;
+	p2 = ISO_PSO_DECRYPT_P2;
+    } else {
+	p1 = ISO_PSO_SIGN_P1;
+	p2 = ISO_PSO_SIGN_P2;
+    }
+    CKYAPDU_SetP1(apdu, p1);
+    CKYAPDU_SetP2(apdu, p2);
+    ret =  CKYAPDU_SetSendDataBuffer(apdu, data);
+    if (ret == CKYSUCCESS && (chain == 0) && retLen != 0) {
+	ret = CKYAPDU_AppendReceiveLength(apdu, retLen);
+    }
+    return ret;
+}
+
+
diff -up ./src/libckyapplet/cky_factory.h.coolkey-update ./src/libckyapplet/cky_factory.h
--- ./src/libckyapplet/cky_factory.h.coolkey-update	2016-01-19 18:05:20.615032151 -0800
+++ ./src/libckyapplet/cky_factory.h	2016-01-19 18:05:30.781948661 -0800
@@ -25,17 +25,41 @@
 /*
  * Various Class bytes 
  */
-#define CKY_CLASS_ISO7816 0x00
+#define CKY_CLASS_ISO7816	  0x00
+#define CKY_CLASS_ISO7816_CHAIN   0x10
 #define CKY_CLASS_GLOBAL_PLATFORM 0x80
-#define CKY_CLASS_SECURE 0x84
-#define CKY_CLASS_COOLKEY 0xb0
+#define CKY_CLASS_SECURE 	  0x84
+#define CKY_CLASS_COOLKEY	  0xb0
 
 /*
  * Applet Instruction Bytes
  */
 /* Card Manager */
-#define CKY_INS_SELECT_FILE	0xa4
-#define CKY_INS_GET_DATA 	0xca
+#define ISO_INS_SELECT_FILE	0xa4
+#define ISO_INS_GET_DATA 	0xca
+#define ISO_INS_READ_BINARY 	0xb0
+#define ISO_INS_READ_RECORD 	0xb2
+#define ISO_INS_MANAGE_SECURITY_ENVIRONMENT 0x22
+#define ISO_INS_PERFORM_SECURITY_OPERATION 0x2a
+
+/* ISO Parameters: */
+#define ISO_LOGIN_LOCAL		0x80
+#define ISO_LOGIN_GLOBAL	0x00
+#define ISO_MSE_SET		0x01
+#define ISO_MSE_STORE		0xf2
+#define ISO_MSE_RESTORE		0xf3
+#define ISO_MSE_ERASE		0xf4
+#define ISO_MSE_QUAL_VERIFY	0x80
+#define ISO_MSE_QUAL_COMPUTE	0x40
+#define ISO_MSE_AUTH		0xa4
+#define ISO_MSE_SIGN		0xb6
+#define ISO_MSE_KEA		0xb8
+#define ISO_PSO_SIGN_P1		0x9e
+#define ISO_PSO_SIGN_P2		0x9a
+#define ISO_PSO_ENCRYPT_P1	0x86
+#define ISO_PSO_ENCRYPT_P2	0x80
+#define ISO_PSO_DECRYPT_P1	0x80
+#define ISO_PSO_DECRYPT_P2	0x86
 
 /* deprecated */
 #define CKY_INS_SETUP    	0x2A
@@ -66,6 +90,8 @@
 /* nonce validated  & Secure Channel */
 #define CKY_INS_IMPORT_KEY	0x32
 #define CKY_INS_COMPUTE_CRYPT	0x36
+#define CKY_INS_COMPUTE_ECC_SIGNATURE 0x37
+#define CKY_INS_COMPUTE_ECC_KEY_AGREEMENT 0x38
 #define CKY_INS_CREATE_PIN	0x40
 #define CKY_INS_CHANGE_PIN	0x44
 #define CKY_INS_CREATE_OBJ	0x5A
@@ -81,6 +107,7 @@
 #define CKY_INS_SEC_READ_IOBUF	0x08
 #define CKY_INS_SEC_START_ENROLLMENT	0x0C
 
+
 /* CAC */
 #define CAC_INS_GET_CERTIFICATE 0x36
 #define CAC_INS_SIGN_DECRYPT	0x42
@@ -92,6 +119,9 @@
 #define CAC_P1_STEP		0x80
 #define CAC_P1_FINAL		0x00
 
+/* PIV */
+#define PIV_INS_GEN_AUTHENTICATE 0x87
+
 /*
  * Fixed return sized from various commands
  */
@@ -112,7 +142,7 @@
 /* functions */
 #define CKY_CIPHER_INIT		1
 #define CKY_CIPHER_PROCESS	2
-#define CKY_CIPHER_FINAL		3
+#define CKY_CIPHER_FINAL	3
 #define CKY_CIPHER_ONE_STEP	4  /* init and final in one APDU */
 
 /* modes */
@@ -123,6 +153,7 @@
 #define CKY_DES_ECB_NOPAD	0x21
 
 /* operations (Cipher Direction) */
+#define CKY_DIR_NONE            0x00
 #define CKY_DIR_SIGN		0x01
 #define CKY_DIR_VERIFY		0x02
 #define CKY_DIR_ENCRYPT		0x03
@@ -163,6 +194,18 @@
 #define CKY_CARDM_MANAGER_LOCKED          0x7f
 #define CKY_CARDM_MANAGER_TERMINATED      0xff
 
+/* Read Record Flags */
+#define P15_READ_P1          0x4
+#define P15_READ_P1_TO_LAST  0x5
+#define P15_READ_LAST_TO_P1  0x6
+#define P15_READ_FIRST       0x0
+#define P15_READ_LAST        0x1
+#define P15_READ_NEXT        0x2
+#define P15_READ_PREV        0x3
+
+/* Read Binary Flags */
+#define P15_USE_SHORT_EF    0x80
+
 /*
  * The following factories 'Fill in' APDUs for each of the
  * functions described below. Nonces are not automatically added.
@@ -187,6 +230,12 @@ CKYStatus CKYAPDUFactory_ComputeCryptFin
 CKYStatus CKYAPDUFactory_ComputeCryptOneStep(CKYAPDU *apdu, CKYByte keyNumber, 
 			    CKYByte mode, CKYByte direction, CKYByte location,
 			    const CKYBuffer *data, const CKYBuffer *sig);
+CKYStatus CKYAPDUFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, CKYByte keyNumber,
+                             CKYByte location,
+                            const CKYBuffer *data, const CKYBuffer *sig);
+CKYStatus CKYAPDUFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, CKYByte keyNumber,
+                             CKYByte location,
+                            const CKYBuffer *publicData, const CKYBuffer *secretKey);
 CKYStatus CKYAPDUFactory_CreatePIN(CKYAPDU *apdu, CKYByte pinNumber, 
 				CKYByte maxAttempts, const char *pinValue);
 CKYStatus CKYAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte pinNumber, 
@@ -218,12 +267,28 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C
 
 CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, 
 				     const CKYBuffer *data);
-CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin);
 CKYStatus CACAPDUFactory_GetCertificate(CKYAPDU *apdu, CKYSize size);
 CKYStatus CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, 
 				  CKYByte type, CKYByte count);
 CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu);
 
+CKYStatus PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, 
+				CKYByte count);
+CKYStatus PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, 
+                           CKYByte key, int len, const CKYBuffer *data);
+
+CKYStatus P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef,
+			   const CKYBuffer *pin);
+CKYStatus P15APDUFactory_ReadRecord(CKYAPDU *apdu, CKYByte record, 
+			   CKYByte short_ef, CKYByte flags, CKYByte count);
+CKYStatus P15APDUFactory_ReadBinary(CKYAPDU *apdu, unsigned short offset, 
+			   CKYByte short_ef, CKYByte flags, CKYByte count);
+CKYStatus P15APDUFactory_ManageSecurityEnvironment(CKYAPDU *apdu, 
+			   CKYByte p1, CKYByte p2, CKYByte key);
+CKYStatus P15APDUFactory_PerformSecurityOperation(CKYAPDU *apdu, CKYByte dir,
+			   int chain, CKYSize retLen, const CKYBuffer *data);
+
+
 CKY_END_PROTOS
 
 #endif /* CKY_FACTORY_H */