components/coolkey/patches/06-slot.cpp.patch
branchs11u3-sru
changeset 6535 8f23248b161c
child 6998 ecb236c14ed7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/coolkey/patches/06-slot.cpp.patch	Mon Aug 01 12:38:38 2016 -0700
@@ -0,0 +1,2571 @@
+Upstream fixes already included in the latest community updates to coolkey v1.1.0
+
+Adds support and fixes for newer versions of CAC and PIV cards.
+Addresses issues seen with pcscd restart.
+
+--- ORIGINAL/./src/coolkey/slot.cpp	2016-06-24 16:07:20.111616788 -0400
++++ ././src/coolkey/slot.cpp	2016-06-27 21:05:04.901200633 -0400
+@@ -56,6 +56,34 @@
+ {  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
+@@ -138,7 +166,11 @@
+ 	    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
+@@ -205,6 +237,29 @@
+     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
+  */
+@@ -216,32 +271,19 @@
+ 
+     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) {
+@@ -251,6 +293,16 @@
+ 	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
+@@ -258,6 +310,33 @@
+      * don't recognize.
+      */
+ 
++    /* 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(unsigned int ri = 0 ; ri < numReaders; ri ++)  {
++        knownState = CKYReader_GetKnownState(&readerStates[ri]);
++ 
++        curReaderName =  CKYReader_GetReaderName(&readerStates[ri]); 
++        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 (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;
+@@ -330,7 +409,9 @@
+     : 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),mCACLocalLogin(false),
++	pivContainer(-1), pivKey(-1), maxCacCerts(MAX_CERT_SLOTS), 
++	algs(ALG_NONE), 
+ #ifdef USE_SHMEM
+ 	shmem(readerName_),
+ #endif
+@@ -370,6 +451,9 @@
+     }
+     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);
+@@ -437,6 +521,9 @@
+     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>
+@@ -527,10 +614,39 @@
+     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()
+ {
+-    CKYStatus status;
++    CKYStatus status = CKYSCARDERR;
+     OSTime time = OSTimeNow();
+ 
+     mCoolkey = 0;
+@@ -539,13 +655,32 @@
+ 
+     // 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
++	status = CKYSCARDERR;
++        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();
+@@ -564,15 +699,10 @@
+         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 */
+@@ -613,17 +743,30 @@
+     // 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;
++	 maxCacCerts = MAX_CERT_SLOTS;
++         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 = CACApplet_SelectPKI(conn, 0, NULL);
++	status = getCACAid();
+ 	if (status != CKYSUCCESS) {
+-            log->log("CAC Select failed 0x%x\n", status);
++	    log->log("CAC Select failed 0x%x\n", status);
+ 	    if (status == CKYSCARDERR) {
+-		log->log("CAC Card Failure 0x%x\n", 
+-			CKYCardConnection_GetLastError(conn));
+-		disconnect();
++		    log->log("Card Failure 0x%x\n",
++				CKYCardConnection_GetLastError(conn));
++		    disconnect();
+ 	    }
++	    /* CARD is unknown */
+ 	    return;
+ 	}
+ 	state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+@@ -633,10 +776,11 @@
+          * unfriendly */
+ 	isVersion1Key = 0;
+ 	needLogin = 1;
+-
++        mCoolkey = 0;
++	mCACLocalLogin = false;
+ 	return;
+     }
+-    mCoolkey = 1;
++    mCoolkey = 1; /* coolkey applet selected */
+     log->log("time connect: Select Applet %d ms\n", OSTimeNow() - time);
+ 
+     state |= APPLET_SELECTABLE;
+@@ -700,8 +844,8 @@
+ 	}
+     } else {
+ 	loggedIn = false;
++	pinCache.invalidate();
+ 	if (hard) {
+-	    pinCache.invalidate();
+ 	    pinCache.clearPin();
+ 	}
+     }
+@@ -716,17 +860,113 @@
+     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);
++	}
++	maxCacCerts = 3;
++	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 */
++    }
++    maxCacCerts = certSlot;
++
++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();
+@@ -924,7 +1164,7 @@
+ //
+ #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);
+@@ -964,7 +1204,7 @@
+ 
+ struct _manList {
+      unsigned short type;
+-     char *string;
++     const char *string;
+ };
+ 
+ static const struct _manList  manList[] = {
+@@ -1046,6 +1286,7 @@
+ 
+ 
+     return CKR_OK;
++
+ }
+ 
+ void
+@@ -1066,7 +1307,16 @@
+     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();
+@@ -1079,52 +1329,98 @@
+ 	    }
+ 	    throw;
+ 	}
+-	if (myNumReaders != numReaders) {
++
++	/* 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 + 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) {
+             break;
+         }
+ 
+         #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);
+@@ -1161,6 +1457,7 @@
+       case SCARD_W_REMOVED_CARD:
+         ckrv = CKR_DEVICE_REMOVED;
+         break;
++      
+       default:
+         ckrv = CKR_DEVICE_ERROR;
+         break;
+@@ -1220,14 +1517,68 @@
+ }
+ 
+ void
+-Slot::selectCACApplet(CKYByte instance)
++Slot::selectCACApplet(CKYByte instance, bool doDisconnect)
+ {
+     CKYStatus status;
+-    status = CACApplet_SelectPKI(conn, instance, NULL);
++    /* 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) {
++	if (doDisconnect) {
++	    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();
++	if (doDisconnect) {
++	    disconnect();
++	}
++        throw PKCS11Exception(CKR_DEVICE_REMOVED);
++    }
++    if (mOldCAC) {
++	return;
++    }
++    status = CACApplet_SelectFile(conn, cardEF[instance], NULL);
++    if ( status == CKYSCARDERR ) handleConnectionError();
++    if ( status != CKYSUCCESS) {
++	if (doDisconnect) {
++	    disconnect();
++	}
+         throw PKCS11Exception(CKR_DEVICE_REMOVED);
+     }
+ }
+@@ -1274,6 +1625,19 @@
+     }
+ };
+ 
++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;
+@@ -1307,6 +1671,29 @@
+     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)
+@@ -1316,24 +1703,31 @@
+     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);
++
++        /*  use key object to determine what algorithms we support */
++        if ( keyObj.getKeyType() == PKCS11Object::ecc) {
++            algs = (SlotAlgs) (algs | ALG_ECC);
++        } else {
++            algs = (SlotAlgs) (algs | ALG_RSA);
++        }
++       
+     }
+     objectList.push_back(keyObj);
+ 
+@@ -1363,6 +1757,7 @@
+ void
+ Slot::unloadObjects()
+ {
++    algs = ALG_NONE;
+     tokenObjects.clear();
+     free(personName);
+     personName = NULL;
+@@ -1421,23 +1816,35 @@
+ // 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
+@@ -1458,7 +1865,7 @@
+     }
+     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;
+@@ -1472,9 +1879,8 @@
+ 	return;
+     }
+ 
+-    SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
+     if (needInit) {
+-	segmentHeader->valid = 0;
++	clearValid(0);
+     }
+     segmentSize = segment->getSHMemSize();
+ }
+@@ -1548,6 +1954,18 @@
+     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)
+ {
+@@ -1571,6 +1989,18 @@
+     segmentHeader->dataVersion = version;
+ }
+ 
++void
++SlotMemSegment::setFirstCacCert(unsigned char firstCacCert)
++{
++    if (!segment) {
++	return;
++    }
++
++    SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr;
++
++    segmentHeader->firstCacCert = firstCacCert;
++}
++
+ bool
+ SlotMemSegment::isValid() const
+ {
+@@ -1645,23 +2075,13 @@
+     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);
+ }
+ 
+@@ -1675,30 +2095,20 @@
+     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);
+ }
+ 
+@@ -1710,15 +2120,18 @@
+ 	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
+@@ -1756,7 +2169,7 @@
+     // 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);
+@@ -1989,108 +2402,401 @@
+     return objInfoList;
+ }
+ 
+-void
+-Slot::loadCACCert(CKYByte instance)
+-{
+-    CKYISOStatus apduRC;
+-    CKYStatus status = CKYSUCCESS;
+-    CKYBuffer cert;
+-    CKYBuffer rawCert;
+-    CKYBuffer shmCert;
+-    CKYSize  nextSize;
++typedef enum {
++	BER_UNWRAP,
++	BER_NEXT
++} BERop;
+ 
+-    OSTime time = OSTimeNow();
++static CKYStatus
++berProcess(CKYBuffer *buf, int matchTag, CKYBuffer *target, BERop type)
++{
++    unsigned char tag;
++    unsigned int used_length= 0;
++    unsigned int data_length;
+ 
+-    CKYBuffer_InitEmpty(&cert);
+-    CKYBuffer_InitEmpty(&rawCert);
+-    CKYBuffer_InitEmpty(&shmCert);
++    tag = CKYBuffer_GetChar(buf,used_length++);
+ 
+-    //
+-    // not all CAC cards have all the PKI instances
+-    // catch the applet selection errors if they don't
+-    //
+-    try {
+-        selectCACApplet(instance);
+-    } 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;
++    /* blow out when we come to the end */
++    if (matchTag && tag != matchTag) {
++        return CKYLIBFAIL;
+     }
+ 
+-    log->log("CAC Cert %d: select CAC applet:  %d ms\n",
+-						 instance, OSTimeNow() - time);
++    data_length = CKYBuffer_GetChar(buf,used_length++);
+ 
+-    if (instance == 0) {
+-	/* get the first 100 bytes of the cert */
+-	status = CACApplet_GetCertificateFirst(conn, &rawCert, 
+-						&nextSize, &apduRC);
+-	if (status != CKYSUCCESS) {
+-	    handleConnectionError();
+-	}
+-	log->log("CAC Cert %d: fetch CAC Cert:  %d ms\n", 
+-						instance, OSTimeNow() - time);
+-    }
++    if (data_length & 0x80) {
++        int  len_count = data_length & 0x7f;
+ 
+-    unsigned short dataVersion = 1;
+-    CKYBool needRead = 1;
++        data_length = 0;
+ 
+-    /* 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);
++        while (len_count-- > 0) {
++            data_length = (data_length << 8) | 
++				CKYBuffer_GetChar(buf,used_length++);
++        }
++    }
+ 
+-	if (instance != 0) {
+-	    needRead = 0;
+-	}
++    if (data_length > (CKYBuffer_Size(buf)-used_length) ) {
++        return CKYLIBFAIL;
++    }
+ 
+-	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 (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) 
++{
++    CKYStatus status;
++    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);
++	/* 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);
+ 	}
+-	if (!needRead && (shmCertSize == 0)) {	
++	
++	return status;
++    }
++
++    if (mOldCAC) {
++	/* get the first 100 bytes of the cert */
++	status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC);
++	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);
++    CKYBuffer_AppendChar(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) {
++	    CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length);
++	}
++	if (tag == CAC_TAG_CERTINFO) {
++	    CKYBuffer_SetChar(cert,0,CKYBuffer_GetChar(&vBuf,voffset));
++	}
++    }
++    status = CKYSUCCESS;
++
++done:
++    CKYBuffer_FreeData(&tBuf);
++    CKYBuffer_FreeData(&vBuf);
++    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....
++ */
++CKYStatus
++Slot::readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize)
++{
++    CKYISOStatus apduRC;
++    assert(mOldCAC);
++    return CACApplet_GetCertificateAppend(conn, cert, nextSize, &apduRC);
++}
++
++void
++Slot::loadCACCert(CKYByte instance)
++{
++    CKYStatus status = CKYSUCCESS;
++    CKYBuffer cert;
++    CKYBuffer rawCert;
++    CKYBuffer shmCert;
++    CKYSize  nextSize;
++    CKYISOStatus apduRC;
++
++    OSTime time = OSTimeNow();
++
++    CKYBuffer_InitEmpty(&cert);
++    CKYBuffer_InitEmpty(&rawCert);
++    CKYBuffer_InitEmpty(&shmCert);
++
++    //
++    // not all CAC cards have all the PKI instances
++    // catch the applet selection errors if they don't
++    //
++    try {
++        selectCACApplet(instance, false);
++    } catch(PKCS11Exception& e) {
++	return;
++    }
++
++    log->log("CAC Cert %d: select CAC applet:  %d ms\n",
++						 instance, OSTimeNow() - time);
++
++
++    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);
++     } else {
++	status = readCACCertificateFirst(&rawCert, &nextSize);
+ 
+-    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 = CACApplet_GetCertificateFirst(conn, &rawCert, 
+-						&nextSize, &apduRC);
+-	
+-	    if (status != CKYSUCCESS) {
+-		/* 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;
+-	    }
++	if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) {
++	    /* this cert doesn't exists, go to the next one */
++	    return;
+ 	}
+ 
+ 	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);
+@@ -2098,37 +2804,66 @@
+ 	    handleConnectionError();
+ 	}
+ 	shmem.writeCACCert(&rawCert, instance);
+-	if (instance == 2) {
+-	    shmem.setValid();
+-	}
+     }
+ 
+ 
+     log->log("CAC Cert %d: Cert has been read:  %d ms\n",
+ 						instance, OSTimeNow() - time);
+-    if (CKYBuffer_GetChar(&rawCert,0) == 1) {
+-	CKYSize guessFinalSize = CKYBuffer_Size(&rawCert);
+-	CKYSize certSize = 0;
++    /* new CACs, and old CACs with the high one bit are compressed, 
++     * uncompress them */
++    if ((CKYBuffer_GetChar(&rawCert,0) & 0x3) == 1) {
++	CKYOffset offset = 1;
+ 	int zret = Z_MEM_ERROR;
+ 
+-	do {
+-	    guessFinalSize *= 2;
+-	    status = CKYBuffer_Resize(&cert, guessFinalSize);
+-	    if (status != CKYSUCCESS) {
+-		    break;
++	/* process the GZIP header if present */
++	/* header_id = 0x1f, 0x8b. CM=8. If we ever support something other
++	 * than CM=8, we need to change the zlib header below. Currently both
++	 * gzip and zlib only support CM=8 (DEFLATE) compression */
++	if ((CKYBuffer_GetChar(&rawCert,1) == 0x1f) &&
++	    (CKYBuffer_GetChar(&rawCert,2) == 0x8b) &&
++	    (CKYBuffer_GetChar(&rawCert,3) == 8)) {
++	    CKYByte flags = CKYBuffer_GetChar(&rawCert,4);
++	    /* this has a gzip header, not raw data. */
++	    offset += 10; /* base size of the gzip header */
++	    if (flags & 4) { /* FEXTRA */
++		CKYSize len = CKYBuffer_GetShortLE(&rawCert,offset);
++		offset += len;
++	    }
++	    if (flags & 8) { /* FNAME */
++		while (CKYBuffer_GetChar(&rawCert,offset) != 0) {
++		    offset++;
++		}
++		offset++;
++	    }
++	    if (flags & 0x10) { /* FComment */
++		while (CKYBuffer_GetChar(&rawCert,offset) != 0) {
++		    offset++;
++		}
++		offset++;
++	    }
++	    if (flags & 2) { /* FHCRC */
++		offset += 2;
+ 	    }
+-	    certSize = guessFinalSize;
+-	    zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize,
+-			CKYBuffer_Data(&rawCert)+1, CKYBuffer_Size(&rawCert)-1);
+-	} while (zret == Z_BUF_ERROR);
++	    offset -= 2;
++
++	    /* add zlib header, so libz will be happy */
++	    /* CINFO=7, CM=8, LEVEL=2, DICTFLAG=0, FCHECK= 1c */
++	    /* NOTE: the zlib will fail when procssing the trailer. this is
++	     * ok because decompress automatically notices the failure and
++	     * and checks the gzip trailer. */
++	    CKYBuffer_SetChar(&rawCert, offset, 0x78);
++	    CKYBuffer_SetChar(&rawCert, offset+1, 0x9c);
++	}
++	/* uncompress. This expands cert as necessary. */
++	zret = decompress(&cert, &rawCert, offset, 
++					CKYBuffer_Size(&rawCert)-offset);
+ 
+ 	if (zret != Z_OK) {
+ 	    CKYBuffer_FreeData(&rawCert);
+ 	    CKYBuffer_FreeData(&cert);
+ 	    throw PKCS11Exception(CKR_DEVICE_ERROR, 
+-				"Corrupted compressed CAC Cert");
++				"Corrupted compressed CAC/PIV Cert");
+ 	}
+-	CKYBuffer_Resize(&cert,certSize);
+     } else {
+ 	CKYBuffer_InitFromBuffer(&cert,&rawCert,1,CKYBuffer_Size(&rawCert)-1);
+     }
+@@ -2136,12 +2871,18 @@
+     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) {
++        algs = (SlotAlgs) (algs | ALG_ECC);
++    } else {
++        algs = (SlotAlgs) (algs | ALG_RSA);
++    }
+ 
+     if (personName == NULL) {
+ 	const char *name = certObj.getName();
+@@ -2153,6 +2894,94 @@
+ }
+ 
+ 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!
+@@ -2170,10 +2999,12 @@
+     list<ListObjectInfo> objInfoList;
+     std::list<ListObjectInfo>::iterator iter;
+ 
+-    if (state & CAC_CARD) {
+-	loadCACCert(0);
+-	loadCACCert(1);
+-	loadCACCert(2);
++    if (state & GOV_CARD) {
++	initCACShMem();
++	for (int i=0; i < maxCacCerts; i++) {
++	   loadCACCert(i);
++	}
++	verifyCACShMem();
+ 	status = trans.end();
+ 	loadReaderObject();
+ 	return;
+@@ -2399,6 +3230,9 @@
+ 	}
+ 	return nonceValid;
+     }
++    if (!needLogin) {
++	return true;
++    }
+     return loggedIn;
+ }
+ 
+@@ -2415,6 +3249,7 @@
+     }
+ 
+     if (!isVersion1Key) {
++	pinCache.invalidate();
+ 	pinCache.set((const char *)pPin, ulPinLen);
+     } else if (nonceValid) {
+ 	throw PKCS11Exception(CKR_USER_ALREADY_LOGGED_IN);
+@@ -2424,15 +3259,15 @@
+     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();
+@@ -2449,16 +3284,19 @@
+     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();
+     }
+     switch( result ) {
+       case CKYISO_SUCCESS:
+         break;
+-      case 6981:
++      case 0x6981:
++	pinCache.clearPin();
+         throw PKCS11Exception(CKR_PIN_LOCKED);
+       default:
++	pinCache.clearPin();
+ 	if ((result & 0xff00) == 0x6300) {
+             throw PKCS11Exception(CKR_PIN_INCORRECT);
+ 	}
+@@ -2487,10 +3325,13 @@
+       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);
+     }
+@@ -2577,7 +3418,7 @@
+         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
+     }
+ 
+-    if (state & CAC_CARD) {
++    if (state & GOV_CARD) {
+ 	CACLogout();
+ 	return;
+     }
+@@ -2704,7 +3545,7 @@
+     ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
+         ObjectHandleMatch(hObject));
+ 
+-    if( iter == tokenObjects.end() ) {
++    if ( iter == tokenObjects.end()) {
+         throw PKCS11Exception(CKR_OBJECT_HANDLE_INVALID);
+     }
+ 
+@@ -2788,6 +3629,21 @@
+ }
+ 
+ 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) ) {
+@@ -2821,6 +3677,23 @@
+     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)
+@@ -2830,7 +3703,10 @@
+     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
+@@ -2842,7 +3718,10 @@
+     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);
+ }
+ 
+ /**
+@@ -2951,6 +3830,93 @@
+     }
+ }
+ 
++class ECCKeyAgreementParams : public CryptParams {
++  public:
++    ECCKeyAgreementParams(unsigned int keysize) : CryptParams(keysize) { }
++
++    CKYByte getDirection() const { return CKY_DIR_NONE;}
++
++    CryptOpState& getOpState(Session& session) const {
++        return session.keyAgreementState;
++    }
++
++    void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const {
++        return;
++    }
++
++    void
++    unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const {
++        return;
++    }
++
++};
++
++class SignatureParams : public CryptParams {
++  public:
++    SignatureParams(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 {
++        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) { }
+@@ -3009,8 +3975,38 @@
+         CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
+         CK_ULONG_PTR pulSignatureLen)
+ {
+-    cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen,
+-        RSASignatureParams(CryptParams::FIXED_KEY_SIZE));
++
++    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
+@@ -3018,14 +4014,15 @@
+         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);
+@@ -3043,6 +4040,11 @@
+     CKYBuffer *result = &opState.result;
+     CKYByte keyNum = opState.keyNum;
+ 
++    unsigned int keySize = getRSAKeySize(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) {
+@@ -3062,7 +4064,8 @@
+   	}
+ 	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);
+@@ -3099,10 +4102,159 @@
+     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 (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)
+ {
++
+     //
+     // establish a transaction
+     //
+@@ -3113,8 +4265,8 @@
+     //
+     // select the applet
+     //
+-    if (state & CAC_CARD) {
+-	selectCACApplet(keyNum);
++    if (state & GOV_CARD) {
++	selectCACApplet(keyNum, true);
+     } else {
+ 	selectApplet();
+     }
+@@ -3122,12 +4274,21 @@
+     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();
+@@ -3138,11 +4299,15 @@
+ 	// 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
+@@ -3162,7 +4327,7 @@
+ 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);
+     }
+@@ -3214,7 +4379,7 @@
+ 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);
+     }
+@@ -3245,3 +4410,268 @@
+ 	throw PKCS11Exception(CKR_DEVICE_ERROR);
+     }
+ }
++
++#define MAX_NUM_KEYS 8
++unsigned int
++Slot::getRSAKeySize(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;
++}
++
++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)
++{
++
++    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);
++
++    } 
++}