components/coolkey/patches/06-slot.cpp.patch
author Ronald Jordan <ron.jordan@oracle.com>
Mon, 01 Aug 2016 12:38:38 -0700
branchs11u3-sru
changeset 6535 8f23248b161c
child 6998 ecb236c14ed7
permissions -rw-r--r--
22017764 Add Coolkey v1.1.0 to Userland consolidation

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);
+
+    } 
+}