diff --git a/.coolkey.metadata b/.coolkey.metadata
new file mode 100644
index 0000000..9c8cdc5
--- /dev/null
+++ b/.coolkey.metadata
@@ -0,0 +1 @@
+54136decf9dfd091c8b231cb77dac97db95e1866 SOURCES/coolkey-1.1.0.tar.gz
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f56700c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/coolkey-1.1.0.tar.gz
diff --git a/SOURCES/coolkey-1.1.0-alt-tokens-2.patch b/SOURCES/coolkey-1.1.0-alt-tokens-2.patch
new file mode 100644
index 0000000..de7c8c1
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-alt-tokens-2.patch
@@ -0,0 +1,141 @@
+diff -up ./src/coolkey/slot.cpp.alt-tokens-2 ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.alt-tokens-2	2018-06-25 17:58:23.472185284 -0700
++++ ./src/coolkey/slot.cpp	2018-06-25 18:02:29.714918126 -0700
+@@ -415,8 +415,9 @@ Slot::Slot(const char *readerName_, Log
+ 	tokenManufacturer(NULL),
+ 	slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), 
+ 	isVersion1Key(false), needLogin(false), fullTokenName(false), 
+-	mCoolkey(false), mOldCAC(false), mCACLocalLogin(false),
+-	pivContainer(-1), pivKey(-1), maxCacCerts(MAX_CERT_SLOTS), 
++	mCoolkey(false), mOldCAC(false), mCACLocalLogin(false), mCAC_ACA(false),
++	pivContainer(-1), pivKey(-1),
++	minCacCerts(0), maxCacCerts(MAX_CERT_SLOTS),
+ 	algs(ALG_NONE), p15aid(0), p15odfAddr(0), p15tokenInfoAddr(0),
+ 	p15Instance(0),
+ #ifdef USE_SHMEM
+@@ -782,9 +783,11 @@ Slot::connectToToken()
+ 	 state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+ 	 isVersion1Key = 0;
+ 	 needLogin = true;
++	 minCacCerts = 0;
+ 	 maxCacCerts = MAX_CERT_SLOTS;
+          mCoolkey = 0;
+ 	 mOldCAC = 0;
++	 mCAC_ACA = 0;
+ 	 mCACLocalLogin = getPIVLoginType();
+ 	return;
+     } 
+@@ -924,23 +927,29 @@ Slot::getCACAid()
+ 	CKYBuffer_Resize(&cardAID[i],0);
+     }
+ 
++    mCAC_ACA=false;
+     status = CACApplet_SelectCCC(conn,NULL);
+     if (status != CKYSUCCESS) {
+ 	/* are we an old CAC */
+-	status = CACApplet_SelectPKI(conn, &cardAID[0], 0, NULL);
+-	if (status != CKYSUCCESS) {
+-	   /* no, just fail */
+-	   return status;
+-	}
+-	/* yes, fill in the old applets */
+-	mOldCAC = true;
+-	maxCacCerts = 1;
+-	for (i=1; i< MAX_CERT_SLOTS; i++) {
++	maxCacCerts = 0;
++	minCacCerts = -1;
++        status = CACApplet_SelectACA(conn,NULL);
++        if (status == CKYSUCCESS) {
++	    mCAC_ACA = true;
++        }
++	for (i=0; i< MAX_CERT_SLOTS; i++) {
+ 	    status = CACApplet_SelectPKI(conn, &cardAID[i], i, NULL);
+ 	    if (status == CKYSUCCESS) {
++		if (minCacCerts == -1) {
++		    minCacCerts = i;
++                }
+ 		maxCacCerts = i+1;
+ 	    }
+ 	}
++	if (minCacCerts == -1) {
++	    return status;
++        }
++	mOldCAC = true;
+ 	return CKYSUCCESS;
+     }
+     /* definately not an old CAC */
+@@ -997,6 +1006,7 @@ Slot::getCACAid()
+     if (certSlot == 0) {
+ 	status = CKYAPDUFAIL; /* probably neeed a beter error code */
+     }
++    minCacCerts = 0;
+     maxCacCerts = certSlot;
+ 
+ done:
+@@ -3840,7 +3850,16 @@ Slot::login(SessionHandleSuffix handleSu
+     if(status != CKYSUCCESS ) handleConnectionError();
+ 
+     if (state & GOV_CARD) {
+-	selectCACApplet(0, true);
++	if (mCAC_ACA) {
++            status = CACApplet_SelectACA(conn,NULL);
++	    if ( status == CKYSCARDERR ) handleConnectionError();
++	    if ( status != CKYSUCCESS) {
++		disconnect();
++        	throw PKCS11Exception(CKR_DEVICE_REMOVED);
++	    }
++	} else {
++	    selectCACApplet(minCacCerts, true);
++	}
+     } else if ((state & P15_CARD)== 0) {
+ 	/* p15 does the select in attemptLogin */
+ 	selectApplet();
+diff -up ./src/coolkey/slot.h.alt-tokens-2 ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.alt-tokens-2	2018-06-25 17:58:23.473185283 -0700
++++ ./src/coolkey/slot.h	2018-06-25 17:58:23.475185280 -0700
+@@ -356,8 +356,10 @@ class Slot {
+     bool mCoolkey;
+     bool mOldCAC;
+     bool mCACLocalLogin;
++    bool mCAC_ACA;
+     int pivContainer;
+     int pivKey;
++    int minCacCerts;
+     int maxCacCerts;
+     SlotAlgs algs;
+     unsigned short p15aid;
+diff -up ./src/libckyapplet/cky_applet.c.alt-tokens-2 ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.alt-tokens-2	2018-06-25 17:58:23.473185283 -0700
++++ ./src/libckyapplet/cky_applet.c	2018-06-25 17:58:23.475185280 -0700
+@@ -626,6 +626,19 @@ CACApplet_SelectCCC(CKYCardConnection *c
+     return ret;
+ }
+ 
++static CKYByte cacACAid[] = {0xa0, 0x00, 0x00, 0x00, 0x79, 0x10, 0x00 };
++CKYStatus
++CACApplet_SelectACA(CKYCardConnection *conn, CKYISOStatus *apduRC)
++{
++    CKYStatus ret;
++    CKYBuffer CAC_CM_AID;
++    CKYBuffer_InitFromData(&CAC_CM_AID, cacACAid, sizeof(cacACAid));
++    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID,
++		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
++    CKYBuffer_FreeData(&CAC_CM_AID);
++    return ret;
++}
++
+ CKYStatus
+ CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef,
+ 						 CKYISOStatus *apduRC)
+diff -up ./src/libckyapplet/cky_applet.h.alt-tokens-2 ./src/libckyapplet/cky_applet.h
+--- ./src/libckyapplet/cky_applet.h.alt-tokens-2	2018-06-25 17:58:23.457185300 -0700
++++ ./src/libckyapplet/cky_applet.h	2018-06-25 17:58:23.475185280 -0700
+@@ -539,6 +539,8 @@ CKYStatus CACApplet_SelectCardManager(CK
+ 							CKYISOStatus *apduRC);
+ /* Select the CAC CC container. Can happen with either applet selected */
+ CKYStatus CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC);
++/* Select the CAC ACA container. Can happen with either applet selected */
++CKYStatus CACApplet_SelectACA(CKYCardConnection *conn, CKYISOStatus *apduRC);
+ /* Select an old CAC applet and fill in the cardAID */
+ CKYStatus CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cardAid,
+ 			      CKYByte instance, CKYISOStatus *apduRC);
diff --git a/SOURCES/coolkey-1.1.0-cardos-5-3.patch b/SOURCES/coolkey-1.1.0-cardos-5-3.patch
new file mode 100644
index 0000000..5f570ea
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-cardos-5-3.patch
@@ -0,0 +1,324 @@
+diff -up ./src/coolkey/object.cpp.cardos-5-3 ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.cardos-5-3	2017-03-16 17:14:02.415338726 -0700
++++ ./src/coolkey/object.cpp	2017-03-16 17:14:02.419338794 -0700
+@@ -32,7 +32,7 @@ const CKYByte eccOID[] = {0x2a,0x86,0x48
+ void dump(const char *label, const CKYBuffer *buf)
+ {
+     CKYSize i;
+-    CKYSize size = CKYBuffer_Size(buf);
++    CKYSize size = buf ? CKYBuffer_Size(buf) : 0;
+ #define ROW_LENGTH 60
+     char string[ROW_LENGTH+1];
+     char *bp = &string[0];
+diff -up ./src/coolkey/object.h.cardos-5-3 ./src/coolkey/object.h
+--- ./src/coolkey/object.h.cardos-5-3	2017-03-16 17:14:02.415338726 -0700
++++ ./src/coolkey/object.h	2017-03-16 17:14:02.419338794 -0700
+@@ -200,9 +200,11 @@ class PKCS11Object {
+     CK_USER_TYPE getUser(void) const { return user; }
+     void setKeyType(KeyType theType) { keyType = theType; }
+     void setKeySize(unsigned int keySize_) { keySize = keySize_; }
++    void setUser(CK_USER_TYPE user_) { user = user_; }
+     const CKYBuffer *getAuthId(void) const { return &authId; }
+     const CKYBuffer *getPinAuthId(void) const { return &pinAuthId; }
+     const PK15ObjectPath &getObjectPath() const { return objectPath; }
++    void setObjectPath(const PK15ObjectPath &newPath) { objectPath = newPath; }
+     void completeKey(const PKCS11Object &cert);
+ };
+ 
+@@ -308,6 +310,7 @@ class PK15Object : public PKCS11Object {
+     bool isLocal(void) const { return 
+ 			(pinInfo.pinFlags & P15PinLocal) ? true : false; }
+     const P15PinInfo *getPinInfo(void) const { return &pinInfo; }
++    void setPinRef(CK_BYTE pinRef) { pinInfo.pinRef = pinRef; }
+ };
+ 
+ class Reader : public PKCS11Object {
+diff -up ./src/coolkey/slot.cpp.cardos-5-3 ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.cardos-5-3	2017-03-16 17:14:02.416338743 -0700
++++ ./src/coolkey/slot.cpp	2017-03-17 13:48:26.661205327 -0700
+@@ -41,6 +41,7 @@
+ #define PRINTF(args)
+ #endif
+ // #define DISPLAY_WHOLE_GET_DATA 1
++void dump(const char *label, const CKYBuffer *buf);
+ 
+ 
+ // The Cyberflex Access 32k egate ATR
+@@ -467,6 +468,8 @@ Slot::Slot(const char *readerName_, Log
+     }
+     CKYBuffer_InitEmpty(&cardATR);
+     CKYBuffer_InitEmpty(&mCUID);
++    CKYBuffer_InitEmpty(&candidateUserAuthId);
++    CKYBuffer_InitEmpty(&candidateContextSpecificAuthId);
+     for (int i=0; i < MAX_CERT_SLOTS; i++) {
+ 	CKYBuffer_InitEmpty(&cardAID[i]);
+     }
+@@ -540,6 +543,8 @@ Slot::~Slot()
+     CKYBuffer_FreeData(&nonce);
+     CKYBuffer_FreeData(&cardATR);
+     CKYBuffer_FreeData(&mCUID);
++    CKYBuffer_FreeData(&candidateUserAuthId);
++    CKYBuffer_FreeData(&candidateContextSpecificAuthId);
+     CKYBuffer_FreeData(&p15AID);
+     CKYBuffer_FreeData(&p15odf);
+     CKYBuffer_FreeData(&p15tokenInfo);
+@@ -1272,6 +1277,41 @@ class ObjectKeyCKAIDMatch {
+     }
+ };
+ 
++#ifdef DEBUG
++void
++dumpPin(const char *label, const PK15Object *pin)
++{
++    const P15PinInfo *pinInfo = pin->getPinInfo();
++    const PK15ObjectPath &pinPath=pin->getObjectPath(); 
++    unsigned int pin_type = (unsigned int) pinInfo->pinType;
++    const char *pin_name[3] = {"BCD", "ASCIINum", "UTF8" };
++
++    printf("Pin Object %s\n",label);
++    printf(" Pin flags=0x%08lx\n",pinInfo->pinFlags);
++    printf(" Pin type=%d (%s)\n",(int) pin_type, 
++		pin_type < 3U ? pin_name[pin_type] :
++		"Invalid");
++    printf(" Pin length= %d (%d - %d)\n",(int)pinInfo->storedLength,
++					(int)pinInfo->minLength, 
++					(int)pinInfo->maxLength);
++    printf(" Pin pad = 0x%02x,<%c>\n", pinInfo->padChar, pinInfo->padChar);
++    printf(" Pin Ref = 0x%02x\n", pinInfo->pinRef);
++    printf(" Pin Path index = %ld\n",(long)pinPath.getIndex());
++    printf(" Pin Path size = %ld\n",(long)pinPath.getLength());
++    dump(" Pin Path:",pinPath.getPath());
++}
++
++void
++dumpPath(const char *label, const PK15ObjectPath &path) 
++{
++    printf(" Path for %s\n", label);
++    printf(" index = %ld\n",(long)path.getIndex());
++    printf(" size = %ld\n",(long)path.getLength());
++    dump(" objPath:",path.getPath());
++}
++#endif
++
++
+ CKYStatus
+ Slot::parseEF_Directory(const CKYByte *current, 
+ 					CKYSize size, PK15ObjectType type)
+@@ -1326,7 +1366,22 @@ Slot::parseEF_Directory(const CKYByte *c
+ 			auth[CKU_SO] = new PK15Object(obj);
+ 		    }
+ 		} else if (auth[CKU_USER] == NULL) {
++		    const CKYBuffer *authid = obj.getPinAuthId();
+ 		    auth[CKU_USER] = new PK15Object(obj);
++		    if ((CKYBuffer_Size(&candidateUserAuthId) != 0) 
++			&& !CKYBuffer_IsEqual(authid, &candidateUserAuthId)) {
++			/* validate our candidates */
++			if ((CKYBuffer_Size(&candidateContextSpecificAuthId)
++			     == 0) || (CKYBuffer_IsEqual(
++				&candidateContextSpecificAuthId, authid))) {
++			    CKYBuffer_Replace(&candidateContextSpecificAuthId,0,
++				CKYBuffer_Data(&candidateUserAuthId),
++			        CKYBuffer_Size(&candidateUserAuthId));
++			}
++			CKYBuffer_Replace(&candidateUserAuthId, 0,
++				CKYBuffer_Data(authid), CKYBuffer_Size(authid));
++		    }
++			
+ 		} else if (auth[CKU_CONTEXT_SPECIFIC] == NULL) {
+ 		    ObjectIter iter;
+ 		    const CKYBuffer *authid = obj.getPinAuthId();
+@@ -1339,6 +1394,8 @@ Slot::parseEF_Directory(const CKYByte *c
+ 			if( CKYBuffer_IsEqual(iter->getAuthId(),authid)) {
+ 			    iter->setAttributeBool(CKA_ALWAYS_AUTHENTICATE,
+ 						   TRUE);
++			    iter->setUser(CKU_CONTEXT_SPECIFIC);
++printf("Setting Context Specific pin on key\n");
+ 			}
+ 		    }
+ 		}
+@@ -1349,7 +1406,19 @@ Slot::parseEF_Directory(const CKYByte *c
+ 		{
+ 		    ObjectConstIter iter;
+ 		    const CKYBuffer *id;
++		    const CKYBuffer *authid;
+ 
++		    authid = obj.getAuthId();
++		    if (authid) {
++			if (CKYBuffer_Size(&candidateUserAuthId) == 0) {
++			    CKYBuffer_Replace(&candidateUserAuthId, 0,
++				CKYBuffer_Data(authid), CKYBuffer_Size(authid));
++			} else if (!CKYBuffer_IsEqual(&candidateUserAuthId, 
++							authid)) {
++			    CKYBuffer_Replace(&candidateContextSpecificAuthId,0,
++				CKYBuffer_Data(authid), CKYBuffer_Size(authid));
++			}
++		    }
+ 		    id = obj.getAttribute(CKA_ID);
+ 		    if ((!id) || (CKYBuffer_Size(id) != 1)) {
+ 			break;
+@@ -1386,6 +1455,31 @@ Slot::parseEF_Directory(const CKYByte *c
+     	    tokenObjects.push_back(obj);
+   	} while ( false );
+     }
++
++    /* handle the case where we have context specific with the same user pin */
++    if ((type == PK15AuthObj) 
++		&& (CKYBuffer_Size(&candidateContextSpecificAuthId) != 0)
++		&& (auth[CKU_CONTEXT_SPECIFIC] == NULL)) {
++	ObjectIter iter;
++
++	/* these should put on the individual keys */
++	auth[CKU_CONTEXT_SPECIFIC] = new PK15Object(*auth[CKU_USER]);
++	/* set the pin ref for the context specific auth */
++	auth[CKU_CONTEXT_SPECIFIC]->setPinRef(
++	    (CK_BYTE) CKYBuffer_GetChar(&candidateContextSpecificAuthId,0));
++	for( iter = tokenObjects.begin(); iter != tokenObjects.end(); ++iter) {
++	    const CKYBuffer *authid = iter->getAuthId();
++	    if(authid && 
++		CKYBuffer_IsEqual(authid,&candidateContextSpecificAuthId)) {
++		 iter->setAttributeBool(CKA_ALWAYS_AUTHENTICATE, TRUE);
++		 iter->setUser(CKU_CONTEXT_SPECIFIC);
++		 /* auth[CKU_CONTEXT_SPECIFIC]->
++				setObjectPath(iter->getObjectPath()); */
++	    }
++	}
++	
++
++    }
+     CKYBuffer_FreeData(&file);
+     return CKYSUCCESS;
+ }
+@@ -2221,6 +2315,12 @@ Slot::unloadObjects()
+ 	tokenManufacturer = NULL;
+     }
+     CKYBuffer_Resize(&p15serialNumber,0);
++    CKYBuffer_Resize(&candidateUserAuthId,0);
++    CKYBuffer_Resize(&candidateContextSpecificAuthId,0);
++    for (int i=0; i < MAX_AUTH_USERS; i++) {
++	if (auth[i]) delete auth[i];
++	auth[i]=NULL;
++    }
+ }
+ 
+ #ifdef USE_SHMEM
+@@ -3766,7 +3866,6 @@ Slot::attemptLogin(CK_USER_TYPE user, bo
+ 	contextPinCache.clearPin();
+     }
+ }
+-void dump(const char *label, const CKYBuffer *buf);
+ 
+ void
+ Slot::attemptP15Login(CK_USER_TYPE user)
+@@ -3794,7 +3893,6 @@ Slot::attemptP15Login(CK_USER_TYPE user)
+ 	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);
+@@ -4636,7 +4734,14 @@ Slot::cryptRSA(SessionHandleSuffix suffi
+ 	    params.padInput(&inputPad, &input);
+             performRSAOp(&output, &inputPad, params.getKeySize(), key, 
+ 							params.getDirection());
+-	    params.unpadOutput(result, &output);
++	    if (CKYBuffer_Size(&output) < CKYBuffer_Size(&inputPad)) {
++		/* if the size is smaller than the input, treat it as 
++	         * unpadded */
++		CKYBuffer_Replace(result, 0, CKYBuffer_Data(&output),
++					CKYBuffer_Size(&output));
++	    } else {
++		params.unpadOutput(result, &output);
++	    }
+ 	    CKYBuffer_FreeData(&input);
+ 	    CKYBuffer_FreeData(&inputPad);
+ 	    CKYBuffer_FreeData(&output);
+@@ -4787,9 +4892,8 @@ retry:
+     } 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,
++	status = P15Applet_SignDecrypt(conn, key->getKeyRef(), (keySize/8)*2,
+ 				CKY_DIR_ENCRYPT, input, output, &result); 
+-	
+     } else {
+         status = CKYApplet_ComputeECCSignature(conn, objectToKeyNum(key), 					input, NULL, output, getNonce(), &result);
+     }
+@@ -4861,8 +4965,32 @@ retry:
+     } 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);
++	if (direction == CKY_DIR_DECRYPT) {
++	    status = P15Applet_SignDecrypt(conn, key->getKeyRef(), 
++			keySize/8, direction, input, output, &result);
++	} else {
++	    CKYBuffer unpadInput;
++	    CKYBuffer_InitEmpty(&unpadInput);
++            stripRSAPadding(&unpadInput, input); /* will throw exception 
++						  * on error */
++	    status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
++				direction, &unpadInput, output, &result);
++            CKYBuffer_FreeData(&unpadInput);
++	    /* if it didn't work, try full padded the input first */
++	    if ((status != CKYSUCCESS) 
++		&& (result != CKYISO_CONDITION_NOT_SATISFIED)
++		&& (result != CKYISO_SECURITY_NOT_SATISFIED))  {
++		status = P15Applet_SignDecrypt(conn, key->getKeyRef(), 
++			keySize/8, direction, input, output, &result);
++	    }
++	    /* finally just lie and try to "decrypt" the buffer */
++	    if ((status != CKYSUCCESS) 
++		&& (result != CKYISO_CONDITION_NOT_SATISFIED) 
++		&& (result != CKYISO_SECURITY_NOT_SATISFIED))  {
++		status = P15Applet_SignDecrypt(conn, key->getKeyRef(),
++			 keySize/8, CKY_DIR_DECRYPT, input, output, &result);
++	    }
++	}
+     } else {
+         status = CKYApplet_ComputeCrypt(conn, objectToKeyNum(key), 
+ 		CKY_RSA_NO_PAD, direction, input, NULL, output, 
+@@ -4883,8 +5011,7 @@ retry:
+             throw PKCS11Exception(CKR_DATA_INVALID);
+ 	}
+ 	// version0 keys could be logged out in the middle by someone else,
+-	// reauthenticate... This code can go away when we depricate.
+-        // version0 applets.
++	// reauthenticate... 
+ 	if (!isVersion1Key && !loginAttempted  && 
+ 				userPinCache(key->getUser())->isValid() &&
+ 					(result == CKYISO_UNAUTHORIZED)) {
+diff -up ./src/coolkey/slot.h.cardos-5-3 ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.cardos-5-3	2017-03-16 17:14:02.417338760 -0700
++++ ./src/coolkey/slot.h	2017-03-16 17:14:02.421338828 -0700
+@@ -368,6 +368,8 @@ class Slot {
+     CKYBuffer p15tokenInfo;
+     CKYBuffer p15odf;
+     CKYBuffer p15serialNumber;
++    CKYBuffer candidateUserAuthId;
++    CKYBuffer candidateContextSpecificAuthId;
+     //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 };
+ 
+ #ifdef USE_SHMEM
+diff -up ./src/libckyapplet/cky_applet.c.cardos-5-3 ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.cardos-5-3	2017-03-16 17:14:02.404338539 -0700
++++ ./src/libckyapplet/cky_applet.c	2017-03-16 17:14:02.422338845 -0700
+@@ -1316,8 +1316,6 @@ P15Applet_SignDecrypt(CKYCardConnection
+     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
+@@ -1367,7 +1365,7 @@ P15Applet_SignDecrypt(CKYCardConnection
+     }
+     CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength);
+     pso.chain = 0;
+-    pso.retLen = dataSize;
++    pso.retLen = keySize;
+ 
+     ret = CKYApplet_HandleAPDU(conn, 
+ 		P15AppletFactory_PerformSecurityOperation, &pso, NULL, 
diff --git a/SOURCES/coolkey-1.1.0-fail-on-bad-mechanisms.patch b/SOURCES/coolkey-1.1.0-fail-on-bad-mechanisms.patch
new file mode 100644
index 0000000..c0d1c43
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-fail-on-bad-mechanisms.patch
@@ -0,0 +1,109 @@
+diff -up ./src/coolkey/coolkey.cpp.fail-on-bad-mechanisms ./src/coolkey/coolkey.cpp
+--- ./src/coolkey/coolkey.cpp.fail-on-bad-mechanisms	2016-06-16 14:36:05.934755563 -0700
++++ ./src/coolkey/coolkey.cpp	2016-06-16 14:36:05.945755372 -0700
+@@ -77,7 +77,8 @@ rsaMechanismList[] = {
+ 
+ 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} }
++    {CKM_ECDSA,{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);
+diff -up ./src/coolkey/slot.cpp.fail-on-bad-mechanisms ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.fail-on-bad-mechanisms	2016-06-16 14:36:05.943755407 -0700
++++ ./src/coolkey/slot.cpp	2016-06-16 15:07:40.255882660 -0700
+@@ -4185,11 +4185,30 @@ Slot::signInit(SessionHandleSuffix suffi
+ {
+     refreshTokenState();
+     SessionIter session = findSession(suffix);
++    PKCS11Object *key = getKeyFromHandle(hKey);
+     if( session == sessions.end() ) {
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
++    if (pMechanism == NULL) {
++        throw PKCS11Exception(CKR_ARGUMENTS_BAD);
++    }
++
++    switch (pMechanism->mechanism) {
++    case CKM_RSA_PKCS:
++	if (key->getKeyType() != Key::rsa) {
++        	throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT);
++	}
++	break;
++    case CKM_ECDSA:
++	if (key->getKeyType() != Key::ecc) {
++        	throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT);
++	}
++	break;
++    default:
++        throw PKCS11Exception(CKR_MECHANISM_INVALID);
++    }
+ 
+-    session->signatureState.initialize(getKeyFromHandle(hKey));
++    session->signatureState.initialize(key);
+ }
+ 
+ void
+@@ -4198,11 +4217,24 @@ Slot::decryptInit(SessionHandleSuffix su
+ {
+     refreshTokenState();
+     SessionIter session = findSession(suffix);
++    PKCS11Object *key = getKeyFromHandle(hKey);
+     if( session == sessions.end() ) {
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
++    if (pMechanism == NULL) {
++        throw PKCS11Exception(CKR_ARGUMENTS_BAD);
++    }
++    switch (pMechanism->mechanism) {
++    case CKM_RSA_PKCS:
++	if (key->getKeyType() != Key::rsa) {
++        	throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT);
++	}
++	break;
++    default:
++        throw PKCS11Exception(CKR_MECHANISM_INVALID);
++    }
+ 
+-    session->decryptionState.initialize(getKeyFromHandle(hKey));
++    session->decryptionState.initialize(key);
+ }
+ 
+ /**
+@@ -5008,8 +5040,23 @@ Slot::derive(SessionHandleSuffix suffix,
+ 
+     ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE);
+     SessionIter session = findSession(suffix);
++    PKCS11Object *key=getKeyFromHandle(hBaseKey);
+ 
+-    session->keyAgreementState.initialize(getKeyFromHandle(hBaseKey));
++    if (pMechanism == NULL ) {
++        throw PKCS11Exception(CKR_ARGUMENTS_BAD);
++    }
++
++    switch (pMechanism->mechanism) {
++    case CKM_ECDH1_DERIVE:
++	if (key->getKeyType() != Key::ecc) {
++        	throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT);
++	}
++	break;
++    default:
++        throw PKCS11Exception(CKR_MECHANISM_INVALID);
++    }
++
++    session->keyAgreementState.initialize(key);
+     deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, 
+ 		phKey, params);
+ 
+@@ -5018,9 +5065,6 @@ Slot::derive(SessionHandleSuffix suffix,
+ 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;
+ 
diff --git a/SOURCES/coolkey-1.1.0-fix-spurious-event.patch b/SOURCES/coolkey-1.1.0-fix-spurious-event.patch
new file mode 100644
index 0000000..739901a
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-fix-spurious-event.patch
@@ -0,0 +1,11 @@
+diff -up ./src/coolkey/slot.cpp.fix-spurious ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.fix-spurious	2014-09-26 15:31:17.277958895 -0700
++++ ./src/coolkey/slot.cpp	2014-09-26 15:34:33.218313227 -0700
+@@ -1412,6 +1412,7 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
+         #endif
+     } while ((status == CKYSUCCESS) ||
+        (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT) ||
++       (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) );
diff --git a/SOURCES/coolkey-1.1.0-max-cpu-bug.patch b/SOURCES/coolkey-1.1.0-max-cpu-bug.patch
new file mode 100644
index 0000000..aab85f7
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-max-cpu-bug.patch
@@ -0,0 +1,12 @@
+diff -up ./src/coolkey/slot.cpp.max-cpu-bug ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.max-cpu-bug	2016-06-30 14:36:10.502785885 -0700
++++ ./src/coolkey/slot.cpp	2016-06-30 14:36:15.812876256 -0700
+@@ -1875,6 +1875,8 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
+ 	if (status != CKYSUCCESS) {
+ 	    if ((CKYCardContext_GetLastError(context) ==
+ 						 SCARD_E_READER_UNAVAILABLE) ||
++	       (CKYCardContext_GetLastError(context) ==
++						 SCARD_E_UNKNOWN_READER) ||
+ 	       (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) {
+ 		OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY);
+ 	    }
diff --git a/SOURCES/coolkey-1.1.0-more-keys.patch b/SOURCES/coolkey-1.1.0-more-keys.patch
new file mode 100644
index 0000000..fb94ec3
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-more-keys.patch
@@ -0,0 +1,61 @@
+diff -up ./src/coolkey/slot.cpp.more_keys ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.more_keys	2016-06-16 11:50:01.027432856 -0700
++++ ./src/coolkey/slot.cpp	2016-06-16 11:50:13.267224824 -0700
+@@ -32,7 +32,8 @@
+ 
+ #define MIN(x, y) ((x) < (y) ? (x) : (y))
+ 
+-
++#define MAX_NUM_KEYS  32
++#define MAX_NUM_CERTS 32
+ 
+ #ifdef DEBUG
+ #define PRINTF(args) printf args
+@@ -3458,7 +3459,7 @@ Slot::loadObjects()
+         } else if( type == 'c' ) {
+             // cert attribute object. find the DER encoding
+             unsigned short certnum = getObjectIndex(iter->obj.objectID);
+-            if( certnum > 9 ) {
++            if( certnum > MAX_NUM_CERTS ) {
+                 //invalid object id
+                 throw PKCS11Exception(CKR_DEVICE_ERROR,
+                     "Invalid object id %08x",iter->obj.objectID);
+@@ -4154,7 +4155,7 @@ Slot::objectToKeyNum(const PKCS11Object
+         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+     }
+     unsigned short keyNum = getObjectIndex(id);
+-    if( keyNum > 9 ) {
++    if( keyNum > MAX_NUM_KEYS ) {
+         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+     }
+     return keyNum & 0xFF;
+@@ -4911,7 +4912,6 @@ Slot::generateRandom(SessionHandleSuffix
+     }
+ }
+ 
+-#define MAX_NUM_KEYS 8
+ unsigned int
+ Slot::getRSAKeySize(PKCS11Object *key)
+ {
+diff -up ./src/coolkey/slot.h.more_keys ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.more_keys	2016-06-16 11:50:08.627303984 -0700
++++ ./src/coolkey/slot.h	2016-06-16 11:54:08.872153180 -0700
+@@ -512,7 +512,17 @@ class Slot {
+         return (char) (objectID >> 24) & 0xff;
+     }
+     unsigned short getObjectIndex(unsigned long objectID) const {
+-        return (char )((objectID >> 16) & 0xff) - '0';
++       char char_index = (char) ((objectID >> 16) & 0xff);
++       if (char_index >= '0' && char_index <= '9') {
++           return char_index - '0';
++       }
++       if (char_index >= 'A' && char_index <= 'Z') {
++           return char_index - 'A' + 10;
++       }
++       if (char_index >= 'a' && char_index <= 'z') {
++           return char_index - 'a' + 26 + 10;
++       }
++       return 0x0100 + char_index;
+     }
+ 
+     // actually get the size of a key in bits from the card
diff --git a/SOURCES/coolkey-1.1.0-noapplet.patch b/SOURCES/coolkey-1.1.0-noapplet.patch
new file mode 100644
index 0000000..dc931fe
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-noapplet.patch
@@ -0,0 +1,18 @@
+diff -up ./src/coolkey/slot.cpp.noapplet ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.noapplet	2013-09-30 14:30:40.069595018 -0700
++++ ./src/coolkey/slot.cpp	2013-09-30 14:31:27.488595000 -0700
+@@ -762,13 +762,7 @@ Slot::connectToToken()
+ 				CKYCardConnection_GetLastError(conn));
+ 		    disconnect();
+ 	    }
+-	    /* CARD is a PIV card */
+-	    state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+-	    isVersion1Key = 0;
+-	    needLogin = 1;
+-            mCoolkey = 0;
+-	    mOldCAC = 0;
+-	    mCACLocalLogin = getPIVLoginType();
++	    /* CARD is unknown */
+ 	    return;
+ 	}
+ 	state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
diff --git a/SOURCES/coolkey-1.1.0-p15-coverity.patch b/SOURCES/coolkey-1.1.0-p15-coverity.patch
new file mode 100644
index 0000000..4118e9c
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-p15-coverity.patch
@@ -0,0 +1,210 @@
+diff -up ./src/coolkey/object.cpp.p15-coverity ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.p15-coverity	2015-07-06 18:02:34.604191118 -0700
++++ ./src/coolkey/object.cpp	2015-07-06 19:06:04.432062377 -0700
+@@ -1558,7 +1558,7 @@ unsigned long GetBits(const CKYByte *ent
+    /* turn the flags into an int */
+    for (i=0; i < entrySize; i++) {
+ 	CKYByte c = rev[entry[i]];
+-	bits  = bits | (c << i*8);
++	bits  = bits | (((unsigned long)c) << (i*8));
+    }
+    return bits | bitFlag;
+ }
+@@ -1585,8 +1585,8 @@ CKYStatus PK15ObjectPath::setObjectPath(
+     if (entry == NULL) { return CKYINVALIDDATA; }
+     tagSize = entry - current;
+     current += entrySize + tagSize;
++    if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+     size -= (entrySize +tagSize);
+-    if (size < 0) { return CKYINVALIDDATA; }
+     status = CKYBuffer_Replace(&path, 0, entry, entrySize);
+     if (status != CKYSUCCESS) {
+ 	return status;
+@@ -1598,8 +1598,8 @@ CKYStatus PK15ObjectPath::setObjectPath(
+ 	if (entry == NULL) { return CKYINVALIDDATA; }
+ 	tagSize = entry - current;
+ 	current += entrySize + tagSize;
++	if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+ 	size -= (entrySize +tagSize);
+-	if (size < 0) { return CKYINVALIDDATA; }
+ 	if (entrySize > 5) { return CKYINVALIDDATA; }
+ 	for (index = 0, i=0; i < entrySize; i++) {
+ 	    index = (index << 8) + (unsigned int) entry[i];
+@@ -1612,8 +1612,8 @@ CKYStatus PK15ObjectPath::setObjectPath(
+ 	if (entry == NULL) { return CKYINVALIDDATA; }
+ 	tagSize = entry - current;
+ 	current += entrySize + tagSize;
++	if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+ 	size -= (entrySize +tagSize);
+-	if (size < 0) { return CKYINVALIDDATA; }
+ 	if (entrySize > 5) { return CKYINVALIDDATA; }
+ 	for (length = 0, i=0; i < entrySize; i++) {
+ 	    length = (length << 8) + (unsigned int) entry[i];
+@@ -1741,8 +1741,8 @@ set_key_type:
+     /* point current to the next section (cass attributes)  */
+     tagSize = commonAttributes - current;
+     current += commonSize + tagSize;
++    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+     currentSize -= (commonSize +tagSize);
+-    if (currentSize < 0) { return CKYINVALIDDATA; }
+ 
+     /* get the CKA_LABEL */
+     if (commonAttributes[0] != ASN1_UTF8_STRING) { return CKYINVALIDDATA; }
+@@ -1835,8 +1835,8 @@ PK15Object::completeCertObject(const CKY
+     /* point current to the next section (type attributes)  */
+     tagSize = commonCertAttributes - current;
+     current += commonSize + tagSize;
++    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+     currentSize -= (commonSize +tagSize);
+-    if (currentSize < 0) { return CKYINVALIDDATA; }
+ 
+     /* get the id */
+     if (commonCertAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; }
+@@ -1907,8 +1907,8 @@ PK15Object::completeAuthObject(const CKY
+ 	if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+ 	tagSize = commonAuthAttributes - current;
+ 	current += commonSize + tagSize;
++	if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+ 	currentSize -= (commonSize + tagSize);
+-	if (currentSize < 0) { return CKYINVALIDDATA; }
+ 	if (commonAuthAttributes[0] != ASN1_OCTET_STRING) {
+ 	    return CKYINVALIDDATA;
+ 	}
+@@ -1930,8 +1930,8 @@ PK15Object::completeAuthObject(const CKY
+     if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+     tagSize = commonAuthAttributes - current;
+     current += commonSize + tagSize;
+-    currentSize -= (commonSize +tagSize);
+-    if (currentSize < 0) { return CKYINVALIDDATA; }
++    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
++    currentSize -= (commonSize + tagSize);
+     /*
+      * parse the Pin Auth Attributes 
+      *     pinFlags  BIT_STRING
+@@ -2093,8 +2093,8 @@ PK15Object::completeKeyObject(const CKYB
+     /* point current to the next section (sublcass attributes)  */
+     tagSize = commonKeyAttributes - current;
+     current += commonSize + tagSize;
+-    currentSize -= (commonSize +tagSize);
+-    if (currentSize < 0) { return CKYINVALIDDATA; }
++    if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
++    currentSize -= (commonSize + tagSize);
+ 
+     /* get the id */
+     if (commonKeyAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; }
+@@ -2263,8 +2263,8 @@ CKYStatus PK15Object::completePrivKeyObj
+ 	/* point current to the next section (type attributes)  */
+ 	tagSize = commonPrivKeyAttributes - current;
+ 	current += commonSize + tagSize;
++	if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
+ 	currentSize -= (commonSize +tagSize);
+-	if (currentSize < 0) { return CKYINVALIDDATA; }
+ 
+  	/* subjectName */
+ 	if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+@@ -2385,8 +2385,8 @@ PK15Object::completePubKeyObject(const C
+ 	/* point current to the next section (type attributes)  */
+ 	tagSize = commonPubKeyAttributes - current;
+ 	current += commonSize + tagSize;
+-	currentSize -= (commonSize +tagSize);
+-	if (currentSize < 0) { return CKYINVALIDDATA; }
++	if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; }
++	currentSize -= (commonSize + tagSize);
+ 
+  	/* subjectName */
+ 	if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+@@ -2535,8 +2535,8 @@ PK15Object::completeRawPublicKey(const C
+     if (entry == NULL) { return CKYINVALIDDATA; }
+     tagSize = entry - current;
+     current += entrySize + tagSize;
++    if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
+     size -= (entrySize +tagSize);
+-    if (size < 0) { return CKYINVALIDDATA; }
+     if ((entry[0] == 0) && (entrySize > 1)) {
+ 	entry++; entrySize--;
+     }
+@@ -2548,8 +2548,8 @@ PK15Object::completeRawPublicKey(const C
+     if (entry == NULL) { return CKYINVALIDDATA; }
+     tagSize = entry - current;
+     current += entrySize + tagSize;
+-    size -= (entrySize +tagSize);
+-    if (size < 0) { return CKYINVALIDDATA; }
++    if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; }
++    size -= (entrySize + tagSize);
+     if ((entry[0] == 0) && (entrySize > 1)) {
+ 	entry++; entrySize--;
+     }
+@@ -2682,11 +2682,11 @@ DEREncodedTokenInfo::DEREncodedTokenInfo
+     if (entry == NULL) return;
+     tagSize = entry - current;
+     current += tagSize + entrySize;
++    if (size < tagSize + entrySize) return;
+     size -= tagSize + entrySize;
+     if (entrySize < 1) {
+ 	version = *entry;
+     }
+-    if (size < 0) return;
+ 
+     /* get the serial number */
+     if (current[0] != ASN1_OCTET_STRING) { return ; }
+@@ -2729,6 +2729,8 @@ DEREncodedTokenInfo::DEREncodedTokenInfo
+     }
+ 
+     /* 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;
+@@ -2739,6 +2741,7 @@ DEREncodedTokenInfo::DEREncodedTokenInfo
+ 	size -= tagSize + entrySize;
+ 	bits = GetBits(entry, entrySize,8,2);
+     }
++#endif
+     return;
+ }
+ 
+diff -up ./src/coolkey/slot.cpp.p15-coverity ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.p15-coverity	2015-07-06 18:02:34.606191081 -0700
++++ ./src/coolkey/slot.cpp	2015-07-06 18:02:34.610191006 -0700
+@@ -3714,7 +3714,6 @@ void
+ Slot::attemptP15Login(CK_USER_TYPE user)
+ {
+     PinCache *pinCachePtr  = userPinCache(user);
+-    const CKYBuffer *path;
+ 
+     if (user == CKU_USER) {
+ 	loggedIn = false;
+@@ -3729,7 +3728,6 @@ Slot::attemptP15Login(CK_USER_TYPE user)
+ 			"No PKCS #15 auth object for user %d\n", user);
+     }
+ 
+-    path = auth[user]->getObjectPath().getPath();
+     status = selectPath(auth[user]->getObjectPath().getPath(), &result);
+     if( status == CKYSCARDERR )  {
+ 	handleConnectionError();
+diff -up ./src/libckyapplet/cky_applet.c.p15-coverity ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.p15-coverity	2015-07-06 18:02:34.606191081 -0700
++++ ./src/libckyapplet/cky_applet.c	2015-07-06 18:02:34.610191006 -0700
+@@ -1361,6 +1361,9 @@ P15Applet_SignDecrypt(CKYCardConnection
+ 	appendLength = length;
+     } else {
+ 	ret = CKYBuffer_Reserve(&tmp, length);
++	if (ret != CKYSUCCESS) {
++	    goto done;
++	}
+     }
+     CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength);
+     pso.chain = 0;
+diff -up ./src/libckyapplet/cky_base.c.p15-coverity ./src/libckyapplet/cky_base.c
+--- ./src/libckyapplet/cky_base.c.p15-coverity	2015-07-06 18:02:34.607191062 -0700
++++ ./src/libckyapplet/cky_base.c	2015-07-06 18:02:34.610191006 -0700
+@@ -736,7 +736,7 @@ CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu
+     CKYStatus ret;
+ 
+     if (recvlen <= CKYAPDU_MAX_DATA_LEN) {
+-	return APDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff));
++	return CKYAPDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff));
+     }
+     ret = CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_HEADER_LEN+2);
+     if (ret != CKYSUCCESS) {
diff --git a/SOURCES/coolkey-1.1.0-p15.patch b/SOURCES/coolkey-1.1.0-p15.patch
new file mode 100644
index 0000000..f1b269f
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-p15.patch
@@ -0,0 +1,4379 @@
+diff -up ./src/coolkey/coolkey.cpp.p15 ./src/coolkey/coolkey.cpp
+--- ./src/coolkey/coolkey.cpp.p15	2015-07-06 10:27:55.769827286 -0700
++++ ./src/coolkey/coolkey.cpp	2015-07-06 10:27:55.784827002 -0700
+@@ -599,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/object.cpp.p15 ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.p15	2015-07-06 10:27:55.770827267 -0700
++++ ./src/coolkey/object.cpp	2015-07-06 10:27:55.785826984 -0700
+@@ -19,9 +19,9 @@
+ 
+ #include "mypkcs11.h"
+ #include "PKCS11Exception.h"
+-#include "object.h"
+ #include <algorithm>
+ #include <string.h>
++#include "object.h"
+ 
+ using std::find_if;
+ 
+@@ -29,7 +29,7 @@ const CKYByte rsaOID[] = {0x2A,0x86,0x48
+ const CKYByte eccOID[] = {0x2a,0x86,0x48,0xce,0x3d,0x02,0x01};
+ 
+ #ifdef DEBUG
+-void dump(CKYBuffer *buf)
++void dump(const char *label, const CKYBuffer *buf)
+ {
+     CKYSize i;
+     CKYSize size = CKYBuffer_Size(buf);
+@@ -38,8 +38,10 @@ void dump(CKYBuffer *buf)
+     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-1)) == 0) ) {
++        if (i && ((i % (ROW_LENGTH)) == 0) ) {
+             *bp = 0;
+             printf(" %s\n",string);
+             bp = &string[0];
+@@ -49,7 +51,35 @@ void dump(CKYBuffer *buf)
+         *bp++ =  (c < ' ') ? '.' : ((c & 0x80) ? '*' : c);
+     }
+     *bp = 0;
+-    for (i= (i % (ROW_LENGTH-1)); i && (i < ROW_LENGTH); i++) {
++    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);
+@@ -77,16 +107,23 @@ class AttributeTypeMatch
+ };
+ 
+ PKCS11Object::PKCS11Object(unsigned long muscleObjID_,CK_OBJECT_HANDLE handle_)
+-    : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL), keyType(unknown)
++    : 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), keyType(unknown)
++                   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
+@@ -582,6 +619,21 @@ PKCS11Object::setAttribute(CK_ATTRIBUTE_
+ }
+ 
+ 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;
+@@ -613,7 +665,7 @@ PKCS11Object::setAttributeULong(CK_ATTRI
+ 
+ typedef struct {
+     const CKYByte*data;
+-    unsigned int len;
++    CKYSize len;
+ } CCItem;
+ 
+ typedef enum {
+@@ -621,9 +673,9 @@ 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;
+ 
+@@ -673,7 +725,7 @@ 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) {
+@@ -687,15 +739,15 @@ unwrapBitString(const CKYByte *buf, unsi
+ }
+ 
+ static SECStatus
+-GetECKeyFieldItems(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;
+-    unsigned int buf_length = spki_length;
++    CKYSize buf_length = spki_length;
+     const CKYByte *algid;
+-    unsigned int algidlen;
++    CKYSize algidlen;
+     const CKYByte *dummy;
+-    unsigned int dummylen;
++    CKYSize dummylen;
+ 
+     if (!point || !params || !buf)
+         return SECFailure;
+@@ -756,12 +808,12 @@ GetKeyOIDMatches(const CKYByte *spki_dat
+ }
+ 
+ static SECStatus
+-GetKeyAlgorithmId(const CKYByte *spki_data, unsigned int spki_length,
++GetKeyAlgorithmId(const CKYByte *spki_data, CKYSize spki_length,
+        CCItem *algorithmId)
+ {
+ 
+     const CKYByte *buf = spki_data;
+-    unsigned int buf_length = spki_length;
++    CKYSize buf_length = spki_length;
+ 
+     if ( algorithmId == NULL) return SECFailure;
+ 
+@@ -786,7 +838,7 @@ GetKeyTypeFromSPKI(const CKYBuffer *key)
+             "Failed to decode key algorithm ID.");
+     }
+ 
+-    unsigned int length = 0;
++    CKYSize length = 0;
+     const CKYByte *keyData = NULL;
+ 
+     /* Get actual oid buffer */
+@@ -829,13 +881,13 @@ GetKeyTypeFromSPKI(const CKYBuffer *key)
+ }
+ 
+ static SECStatus
+-GetKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length,
++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);
+@@ -955,7 +1007,7 @@ Key::Key(unsigned long muscleObjID, cons
+ }
+ 
+ void
+-Key::completeKey(const PKCS11Object &cert)
++PKCS11Object::completeKey(const PKCS11Object &cert)
+ {
+     // infer key attributes from cert
+     bool modulusExists, exponentExists;
+@@ -1015,14 +1067,14 @@ Key::completeKey(const PKCS11Object &cer
+ }
+ 
+ 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);
+@@ -1330,10 +1382,10 @@ static const unsigned char CN_DATA[] = {
+ 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);
+@@ -1341,9 +1393,9 @@ GetCN(const CKYByte *dn, unsigned int dn
+ 
+     while (buf_length) {
+         const CKYByte *name;
+-        unsigned int name_length;
++        CKYSize name_length;
+         const CKYByte *oid;
+-        unsigned int oid_length;
++        CKYSize oid_length;
+ 
+         /* unwrap the set */
+         name = dataStart(buf, buf_length, &name_length, false);
+@@ -1408,13 +1460,6 @@ CACCert::CACCert(CKYByte instance, const
+ {
+     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);
+@@ -1456,6 +1501,1063 @@ CACCert::CACCert(CKYByte instance, const
+     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 | (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;
++    size -= (entrySize +tagSize);
++    if (size < 0) { return CKYINVALIDDATA; }
++    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;
++	size -= (entrySize +tagSize);
++	if (size < 0) { return CKYINVALIDDATA; }
++	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;
++	size -= (entrySize +tagSize);
++	if (size < 0) { return CKYINVALIDDATA; }
++	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;
++    currentSize -= (commonSize +tagSize);
++    if (currentSize < 0) { return CKYINVALIDDATA; }
++
++    /* 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;
++    currentSize -= (commonSize +tagSize);
++    if (currentSize < 0) { return CKYINVALIDDATA; }
++
++    /* 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;
++	currentSize -= (commonSize + tagSize);
++	if (currentSize < 0) { return CKYINVALIDDATA; }
++	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;
++    currentSize -= (commonSize +tagSize);
++    if (currentSize < 0) { return CKYINVALIDDATA; }
++    /*
++     * 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;
++    currentSize -= (commonSize +tagSize);
++    if (currentSize < 0) { return CKYINVALIDDATA; }
++
++    /* 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;
++	currentSize -= (commonSize +tagSize);
++	if (currentSize < 0) { return CKYINVALIDDATA; }
++
++ 	/* 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;
++	currentSize -= (commonSize +tagSize);
++	if (currentSize < 0) { return CKYINVALIDDATA; }
++
++ 	/* 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;
++    size -= (entrySize +tagSize);
++    if (size < 0) { return CKYINVALIDDATA; }
++    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;
++    size -= (entrySize +tagSize);
++    if (size < 0) { return CKYINVALIDDATA; }
++    if ((entry[0] == 0) && (entrySize > 1)) {
++	entry++; entrySize--;
++    }
++    setAttribute(CKA_PUBLIC_EXPONENT, entry, entrySize);
++    state = PK15StateComplete;
++    return CKYSUCCESS;
++}
++
+ DEREncodedSignature::DEREncodedSignature(const CKYBuffer *derSig)
+ {
+ 
+@@ -1483,9 +2585,9 @@ int DEREncodedSignature::getRawSignature
+ 
+     CKYBuffer_Zero(rawSig);
+ 
+-    unsigned int seq_length = 0;
+-    unsigned int expected_sig_len = ( (keySize + 7) / 8 ) * 2 ;
+-    unsigned int expected_piece_size = expected_sig_len / 2 ;
++    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);
+@@ -1494,7 +2596,7 @@ int DEREncodedSignature::getRawSignature
+ 
+     // unwrap first multi byte integer
+    
+-    unsigned int int_length = 0;
++    CKYSize int_length = 0;
+     const CKYByte *int1Buf = NULL;
+     const CKYByte *int2Buf = NULL;
+ 
+@@ -1525,7 +2627,7 @@ int DEREncodedSignature::getRawSignature
+ 
+     // unwrap second multi byte integer
+ 
+-    unsigned int second_int_length = 0;
++    CKYSize second_int_length = 0;
+ 
+     int2Buf = dataStart(buf, seq_length, &second_int_length, false);
+ 
+@@ -1552,3 +2654,91 @@ int DEREncodedSignature::getRawSignature
+ 
+     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;
++    size -= tagSize + entrySize;
++    if (entrySize < 1) {
++	version = *entry;
++    }
++    if (size < 0) return;
++
++    /* 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 */
++    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);
++    }
++    return;
++}
++
+diff -up ./src/coolkey/object.h.p15 ./src/coolkey/object.h
+--- ./src/coolkey/object.h.p15	2015-07-06 10:27:55.770827267 -0700
++++ ./src/coolkey/object.h	2015-07-06 10:27:55.785826984 -0700
+@@ -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;
+@@ -52,9 +79,30 @@ class PKCS11Attribute {
+     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 {
+@@ -72,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);
+@@ -82,19 +132,37 @@ class PKCS11Object {
+   protected :
+     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);
+-			attributes.clear(); }
++    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), keyType(cpy.keyType) { 
+-			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; }
+@@ -107,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);
+@@ -124,14 +194,21 @@ class PKCS11Object {
+ 	return &pubKey;
+     }
+ 
+-    KeyType getKeyType() const { return keyType;}
++    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 {
+@@ -155,6 +232,84 @@ 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, 
+@@ -180,6 +335,21 @@ class DEREncodedSignature  {
+ 
+ };
+ 
++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:
+@@ -202,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.p15 ./src/coolkey/pkcs11t.h
+--- ./src/coolkey/pkcs11t.h.p15	2015-07-06 10:27:55.770827267 -0700
++++ ./src/coolkey/pkcs11t.h	2015-07-06 10:29:32.293005407 -0700
+@@ -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
+ 
+ 
+diff -up ./src/coolkey/slot.cpp.p15 ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.p15	2015-07-06 10:27:55.782827040 -0700
++++ ./src/coolkey/slot.cpp	2015-07-06 10:27:55.786826965 -0700
+@@ -54,6 +54,11 @@ 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).
+@@ -405,19 +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),mCACLocalLogin(false),
+-	pivContainer(-1), pivKey(-1), mECC(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);
+@@ -433,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) {
+@@ -515,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>
+@@ -637,9 +665,10 @@ Slot::getPIVLoginType(void)
+     }
+ done:
+     CKYBuffer_FreeData(&buffer);
+-    return true;
++    return local;
+ }
+ 
++
+ void
+ Slot::connectToToken()
+ {
+@@ -745,7 +774,7 @@ Slot::connectToToken()
+ 	 /* CARD is a PIV card */
+ 	 state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+ 	 isVersion1Key = 0;
+-	 needLogin = 1;
++	 needLogin = true;
+          mCoolkey = 0;
+ 	 mOldCAC = 0;
+ 	 mCACLocalLogin = getPIVLoginType();
+@@ -757,12 +786,22 @@ Slot::connectToToken()
+ 	status = getCACAid();
+ 	if (status != CKYSUCCESS) {
+ 	    log->log("CAC Select failed 0x%x\n", status);
+-	    if (status == CKYSCARDERR) {
++	    status = getP15Params();
++	    if (status != CKYSUCCESS) {
++	    	if (status == CKYSCARDERR) {
+ 		    log->log("Card Failure 0x%x\n",
+ 				CKYCardConnection_GetLastError(conn));
+ 		    disconnect();
+-	    }
+-	    /* CARD is unknown */
++	    	}
++	        /* 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;
+@@ -771,7 +810,7 @@ Slot::connectToToken()
+          * other apps may be running now, so resetting the cac is a bit
+          * unfriendly */
+ 	isVersion1Key = 0;
+-	needLogin = 1;
++	needLogin = true;
+         mCoolkey = 0;
+ 	mCACLocalLogin = false;
+ 	return;
+@@ -841,6 +880,8 @@ Slot::invalidateLogin(bool hard)
+     } else {
+ 	loggedIn = false;
+ 	pinCache.invalidate();
++	contextPinCache.invalidate();
++	contextPinCache.clearPin();
+ 	if (hard) {
+ 	    pinCache.clearPin();
+ 	}
+@@ -951,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()
+ {
+@@ -1132,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);
+@@ -1204,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]);
+@@ -1213,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;
+     }
+@@ -1633,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()
+ {
+@@ -1706,7 +2155,7 @@ Slot::addKeyObject(list<PKCS11Object>& o
+                         "Missing or invalid CKA_ID value");
+         }
+         iter = find_if(objectList.begin(), objectList.end(),
+-                        ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0)));
++                        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
+@@ -1758,6 +2207,11 @@ Slot::unloadObjects()
+     free(personName);
+     personName = NULL;
+     fullTokenName = false;
++    if (tokenManufacturer) {
++	free(tokenManufacturer);
++	tokenManufacturer = NULL;
++    }
++    CKYBuffer_Resize(&p15serialNumber,0);
+ }
+ 
+ #ifdef USE_SHMEM
+@@ -2956,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",
+@@ -3123,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
+@@ -3181,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();
+ 
+@@ -3191,10 +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.invalidate();
+-	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);
+     }
+@@ -3205,17 +3683,85 @@ Slot::login(SessionHandleSuffix handleSu
+ 
+     if (state & GOV_CARD) {
+ 	selectCACApplet(0, true);
+-    } else {
++    } else if ((state & P15_CARD)== 0) {
++	/* p15 does the select in attemptLogin */
+ 	selectApplet();
+     }
+ 
+     if (isVersion1Key) {
+-	attemptLogin((const char *)pPin);
+-    } else if (state & GOV_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);
++    const CKYBuffer *path;
++
++    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);
++    }
++
++    path = auth[user]->getObjectPath().getPath();
++    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
+@@ -3252,6 +3798,7 @@ Slot::attemptCACLogin()
+     loggedIn = true;
+ }
+ 
++
+ void
+ Slot::oldAttemptLogin()
+ {
+@@ -3286,7 +3833,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;
+@@ -3362,7 +3909,7 @@ Slot::logout(SessionHandleSuffix suffix)
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+ 
+-    if (state & GOV_CARD) {
++    if (state & (GOV_CARD|P15_CARD)) {
+ 	CACLogout();
+ 	return;
+     }
+@@ -3601,31 +4148,26 @@ Slot::ensureValidSession(SessionHandleSu
+ // from 0-9.
+ //
+ CKYByte
+-Slot::objectHandleToKeyNum(CK_OBJECT_HANDLE hKey)
++Slot::objectToKeyNum(const PKCS11Object *key)
+ {
+-    ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
+-        ObjectHandleMatch(hKey));
+-
+-    if( iter == tokenObjects.end() ) {
+-        // no such object
+-        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+-    }
++    unsigned long id = key->getMuscleObjID();
+ 
+-    if( getObjectClass(iter->getMuscleObjID()) != 'k' ) {
++    if( getObjectClass(id) != 'k' ) {
+         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+     }
+-    unsigned short keyNum = getObjectIndex(iter->getMuscleObjID());
++    unsigned short keyNum = getObjectIndex(id);
+     if( keyNum > 9 ) {
+         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+     }
+     return keyNum & 0xFF;
+ }
+ 
+-PKCS11Object::KeyType
+-Slot::getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey)
++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() ) {
+          throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+@@ -3635,7 +4177,7 @@ Slot::getKeyTypeFromHandle(CK_OBJECT_HAN
+         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
+     }
+ 
+-    return iter->getKeyType();
++    return &obj;
+ }
+ 
+ void
+@@ -3648,9 +4190,7 @@ Slot::signInit(SessionHandleSuffix suffi
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+ 
+-    PKCS11Object::KeyType  keyType = getKeyTypeFromHandle(hKey);
+-
+-    session->signatureState.initialize(objectHandleToKeyNum(hKey), keyType);
++    session->signatureState.initialize(getKeyFromHandle(hKey));
+ }
+ 
+ void
+@@ -3663,9 +4203,7 @@ Slot::decryptInit(SessionHandleSuffix su
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+ 
+-    PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey);
+-
+-    session->decryptionState.initialize(objectHandleToKeyNum(hKey), keyType);
++    session->decryptionState.initialize(getKeyFromHandle(hKey));
+ }
+ 
+ /**
+@@ -3935,7 +4473,7 @@ Slot::sign(SessionHandleSuffix suffix, C
+ 
+     CryptOpState sigState = dummyParams.getOpState(*session);
+ 
+-    PKCS11Object::KeyType keyType = sigState.keyType;
++    PKCS11Object::KeyType keyType = sigState.key->getKeyType();
+    
+     if ( keyType == PKCS11Object::unknown) {
+         throw PKCS11Exception(CKR_DATA_INVALID);
+@@ -3982,9 +4520,9 @@ Slot::cryptRSA(SessionHandleSuffix suffi
+     }
+     CryptOpState& opState = params.getOpState(*session);
+     CKYBuffer *result = &opState.result;
+-    CKYByte keyNum = opState.keyNum;
++    PKCS11Object  *key = opState.key;
+ 
+-    unsigned int keySize = getRSAKeySize(keyNum);
++    unsigned int keySize = getRSAKeySize(key);
+ 
+     if (keySize != CryptParams::DEFAULT_KEY_SIZE)
+         params.setKeySize(keySize);
+@@ -4008,8 +4546,8 @@ Slot::cryptRSA(SessionHandleSuffix suffi
+   	}
+ 	try {
+ 	    params.padInput(&inputPad, &input);
+-            performRSAOp(&output, &inputPad, params.getKeySize(), 
+-								keyNum, params.getDirection());
++            performRSAOp(&output, &inputPad, params.getKeySize(), key, 
++							params.getDirection());
+ 	    params.unpadOutput(result, &output);
+ 	    CKYBuffer_FreeData(&input);
+ 	    CKYBuffer_FreeData(&inputPad);
+@@ -4072,9 +4610,9 @@ void Slot::signECC(SessionHandleSuffix s
+     }
+     CryptOpState& opState = params.getOpState(*session);
+     CKYBuffer *result = &opState.result;
+-    CKYByte keyNum = opState.keyNum;
++    PKCS11Object  *key = opState.key;
+ 
+-    unsigned int keySize = getECCKeySize(keyNum);
++    unsigned int keySize = getECCKeySize(key);
+ 
+     if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE)
+         params.setKeySize(keySize);
+@@ -4100,7 +4638,7 @@ void Slot::signECC(SessionHandleSuffix s
+             throw PKCS11Exception(CKR_HOST_MEMORY);
+         }
+         try {
+-            performECCSignature(&output, &input, params.getKeySize(), keyNum);
++            performECCSignature(&output, &input, params.getKeySize(), key);
+             params.unpadOutput(result, &output);
+             CKYBuffer_FreeData(&input);
+             CKYBuffer_FreeData(&output);
+@@ -4122,8 +4660,26 @@ void Slot::signECC(SessionHandleSuffix s
+ }
+ 
+ void
++Slot::selectKey(const PKCS11Object *key, bool retry)
++{
++    /* 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, CKYByte keyNum)
++			unsigned int keySize, const PKCS11Object *key)
+ {
+ 
+     /* establish a transaction */
+@@ -4135,22 +4691,23 @@ Slot::performECCSignature(CKYBuffer *out
+         throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+     }
+ 
+-    if (state & GOV_CARD) {
+-	selectCACApplet(keyNum, true);
+-    } else {
+-	selectApplet();
+-    }
+-
+     CKYISOStatus result;
+-    int loginAttempted = 0;
++    bool loginAttempted = false;
+ 
+ retry:
++    selectKey(key, loginAttempted);
++
+     if (state & PIV_CARD) {
+-        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result);
++        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 {
+-        status = CKYApplet_ComputeECCSignature(conn, keyNum, input, NULL, output, getNonce(), &result);
++        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) ||
+@@ -4166,19 +4723,16 @@ retry:
+         if (result == CKYISO_DATA_INVALID) {
+             throw PKCS11Exception(CKR_DATA_INVALID);
+         }
+-        /* version0 keys could be logged out in the middle by someone else,
+-           reauthenticate... This code can go away when we depricate.
+-           version0 applets.
+-        */
++        /* 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 {
+-		if (state & GOV_CARD) {
+-		    attemptCACLogin();
+-		} else {
+-		    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
+@@ -4199,8 +4753,9 @@ retry:
+ 
+ 
+ void
+-Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, unsigned int keySize,
+-					CKYByte keyNum, CKYByte direction)
++Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, 
++		unsigned int keySize, const PKCS11Object *key, 
++		CKYByte direction)
+ {
+     if ( mECC ) {
+         throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+@@ -4212,26 +4767,25 @@ Slot::performRSAOp(CKYBuffer *output, co
+     Transaction trans;
+     CKYStatus status = trans.begin(conn);
+     if( status != CKYSUCCESS ) handleConnectionError();
+-
+-    //
+-    // select the applet
+-    //
+-    if (state & GOV_CARD) {
+-	selectCACApplet(keyNum, true);
+-    } else {
+-	selectApplet();
+-    }
+-
+     CKYISOStatus result;
+-    int loginAttempted = 0;
++    bool loginAttempted = false;
++
+ retry:
++    selectKey(key, loginAttempted);
++
++
+     if (state & PIV_CARD) {
+-        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result);
++        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 */
+@@ -4250,15 +4804,12 @@ retry:
+ 	// version0 keys could be logged out in the middle by someone else,
+ 	// reauthenticate... This code can go away when we depricate.
+         // version0 applets.
+-	if (!isVersion1Key && !loginAttempted  && pinCache.isValid() &&
++	if (!isVersion1Key && !loginAttempted  && 
++				userPinCache(key->getUser())->isValid() &&
+ 					(result == CKYISO_UNAUTHORIZED)) {
+ 	    // try to reauthenticate 
+ 	    try {
+-		if (state & GOV_CARD) {
+-		    attemptCACLogin();
+-		} else {
+-		    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
+@@ -4278,7 +4829,7 @@ void
+ Slot::seedRandom(SessionHandleSuffix suffix, CK_BYTE_PTR pData,
+         CK_ULONG ulDataLen)
+ {
+-    if (state & GOV_CARD) {
++    if (state & (GOV_CARD|P15_CARD)) {
+ 	/* should throw unsupported */
+ 	throw PKCS11Exception(CKR_DEVICE_ERROR);
+     }
+@@ -4330,7 +4881,7 @@ void
+ Slot::generateRandom(SessionHandleSuffix suffix, const CK_BYTE_PTR pData,
+         CK_ULONG ulDataLen)
+ {
+-    if (state & GOV_CARD) {
++    if (state & (GOV_CARD|P15_CARD)) {
+ 	/* should throw unsupported */
+ 	throw PKCS11Exception(CKR_DEVICE_ERROR);
+     }
+@@ -4364,61 +4915,44 @@ Slot::generateRandom(SessionHandleSuffix
+ 
+ #define MAX_NUM_KEYS 8
+ unsigned int
+-Slot::getRSAKeySize(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(CKYByte keyNum)
+-{
+-    return calcECCKeySize(keyNum);
+-} 
+-
+-unsigned int
+-Slot::calcECCKeySize(CKYByte keyNum)
++Slot::getECCKeySize(PKCS11Object *key)
+ {
+     unsigned int keySize = CryptParams::ECC_DEFAULT_KEY_SIZE;
++    unsigned int objKeySize = 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;
++    objKeySize = key->getKeySize();
++    if (objKeySize != 0) {
++	return objKeySize;
+     }
+ 
+-    CKYBuffer const *eccParams = iter->getAttribute(CKA_EC_PARAMS);
++    CKYBuffer const *eccParams = key->getAttribute(CKA_EC_PARAMS);
+ 
+     if (eccParams == NULL) {
+         return keySize;
+@@ -4457,6 +4991,7 @@ Slot::calcECCKeySize(CKYByte keyNum)
+ 
+         if ( match == 1 ) {
+             keySize =  curveBytesNamePair[i].length;
++	    key->setKeySize(keySize);
+             return keySize;
+         }
+ 
+@@ -4476,10 +5011,9 @@ Slot::derive(SessionHandleSuffix suffix,
+     ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE);
+     SessionIter session = findSession(suffix);
+ 
+-    PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hBaseKey);
+-
+-    session->keyAgreementState.initialize(objectHandleToKeyNum(hBaseKey), keyType);
+-    deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey, params);
++    session->keyAgreementState.initialize(getKeyFromHandle(hBaseKey));
++    deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, 
++		phKey, params);
+ 
+ }
+ 
+@@ -4515,9 +5049,8 @@ void Slot::deriveECC(SessionHandleSuffix
+ 
+     CryptOpState& opState = params.getOpState(*session);
+     CKYBuffer *result = &opState.result;
+-    CKYByte keyNum = opState.keyNum;
+ 
+-    unsigned int keySize = getECCKeySize(keyNum);
++    unsigned int keySize = getECCKeySize(opState.key);
+ 
+     if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE)
+         params.setKeySize(keySize);
+@@ -4542,10 +5075,11 @@ void Slot::deriveECC(SessionHandleSuffix
+ 
+     if( CKYBuffer_Size(result) == 0 ) {
+         try {
+-            performECCKeyAgreement(deriveMech, &publicDataBuffer, &secretKeyBuffer,
+-			 keyNum, params.getKeySize());
++            performECCKeyAgreement(deriveMech, &publicDataBuffer, 
++			&secretKeyBuffer, opState.key, params.getKeySize());
+             CK_OBJECT_HANDLE keyObjectHandle = generateUnusedObjectHandle();
+-            secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, pTemplate, ulAttributeCount);
++            secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, 
++						pTemplate, ulAttributeCount);
+         } catch(PKCS11Exception& e) {
+             CKYBuffer_FreeData(&secretKeyBuffer);
+             CKYBuffer_FreeData(&publicDataBuffer);
+@@ -4557,15 +5091,15 @@ void Slot::deriveECC(SessionHandleSuffix
+    CKYBuffer_FreeData(&publicDataBuffer);
+ 
+    if ( secret ) {
+-
+        *phKey = secret->getHandle();
+         delete secret;
+    }
+ }
+ 
+ void
+-Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, CKYBuffer *publicDataBuffer, 
+-			     CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize)
++Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
++	CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, 
++	const PKCS11Object *key, unsigned int keySize)
+ {
+     if (!mECC) {
+        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+@@ -4575,25 +5109,26 @@ Slot::performECCKeyAgreement(CK_MECHANIS
+     CKYStatus status = trans.begin(conn);
+     if( status != CKYSUCCESS ) handleConnectionError();
+ 
+-    if (state & GOV_CARD) {
+-	selectCACApplet(keyNum, true);
+-    } else {
+-	selectApplet();
+-    }
+-
+     CKYISOStatus result;
+-    int loginAttempted = 0;
++    bool loginAttempted = false;
+ 
+ retry:
++    selectKey(key, loginAttempted);
+ 
+     if (state & PIV_CARD) {
+-        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, publicDataBuffer, 
+-			secretKeyBuffer, &result);
++        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, 
++			publicDataBuffer, secretKeyBuffer, &result);
+     } else if (state & CAC_CARD) {
+-        status = CACApplet_SignDecrypt(conn, publicDataBuffer, secretKeyBuffer, &result);
++        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, keyNum, 
+-			publicDataBuffer , NULL, secretKeyBuffer, getNonce(), &result);
++    	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) ||
+@@ -4610,13 +5145,10 @@ retry:
+             throw PKCS11Exception(CKR_DATA_INVALID);
+         }
+         if (!isVersion1Key && !loginAttempted  &&
+-            (result == CKYISO_UNAUTHORIZED)) {
++				userPinCache(key->getUser())->isValid() &&
++            				(result == CKYISO_UNAUTHORIZED)) {
+ 	    try {
+-		if (state & GOV_CARD) {
+-		    attemptCACLogin();
+-		} else {
+-		    oldAttemptLogin();
+-		}
++		attemptLogin(key->getUser(), true);
+ 	    } catch(PKCS11Exception& ) {
+               throw PKCS11Exception(CKR_DEVICE_ERROR);
+ 	    }
+diff -up ./src/coolkey/slot.h.p15 ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.p15	2015-07-06 10:27:55.772827229 -0700
++++ ./src/coolkey/slot.h	2015-07-06 10:27:55.786826965 -0700
+@@ -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,29 +209,26 @@ class CryptOpState {
+   public:
+     enum State { NOT_INITIALIZED, IN_PROCESS, FINALIZED };
+     State state;
+-    CKYByte keyNum;
+     CKYBuffer result;
+-    PKCS11Object::KeyType keyType;
++    PKCS11Object *key;
+ 
+-    CryptOpState() : state(NOT_INITIALIZED), keyNum(0), keyType(PKCS11Object::unknown)
++    CryptOpState() : state(NOT_INITIALIZED), key(NULL)
+ 				{ CKYBuffer_InitEmpty(&result); }
+     CryptOpState(const CryptOpState &cpy) : 
+-				state(cpy.state), keyNum(cpy.keyNum), keyType(cpy.keyType) { 
++				state(cpy.state), key(cpy.key) { 
+ 	CKYBuffer_InitFromCopy(&result, &cpy.result);
+     }
+     CryptOpState &operator=(const CryptOpState &cpy) {
+ 	state = cpy.state,
+-	keyNum = cpy.keyNum;
+-        keyType = cpy.keyType;
++        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, PKCS11Object::KeyType theKeyType) {
++    void initialize(PKCS11Object *theKey) {
+         state = IN_PROCESS;
+-        this->keyNum = keyNum;
+-        this->keyType = theKeyType;
++        this->key = theKey;
+         CKYBuffer_Resize(&result, 0);
+     }
+ };
+@@ -298,6 +295,7 @@ class CryptParams {
+ };
+ 
+ #define MAX_CERT_SLOTS 3
++#define MAX_AUTH_USERS 3
+ class Slot {
+ 
+   public:
+@@ -308,7 +306,8 @@ class Slot {
+         APPLET_SELECTABLE = 0x08,
+         APPLET_PERSONALIZED = 0x10,
+         CAC_CARD = 0x20,
+-        PIV_CARD = 0x40
++        PIV_CARD = 0x40,
++	P15_CARD = 0x80
+     };
+     enum {
+ 	NONCE_SIZE = 8
+@@ -321,6 +320,7 @@ class Slot {
+     char *readerName;
+     char *personName;
+     char *manufacturer;
++    char *tokenManufacturer;
+     //char *model;
+     CK_VERSION hwVersion;
+     CK_VERSION tokenFWVersion;
+@@ -329,6 +329,7 @@ class Slot {
+     CKYCardConnection* conn;
+     unsigned long state; // = UNKNOWN
+     PinCache pinCache;
++    PinCache contextPinCache;
+     bool loggedIn;
+     bool reverify;
+     bool nonceValid;
+@@ -349,6 +350,14 @@ class Slot {
+     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
+@@ -367,6 +376,7 @@ class Slot {
+ 
+     void closeAllSessions();
+     SessionHandleSuffix generateNewSession(Session::Type type);
++    PK15Object *auth[MAX_AUTH_USERS];
+ 
+     bool cardStateMayHaveChanged();
+     void connectToToken();
+@@ -418,48 +428,63 @@ class Slot {
+                               bool throwException);
+     CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize);
+ 
++    CKYStatus getP15Params();
+     void selectApplet();
+     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, unsigned int keySize,
+-						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, CKYByte keyNum);
++				unsigned int keySize, const PKCS11Object *key);
+     void performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
+-        CKYBuffer *publicDataBuffer, 
+-        CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize);
++        CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, 
++	const PKCS11Object *key, 	unsigned int keySize);
+ 
+     void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu);
+ 
+-    CKYByte objectHandleToKeyNum(CK_OBJECT_HANDLE hKey);
+-    unsigned int calcECCKeySize(CKYByte keyNum);
++    CKYByte objectToKeyNum(const PKCS11Object *key);
+     Slot(const Slot &cpy)
+ #ifdef USE_SHMEM
+ 	: shmem(readerName)
+@@ -491,10 +516,10 @@ class Slot {
+     }
+ 
+     // actually get the size of a key in bits from the card
+-    unsigned int getRSAKeySize(CKYByte keyNum);
+-    unsigned int getECCKeySize(CKYByte keyNum);
++    unsigned int getRSAKeySize(PKCS11Object *key);
++    unsigned int getECCKeySize(PKCS11Object *key);
+ 
+-    PKCS11Object::KeyType  getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey);
++    PKCS11Object *getKeyFromHandle(CK_OBJECT_HANDLE hKey);
+ 
+     SessionHandleSuffix openSession(Session::Type type);
+     void closeSession(SessionHandleSuffix handleSuffix);
+@@ -504,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);
+ 
+@@ -604,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);
+ 
+diff -up ./src/libckyapplet/cky_applet.c.p15 ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.p15	2015-07-06 10:27:55.775827172 -0700
++++ ./src/libckyapplet/cky_applet.c	2015-07-06 10:27:55.787826946 -0700
+@@ -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);
+@@ -269,17 +276,10 @@ PIVAppletFactory_SignDecrypt(CKYAPDU *ap
+ }
+ 
+ CKYStatus
+-CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
++P15AppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
+ {
+-    const char *pin=(const char *)param;
+-    return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin);
+-}
+-
+-CKYStatus
+-PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
+-{
+-    const char *pin=(const char *)param;
+-    return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin);
++    const P15AppletArgVerifyPIN *vps = (const P15AppletArgVerifyPIN *)param;
++    return P15APDUFactory_VerifyPIN(apdu, vps->pinRef, vps->pinVal);
+ }
+ 
+ CKYStatus
+@@ -323,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
+@@ -975,9 +1008,6 @@ CKYApplet_ComputeECCSignature(CKYCardCon
+     const CKYBuffer *data, CKYBuffer *sig,
+     CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC)
+ {
+-    int         use2APDUs = 0;
+-    int         use_dl_object =  0; 
+-    short       dataSize = 0;
+     CKYStatus ret = CKYAPDUFAIL;
+     CKYAppletArgComputeECCSignature ccd;
+     CKYBuffer    empty;
+@@ -1052,6 +1082,11 @@ 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
+  */
+@@ -1059,23 +1094,8 @@ CKYStatus
+ CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local,
+ 		    CKYISOStatus *apduRC)
+ {
+-    CKYStatus ret;
+-    CKYISOStatus status;
+-    if (apduRC == NULL) {
+-	apduRC = &status;
+-    }
+-
+-    ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN :
+-			    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);
+ }
+ 
+ 
+@@ -1165,6 +1185,214 @@ 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);
++    }
++    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)
+@@ -1632,6 +1860,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.p15 ./src/libckyapplet/cky_applet.h
+--- ./src/libckyapplet/cky_applet.h.p15	2015-07-06 10:27:55.776827153 -0700
++++ ./src/libckyapplet/cky_applet.h	2015-07-06 10:27:55.788826927 -0700
+@@ -204,6 +204,7 @@ typedef struct _CKYAppletArgReadObject {
+     CKYByte         size;
+ } CKYAppletArgReadObject;
+ 
++
+ typedef struct _CKYAppletArgWriteObject {
+     unsigned long objectID;
+     CKYOffset     offset;
+@@ -262,6 +263,39 @@ typedef struct _PIVAppletRespSignDecrypt
+      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*/
+@@ -514,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);
+@@ -539,6 +574,26 @@ CKYStatus PIVApplet_SignDecrypt(CKYCardC
+ 				   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:
+  *  
+diff -up ./src/libckyapplet/cky_base.c.p15 ./src/libckyapplet/cky_base.c
+--- ./src/libckyapplet/cky_base.c.p15	2015-07-06 10:27:55.776827153 -0700
++++ ./src/libckyapplet/cky_base.c	2015-07-06 10:27:55.788826927 -0700
+@@ -651,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
+@@ -685,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
+@@ -714,11 +731,100 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY
+ }
+ 
+ CKYStatus
++CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen)
++{
++    CKYStatus ret;
++
++    if (recvlen <= CKYAPDU_MAX_DATA_LEN) {
++	return APDU_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(const char *p)
+diff -up ./src/libckyapplet/cky_base.h.p15 ./src/libckyapplet/cky_base.h
+--- ./src/libckyapplet/cky_base.h.p15	2015-07-06 10:27:55.777827135 -0700
++++ ./src/libckyapplet/cky_base.h	2015-07-06 10:27:55.789826908 -0700
+@@ -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,7 +326,11 @@ 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(const char *name);
+diff -up ./src/libckyapplet/cky_card.c.p15 ./src/libckyapplet/cky_card.c
+--- ./src/libckyapplet/cky_card.c.p15	2015-07-06 10:27:55.778827116 -0700
++++ ./src/libckyapplet/cky_card.c	2015-07-06 10:27:55.790826889 -0700
+@@ -910,6 +910,7 @@ ckyCardConnection_init(CKYCardConnection
+     conn->protocol = SCARD_PROTOCOL_T0;
+ }
+ 
++
+ CKYCardConnection *
+ CKYCardConnection_Create(const CKYCardContext *ctx)
+ {
+@@ -984,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)
+ {
+@@ -996,6 +1003,7 @@ ckyCardConnection_reconnectRaw(CKYCardCo
+ 	conn->lastError = rv;
+ 	return CKYSCARDERR;
+     }
++    conn->protocol = protocol;
+     return CKYSUCCESS;
+ }
+ 
+diff -up ./src/libckyapplet/cky_card.h.p15 ./src/libckyapplet/cky_card.h
+--- ./src/libckyapplet/cky_card.h.p15	2015-07-06 10:27:55.766827342 -0700
++++ ./src/libckyapplet/cky_card.h	2015-07-06 10:27:55.790826889 -0700
+@@ -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.p15 ./src/libckyapplet/cky_factory.c
+--- ./src/libckyapplet/cky_factory.c.p15	2015-07-06 10:27:55.778827116 -0700
++++ ./src/libckyapplet/cky_factory.c	2015-07-06 10:27:55.791826870 -0700
+@@ -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,7 +57,7 @@ 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);
+@@ -707,6 +707,7 @@ fail:
+     CKYBuffer_FreeData(&buf);
+     return ret;
+ }
++
+ CKYStatus
+ CACAPDUFactory_GetProperties(CKYAPDU *apdu)
+ {
+@@ -718,37 +719,6 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap
+ }
+ 
+ CKYStatus
+-CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const char *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, keyRef);
+-    /* no pin, send an empty buffer */
+-    if (!pin) {
+-    	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);
+-    }
+-    return ret;
+-
+-}
+-
+-CKYStatus
+ PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, 
+ 			   CKYByte key, int len, const CKYBuffer *data)
+ {
+@@ -807,3 +777,109 @@ fail:
+     return ret;
+ }
+ 
++CKYStatus
++P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const CKYBuffer *pin)
++{
++    CKYStatus ret;
++
++    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
++    CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN);
++    CKYAPDU_SetP1(apdu, 0x00);
++    CKYAPDU_SetP2(apdu, keyRef);
++    /* no pin, send an empty buffer */
++    if (CKYBuffer_Size(pin) == 0) {
++    	return CKYAPDU_SetReceiveLen(apdu, 0);
++    }
++
++    /* all CAC pins are 8 bytes exactly. If to long, truncate it */
++    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.p15 ./src/libckyapplet/cky_factory.h
+--- ./src/libckyapplet/cky_factory.h.p15	2015-07-06 10:27:55.779827097 -0700
++++ ./src/libckyapplet/cky_factory.h	2015-07-06 10:27:55.791826870 -0700
+@@ -35,8 +35,31 @@
+  * 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
+@@ -84,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
+@@ -94,11 +118,8 @@
+ #define CAC_SIZE_GET_PROPERTIES	48
+ #define CAC_P1_STEP		0x80
+ #define CAC_P1_FINAL		0x00
+-#define CAC_LOGIN_GLOBAL	0x00
+ 
+ /* PIV */
+-#define PIV_LOGIN_LOCAL		0x80
+-#define PIV_LOGIN_GLOBAL	CAC_LOGIN_GLOBAL
+ #define PIV_INS_GEN_AUTHENTICATE 0x87
+ 
+ /*
+@@ -121,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 */
+@@ -173,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.
+@@ -234,17 +267,28 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C
+ 
+ CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, 
+ 				     const CKYBuffer *data);
+-CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef,
+-				   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 */
diff --git a/SOURCES/coolkey-1.1.0-rhel7-alt-cac.patch b/SOURCES/coolkey-1.1.0-rhel7-alt-cac.patch
new file mode 100644
index 0000000..f352f42
--- /dev/null
+++ b/SOURCES/coolkey-1.1.0-rhel7-alt-cac.patch
@@ -0,0 +1,858 @@
+diff -up ./src/coolkey/coolkey.cpp.alt-cac ./src/coolkey/coolkey.cpp
+--- ./src/coolkey/coolkey.cpp.alt-cac	2016-12-01 15:37:49.106167768 -0800
++++ ./src/coolkey/coolkey.cpp	2016-12-01 15:37:49.113167892 -0800
+@@ -80,9 +80,16 @@ ecMechanismList[] = {
+     {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}},
+     {CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} }
+ };
++static const MechInfo
++allMechanismList[] = {
++    {CKM_RSA_PKCS, { 1024, 4096, CKF_HW | CKF_SIGN | CKF_DECRYPT } },
++    {CKM_ECDSA,{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);
++unsigned int numAllMechanisms = sizeof(allMechanismList)/sizeof(MechInfo);
+ 
+ /* ------------------------------------------------------------ */
+ 
+@@ -382,13 +389,22 @@ C_GetMechanismList(CK_SLOT_ID slotID, CK
+             return CKR_TOKEN_NOT_PRESENT;
+         }
+ 
+-        if ( slot->getIsECC()) {
++	switch (slot->getAlgs()) {
++	case ALG_ECC|ALG_RSA:
++            mechanismList = allMechanismList;
++            numMechanisms = numAllMechanisms;
++	    break;
++	case ALG_ECC:
+             mechanismList = ecMechanismList;
+             numMechanisms = numECMechanisms;
+-        } else {
++	    break;
++	case ALG_NONE:
++	case ALG_RSA:
++	default:
+             mechanismList = rsaMechanismList;
+             numMechanisms = numRSAMechanisms;
+-        }
++	    break;
++	}
+   
+         if( pMechanismList != NULL ) {
+             if( *pulCount < numMechanisms ) {
+@@ -438,13 +454,22 @@ C_GetMechanismInfo(CK_SLOT_ID slotID, CK
+             return CKR_TOKEN_NOT_PRESENT;
+         }
+ 
+-        if ( slot->getIsECC()) {
++	switch (slot->getAlgs()) {
++	case ALG_ECC|ALG_RSA:
++            mechanismList = allMechanismList;
++            numMechanisms = numAllMechanisms;
++	    break;
++	case ALG_ECC:
+             mechanismList = ecMechanismList;
+             numMechanisms = numECMechanisms;
+-        } else {
++	    break;
++	case ALG_NONE:
++	case ALG_RSA:
++	default:
+             mechanismList = rsaMechanismList;
+             numMechanisms = numRSAMechanisms;
+-        }
++	    break;
++	}
+ 
+         for(unsigned int i=0; i < numMechanisms; ++i ) {
+             if( mechanismList[i].mech == type ) {
+diff -up ./src/coolkey/object.cpp.alt-cac ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.alt-cac	2016-12-01 15:37:49.097167608 -0800
++++ ./src/coolkey/object.cpp	2016-12-01 15:37:49.114167910 -0800
+@@ -1232,7 +1232,7 @@ Reader::Reader(unsigned long muscleObjID
+ }
+ 
+ 
+-CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert) : 
++CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert,bool isPIV) : 
+         PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16,
+                          instance | 0x400)
+ {
+@@ -1242,7 +1242,9 @@ CACPrivKey::CACPrivKey(CKYByte instance,
+ 
+     /* So we know what the key is supposed to be used for based on
+      * the instance */
+-    if (instance == 2) {
++    /* instance 2 is usually a decryption cert. >2 are usually old decryption 
++     * certs */
++    if (instance == 2 || (instance > (isPIV ? 3 : 2))) {
+         decrypt = TRUE;
+     }
+ 
+@@ -1305,8 +1307,8 @@ CACPrivKey::CACPrivKey(CKYByte instance,
+      CKYBuffer_FreeData(&param2);
+ }
+ 
+-CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert) : 
+-        PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16,
++CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert, bool isPIV) : 
++        PKCS11Object( ((int)'k') << 24 | ((int)(instance+'a')) << 16,
+                        instance | 0x500)
+ {
+     CKYBuffer id;
+@@ -1315,7 +1317,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) {
++    if (instance == 2 || (instance > (isPIV ? 3 : 2))) {
+         encrypt = TRUE;
+     }
+ 
+@@ -1359,6 +1361,9 @@ CACPubKey::CACPubKey(CKYByte instance, c
+             setAttribute(CKA_EC_POINT, &param1);
+             setAttribute(CKA_EC_PARAMS, &param2);
+ 	    setAttributeULong(CKA_KEY_TYPE, CKK_EC);
++    	    setAttributeBool(CKA_VERIFY_RECOVER, FALSE);
++    	    setAttributeBool(CKA_ENCRYPT, FALSE);
++    	    setAttributeBool(CKA_DERIVE, encrypt);
+             break;
+         default:
+             break;
+@@ -1376,6 +1381,26 @@ static const char *CAC_Label[] = {
+         "CAC ID Certificate",
+         "CAC Email Signature Certificate",
+         "CAC Email Encryption Certificate",
++        "CAC Cert 3",
++        "CAC Cert 4",
++        "CAC Cert 5",
++        "CAC Cert 6",
++        "CAC Cert 7",
++        "CAC Cert 8",
++        "CAC Cert 9",
++};
++
++static const char *PIV_Label[] = {
++        "PIV ID Certificate",
++        "PIV Email Signature Certificate",
++        "PIV Email Encryption Certificate",
++        "PIV Card Authentication Certificate",
++        "PIV Cert 4",
++        "PIV Cert 5",
++        "PIV Cert 6",
++        "PIV Cert 7",
++        "PIV Cert 8",
++        "PIV Cert 9",
+ };
+ 
+ static const unsigned char CN_DATA[] = { 0x55, 0x4, 0x3 };
+@@ -1454,7 +1479,7 @@ GetUserName(const CKYBuffer *dn)
+     return string;
+ }
+ 
+-CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert) : 
++CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert, bool isPIV) : 
+         PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, 
+                         instance | 0x600)
+ {
+@@ -1471,7 +1496,7 @@ CACCert::CACCert(CKYByte instance, const
+     setAttribute(CKA_ID, &id);
+     CKYBuffer_FreeData(&id);
+     setAttributeULong(CKA_CERTIFICATE_TYPE, CKC_X_509);
+-    setAttribute(CKA_LABEL, CAC_Label[instance]);
++    setAttribute(CKA_LABEL, isPIV ? PIV_Label[instance] : CAC_Label[instance]);
+ 
+     CKYBuffer derSerial; CKYBuffer_InitEmpty(&derSerial);
+     CKYBuffer derSubject; CKYBuffer_InitEmpty(&derSubject);
+diff -up ./src/coolkey/object.h.alt-cac ./src/coolkey/object.h
+--- ./src/coolkey/object.h.alt-cac	2016-12-01 15:37:49.087167430 -0800
++++ ./src/coolkey/object.h	2016-12-01 15:37:49.115167928 -0800
+@@ -219,17 +219,17 @@ class Cert : public PKCS11Object {
+ 
+ class CACPrivKey : public PKCS11Object {
+   public:
+-    CACPrivKey(CKYByte instance, const PKCS11Object &cert);
++    CACPrivKey(CKYByte instance, const PKCS11Object &cert, bool isPIV);
+ };
+ 
+ class CACPubKey : public PKCS11Object {
+   public:
+-    CACPubKey(CKYByte instance, const PKCS11Object &cert);
++    CACPubKey(CKYByte instance, const PKCS11Object &cert, bool isPIV);
+ };
+ 
+ class CACCert : public PKCS11Object {
+   public:
+-    CACCert(CKYByte instance, const CKYBuffer *derCert);
++    CACCert(CKYByte instance, const CKYBuffer *derCert, bool isPIV);
+ };
+ 
+ typedef enum { PK15StateInit, PK15StateNeedObject, 
+diff -up ./src/coolkey/slot.cpp.alt-cac ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.alt-cac	2016-12-01 15:37:49.110167839 -0800
++++ ./src/coolkey/slot.cpp	2016-12-01 15:57:37.307994776 -0800
+@@ -415,8 +415,9 @@ Slot::Slot(const char *readerName_, Log
+ 	slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), 
+ 	isVersion1Key(false), needLogin(false), fullTokenName(false), 
+ 	mCoolkey(false), mOldCAC(false), mCACLocalLogin(false),
+-	pivContainer(-1), pivKey(-1), mECC(false), p15aid(0), p15odfAddr(0),
+-	p15tokenInfoAddr(0), p15Instance(0),
++	pivContainer(-1), pivKey(-1), maxCacCerts(MAX_CERT_SLOTS), 
++	algs(ALG_NONE), p15aid(0), p15odfAddr(0), p15tokenInfoAddr(0),
++	p15Instance(0),
+ #ifdef USE_SHMEM
+ 	shmem(readerName_),
+ #endif
+@@ -776,6 +777,7 @@ Slot::connectToToken()
+ 	 state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+ 	 isVersion1Key = 0;
+ 	 needLogin = true;
++	 maxCacCerts = MAX_CERT_SLOTS;
+          mCoolkey = 0;
+ 	 mOldCAC = 0;
+ 	 mCACLocalLogin = getPIVLoginType();
+@@ -927,8 +929,12 @@ Slot::getCACAid()
+ 	}
+ 	/* yes, fill in the old applets */
+ 	mOldCAC = true;
++	maxCacCerts = 1;
+ 	for (i=1; i< MAX_CERT_SLOTS; i++) {
+-	    CACApplet_SelectPKI(conn, &cardAID[i], i, NULL);
++	    status = CACApplet_SelectPKI(conn, &cardAID[i], i, NULL);
++	    if (status == CKYSUCCESS) {
++		maxCacCerts = i+1;
++	    }
+ 	}
+ 	return CKYSUCCESS;
+     }
+@@ -986,6 +992,7 @@ Slot::getCACAid()
+     if (certSlot == 0) {
+ 	status = CKYAPDUFAIL; /* probably neeed a beter error code */
+     }
++    maxCacCerts = certSlot;
+ 
+ done:
+     CKYBuffer_FreeData(&tBuf);
+@@ -2168,12 +2175,11 @@ Slot::addKeyObject(list<PKCS11Object>& o
+         }
+         keyObj.completeKey(*iter);
+ 
+-        /* For now this is how we determine what type of key.
+-           Also for now, allow only one or the other */
++        /*  use key object to determine what algorithms we support */
+         if ( keyObj.getKeyType() == PKCS11Object::ecc) {
+-            mECC = true;
++            algs = (SlotAlgs) (algs | ALG_ECC);
+         } else {
+-            mECC = false;
++            algs = (SlotAlgs) (algs | ALG_RSA);
+         }
+        
+     }
+@@ -2205,7 +2211,7 @@ Slot::addCertObject(list<PKCS11Object>&
+ void
+ Slot::unloadObjects()
+ {
+-    mECC = false;
++    algs = ALG_NONE;
+     tokenObjects.clear();
+     free(personName);
+     personName = NULL;
+@@ -2269,29 +2275,42 @@ Slot::unloadObjects()
+ // Shared memory segments are fixed size (equal to the object memory size of
+ // the token). 
+ //
++//
++//
++
++struct SlotDataPair {
++    unsigned long dataOffset;
++    unsigned long  dataSize;
++};
+ 
+ struct SlotSegmentHeader {
+     unsigned short version;
+     unsigned short headerSize;
+     unsigned char  valid;
+-    unsigned char  reserved;
++    unsigned char  firstCacCert;
+     unsigned char  cuid[10];
+-    unsigned short reserved2;
++
++    unsigned short reserved; 
+     unsigned short dataVersion;
+     unsigned short dataHeaderOffset;
+     unsigned short dataOffset;
+     unsigned long  dataHeaderSize;
+     unsigned long  dataSize;
+-    unsigned long  cert2Offset;
+-    unsigned long  cert2Size;
++    unsigned long  nextDataOffset;
++    SlotDataPair cacCerts[MAX_CERT_SLOTS];
+ };
+ 
++const unsigned char NOT_A_CAC=0xff; /* place in firstCacCert field */
++const unsigned short CAC_DATA_VERSION=2;
++
++
+ #define MAX_OBJECT_STORE_SIZE 15000
+ //
+ // previous development versions used a segment prefix of
+ // "coolkeypk11s"
+ //
+-#define SEGMENT_PREFIX "coolkeypk11s"
++#define SEGMENT_PREFIX "coolkeypk11t" // update segment since the old cache was
++                                      // incompatible
+ #define CAC_FAKE_CUID "CAC Certs"
+ SlotMemSegment::SlotMemSegment(const char *readerName): 
+ 	segmentAddr(NULL),  segmentSize(0), segment(NULL)
+@@ -2320,9 +2339,8 @@ SlotMemSegment::SlotMemSegment(const cha
+ 	return;
+     }
+ 
+-    SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
+     if (needInit) {
+-	segmentHeader->valid = 0;
++	clearValid(0);
+     }
+     segmentSize = segment->getSHMemSize();
+ }
+@@ -2396,6 +2414,18 @@ SlotMemSegment::getDataVersion() const
+     return segmentHeader->dataVersion;
+ }
+ 
++unsigned char
++SlotMemSegment::getFirstCacCert() const
++{
++    if (!segment) {
++	return NOT_A_CAC;
++    }
++
++    SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
++
++    return segmentHeader->firstCacCert;
++}
++
+ void
+ SlotMemSegment::setVersion(unsigned short version)
+ {
+@@ -2419,6 +2449,18 @@ SlotMemSegment::setDataVersion(unsigned
+     segmentHeader->dataVersion = version;
+ }
+ 
++void
++SlotMemSegment::setFirstCacCert(unsigned char firstCacCert)
++{
++    if (!segment) {
++	return;
++    }
++
++    SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
++
++    segmentHeader->firstCacCert = firstCacCert;
++}
++
+ bool
+ SlotMemSegment::isValid() const
+ {
+@@ -2493,23 +2535,13 @@ SlotMemSegment::readCACCert(CKYBuffer *o
+     int size;
+     CKYByte *data;
+ 
+-    switch (instance) {
+-    case 0:
+-	data  = (CKYByte *) &segmentAddr[segmentHeader->dataHeaderOffset];
+-	size = segmentHeader->dataHeaderSize;
+-	break;
+-    case 1:
+-	data  = (CKYByte *) &segmentAddr[segmentHeader->dataOffset];
+-	size = segmentHeader->dataSize;
+-	break;
+-    case 2:
+-	data  = (CKYByte *) &segmentAddr[segmentHeader->cert2Offset];
+-	size = segmentHeader->cert2Size;
+-	break;
+-    default:
++    if (instance >= MAX_CERT_SLOTS) {
+ 	CKYBuffer_Resize(objData, 0);
+ 	return;
+     }
++    data = (CKYByte *) &segmentAddr[segmentHeader->cacCerts[instance]
++								.dataOffset];
++    size = segmentHeader->cacCerts[instance].dataSize;
+     CKYBuffer_Replace(objData, 0, data, size);
+ }
+ 
+@@ -2523,30 +2555,20 @@ SlotMemSegment::writeCACCert(const CKYBu
+     SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
+     int size = CKYBuffer_Size(data);
+     CKYByte *shmData;
+-    switch (instance) {
+-    case 0:
+-	segmentHeader->headerSize = sizeof *segmentHeader;
+-	segmentHeader->dataHeaderOffset = sizeof *segmentHeader;
+-	segmentHeader->dataHeaderSize = size;
+-	segmentHeader->dataOffset = segmentHeader->dataHeaderOffset + size;
+-	segmentHeader->dataSize = 0;
+-	segmentHeader->cert2Offset = segmentHeader->dataOffset;
+-	segmentHeader->cert2Size = 0;
+-	shmData = (CKYByte *) &segmentAddr[segmentHeader->dataHeaderOffset];
+-	break;
+-    case 1:
+-	segmentHeader->dataSize = size;
+-	segmentHeader->cert2Offset = segmentHeader->dataOffset + size;
+-	segmentHeader->cert2Size = 0;
+-	shmData = (CKYByte *) &segmentAddr[segmentHeader->dataOffset];
+-	break;
+-    case 2:
+-	segmentHeader->cert2Size = size;
+-	shmData = (CKYByte *) &segmentAddr[segmentHeader->cert2Offset];
+-	break;
+-    default:
++
++    if (instance >= MAX_CERT_SLOTS) {
+ 	return;
+     }
++
++    if (segmentHeader->firstCacCert == NOT_A_CAC) {
++	segmentHeader->firstCacCert = instance;
++    }
++    unsigned long dataOffset = segmentHeader->nextDataOffset;
++    segmentHeader->cacCerts[instance].dataOffset = dataOffset;
++    segmentHeader->nextDataOffset += size;
++    segmentHeader->cacCerts[instance].dataSize = size;
++    shmData = (CKYByte *) &segmentAddr[dataOffset];
++
+     memcpy(shmData, CKYBuffer_Data(data), size);
+ }
+ 
+@@ -2558,15 +2580,18 @@ SlotMemSegment::clearValid(CKYByte insta
+ 	return;
+     }
+     SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
+-    switch (instance) {
+-    case 0:
+-	segmentHeader->headerSize = 0;
+-	segmentHeader->dataHeaderSize = 0;
+-	/* fall through */
+-    case 1:
+-	segmentHeader->dataSize = 0;
++
++    segmentHeader->headerSize = sizeof *segmentHeader;
++    segmentHeader->dataHeaderOffset = sizeof *segmentHeader;
++    segmentHeader->dataHeaderSize = 0;
++    segmentHeader->dataSize = 0;
++    for (int i=0; i < MAX_CERT_SLOTS; i++) {
++	segmentHeader->cacCerts[i].dataSize = 0;
+     }
++    segmentHeader->dataOffset = sizeof *segmentHeader;
++    segmentHeader->nextDataOffset = sizeof *segmentHeader;
+     segmentHeader->valid = 0;
++    segmentHeader->firstCacCert = NOT_A_CAC;
+ }
+ 
+ void
+@@ -2882,8 +2907,7 @@ berProcess(CKYBuffer *buf, int matchTag,
+ 
+ 
+ CKYStatus
+-Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, 
+-			      bool throwException)
++Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize)
+ {
+     CKYStatus status;
+     CKYISOStatus apduRC;
+@@ -2897,9 +2921,6 @@ Slot::readCACCertificateFirst(CKYBuffer
+ 	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) {
+@@ -2940,10 +2961,10 @@ Slot::readCACCertificateFirst(CKYBuffer
+     if (mOldCAC) {
+ 	/* get the first 100 bytes of the cert */
+ 	status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC);
+-	if (throwException && (status != CKYSUCCESS)) {
+-	    handleConnectionError();
++	if (status == CKYSUCCESS) {
++	    return status;
+ 	}
+-	return status;
++	/* try to use CACApplet_ReadFile before we give up */
+     }
+ 
+     CKYBuffer tBuf;
+@@ -2959,11 +2980,11 @@ Slot::readCACCertificateFirst(CKYBuffer
+ 
+     /* handle the new CAC card read */
+     /* read the TLV */
+-    status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, NULL);
++    status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, &apduRC);
+     if (status != CKYSUCCESS) {
+ 	goto done;
+     }
+-    status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, NULL);
++    status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, &apduRC);
+     if (status != CKYSUCCESS) {
+ 	goto done;
+     }
+@@ -3199,14 +3220,12 @@ Slot::loadCACCert(CKYByte instance)
+     CKYStatus status = CKYSUCCESS;
+     CKYBuffer cert;
+     CKYBuffer rawCert;
+-    CKYBuffer shmCert;
+     CKYSize  nextSize;
+ 
+     OSTime time = OSTimeNow();
+ 
+     CKYBuffer_InitEmpty(&cert);
+     CKYBuffer_InitEmpty(&rawCert);
+-    CKYBuffer_InitEmpty(&shmCert);
+ 
+     //
+     // not all CAC cards have all the PKI instances
+@@ -3215,78 +3234,24 @@ Slot::loadCACCert(CKYByte instance)
+     try {
+         selectCACApplet(instance, false);
+     } catch(PKCS11Exception& e) {
+-	// all CAC's must have instance '0', throw the error it
+-	// they don't.
+-	if (instance == 0) throw e;
+-	// If the CAC doesn't have instance '2', and we were updating
+-	// the shared memory, set it to valid now.
+-	if ((instance == 2) && !shmem.isValid()) {
+-	    shmem.setValid();
+-	}
+ 	return;
+     }
+ 
+     log->log("CAC Cert %d: select CAC applet:  %d ms\n",
+ 						 instance, OSTimeNow() - time);
+ 
+-    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);
+-    }
+-
+-    unsigned short dataVersion = 1;
+-    CKYBool needRead = 1;
+-
+     /* see if it matches the shared memory */
+-    if (shmem.isValid() &&  shmem.getDataVersion() == dataVersion) {
+-	shmem.readCACCert(&shmCert, instance);
+-	CKYSize certSize = CKYBuffer_Size(&rawCert);
+-	CKYSize shmCertSize = CKYBuffer_Size(&shmCert);
+-	const CKYByte *shmData = CKYBuffer_Data(&shmCert);
+-
+-	if (instance != 0) {
+-	    needRead = 0;
+-	}
+-
+-	if (shmCertSize >= certSize) {
+-	    if (memcmp(shmData, CKYBuffer_Data(&rawCert), certSize) == 0) {
+-		/* yes it does, no need to read the rest of the cert, use
+-		 * the cache */
+-		CKYBuffer_Replace(&rawCert, 0, shmData, shmCertSize);
+-		needRead = 0;
+-	    }
+-	}
+-	if (!needRead && (shmCertSize == 0)) {	
++    if (shmem.isValid() &&  shmem.getDataVersion() == CAC_DATA_VERSION) {
++	shmem.readCACCert(&rawCert, instance);
++	if (CKYBuffer_Size(&rawCert) == 0) {
+ 	    /* no cert of this type, just return */
+ 	    return;
+ 	}
+-    }
+-    CKYBuffer_FreeData(&shmCert);
+-
+-    if (needRead) {
+-	/* it doesn't, read the new cert and update the cache */
+-	if (instance == 0) {
+-	    shmem.clearValid(0);
+-	    shmem.setVersion(SHMEM_VERSION);
+-	    shmem.setDataVersion(dataVersion);
+-	} else {
+-	    status = readCACCertificateFirst(&rawCert, &nextSize, false);
+-	
+-	    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) {
+-		    /* we've attempted to read all the certs, shared memory
+-		     * is now valid */
+-		    shmem.setValid();
+-		}
+-		return;
+-	    }
++    } else {
++	status = readCACCertificateFirst(&rawCert, &nextSize);
++	if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) {
++	    /*this cert doesn't exist, go to the next one */
++	    return;
+ 	}
+ 
+ 	if (nextSize) {
+@@ -3298,9 +3263,6 @@ Slot::loadCACCert(CKYByte instance)
+ 	    handleConnectionError();
+ 	}
+ 	shmem.writeCACCert(&rawCert, instance);
+-	if (instance == 2) {
+-	    shmem.setValid();
+-	}
+     }
+ 
+ 
+@@ -3368,14 +3330,17 @@ Slot::loadCACCert(CKYByte instance)
+     log->log("CAC Cert %d: Cert has been uncompressed:  %d ms\n",
+ 						instance, OSTimeNow() - time);
+ 
+-    CACCert certObj(instance, &cert);
+-    CACPrivKey privKey(instance, certObj);
+-    CACPubKey pubKey(instance, certObj);
++    bool isPIV = (bool)((state & PIV_CARD) == PIV_CARD);
++    CACCert certObj(instance, &cert, isPIV);
++    CACPrivKey privKey(instance, certObj, isPIV);
++    CACPubKey pubKey(instance, certObj, isPIV);
+     tokenObjects.push_back(privKey);
+     tokenObjects.push_back(pubKey);
+     tokenObjects.push_back(certObj);
+     if ( pubKey.getKeyType() == PKCS11Object::ecc) {
+-	mECC = 1;
++        algs = (SlotAlgs) (algs | ALG_ECC);
++    } else {
++        algs = (SlotAlgs) (algs | ALG_RSA);
+     }
+ 
+     if (personName == NULL) {
+@@ -3388,6 +3353,94 @@ Slot::loadCACCert(CKYByte instance)
+ }
+ 
+ void
++Slot::initCACShMem(void)
++{
++    bool failed = false;
++
++    unsigned char firstCert = shmem.getFirstCacCert();
++
++    log->log("init CACShMem: \n");
++    /* check to make sure the shared memory is initialized with a CAC card */
++    if (shmem.isValid() && shmem.getDataVersion() ==  CAC_DATA_VERSION
++				&& firstCert != NOT_A_CAC) {
++	CKYBuffer rawCert;
++	CKYBuffer shmCert;
++	CKYSize  nextSize;
++
++        log->log("init CACShMem: valid CAC cache found firstCert = %d\n",
++						 firstCert);
++	CKYBuffer_InitEmpty(&rawCert);
++	CKYBuffer_InitEmpty(&shmCert);
++
++
++	/* yes, see if it's this cac card by comparing the first cert 
++	 * in the chain */
++
++	/* see if the first cert is in the expected slot */
++	try {
++	    selectCACApplet(firstCert, false);
++ 	} catch(PKCS11Exception& e) {
++	    failed = true;
++            log->log("init CACShMem: applet select failed firstCert = %d\n",
++							firstCert);
++	}
++	if (!failed) {
++	    CKYStatus status = readCACCertificateFirst(&rawCert, &nextSize);
++	    if ((status != CKYSUCCESS) || CKYBuffer_Size(&rawCert) <= 1) {
++		failed = true;
++                log->log("init CACShMem: read Cert failed firstCert = %d\n",
++			 				firstCert);
++	    }
++	}
++	if (!failed) {
++	    shmem.readCACCert(&shmCert, firstCert);
++	    CKYSize certSize = CKYBuffer_Size(&rawCert);
++	    CKYSize shmCertSize = CKYBuffer_Size(&shmCert);
++	    const CKYByte *shmData = CKYBuffer_Data(&shmCert);
++
++	    if (shmCertSize >= certSize) {
++		if (memcmp(shmData, CKYBuffer_Data(&rawCert), certSize) == 0) {
++		    /* this card is cached, go on and use the cache */
++            	    log->log("init CACShMem: entries match, using cache\n");
++		    CKYBuffer_FreeData(&rawCert);
++		    CKYBuffer_FreeData(&shmCert);
++		    return;
++		}
++            }		
++            log->log("init CACShMem: no entry match certSize=%d"
++				" shmCertSize=%d\n",certSize, shmCertSize);
++	}
++	CKYBuffer_FreeData(&rawCert);
++	CKYBuffer_FreeData(&shmCert);
++    }
++
++    log->log("init CACShMem: starting new cache valid=%d version=%d "
++		" firstCert=%d\n",shmem.isValid(), shmem.getDataVersion(), 
++				firstCert);
++    /* cache is either invalid or for another card, start initializing it */
++    shmem.clearValid(0);
++    shmem.setVersion(SHMEM_VERSION);
++    shmem.setDataVersion(CAC_DATA_VERSION);
++}
++
++void
++Slot::verifyCACShMem(void)
++{
++    /* if the memory is valid, then nothing to do */
++    if (shmem.isValid()) {
++	return;
++    }
++    /* if we didn't find any cert fail */
++    if (shmem.getFirstCacCert() == NOT_A_CAC) {
++	shmem.clearValid(0);
++	disconnect();
++        throw PKCS11Exception(CKR_DEVICE_REMOVED);
++    }
++    /* we're all set, let others see our results */
++    shmem.setValid(); 
++}
++
++void
+ Slot::loadObjects()
+ {
+     // throw away all token objects!
+@@ -3406,9 +3459,11 @@ Slot::loadObjects()
+     std::list<ListObjectInfo>::iterator iter;
+ 
+     if (state & GOV_CARD) {
+-	loadCACCert(0);
+-	loadCACCert(1);
+-	loadCACCert(2);
++	initCACShMem();
++	for (int i=0; i < maxCacCerts; i++) {
++	   loadCACCert(i);
++	}
++	verifyCACShMem();
+ 	status = trans.end();
+ 	loadReaderObject();
+ 	return;
+@@ -4720,10 +4775,6 @@ Slot::performECCSignature(CKYBuffer *out
+     CKYStatus status = trans.begin(conn);
+     if( status != CKYSUCCESS ) handleConnectionError();
+ 
+-    if (!mECC) {
+-        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+-    }
+-
+     CKYISOStatus result;
+     bool loginAttempted = false;
+ 
+@@ -4790,9 +4841,6 @@ Slot::performRSAOp(CKYBuffer *output, co
+ 		unsigned int keySize, const PKCS11Object *key, 
+ 		CKYByte direction)
+ {
+-    if ( mECC ) {
+-        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
+-    }
+ 
+     //
+     // establish a transaction
+@@ -5145,10 +5193,6 @@ Slot::performECCKeyAgreement(CK_MECHANIS
+ 	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();
+diff -up ./src/coolkey/slot.h.alt-cac ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.alt-cac	2016-12-01 15:37:49.104167732 -0800
++++ ./src/coolkey/slot.h	2016-12-01 15:37:49.117167964 -0800
+@@ -79,9 +79,11 @@ public:
+     bool CUIDIsEqual(const CKYBuffer *cuid) const;
+     unsigned short getVersion() const;
+     unsigned short getDataVersion() const;
++    unsigned char  getFirstCacCert() const;
+     void setCUID(const CKYBuffer *cuid);
+     void setVersion(unsigned short version);
+     void setDataVersion(unsigned short version);
++    void setFirstCacCert(unsigned char firstCacCert);
+     bool isValid() const;
+     int size() const;
+     const unsigned char *getCUID() const;
+@@ -90,6 +92,7 @@ public:
+     void setSize(int size);
+     void readData(CKYBuffer *data) const;
+     void writeData(const CKYBuffer *data);
++    void initCACHeaders(void);
+     void readCACCert(CKYBuffer *data, CKYByte instance) const;
+     void writeCACCert(const CKYBuffer *data, CKYByte instance);
+     void clearValid(CKYByte instance);
+@@ -294,7 +297,13 @@ class CryptParams {
+ 				 const CKYBuffer *paddedOutput) const = 0;
+ };
+ 
+-#define MAX_CERT_SLOTS 3
++#define MAX_CERT_SLOTS 10
++typedef enum {
++	ALG_NONE= 0x0,
++	ALG_ECC = 0x1,
++	ALG_RSA = 0x2
++} SlotAlgs;
++
+ #define MAX_AUTH_USERS 3
+ class Slot {
+ 
+@@ -349,7 +358,8 @@ class Slot {
+     bool mCACLocalLogin;
+     int pivContainer;
+     int pivKey;
+-    bool mECC;
++    int maxCacCerts;
++    SlotAlgs algs;
+     unsigned short p15aid;
+     unsigned short p15odfAddr;
+     unsigned short p15tokenInfoAddr;
+@@ -424,8 +434,7 @@ class Slot {
+     list<ListObjectInfo> fetchSeparateObjects();
+ 
+     CKYStatus getCACAid();
+-    CKYStatus readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize,
+-                              bool throwException);
++    CKYStatus readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize);
+     CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize);
+ 
+     CKYStatus getP15Params();
+@@ -485,6 +494,8 @@ class Slot {
+     void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu);
+ 
+     CKYByte objectToKeyNum(const PKCS11Object *key);
++    void initCACShMem(void);
++    void verifyCACShMem(void);
+     Slot(const Slot &cpy)
+ #ifdef USE_SHMEM
+ 	: shmem(readerName)
+@@ -580,7 +591,7 @@ class Slot {
+        CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, 
+        CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params);
+ 
+-    bool getIsECC() { return mECC; }
++    SlotAlgs getAlgs() { return algs; }
+ };
+ 
+ class SlotList {
diff --git a/SOURCES/coolkey-cac-1.patch b/SOURCES/coolkey-cac-1.patch
new file mode 100644
index 0000000..ea70187
--- /dev/null
+++ b/SOURCES/coolkey-cac-1.patch
@@ -0,0 +1,28 @@
+diff -up ./src/coolkey/object.cpp.cac-1 ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.cac-1	2010-06-23 04:46:35.726198827 -0700
++++ ./src/coolkey/object.cpp	2010-06-23 04:47:28.073827862 -0700
+@@ -505,6 +505,10 @@ dataStart(const CKYByte *buf, unsigned i
+     unsigned char tag;
+     unsigned int used_length= 0;
+ 
++    if(!buf) {
++        return NULL;
++    }
++
+     tag = buf[used_length++];
+ 
+     /* blow out when we come to the end */
+diff -up ./src/coolkey/slot.cpp.cac-1 ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.cac-1	2010-06-23 04:46:22.718371631 -0700
++++ ./src/coolkey/slot.cpp	2010-06-23 04:57:04.417774402 -0700
+@@ -2192,6 +2192,10 @@ Slot::readCACCertificateFirst(CKYBuffer 
+ 	if (throwException && (status != CKYSUCCESS)) {
+ 	    handleConnectionError();
+ 	}
++        
++        if(CKYBuffer_Size(cert) == 0) {
++            handleConnectionError();
++        }
+ 	return status;
+     }
+ 
diff --git a/SOURCES/coolkey-cac.patch b/SOURCES/coolkey-cac.patch
new file mode 100644
index 0000000..a2a9732
--- /dev/null
+++ b/SOURCES/coolkey-cac.patch
@@ -0,0 +1,1013 @@
+diff -up ./src/coolkey/slot.cpp.cac ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.cac	2010-06-16 13:43:51.477181000 -0700
++++ ./src/coolkey/slot.cpp	2010-06-16 13:43:51.535179000 -0700
+@@ -372,7 +372,7 @@ Slot::Slot(const char *readerName_, Log 
+     : log(log_), readerName(NULL), personName(NULL), manufacturer(NULL),
+ 	slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), 
+ 	isVersion1Key(false), needLogin(false), fullTokenName(false), 
+-	mCoolkey(false),
++	mCoolkey(false), mOldCAC(false),
+ #ifdef USE_SHMEM
+ 	shmem(readerName_),
+ #endif
+@@ -412,6 +412,9 @@ Slot::Slot(const char *readerName_, Log 
+     }
+     CKYBuffer_InitEmpty(&cardATR);
+     CKYBuffer_InitEmpty(&mCUID);
++    for (int i=0; i < MAX_CERT_SLOTS; i++) {
++	CKYBuffer_InitEmpty(&cardAID[i]);
++    }
+   } catch(PKCS11Exception &) {
+ 	if (conn) {
+ 	    CKYCardConnection_Destroy(conn);
+@@ -479,6 +482,9 @@ Slot::~Slot()
+     CKYBuffer_FreeData(&nonce);
+     CKYBuffer_FreeData(&cardATR);
+     CKYBuffer_FreeData(&mCUID);
++    for (int i=0; i < MAX_CERT_SLOTS; i++) {
++	CKYBuffer_FreeData(&cardAID[i]);
++    }
+ }
+ 
+ template <class C>
+@@ -671,15 +677,9 @@ Slot::connectToToken()
+     status = CKYApplet_SelectCoolKeyManager(conn, NULL);
+     if (status != CKYSUCCESS) {
+         log->log("CoolKey Select failed 0x%x\n", status);
+-	status = CACApplet_SelectPKI(conn, 0, NULL);
++	status = getCACAid();
+ 	if (status != CKYSUCCESS) {
+-            log->log("CAC Select failed 0x%x\n", status);
+-	    if (status == CKYSCARDERR) {
+-		log->log("CAC Card Failure 0x%x\n", 
+-			CKYCardConnection_GetLastError(conn));
+-		disconnect();
+-	    }
+-	    return;
++	    goto loser;
+ 	}
+ 	state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+ 	/* skip the read of the cuid. We really don't need it and,
+@@ -690,6 +690,15 @@ Slot::connectToToken()
+ 	needLogin = 1;
+         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();
++	}
++	return;
+     }
+     mCoolkey = 1;
+     log->log("time connect: Select Applet %d ms\n", OSTimeNow() - time);
+@@ -771,17 +780,111 @@ Slot::disconnect()
+     invalidateLogin(false);
+ }
+ 
++CKYStatus
++Slot::getCACAid()
++{
++    CKYBuffer tBuf;
++    CKYBuffer vBuf;
++    CKYSize tlen, vlen;
++    CKYOffset toffset, voffset;
++    int certSlot = 0;
++    int i,length = 0;
++    CKYStatus status;
++
++    CKYBuffer_InitEmpty(&tBuf);
++    CKYBuffer_InitEmpty(&vBuf);
++
++    /* clear out the card AID's */
++    for (i=0; i < MAX_CERT_SLOTS; i++) {
++	CKYBuffer_Resize(&cardAID[i],0);
++    }
++
++    status = CACApplet_SelectCCC(conn,NULL);
++    if (status != CKYSUCCESS) {
++	/* are we an old CAC */
++	status = CACApplet_SelectPKI(conn, &cardAID[0], 0, NULL);
++	if (status != CKYSUCCESS) {
++	   /* no, just fail */
++	   return status;
++	}
++	/* yes, fill in the old applets */
++	mOldCAC = true;
++	for (i=1; i< MAX_CERT_SLOTS; i++) {
++	    CACApplet_SelectPKI(conn, &cardAID[i], i, NULL);
++	}
++	return CKYSUCCESS;
++    }
++    /* definately not an old CAC */
++    mOldCAC = false;
++
++    /* read the TLV */
++    status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, NULL);
++    if (status != CKYSUCCESS) {
++	goto done;
++    }
++    status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, NULL);
++    if (status != CKYSUCCESS) {
++	goto done;
++    }
++    tlen = CKYBuffer_Size(&tBuf);
++    vlen = CKYBuffer_Size(&vBuf);
++
++    for(toffset = 2, voffset=2; 
++	certSlot < MAX_CERT_SLOTS && toffset < tlen && voffset < vlen ; 
++		voffset += length) {
++
++	CKYByte tag = CKYBuffer_GetChar(&tBuf, toffset);
++	length = CKYBuffer_GetChar(&tBuf, toffset+1);
++	toffset += 2;
++	if (length == 0xff) {
++	    length = CKYBuffer_GetShortLE(&tBuf, toffset);
++	    toffset +=2;
++	}
++	if (tag != CAC_TAG_CARDURL) {
++	    continue;
++	}
++	/* CARDURL tags must be at least 10 bytes long */
++	if (length < 10) {
++	    continue;
++	}
++	/* check the app type, should be TLV_APP_PKI */
++	if (CKYBuffer_GetChar(&vBuf, voffset+5) != CAC_TLV_APP_PKI) {
++	    continue;
++	}
++	status = CKYBuffer_AppendBuffer(&cardAID[certSlot], &vBuf, voffset, 5);
++	if (status != CKYSUCCESS) {
++	    goto done;
++	}
++	status = CKYBuffer_AppendBuffer(&cardAID[certSlot], &vBuf, 
++								voffset+8, 2);
++	if (status != CKYSUCCESS) {
++	    goto done;
++	}
++	cardEF[certSlot] = CKYBuffer_GetShortLE(&vBuf, voffset+6);
++
++	certSlot++;
++    }
++    status = CKYSUCCESS;
++    if (certSlot == 0) {
++	status = CKYAPDUFAIL; /* probably neeed a beter error code */
++    }
++
++done:
++    CKYBuffer_FreeData(&tBuf);
++    CKYBuffer_FreeData(&vBuf);
++    return status;
++}
++
+ void
+ Slot::refreshTokenState()
+ {
+     if( cardStateMayHaveChanged() ) {
+-log->log("card changed\n");
++        log->log("card changed\n");
+ 	invalidateLogin(true);
+         closeAllSessions();
+ 	unloadObjects();
+         connectToToken();
+ 
+-
+         if( state & APPLET_PERSONALIZED ) {
+             try {
+                 loadObjects();
+@@ -1019,7 +1122,7 @@ Slot::makeModelString(char *model, int m
+ 
+ struct _manList {
+      unsigned short type;
+-     char *string;
++     const char *string;
+ };
+ 
+ static const struct _manList  manList[] = {
+@@ -1280,13 +1383,30 @@ void
+ Slot::selectCACApplet(CKYByte instance)
+ {
+     CKYStatus status;
+-    status = CACApplet_SelectPKI(conn, instance, NULL);
++    CKYBuffer *aid = &cardAID[instance];
++
++    if (CKYBuffer_Size(aid) == 0) {
++        disconnect();
++        throw PKCS11Exception(CKR_DEVICE_REMOVED);
++	return;
++    }
++    
++    status = CKYApplet_SelectFile(conn, aid, NULL);
+     if ( status == CKYSCARDERR ) handleConnectionError();
+     if ( status != CKYSUCCESS) {
+         // could not select applet: this just means it's not there
+         disconnect();
+         throw PKCS11Exception(CKR_DEVICE_REMOVED);
+     }
++    if (mOldCAC) {
++	return;
++    }
++    status = CACApplet_SelectFile(conn, cardEF[instance], NULL);
++    if ( status == CKYSCARDERR ) handleConnectionError();
++    if ( status != CKYSUCCESS) {
++        disconnect();
++        throw PKCS11Exception(CKR_DEVICE_REMOVED);
++    }
+ }
+ // assume we are already in a transaction
+ void
+@@ -2059,10 +2179,85 @@ Slot::fetchCombinedObjects(const CKYBuff
+     return objInfoList;
+ }
+ 
++CKYStatus
++Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, 
++			      bool throwException)
++{
++    CKYStatus status;
++    CKYISOStatus apduRC;
++
++    if (mOldCAC) {
++	/* get the first 100 bytes of the cert */
++	status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC);
++	if (throwException && (status != CKYSUCCESS)) {
++	    handleConnectionError();
++	}
++	return status;
++    }
++
++    CKYBuffer tBuf;
++    CKYBuffer vBuf;
++    CKYSize tlen, vlen;
++    CKYOffset toffset, voffset;
++    int length = 0;
++
++    CKYBuffer_InitEmpty(&tBuf);
++    CKYBuffer_InitEmpty(&vBuf);
++    CKYBuffer_Resize(cert, 0);
++
++    /* handle the new CAC card read */
++    /* read the TLV */
++    status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, NULL);
++    if (status != CKYSUCCESS) {
++	goto done;
++    }
++    status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, NULL);
++    if (status != CKYSUCCESS) {
++	goto done;
++    }
++    tlen = CKYBuffer_Size(&tBuf);
++    vlen = CKYBuffer_Size(&vBuf);
++
++    /* look for the Cert out of the TLV */
++    for(toffset = 2, voffset=2; toffset < tlen && voffset < vlen ; 
++		voffset += length) {
++
++	CKYByte tag = CKYBuffer_GetChar(&tBuf, toffset);
++	length = CKYBuffer_GetChar(&tBuf, toffset+1);
++	toffset += 2;
++	if (length == 0xff) {
++	    length = CKYBuffer_GetShortLE(&tBuf, toffset);
++	    toffset +=2;
++	}
++	if (tag != CAC_TAG_CERTIFICATE) {
++	    continue;
++	}
++	CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length);
++	break;
++    }
++    status = CKYSUCCESS;
++
++done:
++    CKYBuffer_FreeData(&tBuf);
++    CKYBuffer_FreeData(&vBuf);
++    return status;
++}
++
++/*
++ * only necessary for old CAC cards. New CAC cards have to read the
++ * whole cert in anyway above....
++ */
++CKYStatus
++Slot::readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize)
++{
++    CKYISOStatus apduRC;
++    assert(mOldCAC);
++    return CACApplet_GetCertificateAppend(conn, cert, nextSize, &apduRC);
++}
++
+ void
+ Slot::loadCACCert(CKYByte instance)
+ {
+-    CKYISOStatus apduRC;
+     CKYStatus status = CKYSUCCESS;
+     CKYBuffer cert;
+     CKYBuffer rawCert;
+@@ -2097,12 +2292,7 @@ Slot::loadCACCert(CKYByte instance)
+ 						 instance, OSTimeNow() - time);
+ 
+     if (instance == 0) {
+-	/* get the first 100 bytes of the cert */
+-	status = CACApplet_GetCertificateFirst(conn, &rawCert, 
+-						&nextSize, &apduRC);
+-	if (status != CKYSUCCESS) {
+-	    handleConnectionError();
+-	}
++	readCACCertificateFirst(&rawCert, &nextSize, true);
+ 	log->log("CAC Cert %d: fetch CAC Cert:  %d ms\n", 
+ 						instance, OSTimeNow() - time);
+     }
+@@ -2143,8 +2333,7 @@ Slot::loadCACCert(CKYByte instance)
+ 	    shmem.setVersion(SHMEM_VERSION);
+ 	    shmem.setDataVersion(dataVersion);
+ 	} else {
+-	    status = CACApplet_GetCertificateFirst(conn, &rawCert, 
+-						&nextSize, &apduRC);
++	    status = readCACCertificateFirst(&rawCert, &nextSize, false);
+ 	
+ 	    if (status != CKYSUCCESS) {
+ 		/* CAC only requires the Certificate in pki '0' */
+@@ -2159,8 +2348,7 @@ Slot::loadCACCert(CKYByte instance)
+ 	}
+ 
+ 	if (nextSize) {
+-	    status = CACApplet_GetCertificateAppend(conn, &rawCert, 
+-						nextSize, &apduRC);
++	    status = readCACCertificateAppend(&rawCert, nextSize);
+ 	}
+ 	log->log("CAC Cert %d: Fetch rest :  %d ms\n", 
+ 						instance, OSTimeNow() - time);
+@@ -2176,9 +2364,10 @@ Slot::loadCACCert(CKYByte instance)
+ 
+     log->log("CAC Cert %d: Cert has been read:  %d ms\n",
+ 						instance, OSTimeNow() - time);
+-    if (CKYBuffer_GetChar(&rawCert,0) == 1) {
++    if (!mOldCAC || CKYBuffer_GetChar(&rawCert,0) == 1) {
+ 	CKYSize guessFinalSize = CKYBuffer_Size(&rawCert);
+ 	CKYSize certSize = 0;
++	CKYOffset offset = mOldCAC ? 1 : 0;
+ 	int zret = Z_MEM_ERROR;
+ 
+ 	do {
+@@ -2189,7 +2378,8 @@ Slot::loadCACCert(CKYByte instance)
+ 	    }
+ 	    certSize = guessFinalSize;
+ 	    zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize,
+-			CKYBuffer_Data(&rawCert)+1, CKYBuffer_Size(&rawCert)-1);
++			CKYBuffer_Data(&rawCert)+offset, 
++			CKYBuffer_Size(&rawCert)-offset);
+ 	} while (zret == Z_BUF_ERROR);
+ 
+ 	if (zret != Z_OK) {
+@@ -2526,7 +2716,7 @@ Slot::attemptCACLogin()
+     switch( result ) {
+       case CKYISO_SUCCESS:
+         break;
+-      case 6981:
++      case 0x6981:
+         throw PKCS11Exception(CKR_PIN_LOCKED);
+       default:
+ 	if ((result & 0xff00) == 0x6300) {
+diff -up ./src/coolkey/slot.h.cac ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.cac	2010-06-16 13:43:51.344185000 -0700
++++ ./src/coolkey/slot.h	2010-06-16 13:43:51.546179000 -0700
+@@ -294,6 +294,7 @@ class CryptParams {
+ 				 const CKYBuffer *paddedOutput) const = 0;
+ };
+ 
++#define MAX_CERT_SLOTS 3
+ class Slot {
+ 
+   public:
+@@ -328,6 +329,8 @@ class Slot {
+     CKYBuffer nonce;
+     CKYBuffer cardATR;
+     CKYBuffer mCUID;
++    CKYBuffer cardAID[MAX_CERT_SLOTS];
++    unsigned short cardEF[MAX_CERT_SLOTS];
+     bool isVersion1Key;
+     bool needLogin;
+     long publicFree;
+@@ -335,6 +338,7 @@ class Slot {
+     long privateFree;
+     bool fullTokenName;
+     bool mCoolkey;
++    bool mOldCAC;
+ 
+     //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 };
+ 
+@@ -398,6 +402,11 @@ class Slot {
+     list<ListObjectInfo> fetchCombinedObjects(const CKYBuffer *header);
+     list<ListObjectInfo> fetchSeparateObjects();
+ 
++    CKYStatus getCACAid();
++    CKYStatus readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize,
++                              bool throwException);
++    CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize);
++
+     void selectApplet();
+     void selectCACApplet(CKYByte instance);
+     void unloadObjects();
+diff -up ./src/libckyapplet/cky_applet.c.cac ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.cac	2010-06-16 13:43:51.357181000 -0700
++++ ./src/libckyapplet/cky_applet.c	2010-06-16 14:47:41.305529000 -0700
+@@ -41,7 +41,13 @@
+ CKYStatus
+ CKYAppletFactory_SelectFile(CKYAPDU *apdu, const void *param)
+ {
+-    return CKYAPDUFactory_SelectFile(apdu,(const CKYBuffer *)param);
++    return CKYAPDUFactory_SelectFile(apdu, 4, 0, (const CKYBuffer *)param);
++}
++
++CKYStatus
++CACAppletFactory_SelectFile(CKYAPDU *apdu, const void *param)
++{
++    return CKYAPDUFactory_SelectFile(apdu, 2, 12, (const CKYBuffer *)param);
+ }
+ 
+ CKYStatus
+@@ -225,10 +231,17 @@ CKYAppletFactory_GetBuiltinACL(CKYAPDU *
+ }
+ 
+ CKYStatus
+-CACAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param)
++CACAppletFactory_SignDecryptStep(CKYAPDU *apdu, const void *param)
++{
++    const CKYBuffer *buf=(CKYBuffer *)param;
++    return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_STEP, buf);
++}
++
++CKYStatus
++CACAppletFactory_SignDecryptFinal(CKYAPDU *apdu, const void *param)
+ {
+     const CKYBuffer *buf=(CKYBuffer *)param;
+-    return CACAPDUFactory_SignDecrypt(apdu, buf);
++    return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_FINAL, buf);
+ }
+ 
+ CKYStatus
+@@ -246,6 +259,13 @@ CACAppletFactory_GetCertificate(CKYAPDU 
+ }
+ 
+ CKYStatus
++CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param)
++{
++    const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param;
++    return CACAPDUFactory_ReadFile(apdu, rfs->offset, rfs->type, rfs->count);
++}
++
++CKYStatus
+ CACAppletFactory_GetProperties(CKYAPDU *apdu, const void *param)
+ {
+     return CACAPDUFactory_GetProperties(apdu);
+@@ -457,7 +477,7 @@ CKYApplet_SelectFile(CKYCardConnection *
+ 							 CKYISOStatus *apduRC)
+ {
+     return CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, AID, NULL,
+-		0, CKYAppletFill_Null, NULL, apduRC);
++		CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+ }
+ 
+ static CKYByte coolkeyid[] = {0x62, 0x76, 0x01, 0xff, 0x00, 0x00, 0x00 };
+@@ -477,22 +497,23 @@ CKYApplet_SelectCoolKeyManager(CKYCardCo
+     return ret;
+ }
+ 
+-static CKYByte CACPKIid[] = {0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
++static CKYByte CACPKIid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01 };
+ /*
+  * Select the CoolKey applet. Must happen after we start a transaction and 
+  * before we issue any applet specific command.
+  */
+ CKYStatus
+-CACApplet_SelectPKI(CKYCardConnection *conn, CKYByte instance, 
+-			       CKYISOStatus *apduRC)
++CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cacAID, 
++				CKYByte instance, CKYISOStatus *apduRC)
+ {
+     CKYStatus ret;
+-    CKYBuffer CACPKIAID;
+-    CKYBuffer_InitFromData(&CACPKIAID, CACPKIid, sizeof(CACPKIid));
+-    CKYBuffer_SetChar(&CACPKIAID, 6, instance);
+-    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CACPKIAID,
++    CKYBuffer_AppendData(cacAID, CACPKIid, sizeof(CACPKIid));
++    CKYBuffer_AppendChar(cacAID, instance);
++    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, cacAID,
+ 		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+-    CKYBuffer_FreeData(&CACPKIAID);
++    if (ret != CKYSUCCESS) {
++	CKYBuffer_Resize(cacAID, 0);
++    }
+     return ret;
+ }
+ 
+@@ -515,11 +536,38 @@ CACApplet_SelectCardManager(CKYCardConne
+     CKYBuffer CAC_CM_AID;
+     CKYBuffer_InitFromData(&CAC_CM_AID, cacmgrid, sizeof(cacmgrid));
+     ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID,
+-		 NULL, 0, CKYAppletFill_Null, NULL, apduRC);
++		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+     CKYBuffer_FreeData(&CAC_CM_AID);
+     return ret;
+ }
+ 
++static CKYByte cacCCCid[] = {0xa0, 0x00, 0x00, 0x01, 0x16, 0xdb, 0x00 };
++CKYStatus
++CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC)
++{
++    CKYStatus ret;
++    CKYBuffer CAC_CM_AID;
++    CKYBuffer_InitFromData(&CAC_CM_AID, cacCCCid, sizeof(cacCCCid));
++    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID,
++		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
++    CKYBuffer_FreeData(&CAC_CM_AID);
++    return ret;
++}
++
++CKYStatus
++CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef,
++						 CKYISOStatus *apduRC)
++{
++    CKYStatus ret;
++    CKYBuffer efBuf;
++    CKYBuffer_InitEmpty(&efBuf);
++    CKYBuffer_AppendShortLE(&efBuf, ef);
++    ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SelectFile, &efBuf,
++		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
++    CKYBuffer_FreeData(&efBuf);
++    return ret;
++}
++
+ /*
+  * GetCPLC cluster -- must be called with CM selected
+  */
+@@ -673,8 +721,8 @@ CKYApplet_ComputeCryptProcess(CKYCardCon
+     ccd.keyNumber = keyNumber;
+     ccd.location = location;
+     ccd.data = data;
+-    return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, &ccd,
+-	nonce, 0, CKYAppletFill_Null, NULL, apduRC);
++    return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, 
++	&ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC);
+ }
+ 
+ /* computeCrypt returns data in the form :
+@@ -832,11 +880,39 @@ CACApplet_SignDecrypt(CKYCardConnection 
+ 	 	CKYBuffer *result, CKYISOStatus *apduRC)
+ {
+     CKYStatus ret;
+-
+-    ret = CKYApplet_HandleAPDU(conn, 
+-			    CACAppletFactory_SignDecrypt, data, NULL, 
+-			    CKYBuffer_Size(data), CKYAppletFill_ReplaceBuffer, 
++    CKYSize dataSize = CKYBuffer_Size(data);
++    CKYOffset offset = 0;
++    CKYBuffer tmp;
++
++    CKYBuffer_InitEmpty(&tmp);
++
++    CKYBuffer_Resize(result, 0);
++    for(offset = 0; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; 
++				offset += CKY_MAX_WRITE_CHUNK_SIZE) {
++	CKYBuffer_Resize(&tmp,0);
++	CKYBuffer_AppendBuffer(&tmp, data, offset, CKY_MAX_WRITE_CHUNK_SIZE);
++        ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptStep, 
++			    &tmp, NULL, CKY_SIZE_UNKNOWN, 
++			    CKYAppletFill_AppendBuffer, 
+ 			    result, apduRC);
++	if (ret != CKYSUCCESS) {
++	    goto done;
++	}
++    }
++    CKYBuffer_Resize(&tmp,0);
++    CKYBuffer_AppendBuffer(&tmp, data, offset, dataSize - offset);
++    ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptFinal, 
++			    &tmp, NULL, CKY_SIZE_UNKNOWN, 
++			    CKYAppletFill_AppendBuffer, 
++			    result, apduRC);
++
++    if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != dataSize)) {
++	/* RSA returns the same data size as input, didn't happen, so
++	 * something is wrong. */
++    }
++
++done:
++    CKYBuffer_FreeData(&tmp);
+     return ret;
+ }
+ 
+@@ -895,6 +971,63 @@ CACApplet_GetCertificate(CKYCardConnecti
+     }
+     return ret;
+ }
++
++/*
++ * Read a CAC Tag/Value file 
++ */
++CKYStatus
++CACApplet_ReadFile(CKYCardConnection *conn, CKYByte type, CKYBuffer *buffer, 
++		    CKYISOStatus *apduRC)
++{
++    CKYStatus ret;
++    CKYISOStatus status;
++    CKYByte maxtransfer;
++    unsigned short offset = 0;
++    unsigned short size;
++    CACAppletArgReadFile rfs;
++
++    CKYBuffer_Resize(buffer,0);
++    if (apduRC == NULL) {
++	apduRC = &status;
++    }
++    rfs.offset = 0;
++    rfs.count = 2;
++    rfs.type = type;
++
++    /* APDU's are expensive, Grab a big chunk of the file first if possible */
++    ret = CKYApplet_HandleAPDU(conn, 
++			    CACAppletFactory_ReadFile, &rfs, NULL, 
++			    rfs.count, CKYAppletFill_AppendBuffer,
++			    buffer, apduRC);
++    /* file is probably smaller than 100 bytes, get the actual size first */
++    if (ret != CKYSUCCESS) {
++	return ret;
++    }
++    size = CKYBuffer_GetShortLE(buffer, 0) + 2 /* include the length itself */;
++    maxtransfer = CKY_MAX_READ_CHUNK_SIZE;
++    /* get the rest of the buffer if necessary */
++    for (offset = CKYBuffer_Size(buffer); size > offset; 
++				offset = CKYBuffer_Size(buffer)) {
++	rfs.offset = offset;
++	rfs.count = MIN(size - offset, maxtransfer);
++	ret = CKYApplet_HandleAPDU(conn, 
++			    CACAppletFactory_ReadFile, &rfs, NULL, 
++			    rfs.count, CKYAppletFill_AppendBuffer,
++			    buffer, apduRC);
++	if (ret != CKYSUCCESS) {
++	    if (*apduRC == CAC_INVALID_PARAMS) {
++		maxtransfer = maxtransfer/2;
++		if (maxtransfer == 0) {
++		    return ret;
++		}
++	    } else {
++		return ret;
++	    }
++ 	}
++    }
++    return ret;
++}
++
+ CKYStatus 
+ CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, 
+ 			CKYSize *nextSize, CKYISOStatus *apduRC)
+diff -up ./src/libckyapplet/cky_applet.h.cac ./src/libckyapplet/cky_applet.h
+--- ./src/libckyapplet/cky_applet.h.cac	2010-06-16 13:43:51.370181000 -0700
++++ ./src/libckyapplet/cky_applet.h	2010-06-16 13:43:51.572179000 -0700
+@@ -71,6 +71,15 @@ typedef unsigned short CKYISOStatus; /* 
+ #define CKYISO_INTERNAL_ERROR        0x9cff  /* Reserved for debugging, 
+ 					     * shouldn't happen */
+ 
++#define CAC_INVALID_PARAMS	    0x6a83
++#define CAC_TAG_FILE			1
++#define CAC_VALUE_FILE			2
++
++
++#define CAC_TAG_CARDURL			0xf3
++#define CAC_TAG_CERTIFICATE		0x70
++#define CAC_TLV_APP_PKI			0x04
++
+ /*
+  * Pin Constants as used by our applet
+  */
+@@ -209,6 +218,12 @@ typedef struct _CKYAppletArgComputeCrypt
+     const CKYBuffer *sig;
+ } CKYAppletArgComputeCrypt;
+ 
++typedef struct _CACAppletArgReadFile {
++    CKYByte   type;
++    CKYByte   count;
++    unsigned short offset;
++} CACAppletArgReadFile;
++
+ /* 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*/
+@@ -451,9 +466,17 @@ CKYStatus CKYApplet_DeleteObject(CKYCard
+ /* Select the CAC card manager.  Can happen with either applet selected */
+ CKYStatus CACApplet_SelectCardManager(CKYCardConnection *conn, 
+ 							CKYISOStatus *apduRC);
+-/* Can happen with either applet selected */
+-CKYStatus CACApplet_SelectPKI(CKYCardConnection *conn, CKYByte instance,
+-			      CKYISOStatus *apduRC);
++/* Select the CAC CC container. Can happen with either applet selected */
++CKYStatus CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC);
++/* Select an old CAC applet and fill in the cardAID */
++CKYStatus CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cardAid,
++			      CKYByte instance, CKYISOStatus *apduRC);
++/* read a TLV file */
++CKYStatus CACApplet_ReadFile(CKYCardConnection *conn, CKYByte type, 
++			     CKYBuffer *buffer, CKYISOStatus *apduRC);
++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);
+diff -up ./src/libckyapplet/cky_base.c.cac ./src/libckyapplet/cky_base.c
+--- ./src/libckyapplet/cky_base.c.cac	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_base.c	2010-06-16 13:43:51.583179000 -0700
+@@ -220,6 +220,22 @@ CKYBuffer_AppendShort(CKYBuffer *buf, un
+     return CKYSUCCESS;
+ }
+ 
++/* append a short in network order */
++CKYStatus
++CKYBuffer_AppendShortLE(CKYBuffer *buf, unsigned short val)
++{
++    CKYStatus ret;
++
++    ret = CKYBuffer_Reserve(buf, buf->len + 2);
++    if (ret != CKYSUCCESS) {
++	return ret;
++    }
++    buf->data[buf->len+1] = (CKYByte) ((val >> 8) & 0xff);
++    buf->data[buf->len+0] = (CKYByte) ((val >> 0) & 0xff);
++    buf->len += 2;
++    return CKYSUCCESS;
++}
++
+ /* append a long in applet order */
+ CKYStatus
+ CKYBuffer_AppendLong(CKYBuffer *buf, unsigned long val)
+@@ -238,6 +254,24 @@ CKYBuffer_AppendLong(CKYBuffer *buf, uns
+     return CKYSUCCESS;
+ }
+ 
++/* append a long in applet order */
++CKYStatus
++CKYBuffer_AppendLongLE(CKYBuffer *buf, unsigned long val)
++{
++    CKYStatus ret;
++
++    ret = CKYBuffer_Reserve(buf, buf->len + 4);
++    if (ret != CKYSUCCESS) {
++	return ret;
++    }
++    buf->data[buf->len+3] = (CKYByte) ((val >> 24) & 0xff);
++    buf->data[buf->len+2] = (CKYByte) ((val >> 16) & 0xff);
++    buf->data[buf->len+1] = (CKYByte) ((val >>  8) & 0xff);
++    buf->data[buf->len+0] = (CKYByte) ((val >>  0) & 0xff);
++    buf->len += 4;
++    return CKYSUCCESS;
++}
++
+ CKYStatus
+ CKYBuffer_Replace(CKYBuffer *buf, CKYOffset offset, const CKYByte *data, CKYSize len)
+ {
+@@ -351,6 +385,22 @@ CKYBuffer_SetShort(CKYBuffer *buf, CKYOf
+ }
+ 
+ CKYStatus
++CKYBuffer_SetShortLE(CKYBuffer *buf, CKYOffset offset, unsigned short val)
++{
++    CKYStatus ret;
++
++    if (buf->len < offset+2) {
++	ret = CKYBuffer_Resize(buf,offset+2);
++	if (ret != CKYSUCCESS) {
++	    return ret;
++	}
++    }
++    buf->data[offset+1] = (CKYByte) ((val >> 8) & 0xff);
++    buf->data[offset+0] = (CKYByte) ((val >> 0) & 0xff);
++    return CKYSUCCESS;
++}
++
++CKYStatus
+ CKYBuffer_SetLong(CKYBuffer *buf, CKYOffset offset, unsigned long val)
+ {
+     CKYStatus ret;
+@@ -368,6 +418,24 @@ CKYBuffer_SetLong(CKYBuffer *buf, CKYOff
+     return CKYSUCCESS;
+ }
+ 
++CKYStatus
++CKYBuffer_SetLongLE(CKYBuffer *buf, CKYOffset offset, unsigned long val)
++{
++    CKYStatus ret;
++
++    if (buf->len < offset+4) {
++	ret = CKYBuffer_Resize(buf,offset+4);
++	if (ret != CKYSUCCESS) {
++	    return ret;
++	}
++    }
++    buf->data[offset+3] = (CKYByte) ((val >> 24) & 0xff);
++    buf->data[offset+2] = (CKYByte) ((val >> 16) & 0xff);
++    buf->data[offset+1] = (CKYByte) ((val >>  8) & 0xff);
++    buf->data[offset+0] = (CKYByte) ((val >>  0) & 0xff);
++    return CKYSUCCESS;
++}
++
+ CKYByte
+ CKYBuffer_GetChar(const CKYBuffer *buf, CKYOffset offset)
+ {
+@@ -388,6 +456,18 @@ CKYBuffer_GetShort(const CKYBuffer *buf,
+     val |= ((unsigned short)buf->data[offset+1]) << 0;
+     return val;
+ }
++
++unsigned short
++CKYBuffer_GetShortLE(const CKYBuffer *buf, CKYOffset offset)
++{
++    unsigned short val;
++    if (buf->len < offset+2) {
++	return 0;
++    }
++    val  = ((unsigned short)buf->data[offset+1]) << 8;
++    val |= ((unsigned short)buf->data[offset+0]) << 0;
++    return val;
++}
+ 	
+ unsigned long
+ CKYBuffer_GetLong(const CKYBuffer *buf, CKYOffset offset)
+@@ -402,6 +482,20 @@ CKYBuffer_GetLong(const CKYBuffer *buf, 
+     val |= ((unsigned long)buf->data[offset+3]) << 0;
+     return val;
+ }
++
++unsigned long
++CKYBuffer_GetLongLE(const CKYBuffer *buf, CKYOffset offset)
++{
++    unsigned long val;
++    if (buf->len < offset+4) {
++	return 0;
++    }
++    val  = ((unsigned long)buf->data[offset+3]) << 24;
++    val |= ((unsigned long)buf->data[offset+2]) << 16;
++    val |= ((unsigned long)buf->data[offset+1]) << 8;
++    val |= ((unsigned long)buf->data[offset+0]) << 0;
++    return val;
++}
+ 	
+ CKYStatus
+ CKYBuffer_Resize(CKYBuffer *buf, CKYSize newLen)
+diff -up ./src/libckyapplet/cky_base.h.cac ./src/libckyapplet/cky_base.h
+--- ./src/libckyapplet/cky_base.h.cac	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_base.h	2010-06-16 13:43:51.592179000 -0700
+@@ -170,9 +170,15 @@ CKYStatus CKYBuffer_AppendChar(CKYBuffer
+ /* append a short in applet order */
+ CKYStatus CKYBuffer_AppendShort(CKYBuffer *buf, unsigned short val);
+ 
++/* append a short in little endian order */
++CKYStatus CKYBuffer_AppendShortLE(CKYBuffer *buf, unsigned short val);
++
+ /* append a long in applet order */
+ CKYStatus CKYBuffer_AppendLong(CKYBuffer *buf, unsigned long val);
+ 
++/* append a long in little endian order */
++CKYStatus CKYBuffer_AppendLongLE(CKYBuffer *buf, unsigned long val);
++
+ /* append data. the data starts at data and extends len bytes */
+ CKYStatus CKYBuffer_AppendData(CKYBuffer *buf, const CKYByte *data, CKYSize len);
+ 
+@@ -210,12 +216,18 @@ CKYStatus CKYBuffer_SetChars(CKYBuffer *
+ CKYStatus CKYBuffer_SetShort(CKYBuffer *buf, CKYOffset offset, unsigned short val);
+ CKYStatus CKYBuffer_SetLong(CKYBuffer *buf, CKYOffset offset, unsigned long val);
+ 
++/* These functions work in little endian order */
++CKYStatus CKYBuffer_SetShortLE(CKYBuffer *buf, CKYOffset offset, unsigned short val);
++CKYStatus CKYBuffer_SetLongLE(CKYBuffer *buf, CKYOffset offset, unsigned long val);
+ /* read a character from offset. If offset is beyond the end of the buffer,
+  * then the function returns '0' */
+ CKYByte CKYBuffer_GetChar(const CKYBuffer *buf, CKYOffset offset);
+ /* These functions work in applet order */
+ unsigned short CKYBuffer_GetShort(const CKYBuffer *buf, CKYOffset offset);
+ unsigned long CKYBuffer_GetLong(const CKYBuffer *buf, CKYOffset offset);
++/* These functions work in little endian order */
++unsigned short CKYBuffer_GetShortLE(const CKYBuffer *buf, CKYOffset offset);
++unsigned long CKYBuffer_GetLongLE(const CKYBuffer *buf, CKYOffset offset);
+ 
+ /* clear out all the data in a buffer */
+ void CKYBuffer_Zero(CKYBuffer *buf);
+diff -up ./src/libckyapplet/cky_factory.c.cac ./src/libckyapplet/cky_factory.c
+--- ./src/libckyapplet/cky_factory.c.cac	2010-06-16 13:43:51.393185000 -0700
++++ ./src/libckyapplet/cky_factory.c	2010-06-16 14:48:08.885473000 -0700
+@@ -25,12 +25,13 @@
+  * special commands can be issued at any time 
+  */
+ CKYStatus
+-CKYAPDUFactory_SelectFile(CKYAPDU *apdu, const CKYBuffer *AID)
++CKYAPDUFactory_SelectFile(CKYAPDU *apdu, CKYByte p1, CKYByte p2,
++			  const CKYBuffer *AID)
+ {
+     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+     CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE);
+-    CKYAPDU_SetP1(apdu, 0x04);
+-    CKYAPDU_SetP2(apdu, 0x00);
++    CKYAPDU_SetP1(apdu, p1);
++    CKYAPDU_SetP2(apdu, p2);
+     return CKYAPDU_SetSendDataBuffer(apdu, AID);
+ }
+ 
+@@ -131,6 +132,7 @@ fail:
+     return ret;
+ }
+ 
++
+ CKYStatus
+ CKYAPDUFactory_ComputeCryptFinal(CKYAPDU *apdu, CKYByte keyNumber, 
+ 		CKYByte location, const CKYBuffer *data, const CKYBuffer *sig)
+@@ -572,11 +574,11 @@ CKYAPDUFactory_GetBuiltinACL(CKYAPDU *ap
+ }
+ 
+ CKYStatus
+-CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, const CKYBuffer *data)
++CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, const CKYBuffer *data)
+ {
+     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+     CKYAPDU_SetINS(apdu, CAC_INS_SIGN_DECRYPT);
+-    CKYAPDU_SetP1(apdu, 0x00);
++    CKYAPDU_SetP1(apdu, type);
+     CKYAPDU_SetP2(apdu, 0x00);
+     return CKYAPDU_SetSendDataBuffer(apdu, data);
+ }
+@@ -592,6 +594,36 @@ CACAPDUFactory_GetCertificate(CKYAPDU *a
+ }
+ 
+ CKYStatus
++CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, 	
++					CKYByte type, CKYByte count)
++{
++    CKYStatus ret;
++    CKYBuffer buf;
++
++    CKYBuffer_InitEmpty(&buf);
++    CKYAPDU_SetCLA(apdu, CKY_CLASS_GLOBAL_PLATFORM);
++    CKYAPDU_SetINS(apdu, CAC_INS_READ_FILE);
++    CKYAPDU_SetP1(apdu, (offset >> 8) & 0xff);
++    CKYAPDU_SetP2(apdu, offset & 0xff);
++    ret = CKYBuffer_Reserve(&buf, 2);
++    if (ret != CKYSUCCESS) {
++	    goto fail;
++    }
++    ret = CKYBuffer_AppendChar(&buf, type);
++    if (ret != CKYSUCCESS) {
++	    goto fail;
++    }
++    ret = CKYBuffer_AppendChar(&buf, count);
++    if (ret != CKYSUCCESS) {
++	    goto fail;
++    } 
++    ret = CKYAPDU_SetSendDataBuffer(apdu, &buf);
++fail:
++    CKYBuffer_FreeData(&buf);
++    return ret;
++}
++
++CKYStatus
+ CACAPDUFactory_GetProperties(CKYAPDU *apdu)
+ {
+     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+diff -up ./src/libckyapplet/cky_factory.h.cac ./src/libckyapplet/cky_factory.h
+--- ./src/libckyapplet/cky_factory.h.cac	2010-06-16 13:43:51.402181000 -0700
++++ ./src/libckyapplet/cky_factory.h	2010-06-16 14:43:20.867049000 -0700
+@@ -86,7 +86,11 @@
+ #define CAC_INS_SIGN_DECRYPT	0x42
+ #define CAC_INS_VERIFY_PIN	0x20
+ #define CAC_INS_GET_PROPERTIES	0x56
++#define CAC_INS_READ_FILE	0x52
++
+ #define CAC_SIZE_GET_PROPERTIES	48
++#define CAC_P1_STEP		0x80
++#define CAC_P1_FINAL		0x00
+ 
+ /*
+  * Fixed return sized from various commands
+@@ -169,7 +173,8 @@
+ CKY_BEGIN_PROTOS
+ 
+ /* function based factorys */
+-CKYStatus CKYAPDUFactory_SelectFile(CKYAPDU *apdu, const CKYBuffer *AID);
++CKYStatus CKYAPDUFactory_SelectFile(CKYAPDU *apdu, CKYByte p1, CKYByte p2,
++				    const CKYBuffer *AID);
+ CKYStatus CKYAPDUFactory_SelectCardManager(CKYAPDU *apdu);
+ CKYStatus CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu);
+ CKYStatus CKYAPDUFactory_ListKeys(CKYAPDU *apdu, CKYByte sequence);
+@@ -211,9 +216,12 @@ CKYStatus CKYAPDUFactory_SeedRandom(CKYA
+ CKYStatus CKYAPDUFactory_GetIssuerInfo(CKYAPDU *apdu);
+ CKYStatus CKYAPDUFactory_GetBuiltinACL(CKYAPDU *apdu);
+ 
+-CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, const CKYBuffer *data);
++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);
+ 
+ CKY_END_PROTOS
diff --git a/SOURCES/coolkey-cache-dir-move.patch b/SOURCES/coolkey-cache-dir-move.patch
new file mode 100644
index 0000000..1b5eaff
--- /dev/null
+++ b/SOURCES/coolkey-cache-dir-move.patch
@@ -0,0 +1,177 @@
+Index: src/coolkey/machdep.cpp
+===================================================================
+RCS file: /cvs/dirsec/coolkey/src/coolkey/machdep.cpp,v
+retrieving revision 1.4
+diff -u -r1.4 machdep.cpp
+--- src/coolkey/machdep.cpp	14 Feb 2007 00:46:28 -0000	1.4
++++ src/coolkey/machdep.cpp	15 Aug 2007 01:41:11 -0000
+@@ -185,12 +185,20 @@
+ #define MAP_INHERIT 0
+ #endif
+ 
++#ifndef BASEPATH
++#ifdef MAC
++#define BASEPATH "/var"
++#else
++#define BASEPATH "/var/cache"
++#endif
++#endif
++
+ #ifdef FULL_CLEANUP
+ #define RESERVED_OFFSET 256
+-#define MEMSEGPATH "/tmp/.pk11ipc"
++#define MEMSEGPATH BASEPATH"/coolkey-lock"
+ #else 
+ #define RESERVED_OFFSET 0
+-#define MEMSEGPATH "/tmp/.pk11ipc1"
++#define MEMSEGPATH BASEPATH"/coolkey"
+ #endif
+ 
+ struct SHMemData {
+@@ -208,11 +216,6 @@
+ #ifdef FULL_CLEANUP
+ 	flock(fd,LOCK_EX);
+ 	unsigned long ref = --(*(unsigned long *)addr); 
+-#ifdef notdef
+-	if (ref == 0) {
+-	    unlink(path);
+-	}
+-#endif
+ 	flock(fd, LOCK_UN);
+ #endif
+ 	munmap(addr,size+RESERVED_OFFSET);
+@@ -225,6 +228,73 @@
+     }
+ }
+ 
++/*
++ * The cache directory is shared and accessible by anyone, make
++ * sure the cache file we are opening is really a valid cache file.
++ */
++int safe_open(char *path, int flags, int mode, int size)
++{
++    struct stat buf;
++    int fd, ret;
++
++    fd = open (path, flags|O_NOFOLLOW, mode);
++
++    if (fd < 0) {
++	return fd;
++    }
++
++    ret = fstat(fd, &buf);
++    if (ret < 0) {
++	close (fd);
++	return ret;
++    }
++
++    /* our cache files are pretty specific, make sure we are looking
++     * at the correct one */
++
++    /* first, we should own the file ourselves, don't open a file
++     * that someone else wanted us to see. */
++    if (buf.st_uid != getuid()) {
++	close(fd);
++	errno = EACCES;
++	return -1;
++    }
++
++    /* next, there should only be one link in this file. Don't
++     * use this code to trash another file */
++    if (buf.st_nlink != 1) {
++	close(fd);
++	errno = EMLINK;
++	return -1;
++    }
++
++    /* next, This better be a regular file */
++    if (!S_ISREG(buf.st_mode)) {
++	close(fd);
++	errno = EACCES;
++	return -1;
++    }
++
++    /* if the permissions don't match, something is wrong */
++    if ((buf.st_mode & 03777) != mode) {
++	close(fd);
++	errno = EACCES;
++	return -1;
++    }
++
++    /* finally the file should be the correct size. This 
++     * check isn't so much to protect from an attack, as it is to
++     * detect a corrupted cache file */
++    if (buf.st_size != size) {
++	close(fd);
++	errno = EACCES;
++	return -1;
++    }
++
++    /* OK, the file checked out, ok to continue */
++    return fd;
++}
++
+ SHMem::SHMem(): shmemData(0) {}
+ 
+ SHMem *
+@@ -248,7 +318,7 @@
+ 	return NULL;
+     }
+     int mask = umask(0);
+-    int ret = mkdir (MEMSEGPATH, 0777);
++    int ret = mkdir (MEMSEGPATH, 01777);
+     umask(mask);
+     if ((ret == -1) && (errno != EEXIST)) {
+ 	delete shmemData;
+@@ -264,21 +334,16 @@
+     shmemData->path[sizeof(MEMSEGPATH)-1] = '/';
+     strcpy(&shmemData->path[sizeof(MEMSEGPATH)],name);
+ 
+-    int mode = 0777;
+-    if (strcmp(name,"token_names") != 0) {
+-	/* each user gets his own uid array */
+-    	sprintf(uid_str, "-%u",getuid());
+-    	strcat(shmemData->path,uid_str);
+-	mode = 0700;
+-    } 
++    sprintf(uid_str, "-%u",getuid());
++    strcat(shmemData->path,uid_str);
++    int mode = 0600;
++
+     shmemData->fd = open(shmemData->path, 
+ 		O_CREAT|O_RDWR|O_EXCL|O_APPEND|O_EXLOCK, mode);
+-    if (shmemData->fd  < 0) {
+-	needInit = false;
+-	shmemData->fd = open(shmemData->path,O_RDWR|O_EXLOCK, mode);
+-    }  else {
++    if (shmemData->fd >= 0) {
+ 	char *buf;
+ 	int len = size+RESERVED_OFFSET;
++	int ret;
+ 
+ 	buf = (char *)calloc(1,len);
+ 	if (!buf) {
+@@ -289,8 +354,22 @@
+ 	    delete shmemData;
+ 	    return NULL;
+ 	}
+-	write(shmemData->fd,buf,len);
++	ret = write(shmemData->fd,buf,len);
++	if (ret != len) {
++	    unlink(shmemData->path);
++#ifdef FULL_CLEANUP
++	    flock(shmemData->fd, LOCK_UN);
++#endif
++	    delete shmemData;
++	    return NULL;
++	}
++	
+ 	free(buf);
++    } else if (errno == EEXIST) {
++	needInit = false;
++
++	shmemData->fd = safe_open(shmemData->path,O_RDWR|O_EXLOCK, mode,
++				  size+RESERVED_OFFSET);
+     }
+     if (shmemData->fd < 0) {
+ 	delete shmemData;
diff --git a/SOURCES/coolkey-fix-token-removal-failure.patch b/SOURCES/coolkey-fix-token-removal-failure.patch
new file mode 100644
index 0000000..7f8a954
--- /dev/null
+++ b/SOURCES/coolkey-fix-token-removal-failure.patch
@@ -0,0 +1,86 @@
+Fix insertion/removal detection
+
+pcsc now errors out of the SCardGetStatusChange call with
+SCARD_E_UNKNOWN_READER if any of the passed readers aren't known.
+This includes readers that were very recently forgotton about because
+a user just disconnected them.
+
+(See
+ http://anonscm.debian.org/viewvc/pcsclite/trunk/PCSC/src/winscard_clnt.c?r1=5858&r2=5881
+for the change to pcsc)
+
+Unfortunately, this means SECMOD_WaitForAnyTokenEvent will fail with a
+SC_NO_EVENT error if a user removes their smartcard at the wrong time.
+
+This patch changes coolkey to detect removed readers before calling
+SCardGetStatusChange, so that it can handle the removal itself.
+
+diff -up coolkey-1.1.0/src/coolkey/slot.cpp.fix coolkey-1.1.0/src/coolkey/slot.cpp
+--- coolkey-1.1.0/src/coolkey/slot.cpp.fix	2013-05-22 16:23:41.728846957 -0400
++++ coolkey-1.1.0/src/coolkey/slot.cpp	2013-05-22 17:09:59.813958927 -0400
+@@ -279,24 +279,22 @@ SlotList::updateReaderList()
+      * don't recognize.
+      */
+ 
+-    /* first though, let's check to see if any previously removed readers have 
+-     * come back from the dead. If the ignored bit has been set, we do not need
+-     * it any more.
+-    */
++    /* Iterate through all the readers to see if we need to make unavailable any
++     * freshly removed readers. Also, see if any previously removed
++     * readers have come back from the dead and don't need to be ignored.
++     */
+ 
+     const char *curReaderName = NULL;
+     unsigned long knownState = 0;
+     for(int ri = 0 ; ri < numReaders; ri ++)  {
+-       
+         knownState = CKYReader_GetKnownState(&readerStates[ri]);
+-        if( !(knownState & SCARD_STATE_IGNORE))  {
+-            continue;
+-        }
+- 
++
+         curReaderName =  CKYReader_GetReaderName(&readerStates[ri]); 
+         if(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);
+         }
+     } 
+ 
+@@ -1238,6 +1236,32 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
+ 	    throw;
+ 	}
+ 
++	/* Before round-tripping to the daemon for the duration of the
++	 * timeout, first see if we lost any readers, and pick a slot
++	 * from that set to return
++	 */
++	for (i=0; i < numReaders; 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);
++		readerListLock.releaseLock();
++		*slotp = slotIndexToID(i);
++		found = TRUE;
++		break;
++	    }
++	}
++
++	if (found) {
++	    break;
++	}
++
++	if (shuttingDown) {
++	    readerListLock.releaseLock();
++	    break;
++	}
++
+ 	if (myNumReaders != numReaders) {
+ 	    if (myReaderStates) {
+ 		delete [] myReaderStates;
diff --git a/SOURCES/coolkey-gcc43.patch b/SOURCES/coolkey-gcc43.patch
new file mode 100644
index 0000000..5e41b5a
--- /dev/null
+++ b/SOURCES/coolkey-gcc43.patch
@@ -0,0 +1,54 @@
+diff -up ./src/coolkey/slot.cpp.coolkey-gcc43 ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.coolkey-gcc43	2008-02-13 18:01:45.000000000 -0800
++++ ./src/coolkey/slot.cpp	2008-02-13 18:03:05.000000000 -0800
+@@ -25,7 +25,6 @@
+ #include "PKCS11Exception.h"
+ #include <winscard.h>
+ #include "slot.h"
+-#include <memory.h>
+ #include "zlib.h"
+ #include "params.h"
+ 
+@@ -33,7 +32,6 @@
+ 
+ #define MIN(x, y) ((x) < (y) ? (x) : (y))
+ 
+-using std::auto_ptr;
+ 
+ 
+ #ifdef DEBUG
+diff -up ./src/coolkey/machdep.cpp.coolkey-gcc43 ./src/coolkey/machdep.cpp
+--- ./src/coolkey/machdep.cpp.coolkey-gcc43	2008-02-13 18:02:06.000000000 -0800
++++ ./src/coolkey/machdep.cpp	2008-02-13 18:04:04.000000000 -0800
+@@ -33,6 +33,8 @@
+ #include <sys/stat.h>
+ #include <sys/mman.h>
+ #include <pthread.h>
++#include <string.h>
++#include <stdlib.h>
+ #endif
+ 
+ #ifdef _WIN32
+diff -up ./src/coolkey/log.cpp.coolkey-gcc43 ./src/coolkey/log.cpp
+--- ./src/coolkey/log.cpp.coolkey-gcc43	2008-02-13 18:01:55.000000000 -0800
++++ ./src/coolkey/log.cpp	2008-02-13 18:03:37.000000000 -0800
+@@ -18,6 +18,8 @@
+  * ***** END COPYRIGHT BLOCK *****/
+ 
+ #include <string>
++#include <string.h>
++#include <stdlib.h>
+ #include "mypkcs11.h"
+ #include <assert.h>
+ #include <stdio.h>
+diff -up ./src/coolkey/object.cpp.coolkey-gcc43 ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.coolkey-gcc43	2008-02-13 18:02:20.000000000 -0800
++++ ./src/coolkey/object.cpp	2008-02-13 18:04:22.000000000 -0800
+@@ -21,6 +21,7 @@
+ #include "PKCS11Exception.h"
+ #include "object.h"
+ #include <algorithm>
++#include <string.h>
+ 
+ using std::find_if;
+ 
diff --git a/SOURCES/coolkey-latest.patch b/SOURCES/coolkey-latest.patch
new file mode 100644
index 0000000..bc10eb8
--- /dev/null
+++ b/SOURCES/coolkey-latest.patch
@@ -0,0 +1,685 @@
+diff -up ./src/coolkey/slot.cpp.coolkey-latest ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.coolkey-latest	2009-09-11 13:58:24.423487305 -0700
++++ ./src/coolkey/slot.cpp	2009-09-11 14:04:30.813488220 -0700
+@@ -203,6 +203,29 @@ SlotList::readerExists(const char *reade
+     return FALSE;
+ }
+ 
++bool
++SlotList::readerNameExistsInList(const char *readerName,CKYReaderNameList *readerNameList)
++{
++    if( !readerName || !readerNameList) {
++        return FALSE;
++    }
++
++    int i = 0;
++    int readerNameCnt = CKYReaderNameList_GetCount(*readerNameList);
++
++    const char *curReaderName = NULL;
++    for(i=0; i < readerNameCnt; i++) {
++        curReaderName = CKYReaderNameList_GetValue(*readerNameList,i);
++
++        if(!strcmp(curReaderName,readerName)) {
++            return TRUE;
++        }
++        
++    }
++    
++    return FALSE;
++}
++
+ /*
+  * you need to hold the ReaderList Lock before you can update the ReaderList
+  */
+@@ -256,6 +279,27 @@ SlotList::updateReaderList()
+      * don't recognize.
+      */
+ 
++    /* first though, let's check to see if any previously removed readers have 
++     * come back from the dead. If the ignored bit has been set, we do not need
++     * it any more.
++    */
++
++    const char *curReaderName = NULL;
++    unsigned long knownState = 0;
++    for(int ri = 0 ; ri < numReaders; ri ++)  {
++       
++        knownState = CKYReader_GetKnownState(&readerStates[ri]);
++        if( !(knownState & SCARD_STATE_IGNORE))  {
++            continue;
++        }
++ 
++        curReaderName =  CKYReader_GetReaderName(&readerStates[ri]); 
++        if(readerNameExistsInList(curReaderName,&readerNames)) {
++            CKYReader_SetKnownState(&readerStates[ri], knownState & ~SCARD_STATE_IGNORE); 
++                 
++        }
++    } 
++
+     const char *newReadersData[MAX_READER_DELTA];
+     const char **newReaders = &newReadersData[0];
+     unsigned int newReaderCount = 0;
+@@ -528,7 +572,7 @@ SlotList::getSlotList(CK_BBOOL tokenPres
+ void
+ Slot::connectToToken()
+ {
+-    CKYStatus status;
++    CKYStatus status = CKYSCARDERR;
+     OSTime time = OSTimeNow();
+ 
+     mCoolkey = 0;
+@@ -537,13 +581,31 @@ Slot::connectToToken()
+ 
+     // try to connect to the card
+     if( ! CKYCardConnection_IsConnected(conn) ) {
+-        status = CKYCardConnection_Connect(conn, readerName);
+-        if( status != CKYSUCCESS ) {
+-            log->log("Unable to connect to token\n");
++        int i = 0;
++    //for cranky readers try again a few more times
++        while( i++ < 5 && status != CKYSUCCESS )
++        {
++            status = CKYCardConnection_Connect(conn, readerName);
++            if( status != CKYSUCCESS && 
++                CKYCardConnection_GetLastError(conn) == SCARD_E_PROTO_MISMATCH ) 
++            {
++                log->log("Unable to connect to token status %d ConnGetGetLastError %x .\n",status,CKYCardConnection_GetLastError(conn));
++
++            }
++            else
++            {
++                break;
++            }
++            OSSleep(100000);
++        }
++
++        if( status != CKYSUCCESS)
++        {
+             state = UNKNOWN;
+             return;
+         }
+     }
++
+     log->log("time connect: Connect Time %d ms\n", OSTimeNow() - time);
+     if (!slotInfoFound) {
+ 	readSlotInfo();
+@@ -562,15 +624,10 @@ Slot::connectToToken()
+         state = CARD_PRESENT;
+     }
+ 
+-    if ( CKYBuffer_DataIsEqual(&cardATR, ATR, sizeof (ATR)) || 
+-		CKYBuffer_DataIsEqual(&cardATR, ATR1, sizeof(ATR1)) ||
+-		CKYBuffer_DataIsEqual(&cardATR, ATR2, sizeof(ATR2)) ) {
+-
+-        if (Params::hasParam("noAppletOK"))
+-        {      
+-            state |=  APPLET_SELECTABLE;
+-	    mCoolkey = 1;
+-        }
++    if (Params::hasParam("noAppletOK"))
++    {      
++        state |=  APPLET_SELECTABLE;
++	mCoolkey = 1;
+     }
+ 
+     /* support CAC card. identify the card based on applets, not the ATRS */
+@@ -631,7 +688,7 @@ Slot::connectToToken()
+          * unfriendly */
+ 	isVersion1Key = 0;
+ 	needLogin = 1;
+-
++        mCoolkey = 0;
+ 	return;
+     }
+     mCoolkey = 1;
+@@ -1077,6 +1134,7 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
+ 	    }
+ 	    throw;
+ 	}
++
+ 	if (myNumReaders != numReaders) {
+ 	    if (myReaderStates) {
+ 		delete [] myReaderStates;
+@@ -1103,6 +1161,7 @@ SlotList::waitForSlotEvent(CK_FLAGS flag
+ 		}
+ 	    }
+ 	}
++
+         if (found || (flag == CKF_DONT_BLOCK) || shuttingDown) {
+             break;
+         }
+@@ -1272,6 +1331,19 @@ class ObjectHandleMatch {
+     }
+ };
+ 
++class KeyNumMatch {
++  private:
++    CKYByte keyNum;
++    const Slot &slot;
++  public:
++    KeyNumMatch(CKYByte keyNum_, const Slot &s) : keyNum(keyNum_), slot(s) { }
++    bool operator() (const PKCS11Object& obj) {
++        unsigned long objID = obj.getMuscleObjID();
++        return (slot.getObjectClass(objID) == 'k')
++               && (slot.getObjectIndex(objID) == keyNum);
++    }
++};
++
+ class ObjectCertCKAIDMatch {
+   private:
+     CKYByte cka_id;
+@@ -3007,8 +3079,9 @@ Slot::sign(SessionHandleSuffix suffix, C
+         CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+         CK_ULONG_PTR pulSignatureLen)
+ {
++    RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE);
+     cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen,
+-        RSASignatureParams(CryptParams::FIXED_KEY_SIZE));
++        params);
+ }
+ 
+ void
+@@ -3016,14 +3089,15 @@ Slot::decrypt(SessionHandleSuffix suffix
+         CK_ULONG ulDataLen, CK_BYTE_PTR pDecryptedData,
+         CK_ULONG_PTR pulDecryptedDataLen)
+ {
++    RSADecryptParams params(CryptParams::DEFAULT_KEY_SIZE);
+     cryptRSA(suffix, pData, ulDataLen, pDecryptedData, pulDecryptedDataLen,
+-        RSADecryptParams(CryptParams::FIXED_KEY_SIZE));
++        params);
+ }
+ 
+ void
+ Slot::cryptRSA(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
+         CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
+-        CK_ULONG_PTR pulOutputLen, const CryptParams& params)
++        CK_ULONG_PTR pulOutputLen, CryptParams& params)
+ {
+     refreshTokenState();
+     SessionIter session = findSession(suffix);
+@@ -3041,6 +3115,11 @@ Slot::cryptRSA(SessionHandleSuffix suffi
+     CKYBuffer *result = &opState.result;
+     CKYByte keyNum = opState.keyNum;
+ 
++    unsigned int keySize = getKeySize(keyNum);
++
++    if(keySize != CryptParams::DEFAULT_KEY_SIZE)
++        params.setKeySize(keySize);
++
+     if( CKYBuffer_Size(result) == 0 ) {
+         // we haven't already peformed the decryption, so do it now.
+         if( pInput == NULL || ulInputLen == 0) {
+@@ -3243,3 +3322,36 @@ Slot::generateRandom(SessionHandleSuffix
+ 	throw PKCS11Exception(CKR_DEVICE_ERROR);
+     }
+ }
++
++#define MAX_NUM_KEYS 8
++unsigned int
++Slot::getKeySize(CKYByte keyNum)
++{
++    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;
++    }
++
++    CKYBuffer const *modulus = iter->getAttribute(CKA_MODULUS);
++
++    if(modulus) {
++        modSize = CKYBuffer_Size(modulus);
++        if(CKYBuffer_GetChar(modulus,0) == 0x0) {
++            modSize--;
++        }
++        if(modSize > 0)
++            keySize = modSize * 8;
++    }
++
++    return keySize;
++}
+diff -up ./src/coolkey/slot.h.coolkey-latest ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.coolkey-latest	2006-06-09 11:39:11.000000000 -0700
++++ ./src/coolkey/slot.h	2009-09-11 13:58:24.462488099 -0700
+@@ -270,10 +270,9 @@ class CryptParams {
+   protected:
+     unsigned int getKeySize() const { return keySize; }
+   public:
+-    // !!!XXX hack. The right way to get the key size is to get all the
+-    // key information from the token with MSCListKeys, the same way
+-    // we get all the object information with MSCListObjects.
+-    enum { FIXED_KEY_SIZE = 1024 };
++    // set the actual key size obtained from the card
++    void setKeySize(unsigned int newKeySize) { keySize = newKeySize; }
++    enum { DEFAULT_KEY_SIZE = 1024 };
+ 
+ 
+     CryptParams(unsigned int keySize_) : keySize(keySize_) { }
+@@ -422,7 +421,7 @@ class Slot {
+ 
+     void cryptRSA(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
+         CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
+-        CK_ULONG_PTR pulOutputLen, const CryptParams& params);
++        CK_ULONG_PTR pulOutputLen, CryptParams& params);
+ 
+     void performRSAOp(CKYBuffer *out, const CKYBuffer *input, CKYByte keyNum, 
+ 							     CKYByte direction);
+@@ -460,6 +459,8 @@ class Slot {
+         return (char )((objectID >> 16) & 0xff) - '0';
+     }
+ 
++    // actually get the size of a key in bits from the card
++    unsigned int getKeySize(CKYByte keyNum);
+ 
+     SessionHandleSuffix openSession(Session::Type type);
+     void closeSession(SessionHandleSuffix handleSuffix);
+@@ -527,6 +528,8 @@ class SlotList {
+      * has called 'C_GetSlotList' with a NULL parameter */
+     void updateReaderList();
+ 
++     /* see if a reader name exists in a caller provided reader name list. */
++    bool readerNameExistsInList(const char *readerName,CKYReaderNameList *readerNameList );
+     bool readerExists(const char *readerName, unsigned int *hint = 0);
+   public:
+     SlotList(Log *log);
+diff -up ./src/libckyapplet/cky_applet.c.coolkey-latest ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.coolkey-latest	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_applet.c	2009-09-11 13:58:24.464487796 -0700
+@@ -134,6 +134,13 @@ CKYAppletFactory_Logout(CKYAPDU *apdu, c
+ /* Future add WriteObject */
+ 
+ CKYStatus
++CKYAppletFactory_WriteObject(CKYAPDU *apdu, const void *param)
++{
++    const CKYAppletArgWriteObject *wos = (const CKYAppletArgWriteObject *)param;
++    return CKYAPDUFactory_WriteObject(apdu,wos->objectID,wos->offset,wos->size,wos->data);
++}
++
++CKYStatus
+ CKYAppletFactory_CreateObject(CKYAPDU *apdu, const void *param)
+ {
+     const CKYAppletArgCreateObject *cos=(const CKYAppletArgCreateObject *)param;
+@@ -192,7 +199,6 @@ CKYAppletFactory_GetLifeCycleV2(CKYAPDU 
+ {
+     return CKYAPDUFactory_GetLifeCycleV2(apdu);
+ }
+-
+ CKYStatus
+ CKYAppletFactory_GetRandom(CKYAPDU *apdu, const void *param)
+ {
+@@ -725,24 +731,48 @@ CKYApplet_ComputeCrypt(CKYCardConnection
+     CKYAppletArgComputeCrypt ccd;
+     CKYBuffer    empty;
+     CKYISOStatus status;
++    short       dataSize = 0;
+     int         use2APDUs = 0;
++    int 	use_dl_object =  CKYBuffer_Size(data) > 200 ;
+ 
+     CKYBuffer_InitEmpty(&empty);
+     ccd.keyNumber = keyNumber;
+     ccd.mode      = mode;
+     ccd.direction = direction;
+-    ccd.location  = CKY_DL_APDU;
++    ccd.location  = use_dl_object ? CKY_DL_OBJECT : CKY_DL_APDU;
+ 
+     if (!apduRC)
+     	apduRC = &status;
+ 
++    if (use_dl_object) {
++	CKYBuffer  sizeBuf;
++ 
++	CKYBuffer_InitEmpty(&sizeBuf);
++	CKYBuffer_AppendShort(&sizeBuf, CKYBuffer_Size(data));
++
++        ret = CKYApplet_WriteObjectFull(conn, 0xffffffff,
++                  0, CKYBuffer_Size(&sizeBuf), nonce,
++                  &sizeBuf, apduRC);
++
++        CKYBuffer_FreeData(&sizeBuf);
++        if( ret != CKYSUCCESS)
++           goto fail;
++
++        ret = CKYApplet_WriteObjectFull(conn, 0xffffffff,
++                  2, CKYBuffer_Size(data), nonce,
++                  data, apduRC);
++
++        if(ret != CKYSUCCESS)
++           goto fail; 
++    }
++
+     if (mode == CKY_RSA_NO_PAD) {
+-	ccd.data = data;
++	ccd.data = use_dl_object ? &empty : data;
+ 	ccd.sig  = sig;
+ 	ret = CKYApplet_HandleAPDU(conn, 
+ 			    CKYAppletFactory_ComputeCryptOneStep, &ccd, nonce, 
+ 			    CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, 
+-			    result, apduRC);
++			    use_dl_object ? NULL : result, apduRC);
+     	if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) {
+ 	    use2APDUs = 1;  /* maybe it's an old applet */
+ 	}
+@@ -759,13 +789,38 @@ CKYApplet_ComputeCrypt(CKYCardConnection
+ 			    CKYAppletFactory_ComputeCryptInit, &ccd, nonce, 
+ 			    0, CKYAppletFill_Null, NULL, apduRC);
+ 	if (ret == CKYSUCCESS) {
+-	    ccd.data = data;
++	    ccd.data = use_dl_object ? &empty : data;
+ 	    ret = CKYApplet_HandleAPDU(conn, 
+ 			    CKYAppletFactory_ComputeCryptFinal, &ccd, nonce, 
+ 			    CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, 
+-			    result, apduRC);
++			    use_dl_object ? NULL : result, apduRC);
+ 	}
+     }
++
++    if (use_dl_object && ret == CKYSUCCESS) {
++        CKYBuffer  sizeOutBuf;
++        CKYBuffer_InitEmpty(&sizeOutBuf);
++
++        ret = CKYApplet_ReadObjectFull(conn,0xffffffff,
++                             0, 2,
++                             nonce,&sizeOutBuf,apduRC);
++
++        if(ret != CKYSUCCESS) {
++            CKYBuffer_FreeData(&sizeOutBuf);
++            goto fail;
++        }
++
++        dataSize = CKYBuffer_GetShort(&sizeOutBuf, 0);
++
++        CKYBuffer_FreeData(&sizeOutBuf);
++
++        ret = CKYApplet_ReadObjectFull(conn,0xffffffff, 
++                             2, dataSize,
++                             nonce,result,apduRC); 
++    }
++
++fail:
++
+     return ret;
+ }
+ 
+@@ -1036,6 +1091,44 @@ CKYApplet_ReadObjectFull(CKYCardConnecti
+ }
+ 
+ /*
++ * Write Object
++ * This makes multiple APDU calls to write the entire object.
++ *
++ */
++
++CKYStatus 
++CKYApplet_WriteObjectFull(CKYCardConnection *conn, unsigned long objectID,
++                  CKYOffset offset, CKYSize size, const CKYBuffer *nonce,
++                  const CKYBuffer *data, CKYISOStatus *apduRC)
++{
++
++    CKYBuffer chunk;
++    CKYOffset srcOffset = 0;
++    CKYAppletArgWriteObject wod;
++    CKYStatus ret = CKYSUCCESS;
++
++    wod.objectID = objectID;
++    wod.offset = offset;
++    do {
++        wod.size = (CKYByte) MIN(size, 220);
++        ret = CKYBuffer_InitFromBuffer(&chunk, data,
++                                       srcOffset, wod.size);
++        if(ret == CKYSUCCESS)  {
++            wod.data = &chunk;
++            ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_WriteObject, &wod,
++               nonce, 0, CKYAppletFill_Null, NULL, apduRC);
++            size -= wod.size;
++            wod.offset += wod.size;
++            srcOffset  += wod.size;
++            CKYBuffer_FreeData(&chunk);
++       }
++
++    } while ((size > 0) && (ret == CKYSUCCESS));
++
++    return ret;
++}
++
++/*
+  * List Object cluster
+  */
+ static CKYStatus
+diff -up ./src/libckyapplet/cky_applet.h.coolkey-latest ./src/libckyapplet/cky_applet.h
+--- ./src/libckyapplet/cky_applet.h.coolkey-latest	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_applet.h	2009-09-11 13:58:24.466487772 -0700
+@@ -192,6 +192,14 @@ typedef struct _CKYAppletArgReadObject {
+     CKYByte         size;
+ } CKYAppletArgReadObject;
+ 
++typedef struct _CKYAppletArgWriteObject {
++    unsigned long objectID;
++    CKYOffset     offset;
++    CKYByte       size;
++    CKYBuffer     *data;
++
++} CKYAppletArgWriteObject;
++
+ typedef struct _CKYAppletArgComputeCrypt {
+     CKYByte   keyNumber;
+     CKYByte   mode;
+@@ -250,6 +258,8 @@ CKYStatus CKYAppletFactory_ListPINs(CKYA
+ /* param == CKYByte * (pointer to pinNumber) */
+ CKYStatus CKYAppletFactory_Logout(CKYAPDU *apdu, const void *param);
+ /* Future add WriteObject */
++/* parm == CKYAppletArgWriteObject */
++CKYStatus CKYAppletFactory_WriteObject(CKYAPDU *apdu, const void *param);
+ /* param == CKYAppletArgCreateObject */
+ CKYStatus CKYAppletFactory_CreateObject(CKYAPDU *apdu, const void *param);
+ /* param == CKYAppletArgDeleteObject */
+@@ -482,6 +492,17 @@ CKYStatus CKYApplet_ReadObjectAppend(CKY
+ CKYStatus CKYApplet_ReadObjectFull(CKYCardConnection *conn, 
+ 		unsigned long objectID, CKYOffset offset, CKYSize size,
+ 		 const CKYBuffer *nonce, CKYBuffer *data, CKYISOStatus *apduRC);
++/*
++ * There is 1 write command:
++ * CKYApplet_WriteObjectFull can write an entire data object. It makes multiple
++ * apdu calls in order to write the full amount into the buffer. The buffer is
++ * overwritten.
++*/
++
++CKYStatus CKYApplet_WriteObjectFull(CKYCardConnection *conn,
++        unsigned long objectID, CKYOffset offset, CKYSize size,
++        const CKYBuffer *nonce, const CKYBuffer *data, CKYISOStatus *apduRC);
++
+ CKYStatus CKYApplet_ListObjects(CKYCardConnection *conn, CKYByte seq,
+ 		CKYAppletRespListObjects *lop, CKYISOStatus *apduRC);
+ CKYStatus CKYApplet_GetStatus(CKYCardConnection *conn, 
+diff -up ./src/libckyapplet/cky_card.c.coolkey-latest ./src/libckyapplet/cky_card.c
+--- ./src/libckyapplet/cky_card.c.coolkey-latest	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_card.c	2009-09-11 13:58:24.468487469 -0700
+@@ -129,6 +129,7 @@ typedef struct _SCard {
+     SCardGetStatusChangeFn SCardGetStatusChange;
+     SCardCancelFn SCardCancel;
+     SCARD_IO_REQUEST *SCARD_PCI_T0_;
++    SCARD_IO_REQUEST *SCARD_PCI_T1_;
+ } SCard;
+ 
+ #define GET_ADDRESS(library, scard, name) \
+@@ -195,6 +196,12 @@ ckySCard_Init(void)
+     if( status != CKYSUCCESS ) {
+         goto fail;
+     }
++
++    status = ckyShLibrary_getAddress( library,
++        (void**) &scard->SCARD_PCI_T1_, MAKE_DLL_SYMBOL(g_rgSCardT1Pci));
++    if( status != CKYSUCCESS ) {
++        goto fail;
++    }
+     return scard;
+ 
+ fail:
+@@ -884,6 +891,7 @@ struct _CKYCardConnection {
+     SCARDHANDLE      cardHandle;
+     unsigned long    lastError;
+     CKYBool           inTransaction;
++    unsigned long    protocol;
+ };
+ 
+ static void
+@@ -894,6 +902,7 @@ ckyCardConnection_init(CKYCardConnection
+     conn->cardHandle = 0;
+     conn->lastError = 0;
+     conn->inTransaction = 0;
++    conn->protocol = SCARD_PROTOCOL_T0;
+ }
+ 
+ CKYCardConnection *
+@@ -934,14 +943,13 @@ CKYCardConnection_Connect(CKYCardConnect
+ {
+     CKYStatus ret;
+     unsigned long rv;
+-    unsigned long protocol;
+ 
+     ret = CKYCardConnection_Disconnect(conn);
+     if (ret != CKYSUCCESS) {
+ 	return ret;
+     }
+     rv = conn->scard->SCardConnect( conn->ctx->context, readerName,
+-	SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &conn->cardHandle, &protocol);
++	SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &conn->cardHandle, &conn->protocol);
+     if (rv != SCARD_S_SUCCESS) {
+ 	conn->lastError = rv;
+ 	return CKYSCARDERR;
+@@ -978,7 +986,7 @@ ckyCardConnection_reconnectRaw(CKYCardCo
+     unsigned long protocol;
+ 
+     rv = conn->scard->SCardReconnect(conn->cardHandle,
+-	SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, init, &protocol);
++	SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 , init, &protocol);
+     if (rv != SCARD_S_SUCCESS) {
+ 	conn->lastError = rv;
+ 	return CKYSCARDERR;
+@@ -1039,10 +1047,17 @@ CKYCardConnection_TransmitAPDU(CKYCardCo
+ 	return ret;
+     }
+ 
+-    rv = conn->scard->SCardTransmit(conn->cardHandle, 
+-	conn->scard->SCARD_PCI_T0_,
+-	CKYBuffer_Data(&apdu->apduBuf), CKYBuffer_Size(&apdu->apduBuf), 
+-	NULL, response->data, &response->len);
++    if( conn->protocol == SCARD_PROTOCOL_T0 ) { 
++        rv = conn->scard->SCardTransmit(conn->cardHandle, 
++            conn->scard->SCARD_PCI_T0_,
++	    CKYBuffer_Data(&apdu->apduBuf), CKYBuffer_Size(&apdu->apduBuf), 
++	    NULL, response->data, &response->len);
++    }  else  {
++        rv = conn->scard->SCardTransmit(conn->cardHandle,
++            conn->scard->SCARD_PCI_T1_,
++            CKYBuffer_Data(&apdu->apduBuf), CKYBuffer_Size(&apdu->apduBuf),
++            NULL, response->data, &response->len);
++    } 
+ 
+     if (rv != SCARD_S_SUCCESS) {
+ 	conn->lastError =rv;
+diff -up ./src/libckyapplet/cky_factory.c.coolkey-latest ./src/libckyapplet/cky_factory.c
+--- ./src/libckyapplet/cky_factory.c.coolkey-latest	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_factory.c	2009-09-11 13:58:24.470495267 -0700
+@@ -190,8 +190,11 @@ CKYAPDUFactory_ComputeCryptOneStep(CKYAP
+     CKYSize   len;
+     CKYBuffer buf;
+ 
+-    if (!idata || !(len = CKYBuffer_Size(idata)) || location != CKY_DL_APDU)
+-    	return ret;
++    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_CRYPT);
+@@ -314,8 +317,6 @@ CKYAPDUFactory_Logout(CKYAPDU *apdu, CKY
+     return CKYSUCCESS;
+ }
+ 
+-/* Future add WriteObject */
+-
+ CKYStatus
+ CKYAPDUFactory_CreateObject(CKYAPDU *apdu, unsigned long objectID, CKYSize size,
+     unsigned short readACL, unsigned short writeACL, unsigned short deleteACL)
+@@ -419,6 +420,58 @@ fail:
+ }
+ 
+ CKYStatus
++CKYAPDUFactory_WriteObject(CKYAPDU *apdu, unsigned long objectID,
++                                    CKYOffset offset,CKYSize size,CKYBuffer *data)
++{
++    CKYBuffer buf;
++    CKYStatus ret = CKYSUCCESS;
++    unsigned short dataSize = 0;
++
++    CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY);
++    CKYAPDU_SetINS(apdu, CKY_INS_WRITE_OBJ);
++    CKYAPDU_SetP1(apdu, 0x00);
++    CKYAPDU_SetP2(apdu, 0x00);
++    CKYBuffer_InitEmpty(&buf);
++
++    dataSize = (unsigned short) CKYBuffer_Size(data);
++
++    if(!dataSize) {
++        ret = CKYINVALIDARGS;
++        goto fail;
++    }
++
++    ret = CKYBuffer_AppendLong(&buf,objectID);
++    if (ret != CKYSUCCESS) {
++        goto fail;
++    }
++    ret = CKYBuffer_AppendLong(&buf,offset);
++    if (ret != CKYSUCCESS) {
++        goto fail;
++    }
++    ret = CKYBuffer_AppendChar(&buf, size);
++    if (ret != CKYSUCCESS) {
++        goto fail;
++    }
++
++    ret = CKYAPDU_SetSendDataBuffer(apdu,&buf);
++
++    if (ret != CKYSUCCESS) {
++        goto fail;
++    }
++
++    ret = CKYAPDU_AppendSendDataBuffer(apdu, data);
++
++    if (ret != CKYSUCCESS) {
++        goto fail;
++    }
++
++fail:
++    CKYBuffer_FreeData(&buf);
++    return ret;
++
++}
++
++CKYStatus
+ CKYAPDUFactory_ListObjects(CKYAPDU *apdu, CKYByte sequence)
+ {
+     CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY);
+diff -up ./src/libckyapplet/cky_factory.h.coolkey-latest ./src/libckyapplet/cky_factory.h
+--- ./src/libckyapplet/cky_factory.h.coolkey-latest	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_factory.h	2009-09-11 13:58:24.472487421 -0700
+@@ -190,7 +190,8 @@ CKYStatus CKYAPDUFactory_ChangePIN(CKYAP
+ 				const char *oldPin, const char *newPin);
+ CKYStatus CKYAPDUFactory_ListPINs(CKYAPDU *apdu);
+ CKYStatus CKYAPDUFactory_Logout(CKYAPDU *apdu, CKYByte pinNumber);
+-
++CKYStatus CKYAPDUFactory_WriteObject(CKYAPDU *apdu, unsigned long objectID,
++                CKYOffset offset,CKYSize size,CKYBuffer *data);
+ /* Future add WriteObject */
+ CKYStatus CKYAPDUFactory_CreateObject(CKYAPDU *apdu, unsigned long objectID,
+  CKYSize size, unsigned short readACL, unsigned short writeACL, 
diff --git a/SOURCES/coolkey-pcsc-lite-fix.patch b/SOURCES/coolkey-pcsc-lite-fix.patch
new file mode 100644
index 0000000..f528676
--- /dev/null
+++ b/SOURCES/coolkey-pcsc-lite-fix.patch
@@ -0,0 +1,69 @@
+diff -up ./src/coolkey/slot.cpp.reader-state-fix ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.reader-state-fix	2010-09-08 13:25:14.479109000 -0700
++++ ./src/coolkey/slot.cpp	2010-09-08 13:25:14.506109000 -0700
+@@ -2185,6 +2185,7 @@ Slot::readCACCertificateFirst(CKYBuffer 
+ {
+     CKYStatus status;
+     CKYISOStatus apduRC;
++    *nextSize = 0;
+ 
+     if (mOldCAC) {
+ 	/* get the first 100 bytes of the cert */
+diff -up ./src/libckyapplet/cky_card.c.reader-state-fix ./src/libckyapplet/cky_card.c
+--- ./src/libckyapplet/cky_card.c.reader-state-fix	2010-09-08 14:05:10.859321000 -0700
++++ ./src/libckyapplet/cky_card.c	2010-09-08 14:05:42.792257000 -0700
+@@ -27,7 +27,6 @@
+ 
+ #ifndef WINAPI
+ #define WINAPI
+-typedef SCARD_READERSTATE *LPSCARD_READERSTATE;
+ #endif
+ 
+ #ifndef SCARD_E_NO_READERS_AVAILABLE
+diff -up ./src/libckyapplet/cky_card.h.reader-state-fix ./src/libckyapplet/cky_card.h
+--- ./src/libckyapplet/cky_card.h.reader-state-fix	2006-06-09 11:44:17.000000000 -0700
++++ ./src/libckyapplet/cky_card.h	2010-09-08 13:25:14.518109000 -0700
+@@ -41,23 +41,23 @@ CKYLIST_DECLARE(CKYReaderName, char *)
+ CKYLIST_DECLARE(CKYCardConnection, CKYCardConnection *)
+ 
+ CKY_BEGIN_PROTOS
+-void CKYReader_Init(SCARD_READERSTATE_A *reader);
+-void CKYReader_FreeData(SCARD_READERSTATE_A *reader);
++void CKYReader_Init(SCARD_READERSTATE *reader);
++void CKYReader_FreeData(SCARD_READERSTATE *reader);
+ 
+ /*
+- * "Accessors": for SCARD_READERSTATE_A structure as a class.
+- * These functions take an SCARD_READERSTATE_A which can also be referenced
++ * "Accessors": for SCARD_READERSTATE structure as a class.
++ * These functions take an SCARD_READERSTATE which can also be referenced
+  * directly.
+  */
+-CKYStatus CKYReader_SetReaderName(SCARD_READERSTATE_A *reader, const char *name);
+-const char *CKYReader_GetReaderName(const SCARD_READERSTATE_A *reader);
+-CKYStatus CKYReader_SetKnownState(SCARD_READERSTATE_A *reader, 
++CKYStatus CKYReader_SetReaderName(SCARD_READERSTATE *reader, const char *name);
++const char *CKYReader_GetReaderName(const SCARD_READERSTATE *reader);
++CKYStatus CKYReader_SetKnownState(SCARD_READERSTATE *reader, 
+ 						unsigned long state);
+-unsigned long CKYReader_GetKnownState(const SCARD_READERSTATE_A *reader);
+-unsigned long CKYReader_GetEventState(const SCARD_READERSTATE_A *reader);
+-CKYStatus CKYReader_GetATR(const SCARD_READERSTATE_A *reader, CKYBuffer *buf);
++unsigned long CKYReader_GetKnownState(const SCARD_READERSTATE *reader);
++unsigned long CKYReader_GetEventState(const SCARD_READERSTATE *reader);
++CKYStatus CKYReader_GetATR(const SCARD_READERSTATE *reader, CKYBuffer *buf);
+ /* create an array of READERSTATEs from a LIST of Readers */
+-SCARD_READERSTATE_A *CKYReader_CreateArray(const CKYReaderNameList readerNames, 
++SCARD_READERSTATE *CKYReader_CreateArray(const CKYReaderNameList readerNames, 
+ 					  unsigned long *readerCount);
+ /* frees the reader, then the full array */
+ void CKYReader_DestroyArray(SCARD_READERSTATE *reader, unsigned long count);
+@@ -88,7 +88,7 @@ CKYStatus CKYCardContext_FindReadersByAT
+ 				const CKYBuffer *targetATR);
+ /* return if any of the readers in our array has changed in status */
+ CKYStatus CKYCardContext_WaitForStatusChange(CKYCardContext *context,
+-				SCARD_READERSTATE_A *readers,
++				SCARD_READERSTATE *readers,
+ 				unsigned long readerCount,
+ 				unsigned long timeout);
+ /* cancel any current operation (such as wait for status change) on this
diff --git a/SOURCES/coolkey-piv-ecc-el7.patch b/SOURCES/coolkey-piv-ecc-el7.patch
new file mode 100644
index 0000000..1ca6f61
--- /dev/null
+++ b/SOURCES/coolkey-piv-ecc-el7.patch
@@ -0,0 +1,4792 @@
+diff -up ./src/coolkey/coolkey.cpp.piv-ecc ./src/coolkey/coolkey.cpp
+--- ./src/coolkey/coolkey.cpp.piv-ecc	2013-09-08 15:50:33.085428102 -0700
++++ ./src/coolkey/coolkey.cpp	2013-09-08 15:50:33.119428673 -0700
+@@ -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;
+diff -up ./src/coolkey/machdep.cpp.piv-ecc ./src/coolkey/machdep.cpp
+--- ./src/coolkey/machdep.cpp.piv-ecc	2013-09-08 15:50:33.085428102 -0700
++++ ./src/coolkey/machdep.cpp	2013-09-08 15:50:33.119428673 -0700
+@@ -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.piv-ecc ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.piv-ecc	2013-09-08 15:50:33.104428421 -0700
++++ ./src/coolkey/object.cpp	2013-09-08 15:50:33.121428706 -0700
+@@ -25,12 +25,44 @@
+ 
+ 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(CKYBuffer *buf)
++{
++    CKYSize i;
++    CKYSize size = CKYBuffer_Size(buf);
++#define ROW_LENGTH 60
++    char string[ROW_LENGTH+1];
++    char *bp = &string[0];
++    CKYByte c;
++
++    for (i=0; i < size; i++) {
++        if (i && ((i % (ROW_LENGTH-1)) == 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-1)); 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,14 +77,14 @@ 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), name(NULL), keyType(unknown)
+ { 
+     CKYBuffer_InitEmpty(&pubKey);
+ }
+ 
+ PKCS11Object::PKCS11Object(unsigned long muscleObjID_, const CKYBuffer *data,
+     CK_OBJECT_HANDLE handle_) :  muscleObjID(muscleObjID_), handle(handle_),
+-			label(NULL), name(NULL)
++                        label(NULL), name(NULL), keyType(unknown)
+ {
+     CKYBuffer_InitEmpty(&pubKey);
+ 
+@@ -63,9 +95,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 +216,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 +298,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 +345,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 +397,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 +406,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 +446,7 @@ PKCS11Object::getAttribute(CK_ATTRIBUTE_
+             AttributeTypeMatch(type));
+ 
+     if( iter == attributes.end() ) {
+-	return NULL;
++        return NULL;
+     }
+     return iter->getValue();
+ }
+@@ -349,8 +470,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 +493,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 +528,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 +553,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,7 +575,7 @@ 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));
+     }
+@@ -505,9 +627,15 @@ dataStart(const CKYByte *buf, unsigned i
+     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 +649,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;
+@@ -542,16 +677,158 @@ unwrapBitString(const CKYByte *buf, unsi
+ {
+     /* 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
++GetECKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length,
++        CCItem *point, CCItem *params)
++{
++    const CKYByte *buf = spki_data;
++    unsigned int buf_length = spki_length;
++    const CKYByte *algid;
++    unsigned int algidlen;
++    const CKYByte *dummy;
++    unsigned int 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, unsigned int spki_length,
++       CCItem *algorithmId)
++{
++
++    const CKYByte *buf = spki_data;
++    unsigned int 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.");
++    }
++
++    unsigned int 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,unsigned int spki_length,
+         CCItem *modulus, CCItem *exponent)
+ {
+@@ -596,7 +873,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 +884,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,22 +916,41 @@ 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);
+     }
+ }
+ 
+@@ -640,32 +959,59 @@ Key::completeKey(const PKCS11Object &cer
+ {
+     // 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
+@@ -737,14 +1083,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 +1115,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 +1168,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 +1179,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 +1191,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 +1210,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 +1264,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,34 +1281,49 @@ 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 };
+@@ -959,39 +1340,43 @@ GetCN(const CKYByte *dn, unsigned int dn
+     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;
++        unsigned int name_length;
++        const CKYByte *oid;
++        unsigned int 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;
+-	}
++        /* 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;
+-
+-	/* 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 +1391,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,8 +1403,8 @@ 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;
+@@ -1028,7 +1413,7 @@ CACCert::CACCert(CKYByte instance, const
+     /* 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);
+@@ -1050,19 +1435,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 +1455,100 @@ CACCert::CACCert(CKYByte instance, const
+     CKYBuffer_FreeData(&derSubject);
+     CKYBuffer_FreeData(&derIssuer);
+ }
++
++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);
++
++    unsigned int seq_length = 0;
++    unsigned int expected_sig_len = ( (keySize + 7) / 8 ) * 2 ;
++    unsigned int 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
++   
++    unsigned int 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
++
++    unsigned int 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;
++}
+diff -up ./src/coolkey/object.h.piv-ecc ./src/coolkey/object.h
+--- ./src/coolkey/object.h.piv-ecc	2013-09-08 15:50:33.081428035 -0700
++++ ./src/coolkey/object.h	2013-09-08 15:50:33.121428706 -0700
+@@ -49,7 +49,7 @@ 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() { CKYBuffer_FreeData(&value); }
+@@ -57,6 +57,11 @@ class PKCS11Attribute {
+ 
+ class PKCS11Object {
+   public:
++    enum KeyType {
++        rsa,
++        ecc,
++        unknown
++    };
+ 
+     typedef list<PKCS11Attribute> AttributeList;
+     typedef AttributeList::iterator AttributeIter;
+@@ -75,18 +80,20 @@ class PKCS11Object {
+     PKCS11Object &operator=(PKCS11Object &cpy) { return *this; } //Disallow
+ 
+   protected :
+-    CKYBuffer pubKey; 
+     char *name;
++    KeyType keyType;
++    CKYBuffer pubKey;
+ 
+   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); }
++    ~PKCS11Object() { delete label; delete name; CKYBuffer_FreeData(&pubKey);
++			attributes.clear(); }
+ 
+     PKCS11Object(const PKCS11Object& cpy) :
+         attributes(cpy.attributes), muscleObjID(cpy.muscleObjID),
+-        handle(cpy.handle), label(NULL),  name(NULL) { 
++        handle(cpy.handle), label(NULL),  name(NULL), keyType(cpy.keyType) { 
+ 			CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey); }
+ 
+ 
+@@ -116,14 +123,15 @@ class PKCS11Object {
+     const CKYBuffer *getPubKey(void) const {
+ 	return &pubKey;
+     }
++
++    KeyType getKeyType() const { return keyType;}
++    void setKeyType(KeyType theType) { keyType = theType; }
+ };
+ 
+ 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 {
+@@ -153,6 +161,25 @@ class Reader : public PKCS11Object {
+ 		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 AttributeMatch {
+ 
+   private:
+diff -up ./src/coolkey/pkcs11t.h.piv-ecc ./src/coolkey/pkcs11t.h
+--- ./src/coolkey/pkcs11t.h.piv-ecc	2006-06-09 11:39:11.000000000 -0700
++++ ./src/coolkey/pkcs11t.h	2013-09-08 15:50:33.122428723 -0700
+@@ -1351,4 +1351,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.piv-ecc ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.piv-ecc	2013-09-08 15:50:33.112428555 -0700
++++ ./src/coolkey/slot.cpp	2013-09-08 15:50:33.124428757 -0700
+@@ -54,6 +54,34 @@ const CKYByte ATR2[] =
+ {  0x3B, 0x6F, 0x00, 0xFF, 0x52, 0x53, 0x41, 0x53, 0x65, 0x63, 0x75, 0x72,
+    0x49, 0x44, 0x28, 0x52, 0x29, 0x31, 0x30 };
+ 
++
++/* 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 +164,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 +269,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 +291,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 +315,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;
+@@ -370,7 +407,8 @@ Slot::Slot(const char *readerName_, Log
+     : log(log_), readerName(NULL), personName(NULL), manufacturer(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), 
+ #ifdef USE_SHMEM
+ 	shmem(readerName_),
+ #endif
+@@ -573,6 +611,35 @@ 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 true;
++}
++
+ void
+ Slot::connectToToken()
+ {
+@@ -587,6 +654,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 +740,36 @@ 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 = 1;
++         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);
++	    if (status == CKYSCARDERR) {
++		    log->log("Card Failure 0x%x\n",
++				CKYCardConnection_GetLastError(conn));
++		    disconnect();
++	    }
++	    /* CARD is a PIV card */
++	    state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
++	    isVersion1Key = 0;
++	    needLogin = 1;
++            mCoolkey = 0;
++	    mOldCAC = 0;
++	    mCACLocalLogin = getPIVLoginType();
++	    return;
+ 	}
+ 	state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+ 	/* skip the read of the cuid. We really don't need it and,
+@@ -687,15 +779,7 @@ Slot::connectToToken()
+ 	isVersion1Key = 0;
+ 	needLogin = 1;
+         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 +846,8 @@ Slot::invalidateLogin(bool hard)
+ 	}
+     } else {
+ 	loggedIn = false;
++	pinCache.invalidate();
+ 	if (hard) {
+-	    pinCache.invalidate();
+ 	    pinCache.clearPin();
+ 	}
+     }
+@@ -1202,6 +1286,7 @@ Slot::getTokenInfo(CK_TOKEN_INFO_PTR pTo
+ 
+ 
+     return CKR_OK;
++
+ }
+ 
+ void
+@@ -1222,7 +1307,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 +1335,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 +1358,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 +1407,20 @@ 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_READER_UNAVAILABLE) ||
++       (CKYCardContext_GetLastError(context) == SCARD_E_NO_SERVICE) ||
++       (CKYCardContext_GetLastError(context) == SCARD_E_SERVICE_STOPPED) );
+ #else
+     do {
+ 	OSSleep(100);
+@@ -1345,6 +1457,7 @@ Slot::handleConnectionError()
+       case SCARD_W_REMOVED_CARD:
+         ckrv = CKR_DEVICE_REMOVED;
+         break;
++      
+       default:
+         ckrv = CKR_DEVICE_ERROR;
+         break;
+@@ -1404,13 +1517,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 +1565,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 +1576,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);
+     }
+ }
+@@ -1521,6 +1671,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 +1703,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(CKYBuffer_GetChar(id,0)));
++        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,6 +1758,7 @@ Slot::addCertObject(list<PKCS11Object>&
+ void
+ Slot::unloadObjects()
+ {
++    mECC = false;
+     tokenObjects.clear();
+     free(personName);
+     personName = NULL;
+@@ -1970,7 +2152,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 +2385,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 +2437,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 +2503,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 +2529,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 +2544,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 +2761,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 +2779,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 +2825,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 +2854,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;
+ 	    }
+-	    certSize = guessFinalSize;
+-	    zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize,
+-			CKYBuffer_Data(&rawCert)+offset, 
+-			CKYBuffer_Size(&rawCert)-offset);
+-	} while (zret == Z_BUF_ERROR);
++	    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;
++	    }
++	    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 +2922,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 +2953,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);
+@@ -2704,6 +3198,7 @@ Slot::login(SessionHandleSuffix handleSu
+     }
+ 
+     if (!isVersion1Key) {
++	pinCache.invalidate();
+ 	pinCache.set((const char *)pPin, ulPinLen);
+     } else if (nonceValid) {
+ 	throw PKCS11Exception(CKR_USER_ALREADY_LOGGED_IN);
+@@ -2713,15 +3208,15 @@ Slot::login(SessionHandleSuffix handleSu
+     CKYStatus status = trans.begin(conn);
+     if(status != CKYSUCCESS ) handleConnectionError();
+ 
+-    if (state & CAC_CARD) {
+-	selectCACApplet(0);
++    if (state & GOV_CARD) {
++	selectCACApplet(0, true);
+     } else {
+ 	selectApplet();
+     }
+ 
+     if (isVersion1Key) {
+ 	attemptLogin((const char *)pPin);
+-    } else if (state & CAC_CARD) {
++    } else if (state & GOV_CARD) {
+ 	attemptCACLogin();
+     } else {
+ 	oldAttemptLogin();
+@@ -2738,7 +3233,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 +3242,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);
+ 	}
+@@ -2776,10 +3274,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);
+     }
+@@ -2866,7 +3367,7 @@ Slot::logout(SessionHandleSuffix suffix)
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+ 
+-    if (state & CAC_CARD) {
++    if (state & GOV_CARD) {
+ 	CACLogout();
+ 	return;
+     }
+@@ -2993,7 +3494,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 +3578,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) ) {
+@@ -3110,6 +3626,23 @@ Slot::objectHandleToKeyNum(CK_OBJECT_HAN
+     return keyNum & 0xFF;
+ }
+ 
++PKCS11Object::KeyType
++Slot::getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey)
++{
++    ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
++        ObjectHandleMatch(hKey));
++
++    if( iter == tokenObjects.end() ) {
++         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
++    }
++
++    if( getObjectClass(iter->getMuscleObjID()) != 'k' ) {
++        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
++    }
++
++    return iter->getKeyType();
++}
++
+ void
+ Slot::signInit(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism,
+         CK_OBJECT_HANDLE hKey)
+@@ -3119,7 +3652,10 @@ Slot::signInit(SessionHandleSuffix suffi
+     if( session == sessions.end() ) {
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+-    session->signatureState.initialize(objectHandleToKeyNum(hKey));
++
++    PKCS11Object::KeyType  keyType = getKeyTypeFromHandle(hKey);
++
++    session->signatureState.initialize(objectHandleToKeyNum(hKey), keyType);
+ }
+ 
+ void
+@@ -3131,7 +3667,10 @@ Slot::decryptInit(SessionHandleSuffix su
+     if( session == sessions.end() ) {
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+-    session->decryptionState.initialize(objectHandleToKeyNum(hKey));
++
++    PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey);
++
++    session->decryptionState.initialize(objectHandleToKeyNum(hKey), keyType);
+ }
+ 
+ /**
+@@ -3240,54 +3779,141 @@ 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
++        return;
++    }
++
++};
++
++  
++
++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;
+     }
+@@ -3298,9 +3924,38 @@ Slot::sign(SessionHandleSuffix suffix, C
+         CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+         CK_ULONG_PTR pulSignatureLen)
+ {
+-    RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE);
+-    cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen,
+-        params);
++
++    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.keyType;
++   
++    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
+@@ -3334,9 +3989,9 @@ Slot::cryptRSA(SessionHandleSuffix suffi
+     CKYBuffer *result = &opState.result;
+     CKYByte keyNum = opState.keyNum;
+ 
+-    unsigned int keySize = getKeySize(keyNum);
++    unsigned int keySize = getRSAKeySize(keyNum);
+ 
+-    if(keySize != CryptParams::DEFAULT_KEY_SIZE)
++    if (keySize != CryptParams::DEFAULT_KEY_SIZE)
+         params.setKeySize(keySize);
+ 
+     if( CKYBuffer_Size(result) == 0 ) {
+@@ -3358,7 +4013,8 @@ Slot::cryptRSA(SessionHandleSuffix suffi
+   	}
+ 	try {
+ 	    params.padInput(&inputPad, &input);
+-            performRSAOp(&output, &inputPad, keyNum, params.getDirection());
++            performRSAOp(&output, &inputPad, params.getKeySize(), 
++								keyNum, params.getDirection());
+ 	    params.unpadOutput(result, &output);
+ 	    CKYBuffer_FreeData(&input);
+ 	    CKYBuffer_FreeData(&inputPad);
+@@ -3395,10 +4051,166 @@ 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;
++    CKYByte keyNum = opState.keyNum;
++
++    unsigned int keySize = getECCKeySize(keyNum);
++
++    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(), keyNum);
++            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::performECCSignature(CKYBuffer *output, const CKYBuffer *input, 
++						unsigned int keySize, CKYByte keyNum)
++{
++
++    /* establish a transaction */
++    Transaction trans;
++    CKYStatus status = trans.begin(conn);
++    if( status != CKYSUCCESS ) handleConnectionError();
++
++    if (!mECC) {
++        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
++    }
++
++    if (state & GOV_CARD) {
++	selectCACApplet(keyNum, true);
++    } else {
++	selectApplet();
++    }
++
++    CKYISOStatus result;
++    int loginAttempted = 0;
++
++retry:
++    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 {
++        status = CKYApplet_ComputeECCSignature(conn, keyNum, 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);
++        }
++        /* version0 keys could be logged out in the middle by someone else,
++           reauthenticate... This code can go away when we depricate.
++           version0 applets.
++        */
++        if (!isVersion1Key && !loginAttempted  &&
++                    (result == CKYISO_UNAUTHORIZED)) {
++            /* try to reauthenticate  */
++	    try {
++		if (state & GOV_CARD) {
++		    attemptCACLogin();
++		} else {
++		    oldAttemptLogin();
++		}
++            } 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, 
++Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, unsigned int keySize,
+ 					CKYByte keyNum, CKYByte direction)
+ {
++    if ( mECC ) {
++        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
++    }
++
+     //
+     // establish a transaction
+     //
+@@ -3409,8 +4221,8 @@ Slot::performRSAOp(CKYBuffer *output, co
+     //
+     // select the applet
+     //
+-    if (state & CAC_CARD) {
+-	selectCACApplet(keyNum);
++    if (state & GOV_CARD) {
++	selectCACApplet(keyNum, true);
+     } else {
+ 	selectApplet();
+     }
+@@ -3418,12 +4230,21 @@ Slot::performRSAOp(CKYBuffer *output, co
+     CKYISOStatus result;
+     int loginAttempted = 0;
+ retry:
+-    if (state & CAC_CARD) {
++    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 {
+         status = CKYApplet_ComputeCrypt(conn, keyNum, 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();
+@@ -3434,11 +4255,15 @@ retry:
+ 	// version0 keys could be logged out in the middle by someone else,
+ 	// reauthenticate... This code can go away when we depricate.
+         // version0 applets.
+-	if (!isVersion1Key && !loginAttempted  && 
++	if (!isVersion1Key && !loginAttempted  && pinCache.isValid() &&
+ 					(result == CKYISO_UNAUTHORIZED)) {
+ 	    // try to reauthenticate 
+ 	    try {
+-		oldAttemptLogin();
++		if (state & GOV_CARD) {
++		    attemptCACLogin();
++		} else {
++		    oldAttemptLogin();
++		}
+ 	    } catch(PKCS11Exception& ) {
+ 		// attemptLogin can throw things like CKR_PIN_INCORRECT
+ 		// that don't make sense from a crypto operation. This is
+@@ -3458,7 +4283,7 @@ void
+ Slot::seedRandom(SessionHandleSuffix suffix, CK_BYTE_PTR pData,
+         CK_ULONG ulDataLen)
+ {
+-    if (state & CAC_CARD) {
++    if (state & GOV_CARD) {
+ 	/* should throw unsupported */
+ 	throw PKCS11Exception(CKR_DEVICE_ERROR);
+     }
+@@ -3510,7 +4335,7 @@ void
+ Slot::generateRandom(SessionHandleSuffix suffix, const CK_BYTE_PTR pData,
+         CK_ULONG ulDataLen)
+ {
+-    if (state & CAC_CARD) {
++    if (state & GOV_CARD) {
+ 	/* should throw unsupported */
+ 	throw PKCS11Exception(CKR_DEVICE_ERROR);
+     }
+@@ -3544,7 +4369,7 @@ Slot::generateRandom(SessionHandleSuffix
+ 
+ #define MAX_NUM_KEYS 8
+ unsigned int
+-Slot::getKeySize(CKYByte keyNum)
++Slot::getRSAKeySize(CKYByte keyNum)
+ {
+     unsigned int keySize = CryptParams::DEFAULT_KEY_SIZE;
+     int modSize = 0;
+@@ -3574,3 +4399,238 @@ Slot::getKeySize(CKYByte keyNum)
+ 
+     return keySize;
+ }
++
++unsigned int
++Slot::getECCKeySize(CKYByte keyNum)
++{
++    return calcECCKeySize(keyNum);
++} 
++
++unsigned int
++Slot::calcECCKeySize(CKYByte keyNum)
++{
++    unsigned int keySize = CryptParams::ECC_DEFAULT_KEY_SIZE;
++
++    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;
++    }
++
++    CKYBuffer const *eccParams = iter->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;
++            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);
++
++    PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hBaseKey);
++
++    session->keyAgreementState.initialize(objectHandleToKeyNum(hBaseKey), keyType);
++    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;
++    CKYByte keyNum = opState.keyNum;
++
++    unsigned int keySize = getECCKeySize(keyNum);
++
++    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,
++			 keyNum, 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, CKYByte keyNum, unsigned int keySize)
++{
++    if (!mECC) {
++       throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
++    }
++
++    Transaction trans;
++    CKYStatus status = trans.begin(conn);
++    if( status != CKYSUCCESS ) handleConnectionError();
++
++    if (state & GOV_CARD) {
++	selectCACApplet(keyNum, true);
++    } else {
++	selectApplet();
++    }
++
++    CKYISOStatus result;
++    int loginAttempted = 0;
++
++retry:
++
++    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 {
++    	status = CKYApplet_ComputeECCKeyAgreement(conn, keyNum, 
++			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  &&
++            (result == CKYISO_UNAUTHORIZED)) {
++	    try {
++		if (state & GOV_CARD) {
++		    attemptCACLogin();
++		} else {
++		    oldAttemptLogin();
++		}
++	    } 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.piv-ecc ./src/coolkey/slot.h
+--- ./src/coolkey/slot.h.piv-ecc	2013-09-08 15:50:33.098428320 -0700
++++ ./src/coolkey/slot.h	2013-09-08 15:50:33.125428774 -0700
+@@ -211,24 +211,27 @@ class CryptOpState {
+     State state;
+     CKYByte keyNum;
+     CKYBuffer result;
++    PKCS11Object::KeyType keyType;
+ 
+-    CryptOpState() : state(NOT_INITIALIZED), keyNum(0) 
++    CryptOpState() : state(NOT_INITIALIZED), keyNum(0), keyType(PKCS11Object::unknown)
+ 				{ CKYBuffer_InitEmpty(&result); }
+     CryptOpState(const CryptOpState &cpy) : 
+-				state(cpy.state), keyNum(cpy.keyNum) { 
++				state(cpy.state), keyNum(cpy.keyNum), keyType(cpy.keyType) { 
+ 	CKYBuffer_InitFromCopy(&result, &cpy.result);
+     }
+     CryptOpState &operator=(const CryptOpState &cpy) {
+ 	state = cpy.state,
+ 	keyNum = cpy.keyNum;
++        keyType = cpy.keyType;
+ 	CKYBuffer_Replace(&result, 0, CKYBuffer_Data(&cpy.result),
+ 				CKYBuffer_Size(&cpy.result));
+ 	return *this;
+     }
+     ~CryptOpState() { CKYBuffer_FreeData(&result); }
+-    void initialize(CKYByte keyNum) {
++    void initialize(CKYByte keyNum, PKCS11Object::KeyType theKeyType) {
+         state = IN_PROCESS;
+         this->keyNum = keyNum;
++        this->keyType = theKeyType;
+         CKYBuffer_Resize(&result, 0);
+     }
+ };
+@@ -258,6 +261,7 @@ class Session {
+ 
+     CryptOpState signatureState;
+     CryptOpState decryptionState;
++    CryptOpState keyAgreementState;
+ };
+ 
+ typedef list<Session> SessionList;
+@@ -267,12 +271,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_) { }
+@@ -304,12 +307,15 @@ class Slot {
+         ATR_MATCH = 0x04,
+         APPLET_SELECTABLE = 0x08,
+         APPLET_PERSONALIZED = 0x10,
+-        CAC_CARD = 0x20
++        CAC_CARD = 0x20,
++        PIV_CARD = 0x40
+     };
+     enum {
+ 	NONCE_SIZE = 8
+     };
+ 
++    static const SlotState GOV_CARD = (SlotState)(CAC_CARD|PIV_CARD);
++
+   private:
+     Log *log;
+     char *readerName;
+@@ -339,7 +345,10 @@ class Slot {
+     bool fullTokenName;
+     bool mCoolkey;
+     bool mOldCAC;
+-
++    bool mCACLocalLogin;
++    int pivContainer;
++    int pivKey;
++    bool mECC;
+     //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 };
+ 
+ #ifdef USE_SHMEM
+@@ -386,6 +395,7 @@ class Slot {
+     const CKYBuffer *getATR();
+     bool isLoggedIn();
+     bool needLoggedIn();
++    bool getPIVLoginType();
+     void testNonce();
+ 
+     void addKeyObject(list<PKCS11Object>& objectList,
+@@ -395,6 +405,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);
+ 
+@@ -408,7 +419,7 @@ class Slot {
+     CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize);
+ 
+     void selectApplet();
+-    void selectCACApplet(CKYByte instance);
++    void selectCACApplet(CKYByte instance,bool do_disconnect);
+     void unloadObjects();
+     void loadCACObjects();
+     void loadCACCert(CKYByte instance);
+@@ -432,12 +443,23 @@ class Slot {
+         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,
++						CKYByte keyNum, 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, CKYByte keyNum);
++    void performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
++        CKYBuffer *publicDataBuffer, 
++        CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize);
+ 
+     void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu);
+ 
+     CKYByte objectHandleToKeyNum(CK_OBJECT_HANDLE hKey);
++    unsigned int calcECCKeySize(CKYByte keyNum);
+     Slot(const Slot &cpy)
+ #ifdef USE_SHMEM
+ 	: shmem(readerName)
+@@ -469,7 +491,10 @@ class Slot {
+     }
+ 
+     // actually get the size of a key in bits from the card
+-    unsigned int getKeySize(CKYByte keyNum);
++    unsigned int getRSAKeySize(CKYByte keyNum);
++    unsigned int getECCKeySize(CKYByte keyNum);
++
++    PKCS11Object::KeyType  getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey);
+ 
+     SessionHandleSuffix openSession(Session::Type type);
+     void closeSession(SessionHandleSuffix handleSuffix);
+@@ -511,6 +536,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 {
+@@ -604,6 +639,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.piv-ecc ./src/install/pk11install.c
+--- ./src/install/pk11install.c.piv-ecc	2007-02-06 11:40:36.000000000 -0800
++++ ./src/install/pk11install.c	2013-09-08 15:50:33.126428790 -0700
+@@ -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.piv-ecc ./src/libckyapplet/cky_applet.c
+--- ./src/libckyapplet/cky_applet.c.piv-ecc	2013-09-08 15:50:33.099428337 -0700
++++ ./src/libckyapplet/cky_applet.c	2013-09-08 15:50:33.126428790 -0700
+@@ -103,6 +103,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 +261,25 @@ CACAppletFactory_SignDecryptFinal(CKYAPD
+ }
+ 
+ CKYStatus
++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
+ CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
+ {
+     const char *pin=(const char *)param;
+-    return CACAPDUFactory_VerifyPIN(apdu, pin);
++    return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin);
++}
++
++CKYStatus
++PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
++{
++    const char *pin=(const char *)param;
++    return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin);
+ }
+ 
+ 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;
+@@ -325,6 +363,7 @@ CKYAppletFill_AppendBuffer(const CKYBuff
+ 						CKYBuffer_Size(response) -2);
+ }
+ 
++
+ CKYStatus
+ CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param)
+ {
+@@ -725,6 +764,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 +937,77 @@ 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)
++{
++    int         use2APDUs = 0;
++    int         use_dl_object =  0; 
++    short       dataSize = 0;
++    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
+  */
+@@ -920,7 +1056,7 @@ done:
+  * 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;
+@@ -929,7 +1065,7 @@ CACApplet_VerifyPIN(CKYCardConnection *c
+ 	apduRC = &status;
+     }
+ 
+-    ret = CKYApplet_HandleAPDU(conn, 
++    ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN :
+ 			    CACAppletFactory_VerifyPIN, pin, NULL, 
+ 			    0, CKYAppletFill_Null, 
+ 			    NULL, apduRC);
+@@ -942,6 +1078,7 @@ CACApplet_VerifyPIN(CKYCardConnection *c
+     return ret;
+ }
+ 
++
+ /*
+  * Get a CAC Certificate 
+  */
+@@ -1078,6 +1215,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
+diff -up ./src/libckyapplet/cky_applet.h.piv-ecc ./src/libckyapplet/cky_applet.h
+--- ./src/libckyapplet/cky_applet.h.piv-ecc	2013-09-08 15:50:33.099428337 -0700
++++ ./src/libckyapplet/cky_applet.h	2013-09-08 15:50:33.127428807 -0700
+@@ -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
+ 
+ /*
+@@ -218,12 +221,47 @@ 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;
++
+ /* 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 +373,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 +398,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 +414,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.
+@@ -490,9 +527,18 @@ 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);
+ /*
+  * There are 3 read commands:
+  *  
+@@ -553,6 +599,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.piv-ecc ./src/libckyapplet/cky_base.c
+--- ./src/libckyapplet/cky_base.c.piv-ecc	2013-09-08 15:50:33.100428354 -0700
++++ ./src/libckyapplet/cky_base.c	2013-09-08 15:50:33.128428824 -0700
+@@ -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;
+     }
+@@ -710,8 +713,15 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY
+     return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, recvlen);
+ }
+ 
++CKYStatus
++CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen)
++{
++    return CKYBuffer_AppendChar(&apdu->apduBuf, recvlen);
++}
++
++
+ void
+-CKY_SetName(char *p)
++CKY_SetName(const char *p)
+ {
+ }
+     
+diff -up ./src/libckyapplet/cky_base.h.piv-ecc ./src/libckyapplet/cky_base.h
+--- ./src/libckyapplet/cky_base.h.piv-ecc	2013-09-08 15:50:33.100428354 -0700
++++ ./src/libckyapplet/cky_base.h	2013-09-08 15:50:33.128428824 -0700
+@@ -278,9 +278,10 @@ 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_AppendReceiveLen(CKYAPDU *apdu, CKYByte 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.piv-ecc ./src/libckyapplet/cky_card.c
+--- ./src/libckyapplet/cky_card.c.piv-ecc	2013-09-08 15:50:33.109428505 -0700
++++ ./src/libckyapplet/cky_card.c	2013-09-08 15:50:33.128428824 -0700
+@@ -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;
+     }
+@@ -1071,25 +1077,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.piv-ecc ./src/libckyapplet/cky_card.h
+diff -up ./src/libckyapplet/cky_factory.c.piv-ecc ./src/libckyapplet/cky_factory.c
+--- ./src/libckyapplet/cky_factory.c.piv-ecc	2013-09-08 15:50:33.101428370 -0700
++++ ./src/libckyapplet/cky_factory.c	2013-09-08 15:50:33.129428841 -0700
+@@ -62,7 +62,6 @@ CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu
+     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)
+ {
+@@ -622,7 +707,6 @@ fail:
+     CKYBuffer_FreeData(&buf);
+     return ret;
+ }
+-
+ CKYStatus
+ CACAPDUFactory_GetProperties(CKYAPDU *apdu)
+ {
+@@ -634,7 +718,7 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap
+ }
+ 
+ CKYStatus
+-CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin)
++CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const char *pin)
+ {
+     CKYStatus ret;
+     CKYSize size;
+@@ -642,7 +726,7 @@ CACAPDUFactory_VerifyPIN(CKYAPDU *apdu,
+     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) {
+     	return CKYAPDU_SetReceiveLen(apdu, 0);
+@@ -663,3 +747,63 @@ CACAPDUFactory_VerifyPIN(CKYAPDU *apdu,
+     return ret;
+ 
+ }
++
++CKYStatus
++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;
++}
++
+diff -up ./src/libckyapplet/cky_factory.h.piv-ecc ./src/libckyapplet/cky_factory.h
+--- ./src/libckyapplet/cky_factory.h.piv-ecc	2013-09-08 15:50:33.101428370 -0700
++++ ./src/libckyapplet/cky_factory.h	2013-09-08 15:50:33.130428858 -0700
+@@ -25,10 +25,11 @@
+ /*
+  * 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
+@@ -66,6 +67,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
+@@ -91,6 +94,12 @@
+ #define CAC_SIZE_GET_PROPERTIES	48
+ #define CAC_P1_STEP		0x80
+ #define CAC_P1_FINAL		0x00
++#define CAC_LOGIN_GLOBAL	0x00
++
++/* PIV */
++#define PIV_LOGIN_LOCAL		0x80
++#define PIV_LOGIN_GLOBAL	CAC_LOGIN_GLOBAL
++#define PIV_INS_GEN_AUTHENTICATE 0x87
+ 
+ /*
+  * Fixed return sized from various commands
+@@ -123,6 +132,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
+@@ -187,6 +197,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,11 +234,16 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C
+ 
+ CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, 
+ 				     const CKYBuffer *data);
+-CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin);
++CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef,
++				   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);
+ 
+ CKY_END_PROTOS
+ 
diff --git a/SOURCES/coolkey-simple-bugs.patch b/SOURCES/coolkey-simple-bugs.patch
new file mode 100644
index 0000000..f8f3ab7
--- /dev/null
+++ b/SOURCES/coolkey-simple-bugs.patch
@@ -0,0 +1,71 @@
+diff -up ./configure.in.coolkey-simple-bugs ./configure.in
+--- ./configure.in.coolkey-simple-bugs	2009-09-16 11:21:55.621493844 -0700
++++ ./configure.in	2009-09-16 11:22:23.354492383 -0700
+@@ -124,9 +124,9 @@ then
+ if test $WINDOWS -ne 1; then
+   PKG_CHECK_MODULES(NSS, nss, true, [ AC_MSG_ERROR(could not find NSS Crypto libraries) ])
+ fi
+-  enable_pk11install = "yes"
++  enable_pk11install="yes"
+ else
+-  enable_pk11install = "no"
++  enable_pk11install="no"
+   AC_MSG_WARN([skipping pk11install])
+ fi
+ 
+diff -up ./Makefile.am.coolkey-simple-bugs ./Makefile.am
+--- ./Makefile.am.coolkey-simple-bugs	2009-09-16 11:23:18.715515063 -0700
++++ ./Makefile.am	2009-09-16 13:15:29.570492412 -0700
+@@ -25,7 +25,6 @@ if BUILD_PK11INSTALL
+ SUBDIRS += src/install
+ endif
+ 
+-ACLOCAL_AMFLAGS = -I m4
+ 
+ EXTRA_DIST = cookey.spec LICENSE
+ 
+diff -up ./src/coolkey/object.cpp.coolkey-simple-bugs ./src/coolkey/object.cpp
+--- ./src/coolkey/object.cpp.coolkey-simple-bugs	2009-09-16 10:36:29.300516245 -0700
++++ ./src/coolkey/object.cpp	2009-09-16 10:37:17.747492199 -0700
+@@ -397,7 +397,7 @@ PKCS11Object::getLabel() 
+ {
+     // clean up old one
+     if (label) {
+-	delete label;
++	delete [] label;
+ 	label = NULL;
+     }
+     // find matching attribute
+diff -up ./src/coolkey/object.h.coolkey-simple-bugs ./src/coolkey/object.h
+--- ./src/coolkey/object.h.coolkey-simple-bugs	2009-09-16 16:05:27.233488140 -0700
++++ ./src/coolkey/object.h	2009-09-16 16:05:54.161492421 -0700
+@@ -82,7 +82,7 @@ class PKCS11Object {
+     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); }
++    ~PKCS11Object() { delete [] label; delete [] name; CKYBuffer_FreeData(&pubKey); }
+ 
+     PKCS11Object(const PKCS11Object& cpy) :
+         attributes(cpy.attributes), muscleObjID(cpy.muscleObjID),
+diff -up ./src/coolkey/slot.cpp.coolkey-simple-bugs ./src/coolkey/slot.cpp
+--- ./src/coolkey/slot.cpp.coolkey-simple-bugs	2009-09-16 10:28:15.412492201 -0700
++++ ./src/coolkey/slot.cpp	2009-09-16 10:57:27.692492487 -0700
+@@ -979,7 +979,7 @@ Slot::makeLabelString(char *label, int m
+ //
+ #define COOLKEY "CoolKey"
+ #define POSSESSION " for "
+-    if (!personName || personName == "") {
++    if (!personName || personName[0] == '\0' ) {
+ 	const int coolKeySize = sizeof(COOLKEY) ;
+ 	memcpy(label, COOLKEY, coolKeySize-1);
+ 	makeSerialString(&label[coolKeySize], maxSize-coolKeySize, cuid);
+@@ -1528,7 +1528,7 @@ SlotMemSegment::SlotMemSegment(const cha
+     }
+     sprintf(segName,SEGMENT_PREFIX"%s",readerName); 
+     segment = SHMem::initSegment(segName, MAX_OBJECT_STORE_SIZE, needInit);
+-    delete segName;
++    delete [] segName;
+     if (!segment) {
+ 	// just run without shared memory
+ 	return;
diff --git a/SOURCES/coolkey-thread-fix.patch b/SOURCES/coolkey-thread-fix.patch
new file mode 100644
index 0000000..e3b552a
--- /dev/null
+++ b/SOURCES/coolkey-thread-fix.patch
@@ -0,0 +1,158 @@
+Index: src/coolkey/coolkey.cpp
+===================================================================
+RCS file: /cvs/dirsec/coolkey/src/coolkey/coolkey.cpp,v
+retrieving revision 1.2
+diff -u -r1.2 coolkey.cpp
+--- src/coolkey/coolkey.cpp	14 Feb 2007 19:54:01 -0000	1.2
++++ src/coolkey/coolkey.cpp	18 Dec 2009 23:22:58 -0000
+@@ -42,7 +42,9 @@
+ 
+ static SlotList *slotList = NULL;
+ 
+-static OSLock finalizeLock(false);
++static OSLock *finalizeLock = NULL;
++#define FINALIZE_GETLOCK() if (finalizeLock) finalizeLock->getLock();
++#define FINALIZE_RELEASELOCK() if (finalizeLock) finalizeLock->releaseLock();
+ 
+ static CK_BBOOL initialized = FALSE;
+ static CK_BBOOL finalizing = FALSE;
+@@ -208,11 +210,13 @@
+     if( initialized ) {
+         return CKR_CRYPTOKI_ALREADY_INITIALIZED;
+     }
+-    if (!finalizeLock.isValid()) {
++    if (finalizeLock && !finalizeLock->isValid()) {
+ 	return CKR_CANT_LOCK;
+     }
+     CK_C_INITIALIZE_ARGS* initArgs = (CK_C_INITIALIZE_ARGS*) pInitArgs;
++    OSLock::setThreadSafe(0);
+     if( initArgs != NULL ) {
++	bool needThreads;
+ 	/* work around a bug in NSS where the library parameters are only
+ 	 * send if locking is requested */
+ 	if (initArgs->LibraryParameters) {
+@@ -220,7 +224,17 @@
+ 	} else {
+ 	    Params::ClearParams();
+ 	}
+-        if( (initArgs->flags & CKF_OS_LOCKING_OK) || initArgs->LockMutex ){
++  	needThreads = ((initArgs->flags & CKF_OS_LOCKING_OK) != 0);
++	OSLock::setThreadSafe(needThreads);
++	/* don't get a finalize lock unless someone initializes us asking
++	 * us to use threads */
++	if (needThreads && !finalizeLock) {
++	    finalizeLock = new OSLock(true);
++	    if (finalizeLock == NULL) return CKR_HOST_MEMORY;
++	}
++	/* only support OS LOCKING threads */
++        if( ((initArgs->flags & CKF_OS_LOCKING_OK) == 0) 
++						&& initArgs->LockMutex ){
+             throw PKCS11Exception(CKR_CANT_LOCK);
+         }
+     }
+@@ -259,9 +273,9 @@
+     // the finalizing call first, we know it will set waitEvent before
+     // we can get the lock, so we only need to protect setting finalizing
+     // to true.
+-    finalizeLock.getLock();
++    FINALIZE_GETLOCK();
+     finalizing = TRUE;
+-    finalizeLock.releaseLock();
++    FINALIZE_RELEASELOCK();
+     if (waitEvent) {
+ 	/* we're waiting on a slot event, shutdown first to allow
+ 	 * the wait function to complete before we pull the rug out.
+@@ -273,10 +287,10 @@
+     } 
+     delete slotList;
+     delete log;
+-    finalizeLock.getLock();
++    FINALIZE_GETLOCK();
+     finalizing = FALSE;
+     initialized = FALSE;
+-    finalizeLock.releaseLock();
++    FINALIZE_RELEASELOCK();
+     return CKR_OK;
+ }
+ 
+@@ -595,17 +609,17 @@
+ CK_RV
+ C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved)
+ {
+-    finalizeLock.getLock();
++    FINALIZE_GETLOCK();
+     if( ! initialized ) {
+-        finalizeLock.releaseLock();
++	FINALIZE_RELEASELOCK();
+         return CKR_CRYPTOKI_NOT_INITIALIZED;
+     }
+     if (finalizing) {
+-        finalizeLock.releaseLock();
++	FINALIZE_RELEASELOCK();
+         return CKR_CRYPTOKI_NOT_INITIALIZED;
+     }
+     waitEvent = TRUE;
+-    finalizeLock.releaseLock();
++    FINALIZE_RELEASELOCK();
+     try {
+         log->log("C_WaitForSlotEvent called\n");
+         slotList->waitForSlotEvent(flags, pSlot, pReserved);
+Index: src/coolkey/machdep.cpp
+===================================================================
+RCS file: /cvs/dirsec/coolkey/src/coolkey/machdep.cpp,v
+retrieving revision 1.7
+diff -u -r1.7 machdep.cpp
+--- src/coolkey/machdep.cpp	14 Feb 2008 23:48:19 -0000	1.7
++++ src/coolkey/machdep.cpp	18 Dec 2009 23:22:58 -0000
+@@ -37,6 +37,8 @@
+ #include <stdlib.h>
+ #endif
+ 
++bool OSLock::needThread = 0;
++
+ #ifdef _WIN32
+ //
+ // Windows functions to grab a named shared memory segment of a specific size,
+@@ -123,6 +125,10 @@
+ 
+ OSLock::OSLock(bool exceptionAllowed)
+ {
++    if (!needThread) {
++	lockData = NULL;
++	return;
++    }
+     lockData = new OSLockData;
+     if (lockData) {
+ 	InitializeCriticalSection(&lockData->mutex);
+@@ -360,6 +366,9 @@
+     int rc;
+ 
+     lockData = NULL;
++    if (!needThread) {
++	return;
++    }
+ #ifdef MAC
+     if (!OSLock_attr_init) {
+ 	rc = pthread_mutexattr_init(&OSLock_attr);
+Index: src/coolkey/machdep.h
+===================================================================
+RCS file: /cvs/dirsec/coolkey/src/coolkey/machdep.h,v
+retrieving revision 1.1
+diff -u -r1.1 machdep.h
+--- src/coolkey/machdep.h	9 Jun 2006 18:39:11 -0000	1.1
++++ src/coolkey/machdep.h	18 Dec 2009 23:22:58 -0000
+@@ -40,12 +40,14 @@
+ class OSLock {
+ private:
+    OSLockData *lockData;
++   static bool needThread;
+ public:
+    OSLock(bool exceptionAllowed = true);
+    ~OSLock();
+    bool isValid();
+    void getLock();
+    void releaseLock();
++   static void setThreadSafe(bool thread) { needThread = thread; }
+ };
+ 
+ typedef unsigned long OSTime;
diff --git a/SOURCES/coolkey.module b/SOURCES/coolkey.module
new file mode 100644
index 0000000..521b87b
--- /dev/null
+++ b/SOURCES/coolkey.module
@@ -0,0 +1,8 @@
+# This file describes how to load the coolkey module
+# See: http://p11-glue.freedesktop.org/doc/p11-kit/config.html
+
+# This is a relative path, which means it will be loaded from
+# the p11-kit default path which is usually $(libdir)/pkcs11.
+# Doing it this way allows for packagers to package coolkey for
+# 32-bit and 64-bit and make them parallel installable
+module: libcoolkeypk11.so
diff --git a/SPECS/coolkey.spec b/SPECS/coolkey.spec
new file mode 100644
index 0000000..e6f7f8f
--- /dev/null
+++ b/SPECS/coolkey.spec
@@ -0,0 +1,338 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright (C) 2005 Red Hat, Inc.
+# All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation version
+# 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+# END COPYRIGHT BLOCK
+
+%define coolkey_module "CoolKey PKCS #11 Module"
+%define nssdb %{_sysconfdir}/pki/nssdb
+
+Name: coolkey
+Version: 1.1.0
+Release: 40%{?dist}
+Summary: CoolKey PKCS #11 module
+License: LGPLv2
+URL: https://github.com/dogtagpki/coolkey/tree/redhat
+Source: coolkey-%{version}.tar.gz
+Source1: coolkey.module
+Patch1: coolkey-cache-dir-move.patch
+Patch2: coolkey-gcc43.patch
+Patch3: coolkey-latest.patch
+Patch4: coolkey-simple-bugs.patch
+Patch5: coolkey-thread-fix.patch
+Patch6: coolkey-cac.patch
+Patch7: coolkey-cac-1.patch
+Patch8: coolkey-pcsc-lite-fix.patch
+Patch9: coolkey-fix-token-removal-failure.patch
+Patch10: coolkey-piv-ecc-el7.patch
+Patch20: coolkey-1.1.0-noapplet.patch
+Patch21: coolkey-1.1.0-fix-spurious-event.patch
+Patch22: coolkey-1.1.0-p15.patch
+Patch23: coolkey-1.1.0-p15-coverity.patch
+Patch24: coolkey-1.1.0-more-keys.patch
+Patch25: coolkey-1.1.0-fail-on-bad-mechanisms.patch
+Patch26: coolkey-1.1.0-max-cpu-bug.patch
+Patch27: coolkey-1.1.0-rhel7-alt-cac.patch
+Patch28: coolkey-1.1.0-cardos-5-3.patch
+Patch29: coolkey-1.1.0-alt-tokens-2.patch
+
+
+Group: System Environment/Libraries
+BuildRoot:  %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: autoconf
+BuildRequires: pcsc-lite-devel
+BuildRequires: zlib-devel
+BuildRequires: nss-devel
+BuildRequires: gcc-c++
+Requires: nss-tools
+Requires: pcsc-lite 
+Requires: pcsc-lite-libs
+Requires: ccid
+Provides: CoolKey Openkey
+Obsoletes: CoolKey Openkey
+# 390 does not have libusb or smartCards
+ExcludeArch: s390 s390x
+
+%description
+Linux Driver support for the CoolKey and CAC products. 
+
+%package devel
+Summary: CoolKey Applet libraries
+Group: System Environment/Libraries
+Requires: coolkey = %{version}-%{release}
+
+%description devel
+Linux Driver support to access the CoolKey applet.
+
+
+%prep
+%setup -q
+%patch1 -b .cache.dir.move
+%patch2 -b .coolkey-gcc43
+%patch3 -b .coolkey-latest
+%patch4 -b .coolkey-simple-bugs
+%patch5 -b .coolkey-thread-fix
+%patch6 -b .cac
+%patch7 -b .cac-1
+%patch8 -b .reader-state-fix
+%patch9 -p1 -b .fix-token-removal-failure
+%patch10 -b .piv-ecc
+%patch20 -b .noapplet
+%patch21 -b .fix-spurious
+%patch22 -b .p15
+%patch23 -b .p15-coverity
+%patch24 -b .more-keys
+%patch25 -b .fail-on-bad-mechanisms
+%patch26 -b .max-cpu-bug
+%patch27 -b .alt-cac
+%patch28 -b .cardos-5-3
+%patch29 -b .alt-tokens-2
+
+%build
+autoconf
+%configure --with-debug --disable-dependency-tracking --enable-pk11install
+make %{?_smp_mflags} CFLAGS="$CFLAGS -g -O2 -fno-strict-aliasing $CFLAGS " CXXFLAGS="$CXXFLAGS -g -O2 -fno-strict-aliasing $CFLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+ln -s pkcs11/libcoolkeypk11.so $RPM_BUILD_ROOT/%{_libdir}
+mkdir -p $RPM_BUILD_ROOT/var/cache/coolkey
+mkdir -p $RPM_BUILD_ROOT/%{_datadir}/p11-kit/modules/
+cp %{SOURCE1} $RPM_BUILD_ROOT/%{_datadir}/p11-kit/modules/
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/ldconfig
+if [ -x %{_bindir}/pk11install ]; then
+  isThere=`modutil -rawlist -dbdir dbm:%{nssdb} | grep %{coolkey_module} || echo NO`
+  if [ "$isThere" == "NO" ]; then
+      pk11install -l -p %{nssdb} 'name=%{coolkey_module} library=libcoolkeypk11.so' ||:
+   fi
+  isThere=`modutil -rawlist -dbdir sql:%{nssdb} | grep %{coolkey_module} || echo NO`
+  if [ "$isThere" == "NO" ]; then
+      pk11install -s -p %{nssdb} 'name=%{coolkey_module} library=libcoolkeypk11.so' ||:
+   fi
+fi
+
+
+%postun
+/sbin/ldconfig
+if [ $1 -eq 0 ]; then
+   modutil -delete %{coolkey_module} -dbdir dbm:%{nssdb} -force || :
+   modutil -delete %{coolkey_module} -dbdir sql:%{nssdb} -force || :
+fi
+
+
+%files
+%defattr(-,root,root,-)
+%doc ChangeLog LICENSE 
+%{_bindir}/pk11install
+%{_libdir}/libcoolkeypk11.so
+%{_libdir}/pkcs11
+%{_libdir}/libckyapplet.so.1
+%{_libdir}/libckyapplet.so.1.0.0
+%{_datadir}/p11-kit/modules/coolkey.module
+%attr(1777,root,root) /var/cache/coolkey
+
+
+%files devel
+%{_libdir}/libckyapplet.so
+%{_libdir}/pkgconfig/libckyapplet.pc
+%{_includedir}/*.h
+
+
+%changelog
+* Mon Aug 6 2018 Robert Relyea <rrelyea@redhat.com> - 1.1.0-40
+- fix regression in token removal created by the cac alt tokens patch.
+
+* Thu Jun 21 2018 Robert Relyea <rrelyea@redhat.com> - 1.1.0-39
+- add c++ compiler to the dependencies.
+- update URL to point to the github for coolkey
+
+* Fri Apr 20 2018 Robert Relyea <rrelyea@redhat.com> - 1.1.0-38
+- support cac alt tokens which don't have a cert is slot 0, don't have
+  a CCC, and uses a ACA.
+
+* Tue Mar 14 2017 Robert Relyea <rrelyea@redhat.com> - 1.1.0-37
+- get Cardos 5.3 cards working properly
+
+* Thu Dec 1 2016 Robert Relyea <rrelyea@redhat.com> - 1.1.0-36
+- recognize up to 10 cac and PIV certs rather than 3
+
+* Thu Jun 30 2016 Robert Relyea <rrelyea@redhat.com> - 1.1.0-35
+- include sleep on unknown reader errors.
+
+* Mon Jun 13 2016 Robert Relyea <rrelyea@redhat.com> - 1.1.0-34
+- recognize up to 32 keys and certs in coolkeys. (can go up to 62 with just #define changes)
+- own our cache file.
+- verify we are using the correct mechanisms
+
+* Mon Jul 6 2015 Robert Relyea <rrelyea@redhat.com> - 1.1.0-33
+- fix more coverity issues in p15 patch
+
+* Mon Jul 6 2015 Robert Relyea <rrelyea@redhat.com> - 1.1.0-32
+- fix coverity issues in p15 patch
+
+* Mon Jul 6 2015 Robert Relyea <rrelyea@redhat.com> - 1.1.0-31
+- fix requires as identified in rpmdiff
+
+* Mon Jul 6 2015 Robert Relyea <rrelyea@redhat.com> - 1.1.0-30
+- Add pk11-kit modules file
+
+* Mon Jul 6 2015 Robert Relyea <rrelyea@redhat.com> - 1.1.0-29
+- Add partical support for pkcs15 tokens
+
+* Fri Sep 26 2014 Robert Relyea <rrelyea@redhat.com> - 1.1.0-28
+- update coolkey to handle the new error ccid/pcsc-lite returns on reader
+  removal.
+
+* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 1.1.0-27
+- Mass rebuild 2013-12-27
+
+* Tue Nov 26 2013 Robert Relyea <rrelyea@redhat.com> - 1.1.0-26
+- delete coolkey from sql db as well as the old one.
+
+* Wed Nov 6 2013 Robert Relyea <rrelyea@redhat.com> - 1.1.0-25
+- delete coolkey from sql db as well as the old one.
+
+* Sun Sep 8 2013 Robert Relyea <rrelyea@redhat.com> - 1.1.0-24
+- Add ECC and PIV support
+
+* Wed May 22 2013 Ray Strode <rstrode@redhat.com> 1.1.0-23
+- Fix token removal issue
+
+* Wed Feb 13 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.0-22
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Wed Jul 18 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.0-21
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Thu Jan 12 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.0-20
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.0-19
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Tue Oct 19 2010 Ville Skyttä <ville.skytta@iki.fi> - 1.1.0-18
+- Own the %%{_libdir}/pkcs11 dir.
+
+* Wed Sep 8 2010 Robert Relyea <rrelyea@redhat.com> - 1.1.0-17
+- pscs-lite removed SCARD_READERSTATE_A definition. revert to the prefered
+  SCARD_READERSTATE
+
+* Wed Jun 23 2010 Jack Magne <jmagne@redhat.com> - 1.1.0-16
+- fix possible crash when loading cac certs.
+
+* Wed Jun 16 2010 Robert Relyea <rrelyea@redhat.com> - 1.1.0-15
+- better cac support.
+
+* Mon Feb 22 2010 Robert Relyea <rrelyea@redhat.com> - 1.1.0-14
+- remove dependency on ifd-egate
+
+* Tue Jan 5 2010 Robert Relyea <rrelyea@redhat.com> - 1.1.0-13
+- bump the release number to rebuild
+
+* Fri Dec 18 2009 Robert Relyea <rrelyea@redhat.com> - 1.1.0-12
+- Fix threading issue. Coolkey will now work with non-threaded applications 
+- that don't link with libpthread.
+
+* Wed Sep 16 2009 Jack magne <jmagne@redhat.com> - 1.1.0-11
+- Misc bug fixes. Resolves: 485032, #250738, #497758.
+
+* Fri Sep 11 2009 Jack Magne <jmagne@redhat.com> - 1.1.0-10
+- Include latest changes for Gemalto 64K and Safenet 330J.
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.0-9
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Tue Feb 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.0-8
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Sun Sep 14 2008 Matt Domsch <mdomsch@fedoraproject.org> - 1.1.0-7
+- BR: nss-devel not mozilla-nss-devel (FTBFS BZ#440753)
+
+* Wed Feb 13 2008 Jack magne <jmagne@redhat.com>  - 1.1.0-6
+- Clean up building with gcc 4.3.
+* Thu Sep 27 2007 Jack Magne <jmagne@redhat.com>  - 1.1.0-5
+- Include patch for moving the cache directory to a safe location. 
+- Bug #299481.
+* Mon Aug 20 2007 Bob Relyea <rrelyea@redhat.com> - 1.1.0-4
+- Update License description to the new Fedora standard
+
+* Thu Jun 21 2007 Kai Engert <kengert@redhat.com> - 1.1.0-3.1
+- rebuild
+
+* Tue Jun 5 2007 Bob Relyea <rrelyea@redhat.com> - 1.1.0-3
+- add build requires, bump version number for make tag.
+
+* Thu May 31 2007 Bob Relyea <rrelyea@redhat.com> - 1.1.0-2
+- Back out RHEL-4 version of spec from CVS, add pcsc-lite-lib requires.
+
+* Tue Feb 20 2007 Bob Relyea <rrelyea@redhat.com> - 1.1.0-1
+- Pick up lates release.
+
+* Wed Nov 1 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-15
+- Don't grab the CUID on cac's. Resting the card causes it to
+- logout of other applications.
+
+* Wed Nov 1 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-14
+- Shared memory directory needs to be writeable by all so
+- coolkey can create caches for any user. (lack of caches
+- show up in screen savers reactly slowly).
+
+* Fri Oct 20 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-13
+- fix login hw race failures
+
+* Fri Oct 20 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-12
+- add the dist flag
+
+* Wed Oct 18 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-11
+- CAC cards sometimes need to reset before they can get their
+- initial transaction (problem is noticed on insertion an removal)
+
+* Tue Oct 17 2006 Jesse Keating <jkeating@redhat.com> - 1.0.1-10
+- Only run pk11install if the binary is there (multilib fun)
+
+* Mon Oct 09 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-9
+- use pk11install which does not require loading the module to install it.
+
+* Mon Oct 09 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-8
+- pcscd must be running in order to add coolkey.
+
+* Wed Oct 4 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-7
+- silence modutil warnings
+
+* Fri Sep 29 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-5
+- install and uninstall coolkey in the system secmod.db
+
+* Thu Sep 7 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-4
+- make the coolkey token caches persist over application calls.
+- make a separate cache for each user.
+
+* Sun Jul 16 2006 Florian La Roche <laroche@redhat.com> - 1.0.1-2
+- fix excludearch line
+
+* Mon Jul 10 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.1-1
+- Don't require pthread library in coolkey
+
+* Mon Jul 10 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.0-2
+- remove s390 from the build
+
+* Mon Jun 5 2006 Bob Relyea <rrelyea@redhat.com> - 1.0.0-1
+- Initial revision for fedora