components/coolkey/patches/10-cky_applet.c.patch
author Stephen Gaul Jr <steve.gaul@oracle.com>
Tue, 12 Jul 2016 17:34:11 -0700
changeset 6401 8e624b116c1d
permissions -rw-r--r--
PSARC/2016/219 Coolkey PKCS#11 provider for smartcard devices 22017764 Add Coolkey v1.1.0 to Userland consolidation

Upstream fixes already included in the latest community updates to coolkey v1.1.0

Addresses various known APDU handling issues.

--- ORIGINAL/./src/libckyapplet/cky_applet.c	2016-06-24 16:08:03.920676358 -0400
+++ ././src/libckyapplet/cky_applet.c	2016-06-24 12:37:17.144225159 -0400
@@ -41,7 +41,13 @@
 CKYStatus
 CKYAppletFactory_SelectFile(CKYAPDU *apdu, const void *param)
 {
-    return CKYAPDUFactory_SelectFile(apdu,(const CKYBuffer *)param);
+    return CKYAPDUFactory_SelectFile(apdu, 4, 0, (const CKYBuffer *)param);
+}
+
+CKYStatus
+CACAppletFactory_SelectFile(CKYAPDU *apdu, const void *param)
+{
+    return CKYAPDUFactory_SelectFile(apdu, 2, 12, (const CKYBuffer *)param);
 }
 
 CKYStatus
@@ -97,6 +103,22 @@
 }
 
 CKYStatus
+CKYAppletFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, const void *param)
+{
+    const CKYAppletArgComputeECCSignature *ccs=(const CKYAppletArgComputeECCSignature *)param;
+    return CKYAPDUFactory_ComputeECCSignatureOneStep(apdu, ccs->keyNumber,
+                        ccs->location, ccs->data, ccs->sig);
+}
+
+CKYStatus
+CKYAppletFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, const void *param)
+{
+
+    const CKYAppletArgComputeECCKeyAgreement *ccs=(const CKYAppletArgComputeECCKeyAgreement *)param;
+    return CKYAPDUFactory_ComputeECCKeyAgreementOneStep(apdu, ccs->keyNumber, ccs->location, ccs->publicValue, ccs->secretKey);
+}
+
+CKYStatus
 CKYAppletFactory_CreatePIN(CKYAPDU *apdu, const void *param)
 {
     const CKYAppletArgCreatePIN *cps = (const CKYAppletArgCreatePIN *)param;
@@ -134,6 +156,13 @@
 /* Future add WriteObject */
 
 CKYStatus
+CKYAppletFactory_WriteObject(CKYAPDU *apdu, const void *param)
+{
+    const CKYAppletArgWriteObject *wos = (const CKYAppletArgWriteObject *)param;
+    return CKYAPDUFactory_WriteObject(apdu,wos->objectID,wos->offset,wos->size,wos->data);
+}
+
+CKYStatus
 CKYAppletFactory_CreateObject(CKYAPDU *apdu, const void *param)
 {
     const CKYAppletArgCreateObject *cos=(const CKYAppletArgCreateObject *)param;
@@ -192,7 +221,6 @@
 {
     return CKYAPDUFactory_GetLifeCycleV2(apdu);
 }
-
 CKYStatus
 CKYAppletFactory_GetRandom(CKYAPDU *apdu, const void *param)
 {
@@ -219,17 +247,39 @@
 }
 
 CKYStatus
-CACAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param)
+CACAppletFactory_SignDecryptStep(CKYAPDU *apdu, const void *param)
 {
     const CKYBuffer *buf=(CKYBuffer *)param;
-    return CACAPDUFactory_SignDecrypt(apdu, buf);
+    return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_STEP, buf);
+}
+
+CKYStatus
+CACAppletFactory_SignDecryptFinal(CKYAPDU *apdu, const void *param)
+{
+    const CKYBuffer *buf=(CKYBuffer *)param;
+    return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_FINAL, buf);
+}
+
+CKYStatus
+PIVAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param)
+{
+    const PIVAppletArgSignDecrypt *psd = (const PIVAppletArgSignDecrypt *)param;
+    return PIVAPDUFactory_SignDecrypt(apdu, psd->chain, psd->alg, psd->key, 
+					psd->len, psd->buf);
 }
 
 CKYStatus
 CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
 {
     const char *pin=(const char *)param;
-    return CACAPDUFactory_VerifyPIN(apdu, pin);
+    return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin);
+}
+
+CKYStatus
+PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
+{
+    const char *pin=(const char *)param;
+    return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin);
 }
 
 CKYStatus
@@ -240,6 +290,20 @@
 }
 
 CKYStatus
+PIVAppletFactory_GetCertificate(CKYAPDU *apdu, const void *param)
+{
+    CKYBuffer *tag  =(CKYBuffer*)param;
+    return PIVAPDUFactory_GetData(apdu, tag, 0);
+}
+
+CKYStatus
+CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param)
+{
+    const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param;
+    return CACAPDUFactory_ReadFile(apdu, rfs->offset, rfs->type, rfs->count);
+}
+
+CKYStatus
 CACAppletFactory_GetProperties(CKYAPDU *apdu, const void *param)
 {
     return CACAPDUFactory_GetProperties(apdu);
@@ -299,6 +363,7 @@
 						CKYBuffer_Size(response) -2);
 }
 
+
 CKYStatus
 CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param)
 {
@@ -451,7 +516,7 @@
 							 CKYISOStatus *apduRC)
 {
     return CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, AID, NULL,
-		0, CKYAppletFill_Null, NULL, apduRC);
+		CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
 }
 
 static CKYByte coolkeyid[] = {0x62, 0x76, 0x01, 0xff, 0x00, 0x00, 0x00 };
@@ -471,22 +536,23 @@
     return ret;
 }
 
-static CKYByte CACPKIid[] = {0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
+static CKYByte CACPKIid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01 };
 /*
  * Select the CoolKey applet. Must happen after we start a transaction and 
  * before we issue any applet specific command.
  */
 CKYStatus
-CACApplet_SelectPKI(CKYCardConnection *conn, CKYByte instance, 
-			       CKYISOStatus *apduRC)
+CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cacAID, 
+				CKYByte instance, CKYISOStatus *apduRC)
 {
     CKYStatus ret;
-    CKYBuffer CACPKIAID;
-    CKYBuffer_InitFromData(&CACPKIAID, CACPKIid, sizeof(CACPKIid));
-    CKYBuffer_SetChar(&CACPKIAID, 6, instance);
-    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CACPKIAID,
+    CKYBuffer_AppendData(cacAID, CACPKIid, sizeof(CACPKIid));
+    CKYBuffer_AppendChar(cacAID, instance);
+    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, cacAID,
 		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
-    CKYBuffer_FreeData(&CACPKIAID);
+    if (ret != CKYSUCCESS) {
+	CKYBuffer_Resize(cacAID, 0);
+    }
     return ret;
 }
 
@@ -509,11 +575,38 @@
     CKYBuffer CAC_CM_AID;
     CKYBuffer_InitFromData(&CAC_CM_AID, cacmgrid, sizeof(cacmgrid));
     ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID,
-		 NULL, 0, CKYAppletFill_Null, NULL, apduRC);
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
     CKYBuffer_FreeData(&CAC_CM_AID);
     return ret;
 }
 
+static CKYByte cacCCCid[] = {0xa0, 0x00, 0x00, 0x01, 0x16, 0xdb, 0x00 };
+CKYStatus
+CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer CAC_CM_AID;
+    CKYBuffer_InitFromData(&CAC_CM_AID, cacCCCid, sizeof(cacCCCid));
+    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+    CKYBuffer_FreeData(&CAC_CM_AID);
+    return ret;
+}
+
+CKYStatus
+CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef,
+						 CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer efBuf;
+    CKYBuffer_InitEmpty(&efBuf);
+    CKYBuffer_AppendShortLE(&efBuf, ef);
+    ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SelectFile, &efBuf,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+    CKYBuffer_FreeData(&efBuf);
+    return ret;
+}
+
 /*
  * GetCPLC cluster -- must be called with CM selected
  */
@@ -667,8 +760,34 @@
     ccd.keyNumber = keyNumber;
     ccd.location = location;
     ccd.data = data;
-    return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, &ccd,
-	nonce, 0, CKYAppletFill_Null, NULL, apduRC);
+    return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, 
+	&ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC);
+}
+
+/* computeECCValue returns data in the form :
+ *            len: short
+ *            data: byte[len]
+ * This fill routine returns A buffer with a copy of data and a length of len */
+static CKYStatus
+ckyAppletFill_ComputeECCValueFinal(const CKYBuffer *response,
+                                                CKYSize size, void *param)
+{
+    CKYBuffer *cbuf = (CKYBuffer *)param;
+    CKYSize respSize = CKYBuffer_Size(response);
+    CKYSize dataLen;
+
+    if (cbuf == 0) {
+        return CKYSUCCESS; /* app didn't want the result */
+    }
+    /* data response code + length code */
+    if (respSize < 4) {
+        return CKYAPDUFAIL;
+    }
+    dataLen = CKYBuffer_GetShort(response, 0);
+    if (dataLen > (respSize-4)) {
+        return CKYAPDUFAIL;
+    }
+    return CKYBuffer_Replace(cbuf, 0, CKYBuffer_Data(response)+2, dataLen);
 }
 
 /* computeCrypt returns data in the form :
@@ -725,24 +844,48 @@
     CKYAppletArgComputeCrypt ccd;
     CKYBuffer    empty;
     CKYISOStatus status;
+    short       dataSize = 0;
     int         use2APDUs = 0;
+    int 	use_dl_object =  CKYBuffer_Size(data) > 200 ;
 
     CKYBuffer_InitEmpty(&empty);
     ccd.keyNumber = keyNumber;
     ccd.mode      = mode;
     ccd.direction = direction;
-    ccd.location  = CKY_DL_APDU;
+    ccd.location  = use_dl_object ? CKY_DL_OBJECT : CKY_DL_APDU;
 
     if (!apduRC)
     	apduRC = &status;
 
+    if (use_dl_object) {
+	CKYBuffer  sizeBuf;
+ 
+	CKYBuffer_InitEmpty(&sizeBuf);
+	CKYBuffer_AppendShort(&sizeBuf, CKYBuffer_Size(data));
+
+        ret = CKYApplet_WriteObjectFull(conn, 0xffffffff,
+                  0, CKYBuffer_Size(&sizeBuf), nonce,
+                  &sizeBuf, apduRC);
+
+        CKYBuffer_FreeData(&sizeBuf);
+        if( ret != CKYSUCCESS)
+           goto fail;
+
+        ret = CKYApplet_WriteObjectFull(conn, 0xffffffff,
+                  2, CKYBuffer_Size(data), nonce,
+                  data, apduRC);
+
+        if(ret != CKYSUCCESS)
+           goto fail; 
+    }
+
     if (mode == CKY_RSA_NO_PAD) {
-	ccd.data = data;
+	ccd.data = use_dl_object ? &empty : data;
 	ccd.sig  = sig;
 	ret = CKYApplet_HandleAPDU(conn, 
 			    CKYAppletFactory_ComputeCryptOneStep, &ccd, nonce, 
 			    CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, 
-			    result, apduRC);
+			    use_dl_object ? NULL : result, apduRC);
     	if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) {
 	    use2APDUs = 1;  /* maybe it's an old applet */
 	}
@@ -759,13 +902,109 @@
 			    CKYAppletFactory_ComputeCryptInit, &ccd, nonce, 
 			    0, CKYAppletFill_Null, NULL, apduRC);
 	if (ret == CKYSUCCESS) {
-	    ccd.data = data;
+	    ccd.data = use_dl_object ? &empty : data;
 	    ret = CKYApplet_HandleAPDU(conn, 
 			    CKYAppletFactory_ComputeCryptFinal, &ccd, nonce, 
 			    CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, 
-			    result, apduRC);
+			    use_dl_object ? NULL : result, apduRC);
 	}
     }
+
+    if (use_dl_object && ret == CKYSUCCESS) {
+        CKYBuffer  sizeOutBuf;
+        CKYBuffer_InitEmpty(&sizeOutBuf);
+
+        ret = CKYApplet_ReadObjectFull(conn,0xffffffff,
+                             0, 2,
+                             nonce,&sizeOutBuf,apduRC);
+
+        if(ret != CKYSUCCESS) {
+            CKYBuffer_FreeData(&sizeOutBuf);
+            goto fail;
+        }
+
+        dataSize = CKYBuffer_GetShort(&sizeOutBuf, 0);
+
+        CKYBuffer_FreeData(&sizeOutBuf);
+
+        ret = CKYApplet_ReadObjectFull(conn,0xffffffff, 
+                             2, dataSize,
+                             nonce,result,apduRC); 
+    }
+
+fail:
+
+    return ret;
+}
+
+CKYStatus
+CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber,
+    const CKYBuffer *publicValue, CKYBuffer *sharedSecret,
+    CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC)
+{
+    CKYStatus ret = CKYAPDUFAIL;
+    CKYAppletArgComputeECCKeyAgreement ccd;
+    CKYBuffer    empty;
+    CKYISOStatus status;
+    /* Routine creates a sym key, should easily fit in one apdu */
+
+    CKYBuffer_InitEmpty(&empty);
+    ccd.keyNumber = keyNumber;
+    ccd.location  = CKY_DL_APDU;
+
+    if (!apduRC)
+        apduRC = &status;
+
+    if (ccd.location == CKY_DL_APDU) {
+        ccd.publicValue = publicValue;
+        ccd.secretKey  = sharedSecret;
+        ret =   CKYApplet_HandleAPDU(conn,
+                            CKYAppletFactory_ComputeECCKeyAgreementOneStep, &ccd, nonce,
+                            CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal,
+                            result, apduRC);
+        if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) {
+            return ret;
+        }
+    } 
+
+    return ret;
+}
+
+CKYStatus
+CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber,
+    const CKYBuffer *data, CKYBuffer *sig,
+    CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC)
+{
+    int         use2APDUs = 0;
+    int         use_dl_object =  0; 
+    short       dataSize = 0;
+    CKYStatus ret = CKYAPDUFAIL;
+    CKYAppletArgComputeECCSignature ccd;
+    CKYBuffer    empty;
+    CKYISOStatus status;
+
+    CKYBuffer_InitEmpty(&empty);
+    ccd.keyNumber = keyNumber;
+
+    /* Assume APDU, the signature can only get so big with our key sizes, ~ 130 for 521 bit key. */
+    ccd.location  = CKY_DL_APDU;
+
+    if (!apduRC)
+        apduRC = &status;
+
+    if (ccd.location == CKY_DL_APDU) {
+        ccd.data = data;
+        ccd.sig  = sig;
+        ret =   CKYApplet_HandleAPDU(conn,
+                            CKYAppletFactory_ComputeECCSignatureOneStep, &ccd, nonce,
+                            CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal,
+                            result, apduRC);
+        if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) {
+            return ret;
+        }
+
+    } 
+
     return ret;
 }
 
@@ -777,11 +1016,39 @@
 	 	CKYBuffer *result, CKYISOStatus *apduRC)
 {
     CKYStatus ret;
-
-    ret = CKYApplet_HandleAPDU(conn, 
-			    CACAppletFactory_SignDecrypt, data, NULL, 
-			    CKYBuffer_Size(data), CKYAppletFill_ReplaceBuffer, 
+    CKYSize dataSize = CKYBuffer_Size(data);
+    CKYOffset offset = 0;
+    CKYBuffer tmp;
+
+    CKYBuffer_InitEmpty(&tmp);
+
+    CKYBuffer_Resize(result, 0);
+    for(offset = 0; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; 
+				offset += CKY_MAX_WRITE_CHUNK_SIZE) {
+	CKYBuffer_Resize(&tmp,0);
+	CKYBuffer_AppendBuffer(&tmp, data, offset, CKY_MAX_WRITE_CHUNK_SIZE);
+        ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptStep, 
+			    &tmp, NULL, CKY_SIZE_UNKNOWN, 
+			    CKYAppletFill_AppendBuffer, 
+			    result, apduRC);
+	if (ret != CKYSUCCESS) {
+	    goto done;
+	}
+    }
+    CKYBuffer_Resize(&tmp,0);
+    CKYBuffer_AppendBuffer(&tmp, data, offset, dataSize - offset);
+    ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptFinal, 
+			    &tmp, NULL, CKY_SIZE_UNKNOWN, 
+			    CKYAppletFill_AppendBuffer, 
 			    result, apduRC);
+
+    if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != dataSize)) {
+	/* RSA returns the same data size as input, didn't happen, so
+	 * something is wrong. */
+    }
+
+done:
+    CKYBuffer_FreeData(&tmp);
     return ret;
 }
 
@@ -789,7 +1056,7 @@
  * do a CAC VerifyPIN
  */
 CKYStatus
-CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local,
 		    CKYISOStatus *apduRC)
 {
     CKYStatus ret;
@@ -798,7 +1065,7 @@
 	apduRC = &status;
     }
 
-    ret = CKYApplet_HandleAPDU(conn, 
+    ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN :
 			    CACAppletFactory_VerifyPIN, pin, NULL, 
 			    0, CKYAppletFill_Null, 
 			    NULL, apduRC);
@@ -811,6 +1078,7 @@
     return ret;
 }
 
+
 /*
  * Get a CAC Certificate 
  */
@@ -840,6 +1108,63 @@
     }
     return ret;
 }
+
+/*
+ * Read a CAC Tag/Value file 
+ */
+CKYStatus
+CACApplet_ReadFile(CKYCardConnection *conn, CKYByte type, CKYBuffer *buffer, 
+		    CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYISOStatus status;
+    CKYByte maxtransfer;
+    unsigned short offset = 0;
+    unsigned short size;
+    CACAppletArgReadFile rfs;
+
+    CKYBuffer_Resize(buffer,0);
+    if (apduRC == NULL) {
+	apduRC = &status;
+    }
+    rfs.offset = 0;
+    rfs.count = 2;
+    rfs.type = type;
+
+    /* APDU's are expensive, Grab a big chunk of the file first if possible */
+    ret = CKYApplet_HandleAPDU(conn, 
+			    CACAppletFactory_ReadFile, &rfs, NULL, 
+			    rfs.count, CKYAppletFill_AppendBuffer,
+			    buffer, apduRC);
+    /* file is probably smaller than 100 bytes, get the actual size first */
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    size = CKYBuffer_GetShortLE(buffer, 0) + 2 /* include the length itself */;
+    maxtransfer = CKY_MAX_READ_CHUNK_SIZE;
+    /* get the rest of the buffer if necessary */
+    for (offset = CKYBuffer_Size(buffer); size > offset; 
+				offset = CKYBuffer_Size(buffer)) {
+	rfs.offset = offset;
+	rfs.count = MIN(size - offset, maxtransfer);
+	ret = CKYApplet_HandleAPDU(conn, 
+			    CACAppletFactory_ReadFile, &rfs, NULL, 
+			    rfs.count, CKYAppletFill_AppendBuffer,
+			    buffer, apduRC);
+	if (ret != CKYSUCCESS) {
+	    if (*apduRC == CAC_INVALID_PARAMS) {
+		maxtransfer = maxtransfer/2;
+		if (maxtransfer == 0) {
+		    return ret;
+		}
+	    } else {
+		return ret;
+	    }
+ 	}
+    }
+    return ret;
+}
+
 CKYStatus 
 CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, 
 			CKYSize *nextSize, CKYISOStatus *apduRC)
@@ -890,6 +1215,278 @@
     return ret;
 }
 
+/* Select the PIV applet */
+static CKYByte pivAid[] = {0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 
+			   0x10, 0x00};
+CKYStatus
+PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer PIV_Applet_AID,return_AID;
+    
+    CKYBuffer_InitEmpty(&return_AID);
+    CKYBuffer_InitFromData(&PIV_Applet_AID, pivAid, sizeof(pivAid));
+    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, 
+		 &PIV_Applet_AID,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, 
+		 &return_AID, apduRC);
+    /* Some cards return OK, but don't switch to our applet */
+    /* PIV has a well defined return for it's select, check to see if we have
+     * a PIV card here */
+    if (CKYBuffer_GetChar(&return_AID,0) != 0x61) {
+	/* not an application property template, so not a PIV. We could
+	 * check that the aid tag (0x4f) and theallocation authority tag (0x79)
+	 * are present, but what we are really avoiding is broken cards that
+	 * lie about being able to switch to a particular applet, so the first
+	 * tag should be sufficient */
+	ret = CKYAPDUFAIL; /* what we should have gotten */
+    }
+    CKYBuffer_FreeData(&PIV_Applet_AID);
+    CKYBuffer_FreeData(&return_AID);
+    return ret;
+}
+
+/*
+ * Get a PIV Certificate 
+ */
+CKYStatus
+PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, int tag,
+		    CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYISOStatus status;
+    CKYBuffer tagBuf;
+
+    CKYBuffer_InitEmpty(&tagBuf);
+    CKYBuffer_Reserve(&tagBuf,4); /* can be up to 4 bytes */
+
+    CKYBuffer_Resize(cert,0);
+    if (apduRC == NULL) {
+	apduRC = &status;
+    }
+    if (tag >= 0x01000000) {
+	ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 24) & 0xff);
+        if (ret != CKYSUCCESS) { goto loser; }
+    }
+    if (tag >= 0x010000) {
+	ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 16) & 0xff);
+        if (ret != CKYSUCCESS) { goto loser; }
+    }
+    if (tag >= 0x0100) {
+	ret =CKYBuffer_AppendChar(&tagBuf, (tag >> 8) & 0xff);
+        if (ret != CKYSUCCESS) { goto loser; }
+    }
+    ret = CKYBuffer_AppendChar(&tagBuf, tag  & 0xff);
+    if (ret != CKYSUCCESS) { goto loser; }
+	
+
+    ret = CKYApplet_HandleAPDU(conn, 
+			    PIVAppletFactory_GetCertificate, &tagBuf, NULL, 
+			    CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, cert,
+			    apduRC);
+loser:
+    CKYBuffer_FreeData(&tagBuf);
+
+    return ret;
+}
+
+
+/*
+ * record the next ber tag and length. NOTE: this is a state machine.
+ * we can handle the case where we are passed the data just one byte
+ * at a time.
+ */
+static CKYStatus
+pivUnwrap(const CKYBuffer *buf, CKYOffset *offset, 
+		 CKYSize *dataSize, PIVUnwrapState *unwrap)
+{
+    if (unwrap->tag == 0) {
+	unwrap->tag = CKYBuffer_GetChar(buf, *offset);
+	if (unwrap->tag == 0) unwrap->tag = 0xff;
+	(*offset)++;
+	(*dataSize)--;
+    }
+    if (*dataSize == 0) {
+	return CKYSUCCESS;
+    }
+    if (unwrap->length_bytes != 0) {
+	int len;
+	if (unwrap->length_bytes == -1) {
+	    len = CKYBuffer_GetChar(buf, *offset);
+	    unwrap->length_bytes = 0;
+	    unwrap->length = len;
+	    (*offset)++;
+	    (*dataSize)--;
+	    if (len & 0x80) {
+		unwrap->length = 0;
+		unwrap->length_bytes = len & 0x7f;
+	    }
+	}
+	while ((*dataSize != 0) && (unwrap->length_bytes != 0)) {
+		len = CKYBuffer_GetChar(buf, *offset);
+		(*offset) ++;
+		(*dataSize) --;
+		unwrap->length = ((unwrap->length) << 8 | len);
+		unwrap->length_bytes--;
+	}
+    }
+    return CKYSUCCESS;
+}
+
+/*
+ * Remove the BER wrapping first...
+ */
+static CKYStatus
+pivAppletFill_AppendUnwrapBuffer(const CKYBuffer *response, 
+				 CKYSize size, void *param)
+{
+    PIVAppletRespSignDecrypt *prsd = (PIVAppletRespSignDecrypt *)param;
+    CKYBuffer *buf = prsd->buf;
+    CKYSize dataSize = CKYBuffer_Size(response);
+    CKYOffset offset = 0;
+
+    if (dataSize <= 2) {
+	return CKYSUCCESS;
+    }
+    dataSize -= 2;
+    /* remove the first tag */
+    (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_1);
+    if (dataSize == 0) {
+	return CKYSUCCESS;
+    }
+    /* remove the second tag */
+    (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_2);
+    if (dataSize == 0) {
+	return CKYSUCCESS;
+    }
+    /* the rest is real data */
+    return CKYBuffer_AppendData(buf, CKYBuffer_Data(response) + offset, 
+						dataSize);
+}
+
+static CKYStatus
+piv_wrapEncodeLength(CKYBuffer *buf, int length, int ber_len)
+{
+    if (ber_len== 1) {
+	CKYBuffer_AppendChar(buf,length);
+    } else {
+	ber_len--;
+	CKYBuffer_AppendChar(buf,0x80+ber_len);
+	while(ber_len--) {
+	    CKYBuffer_AppendChar(buf,(length >> (8*ber_len)) & 0xff);
+ 	}
+    }
+    return CKYSUCCESS;
+}
+/*
+ * do a PIV Sign/Decrypt
+ */
+CKYStatus
+PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, unsigned int keySize, int derive,
+		const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYSize dataSize = CKYBuffer_Size(data);
+    CKYSize outputSize = keySize;
+    CKYOffset offset = 0;
+    CKYBuffer tmp;
+    CKYByte  alg;
+    int ber_len_1;
+    int ber_len_2;
+    int length;
+    PIVAppletArgSignDecrypt pasd; 
+    PIVAppletRespSignDecrypt prsd; 
+
+    /* PIV only defines RSA 1024 and 2048, ECC 256 and ECC 384!!! */
+    if (keySize == 128) { /* 1024 bit == 128 bytes */
+	ber_len_2 = 2;
+	ber_len_1 = 2;
+	alg = 0x6;
+    } else if (keySize == 256) { /* 2048 bits == 256 bytes */
+	ber_len_2 = 3;
+	ber_len_1 = 3;
+	alg = 0x7;
+    } else if (keySize == 32) {  /* 256 bits = 32 bytes */
+	ber_len_2 = 1;
+	ber_len_1 = 1;
+	alg = 0x11;
+	if (!derive) outputSize = keySize*2;
+    } else if (keySize == 48) {  /* 384 bits = 48 bytes */
+	ber_len_2 = 1;
+	ber_len_1 = 1;
+	alg = 0x14;
+	if (!derive) outputSize = keySize*2;
+    } else {
+	return CKYINVALIDARGS; 
+    }
+
+    CKYBuffer_InitEmpty(&tmp);
+    ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE);
+    if (ret != CKYSUCCESS) {
+	goto done;
+    }
+    CKYBuffer_AppendChar(&tmp,0x7c);
+    piv_wrapEncodeLength(&tmp,dataSize + ber_len_2 + 3,ber_len_1);
+    CKYBuffer_AppendChar(&tmp,0x82);
+    CKYBuffer_AppendChar(&tmp,0x0);
+    CKYBuffer_AppendChar(&tmp, derive ? 0x85 : 0x81);
+    piv_wrapEncodeLength(&tmp,dataSize,ber_len_2);
+
+    /* now length == header length from here to the end*/
+    length = CKYBuffer_Size(&tmp);
+
+    if (length + dataSize > CKY_MAX_WRITE_CHUNK_SIZE) {
+	CKYBuffer_AppendBuffer(&tmp, data, 0, CKY_MAX_WRITE_CHUNK_SIZE-length);
+    } else {
+	CKYBuffer_AppendBuffer(&tmp, data, 0, dataSize);
+    }
+
+    prsd.tag_1.tag = 0;
+    prsd.tag_1.length_bytes = -1;
+    prsd.tag_1.length = 0;
+    prsd.tag_2.tag = 0;
+    prsd.tag_2.length_bytes = -1;
+    prsd.tag_2.length = 0;
+    prsd.buf = result;
+    pasd.alg = alg;
+    pasd.key = key;
+    pasd.buf = &tmp;
+
+    CKYBuffer_Resize(result,0);
+    for(offset = -length; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; ) {
+	pasd.chain = 1;
+	pasd.len = 0;
+        ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, 
+			    &pasd, NULL, CKY_SIZE_UNKNOWN, 
+			    pivAppletFill_AppendUnwrapBuffer, 
+			    &prsd, apduRC);
+	if (ret != CKYSUCCESS) {
+	    goto done;
+	}
+	CKYBuffer_Resize(&tmp,0);
+	/* increment before we append the next tmp buffer */
+	offset += CKY_MAX_WRITE_CHUNK_SIZE;
+	CKYBuffer_AppendBuffer(&tmp, data, offset,
+			MIN(dataSize-offset, CKY_MAX_WRITE_CHUNK_SIZE));
+    }
+
+    pasd.chain = 0;
+    pasd.len = outputSize;
+
+    ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, 
+			    &pasd, NULL, CKY_SIZE_UNKNOWN, 
+			    pivAppletFill_AppendUnwrapBuffer, 
+			    &prsd, apduRC);
+
+    if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != outputSize)) {
+	/* RSA returns the same data size as input, didn't happen, so
+	 * something is wrong. */
+    }
+
+done:
+    CKYBuffer_FreeData(&tmp);
+    return ret;
+}
 
 /*
  * PIN cluster
@@ -1033,6 +1630,44 @@
     } while ((size > 0) && (ret == CKYSUCCESS));
 
     return ret;
+}
+
+/*
+ * Write Object
+ * This makes multiple APDU calls to write the entire object.
+ *
+ */
+
+CKYStatus 
+CKYApplet_WriteObjectFull(CKYCardConnection *conn, unsigned long objectID,
+                  CKYOffset offset, CKYSize size, const CKYBuffer *nonce,
+                  const CKYBuffer *data, CKYISOStatus *apduRC)
+{
+
+    CKYBuffer chunk;
+    CKYOffset srcOffset = 0;
+    CKYAppletArgWriteObject wod;
+    CKYStatus ret = CKYSUCCESS;
+
+    wod.objectID = objectID;
+    wod.offset = offset;
+    do {
+        wod.size = (CKYByte) MIN(size, 220);
+        ret = CKYBuffer_InitFromBuffer(&chunk, data,
+                                       srcOffset, wod.size);
+        if(ret == CKYSUCCESS)  {
+            wod.data = &chunk;
+            ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_WriteObject, &wod,
+               nonce, 0, CKYAppletFill_Null, NULL, apduRC);
+            size -= wod.size;
+            wod.offset += wod.size;
+            srcOffset  += wod.size;
+            CKYBuffer_FreeData(&chunk);
+       }
+
+    } while ((size > 0) && (ret == CKYSUCCESS));
+
+    return ret;
 }
 
 /*