Blob Blame History Raw
diff -up ./src/coolkey/coolkey.cpp.p15 ./src/coolkey/coolkey.cpp
--- ./src/coolkey/coolkey.cpp.p15	2015-07-06 10:27:55.769827286 -0700
+++ ./src/coolkey/coolkey.cpp	2015-07-06 10:27:55.784827002 -0700
@@ -599,13 +599,10 @@ C_Login(CK_SESSION_HANDLE hSession, CK_U
     }
     try {
         log->log("C_Login called\n");
-        if( userType != CKU_USER ) {
-            throw PKCS11Exception(CKR_USER_TYPE_INVALID);
-        }
         if( pPin == NULL ) {
             throw PKCS11Exception(CKR_ARGUMENTS_BAD);
         }
-        slotList->login(hSession, pPin, ulPinLen);
+        slotList->login(hSession, userType, pPin, ulPinLen);
         return CKR_OK;
     } catch(PKCS11Exception &e) {
         e.log(log);
diff -up ./src/coolkey/object.cpp.p15 ./src/coolkey/object.cpp
--- ./src/coolkey/object.cpp.p15	2015-07-06 10:27:55.770827267 -0700
+++ ./src/coolkey/object.cpp	2015-07-06 10:27:55.785826984 -0700
@@ -19,9 +19,9 @@
 
 #include "mypkcs11.h"
 #include "PKCS11Exception.h"
-#include "object.h"
 #include <algorithm>
 #include <string.h>
+#include "object.h"
 
 using std::find_if;
 
@@ -29,7 +29,7 @@ const CKYByte rsaOID[] = {0x2A,0x86,0x48
 const CKYByte eccOID[] = {0x2a,0x86,0x48,0xce,0x3d,0x02,0x01};
 
 #ifdef DEBUG
-void dump(CKYBuffer *buf)
+void dump(const char *label, const CKYBuffer *buf)
 {
     CKYSize i;
     CKYSize size = CKYBuffer_Size(buf);
@@ -38,8 +38,10 @@ void dump(CKYBuffer *buf)
     char *bp = &string[0];
     CKYByte c;
 
+    printf("%s size=%d\n", label, (int)size);
+
     for (i=0; i < size; i++) {
-        if (i && ((i % (ROW_LENGTH-1)) == 0) ) {
+        if (i && ((i % (ROW_LENGTH)) == 0) ) {
             *bp = 0;
             printf(" %s\n",string);
             bp = &string[0];
@@ -49,7 +51,35 @@ void dump(CKYBuffer *buf)
         *bp++ =  (c < ' ') ? '.' : ((c & 0x80) ? '*' : c);
     }
     *bp = 0;
-    for (i= (i % (ROW_LENGTH-1)); i && (i < ROW_LENGTH); i++) {
+    for (i= (i % (ROW_LENGTH)); i && (i < ROW_LENGTH); i++) {
+        printf("   ");
+    }
+    printf(" %s\n",string);
+    fflush(stdout);
+}
+
+void dumpData(const char *label, const CKYByte *buf, CKYSize size)
+{
+    CKYSize i;
+#define ROW_LENGTH 16
+    char string[ROW_LENGTH+1];
+    char *bp = &string[0];
+    CKYByte c;
+
+    printf("%s size=%d:\n",label, (int)size);
+
+    for (i=0; i < size; i++) {
+        if (i && ((i % (ROW_LENGTH)) == 0) ) {
+            *bp = 0;
+            printf(" %s\n",string);
+            bp = &string[0];
+        }
+        c = buf[i];
+        printf("%02x ",c);
+        *bp++ =  (c < ' ') ? '.' : ((c & 0x80) ? '*' : c);
+    }
+    *bp = 0;
+    for (i= (i % (ROW_LENGTH)); i && (i < ROW_LENGTH); i++) {
         printf("   ");
     }
     printf(" %s\n",string);
@@ -77,16 +107,23 @@ class AttributeTypeMatch
 };
 
 PKCS11Object::PKCS11Object(unsigned long muscleObjID_,CK_OBJECT_HANDLE handle_)
-    : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL), keyType(unknown)
+    : muscleObjID(muscleObjID_), handle(handle_), label(NULL), keySize(0),
+                   user(CKU_USER), name(NULL), keyType(unknown),
+		   keyRef(PK15_INVALID_KEY_REF)
 { 
     CKYBuffer_InitEmpty(&pubKey);
+    CKYBuffer_InitEmpty(&authId);
+    CKYBuffer_InitEmpty(&pinAuthId);
 }
 
 PKCS11Object::PKCS11Object(unsigned long muscleObjID_, const CKYBuffer *data,
     CK_OBJECT_HANDLE handle_) :  muscleObjID(muscleObjID_), handle(handle_),
-                        label(NULL), name(NULL), keyType(unknown)
+                   label(NULL), keySize(0), user(CKU_USER), name(NULL), 
+		   keyType(unknown), keyRef(PK15_INVALID_KEY_REF) 
 {
     CKYBuffer_InitEmpty(&pubKey);
+    CKYBuffer_InitEmpty(&authId);
+    CKYBuffer_InitEmpty(&pinAuthId);
 
     CKYByte type = CKYBuffer_GetChar(data,0);
     // verify object ID is what we think it is
@@ -582,6 +619,21 @@ PKCS11Object::setAttribute(CK_ATTRIBUTE_
 }
 
 void
+PKCS11Object::setAttribute(CK_ATTRIBUTE_TYPE type, const CKYByte *data,
+			CKYSize size)
+{
+    AttributeIter iter;  
+
+    iter = find_if(attributes.begin(), attributes.end(),
+        AttributeTypeMatch(type));
+    if( iter != attributes.end() )  {
+        iter->setValue( data, size);
+    } else {
+        attributes.push_back(PKCS11Attribute(type, data, size));
+    }
+}
+
+void
 PKCS11Object::setAttribute(CK_ATTRIBUTE_TYPE type, const char *string)
 {
     CKYBuffer buf;
@@ -613,7 +665,7 @@ PKCS11Object::setAttributeULong(CK_ATTRI
 
 typedef struct {
     const CKYByte*data;
-    unsigned int len;
+    CKYSize len;
 } CCItem;
 
 typedef enum {
@@ -621,9 +673,9 @@ typedef enum {
     SECFailure=1
 } SECStatus;
 
-static const CKYByte*
-dataStart(const CKYByte *buf, unsigned int length,
-                        unsigned int *data_length, bool includeTag) {
+const CKYByte*
+dataStart(const CKYByte *buf, CKYSize length,
+                        CKYSize *data_length, bool includeTag) {
     unsigned char tag;
     unsigned int used_length= 0;
 
@@ -673,7 +725,7 @@ dataStart(const CKYByte *buf, unsigned i
 }
 
 static const CKYByte *
-unwrapBitString(const CKYByte *buf, unsigned int len, unsigned int *retLen)
+unwrapBitString(const CKYByte *buf, CKYSize len, CKYSize *retLen)
 {
     /* for RSA, bit string always has byte number of bits */
     if (buf[0] != 0) {
@@ -687,15 +739,15 @@ unwrapBitString(const CKYByte *buf, unsi
 }
 
 static SECStatus
-GetECKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length,
+GetECKeyFieldItems(const CKYByte *spki_data, CKYSize spki_length,
         CCItem *point, CCItem *params)
 {
     const CKYByte *buf = spki_data;
-    unsigned int buf_length = spki_length;
+    CKYSize buf_length = spki_length;
     const CKYByte *algid;
-    unsigned int algidlen;
+    CKYSize algidlen;
     const CKYByte *dummy;
-    unsigned int dummylen;
+    CKYSize dummylen;
 
     if (!point || !params || !buf)
         return SECFailure;
@@ -756,12 +808,12 @@ GetKeyOIDMatches(const CKYByte *spki_dat
 }
 
 static SECStatus
-GetKeyAlgorithmId(const CKYByte *spki_data, unsigned int spki_length,
+GetKeyAlgorithmId(const CKYByte *spki_data, CKYSize spki_length,
        CCItem *algorithmId)
 {
 
     const CKYByte *buf = spki_data;
-    unsigned int buf_length = spki_length;
+    CKYSize buf_length = spki_length;
 
     if ( algorithmId == NULL) return SECFailure;
 
@@ -786,7 +838,7 @@ GetKeyTypeFromSPKI(const CKYBuffer *key)
             "Failed to decode key algorithm ID.");
     }
 
-    unsigned int length = 0;
+    CKYSize length = 0;
     const CKYByte *keyData = NULL;
 
     /* Get actual oid buffer */
@@ -829,13 +881,13 @@ GetKeyTypeFromSPKI(const CKYBuffer *key)
 }
 
 static SECStatus
-GetKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length,
+GetKeyFieldItems(const CKYByte *spki_data,CKYSize spki_length,
         CCItem *modulus, CCItem *exponent)
 {
     const CKYByte *buf = spki_data;
-    unsigned int buf_length = spki_length;
+    CKYSize buf_length = spki_length;
     const CKYByte*dummy;
-    unsigned int dummylen;
+    CKYSize dummylen;
 
     /* skip past the algorithm id */
     dummy = dataStart(buf,buf_length,&dummylen,false);
@@ -955,7 +1007,7 @@ Key::Key(unsigned long muscleObjID, cons
 }
 
 void
-Key::completeKey(const PKCS11Object &cert)
+PKCS11Object::completeKey(const PKCS11Object &cert)
 {
     // infer key attributes from cert
     bool modulusExists, exponentExists;
@@ -1015,14 +1067,14 @@ Key::completeKey(const PKCS11Object &cer
 }
 
 static SECStatus
-GetCertFieldItems(const CKYByte *dercert,unsigned int cert_length,
+GetCertFieldItems(const CKYByte *dercert, CKYSize cert_length,
         CCItem *issuer, CCItem *serial, CCItem *derSN, CCItem *subject,
         CCItem *valid, CCItem *subjkey)
 {
     const CKYByte *buf;
-    unsigned int buf_length;
+    CKYSize buf_length;
     const CKYByte*dummy;
-    unsigned int dummylen;
+    CKYSize dummylen;
 
     /* get past the signature wrap */
     buf = dataStart(dercert,cert_length,&buf_length, false);
@@ -1330,10 +1382,10 @@ static const unsigned char CN_DATA[] = {
 const unsigned int CN_LENGTH = sizeof(CN_DATA);
 
 static SECStatus
-GetCN(const CKYByte *dn, unsigned int dn_length, CCItem *cn)
+GetCN(const CKYByte *dn, CKYSize dn_length, CCItem *cn)
 {
     const CKYByte *buf;
-    unsigned int buf_length;
+    CKYSize buf_length;
 
     /* unwrap the sequence */
     buf = dataStart(dn,dn_length,&buf_length, false);
@@ -1341,9 +1393,9 @@ GetCN(const CKYByte *dn, unsigned int dn
 
     while (buf_length) {
         const CKYByte *name;
-        unsigned int name_length;
+        CKYSize name_length;
         const CKYByte *oid;
-        unsigned int oid_length;
+        CKYSize oid_length;
 
         /* unwrap the set */
         name = dataStart(buf, buf_length, &name_length, false);
@@ -1408,13 +1460,6 @@ CACCert::CACCert(CKYByte instance, const
 {
     CKYBuffer id;
     CKYBuffer empty;
-    CK_BBOOL decrypt = FALSE;
-
-    /* So we know what the key is supposed to be used for based on
-     * the instance */
-    if (instance == 2) {
-        decrypt = TRUE;
-    }
 
     CKYBuffer_InitEmpty(&empty);
     setAttributeULong(CKA_CLASS, CKO_CERTIFICATE);
@@ -1456,6 +1501,1063 @@ CACCert::CACCert(CKYByte instance, const
     CKYBuffer_FreeData(&derIssuer);
 }
 
+static const CKYByte rev[] = {
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+unsigned long GetBits(const CKYByte *entry, CKYSize entrySize,
+				unsigned int numBits, unsigned int numBytes)
+{
+   unsigned long bits = 0;
+   unsigned long bitFlag = 0;
+   unsigned int i;
+
+   /* size of zero is valid for no bits */
+   if (entrySize <= 1) {
+	return 0;
+   }
+   entrySize--;
+   entry++;
+
+   /* if we are longer than and unsigned, just bail now */
+   if (entrySize > sizeof (unsigned long)) {
+	bitFlag = BROKEN_FLAG;
+	entrySize = sizeof(unsigned long);
+   }
+   /* turn the flags into an int */
+   for (i=0; i < entrySize; i++) {
+	CKYByte c = rev[entry[i]];
+	bits  = bits | (c << i*8);
+   }
+   return bits | bitFlag;
+}
+
+
+/*
+ * parse the path object.
+ * Caller has already unwrapped the outer ASN1Sequence
+ */
+CKYStatus PK15ObjectPath::setObjectPath(const CKYByte *current, CKYSize size)
+{
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    unsigned int i;
+    CKYStatus status;
+
+
+    if ((current == NULL) || (current[0] != ASN1_OCTET_STRING)) {
+	return CKYINVALIDDATA;
+    }
+    /* entry */
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    tagSize = entry - current;
+    current += entrySize + tagSize;
+    size -= (entrySize +tagSize);
+    if (size < 0) { return CKYINVALIDDATA; }
+    status = CKYBuffer_Replace(&path, 0, entry, entrySize);
+    if (status != CKYSUCCESS) {
+	return status;
+    }
+
+    /* index */
+    if ((size != 0) && current[0] ==  ASN1_INTEGER) { 
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDDATA; }
+	tagSize = entry - current;
+	current += entrySize + tagSize;
+	size -= (entrySize +tagSize);
+	if (size < 0) { return CKYINVALIDDATA; }
+	if (entrySize > 5) { return CKYINVALIDDATA; }
+	for (index = 0, i=0; i < entrySize; i++) {
+	    index = (index << 8) + (unsigned int) entry[i];
+	}
+    }
+
+    /* length */
+    if ((size != 0) && ((current[0]|ASN1_CONSTRUCTED) ==  ASN1_CHOICE_0)) { 
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDDATA; }
+	tagSize = entry - current;
+	current += entrySize + tagSize;
+	size -= (entrySize +tagSize);
+	if (size < 0) { return CKYINVALIDDATA; }
+	if (entrySize > 5) { return CKYINVALIDDATA; }
+	for (length = 0, i=0; i < entrySize; i++) {
+	    length = (length << 8) + (unsigned int) entry[i];
+	}
+    }
+    return CKYSUCCESS;
+}
+
+static unsigned int pK15GetTag(PK15ObjectType type) {
+     switch (type) { case PK15PvKey: case PK15PuKey: return 'k'<<24;
+		     case PK15Cert: return 'c' << 24; default: break; }
+     return 'v';
+}
+
+
+PK15Object::PK15Object(CKYByte inst, PK15ObjectType type, 
+	const CKYByte *der, CKYSize derSize) 
+	: PKCS11Object(pK15GetTag(type) | ((inst+'0') << 16), 0xa000 | inst)
+{
+    CKYStatus status;
+
+    instance = inst;
+    p15Type =  type;
+    CKYBuffer_InitEmpty(&authId);
+    CKYBuffer_InitEmpty(&pinAuthId);
+    state = PK15StateInit;
+    pinInfo.pinFlags = 0;
+    pinInfo.pinType = P15PinUTF8;
+    pinInfo.minLength = 4;
+    pinInfo.storedLength = 0;
+    pinInfo.maxLength = 0;
+    pinInfo.pinRef = 0;
+    pinInfo.padChar = 0xff;
+
+    status = completeObject(der, derSize);
+    if (status != CKYSUCCESS) {
+	state = PK15StateInit; /* don't try to fetch any more if we failed */
+    }
+}
+
+/* returns true if there is more work to do... */
+CKYStatus 
+PK15Object::completeObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYByte objectTag;
+    CKYStatus status;
+    CKYBitFlags bits;
+
+    switch (state) {
+    case PK15StateInit:
+    case PK15StateNeedObject:
+	break;
+    case PK15StateNeedRawPublicKey:
+	return  completeRawPublicKey(current, currentSize);
+    case PK15StateNeedRawCertificate:
+	return  completeRawCertificate(current, currentSize);
+    case PK15StateComplete:
+	return CKYSUCCESS;
+    }
+
+    if (current == NULL) { return CKYINVALIDARGS; }
+
+    objectTag = current[0];
+
+    setAttributeBool(CKA_TOKEN, TRUE);
+
+    /* set type specific attributes */
+    switch (p15Type) {
+    case PK15Cert:
+	setAttributeULong(CKA_CLASS, CKO_CERTIFICATE);
+    	setAttributeULong(CKA_CERTIFICATE_TYPE, CKC_X_509);
+	if (objectTag != PK15X509CertType) {
+	    return CKYUNSUPPORTED;
+	}
+	break;
+    case PK15PvKey:
+	setAttributeULong(CKA_CLASS, CKO_PRIVATE_KEY);
+	goto set_key_type;
+    case PK15PuKey:
+	setAttributeULong(CKA_CLASS, CKO_PUBLIC_KEY);
+set_key_type:
+	switch (objectTag) {
+	case PK15RSAKeyType:
+	    keyType = rsa;
+	    setAttributeULong(CKA_KEY_TYPE, CKK_RSA);
+	    break;
+	case PK15ECCKeyType:
+	    keyType = ecc;
+	    setAttributeULong(CKA_KEY_TYPE, CKK_EC);
+	    break;
+	case PK15DSAKeyType:
+	case PK15DHKeyType:
+	default:
+	    return CKYUNSUPPORTED;
+	}
+	break;
+    case PK15AuthObj:
+	setAttributeULong(CKA_CLASS, CKO_DATA);
+	break;
+    default:
+	return CKYUNSUPPORTED;
+    }
+
+    /* unwrap the object */	
+    current = dataStart(current, currentSize, &currentSize, false);
+    if (current == NULL) { return CKYINVALIDDATA; }
+
+    /*
+     * parse the Common Attributes 
+     *     label UTF8_STRING
+     *     flags BIT_STRING (optional)
+     *     authid OCTET_STRING (optional)
+     */
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) 
+	{ return CKYINVALIDDATA; }
+    /* unwrap */
+    commonAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* point current to the next section (cass attributes)  */
+    tagSize = commonAttributes - current;
+    current += commonSize + tagSize;
+    currentSize -= (commonSize +tagSize);
+    if (currentSize < 0) { return CKYINVALIDDATA; }
+
+    /* get the CKA_LABEL */
+    if (commonAttributes[0] != ASN1_UTF8_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAttributes;
+    commonAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    setAttribute(CKA_LABEL, entry, entrySize);
+
+    /* parse optional flags */
+    bits = BROKEN_FLAG;
+    if (commonAttributes[0] == ASN1_BIT_STRING) {
+	entry = dataStart(commonAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAttributes;
+	commonAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	bits = GetBits(entry,entrySize,2,1);
+    }
+
+    if (commonAttributes[0] == ASN1_OCTET_STRING) {
+	entry = dataStart(commonAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAttributes;
+	commonAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	status = CKYBuffer_Replace(&authId, 0, entry, entrySize);
+	if (status != CKYSUCCESS) {
+	   return status;
+	}
+    }
+
+    if (bits & BROKEN_FLAG) {
+	bits = defaultCommonBits();
+    }
+    setAttributeBool(CKA_PRIVATE, 
+		(bits & P15FlagsPrivate) ? TRUE: FALSE);
+    setAttributeBool(CKA_MODIFIABLE, FALSE); /* our token is ReadOnly, so the
+					      * object is never modifiable for
+					      * us */
+    /* future common attributes here */
+
+    /*
+     *  Parse Class variables
+     *
+     */
+    switch (p15Type) {
+    case PK15Cert:
+	status = completeCertObject(current,currentSize);
+	break;
+    case PK15PuKey:
+    case PK15PvKey:
+	status = completeKeyObject(current,currentSize);
+	break;
+    case PK15AuthObj:
+	status = completeAuthObject(current, currentSize);
+	break;
+    }
+    return status;
+}
+
+
+CKYStatus 
+PK15Object::completeCertObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonCertAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    CKYByte valueTag;
+
+    CKYBuffer_InitEmpty(&empty);
+
+    /*
+     * parse the Common Cert Attributes 
+     *     id OCTET_STRING
+     *     authority BOOLEAN DEFAULT FALSE
+     *     requestId BIT_STRING (optional)
+     *     thumbprint [0] PKS15OOBCertHash (optional)
+     */
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) 
+		{ return CKYINVALIDARGS; }
+    /* unwrap */
+    commonCertAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonCertAttributes == NULL) { return CKYINVALIDDATA; }
+    /* point current to the next section (type attributes)  */
+    tagSize = commonCertAttributes - current;
+    current += commonSize + tagSize;
+    currentSize -= (commonSize +tagSize);
+    if (currentSize < 0) { return CKYINVALIDDATA; }
+
+    /* get the id */
+    if (commonCertAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonCertAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonCertAttributes;
+    commonCertAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    setAttribute(CKA_ID, entry, entrySize);
+
+
+    /* skip authority (currently unused) */
+    /* skip requestID */
+    /* skip thumbprint */
+    /* future common cert attributes here */
+
+    /* certs have not subclass attributes  ASN1_CHOICE_0 */
+
+    /* handle the X509 type attributes */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDDATA; }
+    /* unwrap */
+    commonCertAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonCertAttributes == NULL) { return CKYINVALIDDATA; }
+   
+    /*
+     * PCKS11X504CertificateAttributes
+     *     value   SEQUENCE or CHOICE_0
+     *     ... don't care about the rest.
+     */
+    valueTag = commonCertAttributes[0];
+    /* unwrapp */
+    entry = dataStart(commonCertAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    if (valueTag == ASN1_SEQUENCE) {
+    	entry = dataStart(entry, entrySize, &entrySize, false);
+    	if (entry == NULL) { return CKYINVALIDDATA; }
+	/* if we have a path, the actual object is in another file,
+	 * tell the caller to get it and come back here */
+	status = objectPath.setObjectPath(entry, entrySize);
+	state = PK15StateNeedRawCertificate;
+        return status;
+    }
+    if (valueTag != ASN1_CHOICE_0) {
+	return CKYINVALIDDATA;
+    }
+    return  completeRawCertificate(entry, entrySize);
+}
+
+CKYStatus 
+PK15Object::completeAuthObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonAuthAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+
+    CKYBuffer_InitEmpty(&empty);
+
+    if (current == NULL) { return CKYINVALIDARGS; }
+    /* common Auth attributes */
+    if (current[0] == ASN1_SEQUENCE) {
+         /* unwrap */
+        commonAuthAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+	if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+	tagSize = commonAuthAttributes - current;
+	current += commonSize + tagSize;
+	currentSize -= (commonSize + tagSize);
+	if (currentSize < 0) { return CKYINVALIDDATA; }
+	if (commonAuthAttributes[0] != ASN1_OCTET_STRING) {
+	    return CKYINVALIDDATA;
+	}
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	status = CKYBuffer_Replace(&pinAuthId, 0, entry, entrySize);
+	if (status != CKYSUCCESS) {
+	   return status;
+	}
+	
+    }
+    /* auth specific values */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDARGS; }
+    /* unwrap */
+    commonAuthAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+    tagSize = commonAuthAttributes - current;
+    current += commonSize + tagSize;
+    currentSize -= (commonSize +tagSize);
+    if (currentSize < 0) { return CKYINVALIDDATA; }
+    /*
+     * parse the Pin Auth Attributes 
+     *     pinFlags  BIT_STRING
+     *     pinType   ENUMERATED (bcd, ascii-numeric, utf8)
+     *     minLength INTEGER
+     *     storedLength INTEGER
+     *     maxlength INTEGER (optional)
+     *     pinReference CHOICE_0 (optional)
+     *     padChar OCTET_STRING (optional)
+     *     lastPinChange GENERALIZED_TIME (optional)
+     *     path PKCS15Path (optional)
+     */
+    if (commonAuthAttributes[0] != ASN1_SEQUENCE) { return CKYINVALIDARGS; }
+    commonAuthAttributes = dataStart(commonAuthAttributes, 
+					commonSize, &commonSize, false);
+    if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* parse pin flags */
+    if (commonAuthAttributes[0] != ASN1_BIT_STRING) { return CKYINVALIDDATA; }
+
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    pinInfo.pinFlags = GetBits(entry,entrySize,9,2);
+
+
+    /* parse PinType */
+    if (commonAuthAttributes[0] != ASN1_ENUMERATED) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    /* turn entry into an int */
+    if (entrySize > 1) { return CKYINVALIDARGS; }
+    pinInfo.pinType = (P15PinType) *entry;
+
+    /* parse minLength */
+    if (commonAuthAttributes[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    if (entrySize > 1) { return CKYINVALIDARGS; }
+    pinInfo.minLength = *entry;
+
+    /* parse storedLength */
+    if (commonAuthAttributes[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonAuthAttributes;
+    commonAuthAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    if (entrySize > 1) { return CKYINVALIDARGS; }
+    pinInfo.storedLength = *entry;
+
+    /* parse maxLength (optional) */
+    if (commonAuthAttributes[0] == ASN1_INTEGER) { 
+	unsigned long maxPin;
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > sizeof (maxPin)) { return CKYINVALIDARGS; }
+	maxPin = 0; 
+	CKYSize i;
+	for (i=0; i < entrySize; i++) {
+	    maxPin = (maxPin << 8) | entry[i];
+	}
+	pinInfo.maxLength = maxPin;
+    }
+
+    /* parse pin ref  (optional) */
+    if ((commonAuthAttributes[0]|ASN1_CONSTRUCTED) == ASN1_CHOICE_0)  {
+	CKYByte pinRef;
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 2) { return CKYINVALIDARGS; }
+	if (entrySize == 2) {
+	    if (*entry != 0) { return CKYINVALIDARGS; }
+	    pinRef = entry[1];
+	} else pinRef = entry[0];
+	pinInfo.pinRef = pinRef;
+    }
+
+    /* parse padChar */
+    if (commonAuthAttributes[0] == ASN1_OCTET_STRING) { 
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 1) { return CKYINVALIDARGS; }
+	pinInfo.padChar = *entry;
+    }
+
+    /* skip lastPinChange */
+    if (commonAuthAttributes[0] == ASN1_GENERALIZED_TIME) { 
+	entry = dataStart(commonAuthAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+    }
+    /* parse path */
+    if (commonAuthAttributes[0] == ASN1_SEQUENCE) { 
+	entry = dataStart(commonAuthAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonAuthAttributes;
+	commonAuthAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	/* if we have a path, the actual object is in another file,
+	 * tell the caller to get it and come back here */
+	status = objectPath.setObjectPath(entry, entrySize);
+	if (status != CKYSUCCESS) { return status; }
+    }
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+CKYStatus
+PK15Object::completeKeyObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonKeyAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    unsigned long bits;
+    /*bool native; */
+
+    CKYBuffer_InitEmpty(&empty);
+    /*
+     * parse the Common Key Attributes 
+     *     id OCTET_STRING
+     *     usageFlags BIT_STRING 
+     *     native BOOLEAN DEFAULT TRUE
+     *     accessFlags BIT_STRING (optional)
+     *     keyReference OCTET_STRING (optional)
+     *     startDate GENERALIZED_TIME (optional)
+     *     endDate [0] GENERALIZED_TYPE (optional)
+     */
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) 
+		{ return CKYINVALIDARGS; }
+    /* unwrap */
+    commonKeyAttributes = dataStart(current, currentSize, &commonSize, false);
+    if (commonKeyAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* point current to the next section (sublcass attributes)  */
+    tagSize = commonKeyAttributes - current;
+    current += commonSize + tagSize;
+    currentSize -= (commonSize +tagSize);
+    if (currentSize < 0) { return CKYINVALIDDATA; }
+
+    /* get the id */
+    if (commonKeyAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonKeyAttributes;
+    commonKeyAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    setAttribute(CKA_ID, entry, entrySize);
+
+    /* parse flags */
+    if (commonKeyAttributes[0] != ASN1_BIT_STRING) { return CKYINVALIDDATA; }
+    entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonKeyAttributes;
+    commonKeyAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    bits = GetBits(entry,entrySize,10,2);
+    if (bits & BROKEN_FLAG) {
+	bits = defaultUsageBits();
+    }
+    setAttributeBool(CKA_ENCRYPT,
+			(bits & P15UsageEncrypt)          ? TRUE : FALSE);
+    setAttributeBool(CKA_DECRYPT,
+			(bits & P15UsageDecrypt)          ? TRUE : FALSE);
+    setAttributeBool(CKA_SIGN,
+			(bits & P15UsageSign)             ? TRUE : FALSE);
+    setAttributeBool(CKA_SIGN_RECOVER,
+			(bits & P15UsageSignRecover)      ? TRUE : FALSE);
+    setAttributeBool(CKA_WRAP,
+			(bits & P15UsageWrap)             ? TRUE : FALSE);
+    setAttributeBool(CKA_UNWRAP,
+			(bits & P15UsageUnwrap)           ? TRUE : FALSE);
+    setAttributeBool(CKA_VERIFY,
+			(bits & P15UsageVerify)           ? TRUE : FALSE);
+    setAttributeBool(CKA_VERIFY_RECOVER,
+			(bits & P15UsageVerifyRecover)    ? TRUE : FALSE);
+    setAttributeBool(CKA_DERIVE,
+			(bits & P15UsageDerive)           ? TRUE : FALSE);
+    /* no CKA value for P15UsageNonRepudiation */
+    if (bits & P15UsageNonRepudiation) {
+	/* set signing and sign recover. Non-repudiation keys are automatically
+         * signing keys */
+	setAttributeBool(CKA_SIGN, TRUE);
+	if (keyType == rsa) {
+	    setAttributeBool(CKA_SIGN_RECOVER, TRUE);
+	}
+    }
+
+    /* parse native (currently unused) */
+    /*native=true; */
+    if (commonKeyAttributes[0] == ASN1_BOOLEAN) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	/*if ((entrySize == 1) && (entry[0] == 0)) {
+	    native = false;
+	} */
+    }
+    /* parse access flags */
+    bits = BROKEN_FLAG;
+    if (commonKeyAttributes[0] == ASN1_BIT_STRING) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	bits = GetBits(entry,entrySize,4,1);
+    }
+    if (bits & BROKEN_FLAG) {
+	bits = defaultAccessBits();
+    }
+    setAttributeBool(CKA_SENSITIVE,  
+			(bits & P15AccessSensitive)       ? TRUE : FALSE);
+    setAttributeBool(CKA_EXTRACTABLE,
+			(bits & P15AccessExtractable)     ? TRUE : FALSE);
+    setAttributeBool(CKA_ALWAYS_SENSITIVE, 
+			(bits & P15AccessAlwaysSenstive)  ? TRUE : FALSE);
+    setAttributeBool(CKA_NEVER_EXTRACTABLE,
+			(bits & P15AccessNeverExtractable)? TRUE : FALSE);
+    setAttributeBool(CKA_LOCAL,      
+			(bits & P15AccessLocal)           ? TRUE : FALSE);
+
+    /* parse the key reference */
+    keyRef = PK15_INVALID_KEY_REF; /* invalid keyRef */
+    if (commonKeyAttributes[0] == ASN1_INTEGER) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize == 1) {
+	    keyRef = entry[0];
+	} else if ((entrySize == 2) && (entry[0] == 0)) {
+	    keyRef = entry[1];
+	}
+    }
+    setAttribute(CKA_START_DATE, &empty);
+    if (commonKeyAttributes[0] == ASN1_GENERALIZED_TIME) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	setAttribute(CKA_START_DATE,entry, entrySize);
+    }
+    setAttribute(CKA_END_DATE, &empty);
+    if (commonKeyAttributes[0] == ASN1_CHOICE_0) {
+	entry = dataStart(commonKeyAttributes, commonSize, &entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonKeyAttributes;
+	commonKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	setAttribute(CKA_END_DATE,entry, entrySize);
+    }
+    /* future common key attributes here */
+
+    /*
+     *  Parse Class variables
+     *
+     */
+    switch (p15Type) {
+    case PK15PuKey:
+	status = completePubKeyObject(current,currentSize);
+	break;
+    case PK15PvKey:
+	status = completePrivKeyObject(current,currentSize);
+	break;
+    default:
+	status=CKYLIBFAIL; /* shouldn't happen */
+	break;
+    }
+    return status;
+}
+
+CKYStatus PK15Object::completePrivKeyObject(const CKYByte *current,
+							CKYSize currentSize)
+{
+    const CKYByte *commonPrivKeyAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    unsigned int modulusSize;
+    unsigned int i;
+
+    CKYBuffer_InitEmpty(&empty);
+    if (current == NULL) { return CKYINVALIDARGS; }
+
+    /* optional subclass = CommonPrivateKeyAttributes */
+    if (current[0] == ASN1_CHOICE_0) {
+	/*
+         * PKCS15CommonPrivateKeyAttributes
+         *
+         * subjectName   SEQUENCE optional
+         * keyIdentifiers CHOICE 0  optional
+         */
+	/* unwrap */
+	commonPrivKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+	if (commonPrivKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	/* point current to the next section (type attributes)  */
+	tagSize = commonPrivKeyAttributes - current;
+	current += commonSize + tagSize;
+	currentSize -= (commonSize +tagSize);
+	if (currentSize < 0) { return CKYINVALIDDATA; }
+
+ 	/* subjectName */
+	if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPrivKeyAttributes, commonSize, 
+							&entrySize, false);
+	    if (entry == NULL) { return CKYINVALIDARGS; }
+	    tagSize = entry - commonPrivKeyAttributes;
+	    commonPrivKeyAttributes += entrySize + tagSize;
+	    commonSize -= (entrySize +tagSize);
+	    setAttribute(CKA_SUBJECT, entry, entrySize);
+	}
+
+	/* keyIdentfiers */
+	/* future CommonPrivateKeyAttributes here */
+    }
+
+    
+    /* Type attributes (either PKCS15RSAPrivateKeyAttributes or 
+     * PKCS15ECCPrivateKeyAttributes) -- Not Optional */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDDATA; }
+    /*
+     *    PKCS15RSAPrivateKeyAttributes
+     *        value PKCS15ObjectValue
+     *        modulusLength INTEGER
+     *        keyInfo SEQUENCE optional
+     *    PKCS15ECCPrivateKeyAttributes
+     *        value PKCS15ObjectValue
+     *        keyInfo SEQUENCE optional
+     */
+    /* unwrap */
+    commonPrivKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+    if (commonPrivKeyAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* value */
+     /* don't support direct private key objects */
+    if (commonPrivKeyAttributes[0] == ASN1_CHOICE_0) { return CKYUNSUPPORTED;  }
+    if (commonPrivKeyAttributes[0] != ASN1_SEQUENCE) { return CKYINVALIDDATA; }
+    commonPrivKeyAttributes = dataStart(commonPrivKeyAttributes, commonSize, &commonSize, false);
+    if (commonPrivKeyAttributes == NULL) { return CKYINVALIDARGS; }
+    entry = dataStart(commonPrivKeyAttributes, commonSize, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDARGS; }
+    tagSize = entry - commonPrivKeyAttributes;
+    commonPrivKeyAttributes += entrySize + tagSize;
+    commonSize -= (entrySize +tagSize);
+    /* if we have a path, the actual object is in another file,
+     * tell the caller to get it and come back here */
+    status = objectPath.setObjectPath(entry, entrySize);
+    if (status != CKYSUCCESS) { return status; }
+
+    /* parse modulus size */
+    if ((keyType == rsa) && commonPrivKeyAttributes[0] == ASN1_INTEGER) {
+	entry = dataStart(commonPrivKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonPrivKeyAttributes;
+	commonPrivKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 4) {
+	   return CKYINVALIDDATA;
+	}
+	for (modulusSize = 0, i=0; i < entrySize; i++) {
+	   modulusSize = (modulusSize << 8) + entry[i];
+	}
+	setKeySize(modulusSize);
+    }
+
+    if (keyType == rsa) {
+	state = PK15StateComplete;
+	return CKYSUCCESS; /* we're done with RSA */
+    }
+
+    /* parse keyinfo  at this point all we are after is the EC_PARAM*/
+    if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+	/* unwrap */
+	commonPrivKeyAttributes = dataStart(commonPrivKeyAttributes, 
+					commonSize, &commonSize, true);
+	if (commonPrivKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPrivKeyAttributes, commonSize, 
+							&entrySize, true);
+	    if (entry == NULL) { return CKYINVALIDDATA; }
+	    setAttribute(CKA_EC_PARAMS, entry, entrySize);
+	}
+    }
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+CKYStatus 
+PK15Object::completePubKeyObject(const CKYByte *current, CKYSize currentSize)
+{
+    const CKYByte *commonPubKeyAttributes;
+    CKYSize commonSize;
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+    CKYBuffer empty;
+    CKYStatus status;
+    unsigned int modulusSize;
+    unsigned int i;
+
+    CKYBuffer_InitEmpty(&empty);
+    if (current == NULL) { return CKYINVALIDDATA; }
+
+    /* optional subclass = CommonPublicKeyAttributes */
+    if (current[0] == ASN1_CHOICE_0) {
+	/*
+         * PKCS15CommonPublicKeyAttributes
+         *
+         * subjectName   SEQUENCE optional
+         * keyIdentifiers CHOICE 0  optional
+         */
+	/* unwrap */
+	commonPubKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+	if (commonPubKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	/* point current to the next section (type attributes)  */
+	tagSize = commonPubKeyAttributes - current;
+	current += commonSize + tagSize;
+	currentSize -= (commonSize +tagSize);
+	if (currentSize < 0) { return CKYINVALIDDATA; }
+
+ 	/* subjectName */
+	if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	    if (entry == NULL) { return CKYINVALIDARGS; }
+	    tagSize = entry - commonPubKeyAttributes;
+	    commonPubKeyAttributes += entrySize + tagSize;
+	    commonSize -= (entrySize +tagSize);
+	    setAttribute(CKA_SUBJECT, entry, entrySize);
+	}
+	/* future CommonPublicKeyAttributes here */
+    }
+
+    
+    /* Type attributes (either PKCS15RSAPublicKeyAttributes or 
+     * PKCS15ECCPublicKeyAttributes) -- Not Optional */
+    if (current[0] != ASN1_CHOICE_1) { return CKYINVALIDDATA; }
+    /*
+     *    PKCS15RSAPublicKeyAttributes
+     *        value PKCS15ObjectValue
+     *        modulusLength INTEGER
+     *        keyInfo SEQUENCE optional
+     *    PKCS15ECCPublicKeyAttributes
+     *        value PKCS15ObjectValue
+     *        keyInfo SEQUENCE optional
+     */
+    /* unwrap */
+    commonPubKeyAttributes = 
+			dataStart(current, currentSize, &commonSize, false);
+    if (commonPubKeyAttributes == NULL) { return CKYINVALIDDATA; }
+
+    /* value */
+    if (commonPubKeyAttributes[0] == ASN1_CHOICE_0) { 
+    	entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	status = completeRawPublicKey(entry, entrySize);
+	if (status != CKYSUCCESS) { return status; }
+    } else if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) { 
+	entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonPubKeyAttributes;
+	commonPubKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	/* if we have a path, the actual object is in another file,
+	 * tell the caller to get it and come back here */
+	status = objectPath.setObjectPath(entry, entrySize);
+	if (status != CKYSUCCESS) { return status; }
+	state = PK15StateNeedRawPublicKey;
+    }
+
+    /* parse modulus size */
+    if ((keyType == rsa) && commonPubKeyAttributes[0] == ASN1_INTEGER) {
+	entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, false);
+	if (entry == NULL) { return CKYINVALIDARGS; }
+	tagSize = entry - commonPubKeyAttributes;
+	commonPubKeyAttributes += entrySize + tagSize;
+	commonSize -= (entrySize +tagSize);
+	if (entrySize > 4) {
+	   return CKYINVALIDDATA;
+	}
+	for (modulusSize = 0, i=0; i < entrySize; i++) {
+	   modulusSize = (modulusSize << 8) + entry[i];
+	}
+	setKeySize(modulusSize);
+    }
+
+    if (keyType == rsa) {
+	return CKYSUCCESS; /* we're done with RSA */
+    }
+
+    /* parse keyinfo  at this point all we are after is the EC_PARAM*/
+    if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+	/* unwrap */
+	commonPubKeyAttributes = dataStart(commonPubKeyAttributes, 
+					commonSize, &commonSize, true);
+	if (commonPubKeyAttributes == NULL) { return CKYINVALIDDATA; }
+	if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) {
+	    entry = dataStart(commonPubKeyAttributes, commonSize, 
+							&entrySize, true);
+	    if (entry == NULL) { return CKYINVALIDDATA; } 
+	    setAttribute(CKA_EC_PARAMS, entry, entrySize);
+	}
+    }
+    return CKYSUCCESS;
+
+}
+
+CKYStatus 
+PK15Object::completeRawCertificate(const CKYByte *derCert, CKYSize derCertSize)
+{
+    SECStatus rv;
+    CCItem issuerItem, serialItem, derSerialItem, subjectItem,
+        validityItem, subjectKeyItem;
+    const char *certLabel;
+
+    setAttribute(CKA_VALUE, derCert, derCertSize);
+    rv = GetCertFieldItems(derCert, derCertSize, 
+        &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem,
+        &subjectKeyItem);
+    if (rv != SECSuccess) {
+	return CKYINVALIDDATA;
+    }
+    setAttribute(CKA_SERIAL_NUMBER, derSerialItem.data, derSerialItem.len);
+    setAttribute(CKA_SUBJECT, subjectItem.data, subjectItem.len);
+    setAttribute(CKA_ISSUER, issuerItem.data, issuerItem.len);
+    CKYBuffer_Replace(&pubKey, 0, subjectKeyItem.data, subjectKeyItem.len);
+    /* if we didn't get a label, set one based on the CN */
+    certLabel = getLabel();
+    if ((certLabel == NULL) || (*certLabel == 0)) {
+	CKYBuffer subject;
+	char *newLabel;
+	CKYBuffer_InitFromData(&subject, subjectItem.data, subjectItem.len);
+	newLabel = GetUserName(&subject);
+	if (newLabel) {
+	    setAttribute(CKA_LABEL, (CKYByte *)newLabel, 
+					(CKYSize) strlen(newLabel)-1);
+	    delete [] newLabel;
+	}
+	CKYBuffer_FreeData(&subject);
+    }
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
+CKYStatus 
+PK15Object::completeRawPublicKey(const CKYByte *current, CKYSize size)
+{
+    const CKYByte *entry;
+    CKYSize entrySize;
+    CKYSize tagSize;
+
+    if ((current == NULL) || (current[0] != ASN1_SEQUENCE)) {
+	return CKYINVALIDDATA;
+    }
+    /* unwrap*/
+    current = dataStart(current, size, &size, false);
+    if (current == NULL) { return CKYINVALIDDATA; }
+
+    /* modulus */
+    if (current[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    tagSize = entry - current;
+    current += entrySize + tagSize;
+    size -= (entrySize +tagSize);
+    if (size < 0) { return CKYINVALIDDATA; }
+    if ((entry[0] == 0) && (entrySize > 1)) {
+	entry++; entrySize--;
+    }
+    setAttribute(CKA_MODULUS, entry, entrySize);
+
+    /* exponent */
+    if (current[0] != ASN1_INTEGER) { return CKYINVALIDDATA; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) { return CKYINVALIDDATA; }
+    tagSize = entry - current;
+    current += entrySize + tagSize;
+    size -= (entrySize +tagSize);
+    if (size < 0) { return CKYINVALIDDATA; }
+    if ((entry[0] == 0) && (entrySize > 1)) {
+	entry++; entrySize--;
+    }
+    setAttribute(CKA_PUBLIC_EXPONENT, entry, entrySize);
+    state = PK15StateComplete;
+    return CKYSUCCESS;
+}
+
 DEREncodedSignature::DEREncodedSignature(const CKYBuffer *derSig)
 {
 
@@ -1483,9 +2585,9 @@ int DEREncodedSignature::getRawSignature
 
     CKYBuffer_Zero(rawSig);
 
-    unsigned int seq_length = 0;
-    unsigned int expected_sig_len = ( (keySize + 7) / 8 ) * 2 ;
-    unsigned int expected_piece_size = expected_sig_len / 2 ;
+    CKYSize seq_length = 0;
+    CKYSize expected_sig_len = ( (keySize + 7) / 8 ) * 2 ;
+    CKYSize expected_piece_size = expected_sig_len / 2 ;
 
     /* unwrap the sequence */
     buf = dataStart(CKYBuffer_Data(&derEncodedSignature), CKYBuffer_Size(&derEncodedSignature),&seq_length, false);
@@ -1494,7 +2596,7 @@ int DEREncodedSignature::getRawSignature
 
     // unwrap first multi byte integer
    
-    unsigned int int_length = 0;
+    CKYSize int_length = 0;
     const CKYByte *int1Buf = NULL;
     const CKYByte *int2Buf = NULL;
 
@@ -1525,7 +2627,7 @@ int DEREncodedSignature::getRawSignature
 
     // unwrap second multi byte integer
 
-    unsigned int second_int_length = 0;
+    CKYSize second_int_length = 0;
 
     int2Buf = dataStart(buf, seq_length, &second_int_length, false);
 
@@ -1552,3 +2654,91 @@ int DEREncodedSignature::getRawSignature
 
     return CKYSUCCESS;
 }
+
+DEREncodedTokenInfo::DEREncodedTokenInfo(CKYBuffer *derTokenInfo)
+{
+    const CKYByte *current = CKYBuffer_Data(derTokenInfo);
+    const CKYByte *entry;
+    CKYSize size = CKYBuffer_Size(derTokenInfo);
+    CKYSize entrySize;
+    CKYSize tagSize;
+    /* set token name, etc */
+
+    version = -1;
+    CKYBuffer_InitEmpty(&serialNumber);
+    manufacturer = NULL;
+    tokenName = NULL;
+
+    if (current[0] != ASN1_SEQUENCE) {
+	return; /* just use the defaults */
+    }
+    /* unwrap */
+    current = dataStart(current, size, &size, false);
+    if (current == NULL) return;
+
+    /* parse the version */
+    if (current[0] != ASN1_INTEGER) { return; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) return;
+    tagSize = entry - current;
+    current += tagSize + entrySize;
+    size -= tagSize + entrySize;
+    if (entrySize < 1) {
+	version = *entry;
+    }
+    if (size < 0) return;
+
+    /* get the serial number */
+    if (current[0] != ASN1_OCTET_STRING) { return ; }
+    entry = dataStart(current, size, &entrySize, false);
+    if (entry == NULL) return;
+    tagSize = entry - current;
+    current += tagSize + entrySize;
+    size -= tagSize + entrySize;
+    CKYBuffer_Replace(&serialNumber, 0, entry, entrySize);
+    /* should we fake the cuid here? */
+
+    /* get the optional manufacture ID */
+    if (current[0] == ASN1_UTF8_STRING) {
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) return;
+	tagSize = entry - current;
+	current += tagSize + entrySize;
+	size -= tagSize + entrySize;
+	manufacturer = (char *)malloc(entrySize+1);
+	if (manufacturer) {
+	    memcpy(manufacturer, entry, entrySize);
+	    manufacturer[entrySize] = 0;
+	}
+    }
+
+    /* get the optional token name */
+    /* most choices are constructed, 
+     * but this one isn't explicity add the flag */
+    if ((current[0]|ASN1_CONSTRUCTED) == ASN1_CHOICE_0) {
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) return;
+	tagSize = entry - current;
+	current += tagSize + entrySize;
+	size -= tagSize + entrySize;
+	tokenName = (char *)malloc(entrySize+1);
+	if (tokenName) {
+	    memcpy(tokenName, entry, entrySize);
+	    tokenName[entrySize] = 0;
+	}
+    }
+
+    /* parsing flags */
+    if (current[0] == ASN1_BIT_STRING) {
+    /* recordinfo parsing would go here */
+	unsigned long bits;
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) return;
+	tagSize = entry - current;
+	current += tagSize + entrySize;
+	size -= tagSize + entrySize;
+	bits = GetBits(entry, entrySize,8,2);
+    }
+    return;
+}
+
diff -up ./src/coolkey/object.h.p15 ./src/coolkey/object.h
--- ./src/coolkey/object.h.p15	2015-07-06 10:27:55.770827267 -0700
+++ ./src/coolkey/object.h	2015-07-06 10:27:55.785826984 -0700
@@ -27,6 +27,33 @@
 
 using std::list;
 
+/*
+ * Sigh PKCS 15 is heavily ASN.1...
+ */
+const CKYByte  ASN1_BOOLEAN           = 0x01;
+const CKYByte  ASN1_INTEGER           = 0x02;
+const CKYByte  ASN1_BIT_STRING        = 0x03;
+const CKYByte  ASN1_OCTET_STRING      = 0x04;
+const CKYByte  ASN1_ENUMERATED        = 0x0a;
+const CKYByte  ASN1_UTF8_STRING       = 0x0c;
+const CKYByte  ASN1_GENERALIZED_TIME  = 0x18;
+const CKYByte  ASN1_CONSTRUCTED       = 0x20;
+const CKYByte  ASN1_SEQUENCE          = 0x30;
+const CKYByte  ASN1_CHOICE_0          = 0xa0;
+const CKYByte  ASN1_CHOICE_1          = 0xa1;
+const CKYByte  ASN1_CHOICE_2          = 0xa2;
+const CKYByte  ASN1_CHOICE_3          = 0xa3;
+
+const CKYBitFlags BROKEN_FLAG = 0x80000000;
+const unsigned int PK11_INVALID_KEY_REF = -1;
+
+const CKYByte PK15X509CertType = ASN1_SEQUENCE;
+const CKYByte PK15RSAKeyType   = ASN1_SEQUENCE;
+const CKYByte PK15ECCKeyType   = ASN1_CHOICE_0;
+const CKYByte PK15DHKeyType    = ASN1_CHOICE_1;
+const CKYByte PK15DSAKeyType   = ASN1_CHOICE_2;
+const CKYByte PK15KEAKeyType   = ASN1_CHOICE_3;
+
 class PKCS11Attribute {
   private:
     CK_ATTRIBUTE_TYPE type;
@@ -52,9 +79,30 @@ class PKCS11Attribute {
     PKCS11Attribute() : type(0){ CKYBuffer_InitEmpty(&value); }
     PKCS11Attribute(CK_ATTRIBUTE_TYPE type_, const CKYBuffer *value_)
         : type(type_) { CKYBuffer_InitFromCopy(&value, value_); }
+    PKCS11Attribute(CK_ATTRIBUTE_TYPE type_, const CKYByte *data_,
+	 CKYSize size_) : type(type_) 
+		{ CKYBuffer_InitFromData(&value, data_, size_); }
     ~PKCS11Attribute() { CKYBuffer_FreeData(&value); }
 };
 
+class PK15ObjectPath {
+  private:
+    CKYBuffer path;
+    CKYOffset index;
+    CKYSize   length;
+   public:
+    PK15ObjectPath() : index(0), length(0) { CKYBuffer_InitEmpty(&path); }
+    PK15ObjectPath(const PK15ObjectPath &cpy) : 
+		index(cpy.index), length(cpy.length) 
+		{ CKYBuffer_InitFromCopy(&path, &cpy.path); }
+    ~PK15ObjectPath() { CKYBuffer_FreeData(&path); }
+    const CKYBuffer *getPath() const { return &path; }
+    CKYOffset getIndex() const { return index; }
+    CKYSize getLength() const { return length; }
+    CKYStatus setObjectPath(const CKYByte *entry, CKYSize size);
+};
+
+
 class PKCS11Object {
   public:
     enum KeyType {
@@ -72,6 +120,8 @@ class PKCS11Object {
     unsigned long muscleObjID;
     CK_OBJECT_HANDLE handle;
     char *label;
+    unsigned int keySize;
+    CK_USER_TYPE user;
 
     void parseOldObject(const CKYBuffer *data);
     void parseNewObject(const CKYBuffer *data);
@@ -82,19 +132,37 @@ class PKCS11Object {
   protected :
     char *name;
     KeyType keyType;
+    unsigned int keyRef;
     CKYBuffer pubKey;
+    CKYBuffer authId;
+    CKYBuffer pinAuthId;
+    PK15ObjectPath objectPath;
 
   public:
     PKCS11Object(unsigned long muscleObjID, CK_OBJECT_HANDLE handle);
     PKCS11Object(unsigned long muscleObjID, const CKYBuffer *data,
         CK_OBJECT_HANDLE handle);
-    ~PKCS11Object() { delete label; delete name; CKYBuffer_FreeData(&pubKey);
-			attributes.clear(); }
+    virtual ~PKCS11Object() { delete [] label; delete [] name; 
+		CKYBuffer_FreeData(&pubKey); CKYBuffer_FreeData(&authId);
+		CKYBuffer_FreeData(&pinAuthId); attributes.clear(); }
 
     PKCS11Object(const PKCS11Object& cpy) :
         attributes(cpy.attributes), muscleObjID(cpy.muscleObjID),
-        handle(cpy.handle), label(NULL),  name(NULL), keyType(cpy.keyType) { 
-			CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey); }
+        handle(cpy.handle), label(NULL),  keySize(cpy.keySize), 
+	user(cpy.user), name(NULL), keyType(cpy.keyType), keyRef(cpy.keyRef),
+	objectPath(cpy.objectPath) { 
+			/* label is just a cached value, don't need 
+  			 * to copy it. */
+			if (cpy.name != NULL) {
+			    int len = strlen(cpy.name);
+			    name= new char [len+1];
+			    if (name) {
+				memcpy(name,cpy.name,len+1);
+			    }
+			}
+			CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey);
+			CKYBuffer_InitFromCopy(&authId,&cpy.authId);
+			CKYBuffer_InitFromCopy(&pinAuthId,&cpy.pinAuthId); }
 
 
     unsigned long getMuscleObjID() const { return muscleObjID; }
@@ -107,6 +175,8 @@ class PKCS11Object {
 
     void setAttribute(CK_ATTRIBUTE_TYPE type, const CKYBuffer *value);
     void setAttribute(CK_ATTRIBUTE_TYPE type, const char *);
+    void setAttribute(CK_ATTRIBUTE_TYPE type, const CKYByte *data, 
+							CKYSize size);
     /* bools and ulongs are too close, don't abuse function overloading
      * for these cases */
     void setAttributeBool(CK_ATTRIBUTE_TYPE type, CK_BBOOL);
@@ -124,14 +194,21 @@ class PKCS11Object {
 	return &pubKey;
     }
 
-    KeyType getKeyType() const { return keyType;}
+    KeyType getKeyType(void) const { return keyType;}
+    unsigned int getKeySize(void) const { return keySize; }
+    unsigned int getKeyRef(void) const { return keyRef; }
+    CK_USER_TYPE getUser(void) const { return user; }
     void setKeyType(KeyType theType) { keyType = theType; }
+    void setKeySize(unsigned int keySize_) { keySize = keySize_; }
+    const CKYBuffer *getAuthId(void) const { return &authId; }
+    const CKYBuffer *getPinAuthId(void) const { return &pinAuthId; }
+    const PK15ObjectPath &getObjectPath() const { return objectPath; }
+    void completeKey(const PKCS11Object &cert);
 };
 
 class Key : public PKCS11Object {
   public:
     Key(unsigned long muscleObjID, const CKYBuffer *data, CK_OBJECT_HANDLE handle);
-    void completeKey(const PKCS11Object &cert);
 };
 
 class Cert : public PKCS11Object {
@@ -155,6 +232,84 @@ class CACCert : public PKCS11Object {
     CACCert(CKYByte instance, const CKYBuffer *derCert);
 };
 
+typedef enum { PK15StateInit, PK15StateNeedObject, 
+	PK15StateNeedRawPublicKey,PK15StateNeedRawCertificate, 
+	PK15StateComplete } PK15State;
+
+typedef enum {PK15PvKey, PK15PuKey, PK15Cert, PK15AuthObj} PK15ObjectType;
+const unsigned int PK15_INVALID_KEY_REF = -1;
+
+class PK15Object : public PKCS11Object {
+  private:
+    CKYByte	instance;
+    PK15ObjectType p15Type;
+    PK15State state;
+    P15PinInfo pinInfo;
+
+    CKYStatus completeCertObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completeAuthObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completeKeyObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completePrivKeyObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completePubKeyObject(const CKYByte *buf, CKYSize size);
+    CKYStatus completeRawPublicKey(const CKYByte *buf, CKYSize size);
+    CKYStatus completeRawCertificate(const CKYByte *buf, CKYSize size);
+   
+    CKYBitFlags defaultCommonBits() {
+	return ((p15Type == PK15PvKey) && (CKYBuffer_Size(&authId) != 0)) ?
+		P15FlagsPrivate : 0;
+    }
+    CKYBitFlags defaultUsageBits() {
+	CKYBitFlags sign, recover, encrypt;
+	switch (p15Type) {
+	case PK15PuKey:
+	    sign = P15UsageVerify; recover = P15UsageVerifyRecover;
+	    encrypt = P15UsageEncrypt;
+	    break;
+	case PK15PvKey:
+	    sign = P15UsageSign; recover = P15UsageSignRecover;
+	    encrypt = P15UsageDecrypt;
+	    break;
+	default:
+	    sign = 0; recover = 0; encrypt = 0;
+	    break;
+	}
+	switch(keyType) {
+	case rsa:
+	    return sign | recover | encrypt;
+	case ecc:
+	    return sign | P15UsageDerive;
+	default:
+	    break;
+	}
+	return 0;
+    }
+    CKYBitFlags defaultAccessBits() {
+	switch (p15Type) {
+	case PK15PuKey:
+		return P15AccessExtractable | P15AccessLocal;
+	case PK15PvKey:
+		return P15AccessSensitive | P15AccessLocal;
+	default:
+		break;
+	}
+	return 0;
+    }
+    CKYBitFlags defaultPinBits() {
+	return ((p15Type == PK15AuthObj) ?  P15PinInitialized : 0);
+    }
+		
+  public:
+    PK15Object(CKYByte inst, PK15ObjectType type, 
+					const CKYByte *derObject, CKYSize size);
+    CKYStatus completeObject(const CKYByte *data, CKYSize size);
+    PK15State getState(void) const { return state; }
+    bool isSO(void) const { return 
+		(pinInfo.pinFlags & P15PinSOPin) ? true : false; }
+    bool isLocal(void) const { return 
+			(pinInfo.pinFlags & P15PinLocal) ? true : false; }
+    const P15PinInfo *getPinInfo(void) const { return &pinInfo; }
+};
+
 class Reader : public PKCS11Object {
   public:
     Reader(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, 
@@ -180,6 +335,21 @@ class DEREncodedSignature  {
 
 };
 
+class DEREncodedTokenInfo {
+public:
+   int   version;
+   CKYBuffer serialNumber;
+   char *manufacturer;
+   char *tokenName;
+   public :
+   DEREncodedTokenInfo(CKYBuffer *derTokenInfo);
+   ~DEREncodedTokenInfo() { 
+	CKYBuffer_FreeData(&serialNumber);
+	free(manufacturer);
+	free(tokenName);
+    }
+};
+
 class AttributeMatch {
 
   private:
@@ -202,6 +372,9 @@ makeLEUInt(const CKYBuffer *buf, unsigne
             (b[offset+0] <<  0) ;
 }
 
+const CKYByte* dataStart(const CKYByte *buf, CKYSize length,
+                        CKYSize *data_length, bool includeTag);
+
 // fixed object ID constants 
 #define READER_ID 0x72300000 /* 'r0\0\0' */
 #define COMBINED_ID 0x7a300000 /* 'z0\0\0' */
diff -up ./src/coolkey/pkcs11t.h.p15 ./src/coolkey/pkcs11t.h
--- ./src/coolkey/pkcs11t.h.p15	2015-07-06 10:27:55.770827267 -0700
+++ ./src/coolkey/pkcs11t.h	2015-07-06 10:29:32.293005407 -0700
@@ -274,6 +274,7 @@ typedef CK_ULONG          CK_USER_TYPE;
 #define CKU_SO    0
 /* Normal user */
 #define CKU_USER  1
+#define CKU_CONTEXT_SPECIFIC 2
 
 
 /* CK_STATE enumerates the session states */
@@ -492,6 +493,9 @@ typedef CK_ULONG          CK_ATTRIBUTE_T
 #define CKA_RESET_ON_INIT      0x00000301
 #define CKA_HAS_RESET          0x00000302
 
+/* new for v2.20 */
+#define CKA_ALWAYS_AUTHENTICATE  0x00000202
+
 #define CKA_VENDOR_DEFINED     0x80000000
 
 
diff -up ./src/coolkey/slot.cpp.p15 ./src/coolkey/slot.cpp
--- ./src/coolkey/slot.cpp.p15	2015-07-06 10:27:55.782827040 -0700
+++ ./src/coolkey/slot.cpp	2015-07-06 10:27:55.786826965 -0700
@@ -54,6 +54,11 @@ const CKYByte ATR2[] =
 {  0x3B, 0x6F, 0x00, 0xFF, 0x52, 0x53, 0x41, 0x53, 0x65, 0x63, 0x75, 0x72,
    0x49, 0x44, 0x28, 0x52, 0x29, 0x31, 0x30 };
 
+/* PKCS #15 AID */
+const CKYByte P15AID[] =
+{ 0xa0, 0, 0,  0, 0x63, 'P', 'K', 'C', 'S', '-', '1', '5'};
+
+
 
 /* ECC curve information
  *    Provide information for the limited set of curves supported by our smart card(s).
@@ -405,19 +410,29 @@ SlotList::updateReaderList()
 
 Slot::Slot(const char *readerName_, Log *log_, CKYCardContext* context_)
     : log(log_), readerName(NULL), personName(NULL), manufacturer(NULL),
+	tokenManufacturer(NULL),
 	slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), 
 	isVersion1Key(false), needLogin(false), fullTokenName(false), 
-	mCoolkey(false), mOldCAC(false),mCACLocalLogin(false),
-	pivContainer(-1), pivKey(-1), mECC(false), 
+	mCoolkey(false), mOldCAC(false), mCACLocalLogin(false),
+	pivContainer(-1), pivKey(-1), mECC(false), p15aid(0), p15odfAddr(0),
+	p15tokenInfoAddr(0), p15Instance(0),
 #ifdef USE_SHMEM
 	shmem(readerName_),
 #endif
 	sessionHandleCounter(1), objectHandleCounter(1)
 {
+  int i; 
+
+  for (i=0; i < MAX_AUTH_USERS; i++) {
+    auth[i]=NULL;
+  }
 
   tokenFWVersion.major = 0;
   tokenFWVersion.minor = 0;
-
+  CKYBuffer_InitFromData(&p15AID, P15AID, sizeof(P15AID));
+  CKYBuffer_InitEmpty(&p15tokenInfo);
+  CKYBuffer_InitEmpty(&p15odf);
+  CKYBuffer_InitEmpty(&p15serialNumber);
 
   try {
     conn = CKYCardConnection_Create(context);
@@ -433,6 +448,8 @@ Slot::Slot(const char *readerName_, Log
     loggedIn = false;
     pinCache.invalidate();
     pinCache.clearPin();
+    contextPinCache.invalidate();
+    contextPinCache.clearPin();
     //readSlotInfo();
     manufacturer = strdup("Unknown");
     if (!manufacturer) {
@@ -515,12 +532,23 @@ Slot::~Slot()
     if (manufacturer) {
 	free(manufacturer);
     }
+    if (tokenManufacturer) {
+	free(tokenManufacturer);
+    }
     CKYBuffer_FreeData(&nonce);
     CKYBuffer_FreeData(&cardATR);
     CKYBuffer_FreeData(&mCUID);
+    CKYBuffer_FreeData(&p15AID);
+    CKYBuffer_FreeData(&p15odf);
+    CKYBuffer_FreeData(&p15tokenInfo);
+    CKYBuffer_FreeData(&p15serialNumber);
     for (int i=0; i < MAX_CERT_SLOTS; i++) {
 	CKYBuffer_FreeData(&cardAID[i]);
     }
+    for (int i=0; i < MAX_AUTH_USERS; i++) {
+	if (auth[i]) delete auth[i];
+	auth[i]=NULL;
+    }
 }
 
 template <class C>
@@ -637,9 +665,10 @@ Slot::getPIVLoginType(void)
     }
 done:
     CKYBuffer_FreeData(&buffer);
-    return true;
+    return local;
 }
 
+
 void
 Slot::connectToToken()
 {
@@ -745,7 +774,7 @@ Slot::connectToToken()
 	 /* CARD is a PIV card */
 	 state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
 	 isVersion1Key = 0;
-	 needLogin = 1;
+	 needLogin = true;
          mCoolkey = 0;
 	 mOldCAC = 0;
 	 mCACLocalLogin = getPIVLoginType();
@@ -757,12 +786,22 @@ Slot::connectToToken()
 	status = getCACAid();
 	if (status != CKYSUCCESS) {
 	    log->log("CAC Select failed 0x%x\n", status);
-	    if (status == CKYSCARDERR) {
+	    status = getP15Params();
+	    if (status != CKYSUCCESS) {
+	    	if (status == CKYSCARDERR) {
 		    log->log("Card Failure 0x%x\n",
 				CKYCardConnection_GetLastError(conn));
 		    disconnect();
-	    }
-	    /* CARD is unknown */
+	    	}
+	        /* CARD is unknown */
+	        return;
+	    } 
+	    /* enable PCKS 15 */
+	    state |= P15_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
+	    isVersion1Key = 0;
+	    needLogin = false; /* get it from token info */
+            mCoolkey = 0;
+	    mCACLocalLogin = false;
 	    return;
 	}
 	state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED;
@@ -771,7 +810,7 @@ Slot::connectToToken()
          * other apps may be running now, so resetting the cac is a bit
          * unfriendly */
 	isVersion1Key = 0;
-	needLogin = 1;
+	needLogin = true;
         mCoolkey = 0;
 	mCACLocalLogin = false;
 	return;
@@ -841,6 +880,8 @@ Slot::invalidateLogin(bool hard)
     } else {
 	loggedIn = false;
 	pinCache.invalidate();
+	contextPinCache.invalidate();
+	contextPinCache.clearPin();
 	if (hard) {
 	    pinCache.clearPin();
 	}
@@ -951,6 +992,414 @@ done:
     return status;
 }
 
+CKYStatus Slot::getP15Params()
+{
+    CKYStatus status = CKYSCARDERR;
+    int i;
+    CKYISOStatus apduRC;
+
+    /* read the EF(DIR) */  
+    status = CACApplet_SelectFile(conn, 0x2f00, &apduRC);
+    if (status == CKYSUCCESS) {
+	CKYBuffer record;
+
+	CKYBuffer_InitEmpty(&record);
+	/* dump it out */
+        for (i=1; i < 255; i++) {
+	    status = P15Applet_ReadRecord(conn, i, 0, P15_READ_P1, 255,
+			&record, &apduRC);
+	    if (status != CKYSUCCESS) {
+		log->log("EF(DIR) Read Record %d failed 0x%x apduRC=0x%x\n",
+							 i, status, apduRC);
+		break;
+	    }
+	}
+	CKYBuffer_FreeData(&record);
+	return CKYSCARDERR; /* don't yet support EF(DIR)*/
+
+    } else {
+	log->log("EF(DIR) Select failed 0x%x apduRC=0x%0x\n", status, apduRC);
+	p15aid = 0; /* use the default */
+	p15odfAddr=0x5031;
+	p15tokenInfoAddr=0x5032;
+    }
+   
+    status = CKYApplet_SelectFile(conn, &p15AID, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("DF(PKCS-15) select failed 0x%x apduRC=0x%0x\n", status,
+				apduRC);
+	return status;
+    }
+    status = P15Applet_SelectFile(conn, p15tokenInfoAddr, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("EF(TokenInfo) select failed 0x%x apduRC=0x%0x\n", status, 
+				apduRC);
+	return status;
+    }
+    /* dump it out */
+    CKYBuffer_Resize(&p15tokenInfo, 0);
+    status = P15Applet_ReadBinary(conn, 0, 0, 0, 0, &p15tokenInfo, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("EF(TokenInfo) Read binary failed 0x%x apduRC=0x%x\n", status,
+				 apduRC);
+	    return status;
+    }
+    status = P15Applet_SelectFile(conn, p15odfAddr, &apduRC);
+    if (status != CKYSUCCESS) {
+	log->log("EF(ODF) select failed 0x%x apduRC=0x%0x\n", status,
+				apduRC);
+	return status;
+    }
+
+    CKYBuffer_Resize(&p15odf, 0);
+    status = P15Applet_ReadBinary(conn, 0, 0, 0, 0, &p15odf, &apduRC);
+    if (status != CKYSUCCESS) {
+	    log->log("EF(ODF) Read binary failed 0x%x apduRC=0x%x\n", status,
+				 apduRC);
+	    return status; 
+    }
+
+    return CKYSUCCESS;
+}
+
+CKYStatus
+Slot::readFromPath(const PK15ObjectPath &obj, CKYBuffer *file)
+{
+    CKYStatus status;
+    CKYISOStatus apduRC;
+    CKYSize bufSize;
+    CKYOffset index = obj.getIndex();
+    CKYSize length = obj.getLength();
+
+    CKYBuffer_Resize(file, 0);
+    status = selectPath(obj.getPath(), &apduRC);
+    if (status != CKYSUCCESS) {
+	return status;
+    }
+    status = P15Applet_ReadBinary(conn, index, 0, 0, 
+			(length >= 256)?0:length, file, &apduRC);
+    if (status != CKYSUCCESS) {
+	return status;
+    }
+
+    /* if we asked for a specific length and got it, or we asked for 
+     * an indeterminate length and got less than 256 bytes, then we 
+     * got everything. */
+    bufSize = CKYBuffer_Size(file);
+    if ((length && (bufSize >= length)) || ((length == 0) && (bufSize < 256))) {
+	/* we've already got it all */
+	return status;
+    }
+    if (bufSize < 0x82) {
+	/* make sure we have enough bytes to handle the worst case ANS.1
+         * mistake */
+	return CKYINVALIDDATA;
+    }
+    
+    if (length == 0) {
+	/* we don't yet know how long the length is, use the ASN.1 parser to
+         * find out. We lie to dataStart about actual size so that it won't
+         * fail since we know we don't have the whole buffer yet.*/
+	(void) dataStart(CKYBuffer_Data(file), 65535, &length, true);
+    }
+    if (length > 65535) {
+	return CKYINVALIDDATA;
+    }
+    while ((bufSize =CKYBuffer_Size(file)) < length) {
+	CKYSize tmpLength = length - bufSize;
+
+	if (tmpLength >= 256) tmpLength = 0;
+
+	status = P15Applet_ReadBinary(conn, (unsigned short)(index+bufSize),
+			 0, 0, tmpLength, file, &apduRC);
+	if (status != CKYSUCCESS) {
+	    return status;
+	}
+    }
+	
+    return CKYSUCCESS;
+}
+
+void
+Slot::parseEF_TokenInfo(void)
+{
+    DEREncodedTokenInfo derTokenInfo(&p15tokenInfo);
+    const CKYBuffer *serial=&derTokenInfo.serialNumber;
+
+    if (derTokenInfo.version >= 0) {
+	tokenFWVersion.major = derTokenInfo.version;
+	tokenFWVersion.minor = 0;
+    }
+
+    if (CKYSize(serial) != 0) {
+	CKYBuffer_Replace(&p15serialNumber, 0, CKYBuffer_Data(serial),
+						CKYBuffer_Size(serial));
+    }
+
+    if (derTokenInfo.manufacturer) {
+	if (tokenManufacturer) {
+	    free(tokenManufacturer);
+	    tokenManufacturer = NULL;
+	}
+	tokenManufacturer = derTokenInfo.manufacturer;
+	derTokenInfo.manufacturer = NULL; /* adopted */
+    }
+
+    if (derTokenInfo.tokenName) {
+	if (personName) {
+	    free(personName);
+	    personName = NULL;
+	}
+	personName = derTokenInfo.tokenName;
+	derTokenInfo.tokenName = NULL; /* adopted */
+	fullTokenName = true;
+    }
+    return;
+}
+
+void
+Slot::parseEF_ODF(void)
+{
+    const CKYByte *current = CKYBuffer_Data(&p15odf);
+    CKYSize size = CKYBuffer_Size(&p15odf);
+    CKYBuffer files;
+
+    CKYBuffer_InitEmpty(&files);
+
+    while (size > 0) {
+	const CKYByte *entry;
+	CKYSize entrySize;
+	CKYSize tagSize;
+	CKYByte type, type1;
+	PK15ObjectPath objPath;
+	bool skip;
+
+	type = current[0];
+	entry = dataStart(current, size, &entrySize, false);
+	if (entry == NULL) { break; }
+	tagSize = entry-current;
+	current += entrySize + tagSize;
+	size -= (entrySize + tagSize);
+
+	/* skip those entries we aren't going to parse */
+	skip = false;
+	switch (type) {
+	case 0xa2: skip=true; break; /* skip EF(PuKDF-trusted) */
+	case 0xa3: skip=true; break; /* skip EF(SKDF) */
+	case 0xa7: skip=true; break; /* skip EF(DODF) */
+	default: skip=true; break;
+	case 0xa0:        /* EF(PvKDF) */
+	case 0xa1:        /* EF(PuKDF) */
+	case 0xa4:        /* EF(CDF) */
+	case 0xa5:        /* EF(CDF-trusted) */
+	case 0xa6:        /* EF(CDF-useful) */
+	case 0xa8: break; /* EF(AODF) */
+ 	}
+	if (skip) continue;
+
+	type1 = entry[0];
+	/* unwrap */
+	entry = dataStart(entry, entrySize, &entrySize, false);
+	if (entry == NULL) continue;
+	if (type1 == ASN1_SEQUENCE) {
+	    objPath.setObjectPath(entry, entrySize);
+	    CKYBuffer_Resize(&files, 0);
+	    readFromPath(objPath, &files);
+	    entry = CKYBuffer_Data(&files);
+	    entrySize = CKYBuffer_Size(&files);
+	} else if (type1 != ASN1_CHOICE_0) {
+	    continue;
+	}
+	
+	switch (type) {
+	case 0xa0: parseEF_Directory(entry, entrySize, PK15PvKey); break;
+	case 0xa1: parseEF_Directory(entry, entrySize, PK15PuKey); break;
+	case 0xa4: parseEF_Directory(entry, entrySize, PK15Cert); break;
+	case 0xa5: parseEF_Directory(entry, entrySize, PK15Cert); break;
+	case 0xa6: parseEF_Directory(entry, entrySize, PK15Cert); break;
+	case 0xa8: parseEF_Directory(entry, entrySize, PK15AuthObj); break;
+	default: break;
+	}
+    }
+    CKYBuffer_FreeData(&files);
+    return;
+}
+
+
+class ObjectCertCKAIDMatch {
+  private:
+    const CKYBuffer *cka_id;
+  public:
+    ObjectCertCKAIDMatch(const CKYBuffer *cka_id_) : cka_id(cka_id_) {}
+    bool operator()(const PKCS11Object& obj) {
+	const CKYBuffer *id;
+        const CKYBuffer *objClass;
+	CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+	objClass = obj.getAttribute(CKA_CLASS);
+        if (objClass == NULL || !CKYBuffer_DataIsEqual(objClass, 
+				(CKYByte *)&certClass, sizeof(certClass))) {
+	    return false;
+        }
+ 	id = obj.getAttribute(CKA_ID);
+        return (id != NULL && CKYBuffer_IsEqual(id,cka_id)) ? true : false;
+    }
+};
+
+class ObjectKeyCKAIDMatch {
+  private:
+    const CKYBuffer *cka_id;
+  public:
+    ObjectKeyCKAIDMatch(const CKYBuffer *cka_id_) : cka_id(cka_id_) {}
+    bool operator()(const PKCS11Object& obj) {
+	const CKYBuffer *id;
+        const CKYBuffer *objClass;
+	CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+	objClass = obj.getAttribute(CKA_CLASS);
+        if (objClass == NULL || !CKYBuffer_DataIsEqual(objClass, 
+				(CKYByte *)&keyClass, sizeof(keyClass))) {
+	    return false;
+        }
+ 	id = obj.getAttribute(CKA_ID);
+        return (id != NULL && CKYBuffer_IsEqual(id,cka_id)) ? true : false;
+    }
+};
+
+CKYStatus
+Slot::parseEF_Directory(const CKYByte *current, 
+					CKYSize size, PK15ObjectType type)
+{
+    CKYBuffer file;
+    CKYBuffer_InitEmpty(&file);
+    CKYStatus status;
+
+
+    while (size > 0) {
+	const CKYByte *entry;
+	CKYSize entrySize;
+
+	if (current[0] != ASN1_SEQUENCE) {
+	    /* no more */
+	    break;
+	}
+
+	entry = dataStart(current, size, &entrySize, true);
+	if (entry == NULL) { break; }
+	current += entrySize;
+	size -= entrySize;
+
+	do {
+	    PK15Object obj(PK15Instance(), type, entry, entrySize);
+
+	    /* if state failed, then there is something wrong with this
+	     * der, skip this object */
+	    if (obj.getState() == PK15StateInit) {
+		break;
+	    }
+	    status = CKYSUCCESS;
+	    while (obj.getState() != PK15StateComplete) {
+	        CKYBuffer_Resize(&file, 0);
+	        readFromPath(obj.getObjectPath(), &file);
+		status = obj.completeObject(CKYBuffer_Data(&file), 
+						CKYBuffer_Size(&file));
+		if (status != CKYSUCCESS) {
+		    break;
+		}
+	    }
+	    if (status != CKYSUCCESS) {
+		break;
+	    }
+	    assert(obj.getState() == PK15StateComplete);
+	    /* handle type specific per object fixups */
+	    switch (type) {
+	    case PK15AuthObj:
+	    /* if we're an auth object, squirrel us away for future use */
+		if (obj.isSO()) {
+		    if (auth[CKU_SO] != 0) {
+			auth[CKU_SO] = new PK15Object(obj);
+		    }
+		} else if (auth[CKU_USER] == NULL) {
+		    auth[CKU_USER] = new PK15Object(obj);
+		} else if (auth[CKU_CONTEXT_SPECIFIC] == NULL) {
+		    ObjectIter iter;
+		    const CKYBuffer *authid = obj.getPinAuthId();
+
+		    /* these should put on the individual keys */
+		    auth[CKU_CONTEXT_SPECIFIC] = new PK15Object(obj);
+
+		    for( iter = tokenObjects.begin(); 
+			 iter != tokenObjects.end(); ++iter) {
+			if( CKYBuffer_IsEqual(iter->getAuthId(),authid)) {
+			    iter->setAttributeBool(CKA_ALWAYS_AUTHENTICATE,
+						   TRUE);
+			}
+		    }
+		}
+		/* drop unkown */
+		break;
+	    case PK15PvKey:
+		/* does the cert already exist? */
+		{
+		    ObjectConstIter iter;
+		    const CKYBuffer *id;
+
+		    id = obj.getAttribute(CKA_ID);
+		    if ((!id) || (CKYBuffer_Size(id) != 1)) {
+			break;
+		    }
+		    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
+                        ObjectCertCKAIDMatch(id));
+			
+		    if ( iter != tokenObjects.end() ) {
+			obj.completeKey(*iter);
+		    }
+		}
+		break;
+	    case PK15Cert:
+		/* does a corresponding key already exist? */
+		{
+		    ObjectIter iter;
+		    const CKYBuffer *id;
+
+		    id = obj.getAttribute(CKA_ID);
+		    if ((!id) || (CKYBuffer_Size(id) != 1)) {
+			break;
+		    }
+		    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
+                        ObjectKeyCKAIDMatch(id));
+			
+		    if ( iter != tokenObjects.end() ) {
+			iter->completeKey(obj);
+		    }
+		}
+		break;
+	    case PK15PuKey:
+		break;
+	    }
+    	    tokenObjects.push_back(obj);
+  	} while ( false );
+    }
+    CKYBuffer_FreeData(&file);
+    return CKYSUCCESS;
+}
+
+
+CKYStatus
+Slot::selectPath(const CKYBuffer *path, CKYISOStatus  *apduRC)
+{
+    CKYSize size = CKYBuffer_Size(path);
+    CKYStatus status = CKYINVALIDARGS;
+    CKYOffset pos;
+
+    for (pos=0; pos < size; pos +=2) {
+	unsigned short ef = CKYBuffer_GetShort(path, pos);
+	status = P15Applet_SelectFile(conn, ef, apduRC);
+	if (status != CKYSUCCESS) {
+		break;
+	}
+    }
+    return status;
+}
+
 void
 Slot::refreshTokenState()
 {
@@ -1132,8 +1581,20 @@ void
 Slot::makeSerialString(char *serialNumber, int maxSize,
 						 const unsigned char *cuid)
 {
+    CKYSize ssize = CKYBuffer_Size(&p15serialNumber);
     memset(serialNumber, ' ', maxSize);
 
+    if (ssize != 0) {
+	CKYSize i;
+	ssize = MIN((CKYSize)maxSize/2, ssize);
+	for (i=0; i < ssize; i++) {
+	   CKYByte c = CKYBuffer_GetChar(&p15serialNumber, i);
+	   serialNumber[2*i] = hex((c >> 4) & 0xf);
+	   serialNumber[2*i+1] = hex(c & 0xf);
+	}
+    }
+	    
+
     // otherwise we use the eepromSerialNumber as a hex value 
     if (cuid) {
          makeCUIDString(serialNumber, maxSize, cuid);
@@ -1204,7 +1665,8 @@ struct _manList {
 static const struct _manList  manList[] = {
         { 0x4090, "Axalto" },
         { 0x2050, "Oberthur" },
-        { 0x4780, "RSA" }
+        { 0x4780, "RSA" },
+        { 0x534e, "SafeNet" }
 };
 
 static int manListSize = sizeof(manList)/sizeof(manList[0]);
@@ -1213,8 +1675,15 @@ void
 Slot::makeManufacturerString(char *man, int maxSize, const unsigned char *cuid)
 {
     char *cp = man;
+    int manLen;
     memset(man, ' ', maxSize);
 
+    if (tokenManufacturer) {
+	manLen = strlen(tokenManufacturer);
+	memcpy(man, tokenManufacturer, MIN(manLen, maxSize));
+        // UTF8 Truncate fixup! don't drop halfway through a UTF8 character 
+        return;
+    }
     if (!cuid) {
 	return;
     }
@@ -1633,26 +2102,6 @@ class KeyNumMatch {
     }
 };
 
-class ObjectCertCKAIDMatch {
-  private:
-    CKYByte cka_id;
-  public:
-    ObjectCertCKAIDMatch(CKYByte cka_id_) : cka_id(cka_id_) {}
-    bool operator()(const PKCS11Object& obj) {
-	const CKYBuffer *id;
-        const CKYBuffer *objClass;
-	CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
-	objClass = obj.getAttribute(CKA_CLASS);
-        if (objClass == NULL || !CKYBuffer_DataIsEqual(objClass, 
-				(CKYByte *)&certClass, sizeof(certClass))) {
-	    return false;
-        }
- 	id = obj.getAttribute(CKA_ID);
-        return (id != NULL && CKYBuffer_DataIsEqual(id,&cka_id, 1))
-						 ? true : false;
-    }
-};
-
 CK_OBJECT_HANDLE
 Slot::generateUnusedObjectHandle()
 {
@@ -1706,7 +2155,7 @@ Slot::addKeyObject(list<PKCS11Object>& o
                         "Missing or invalid CKA_ID value");
         }
         iter = find_if(objectList.begin(), objectList.end(),
-                        ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0)));
+                        ObjectCertCKAIDMatch(id));
         if ( iter == objectList.end() ) {
             // We failed to find a cert with a matching CKA_ID. This
             // can happen if the cert is not present on the token, or
@@ -1758,6 +2207,11 @@ Slot::unloadObjects()
     free(personName);
     personName = NULL;
     fullTokenName = false;
+    if (tokenManufacturer) {
+	free(tokenManufacturer);
+	tokenManufacturer = NULL;
+    }
+    CKYBuffer_Resize(&p15serialNumber,0);
 }
 
 #ifdef USE_SHMEM
@@ -2956,6 +3410,17 @@ Slot::loadObjects()
 	loadReaderObject();
 	return;
     }
+    if (state & P15_CARD) {
+	parseEF_TokenInfo();
+	parseEF_ODF();
+	if (auth[CKU_USER] != NULL) {
+	    /* set need login */
+	    needLogin = true;
+	}
+	status = trans.end();
+	loadReaderObject();
+	return;
+    }
 
     selectApplet();
     log->log("time load object: Select Applet (again) %d ms\n",
@@ -3123,15 +3588,15 @@ Slot::getSessionInfo(SessionHandleSuffix
 }
 
 void
-SlotList::login(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin,
-    CK_ULONG ulPinLen)
+SlotList::login(CK_SESSION_HANDLE hSession, CK_USER_TYPE user,
+    CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
 {
     CK_SLOT_ID slotID;
     SessionHandleSuffix suffix;
 
     decomposeSessionHandle(hSession, slotID, suffix);
 
-    slots[slotIDToIndex(slotID)]->login(suffix, pPin, ulPinLen);
+    slots[slotIDToIndex(slotID)]->login(suffix, user, pPin, ulPinLen);
 }
 
 void
@@ -3181,8 +3646,8 @@ Slot::isLoggedIn()
 }
 
 void
-Slot::login(SessionHandleSuffix handleSuffix, CK_UTF8CHAR_PTR pPin,
-    CK_ULONG ulPinLen)
+Slot::login(SessionHandleSuffix handleSuffix, CK_USER_TYPE user,
+    CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
 {
     refreshTokenState();
 
@@ -3191,10 +3656,23 @@ Slot::login(SessionHandleSuffix handleSu
             "Slot::login\n", (unsigned long) handleSuffix);
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
+    /* only support CKU_USER for CAC, PIV, and coolkey... CKU_USER and
+     * CKU_CONTEX_SPECIFIC for P15Card */
+    if (user != CKU_USER) {
+	if ((user != CKU_CONTEXT_SPECIFIC) || ((state & P15_CARD) == 0)) {
+	    throw PKCS11Exception(CKR_USER_TYPE_INVALID);
+	}
+    }
+
 
     if (!isVersion1Key) {
-	pinCache.invalidate();
-	pinCache.set((const char *)pPin, ulPinLen);
+	if (user == CKU_USER) {
+	    pinCache.invalidate();
+	    pinCache.set((const char *)pPin, ulPinLen);
+	} else { 
+	    contextPinCache.invalidate();
+	    contextPinCache.set((const char *)pPin, ulPinLen);
+	}
     } else if (nonceValid) {
 	throw PKCS11Exception(CKR_USER_ALREADY_LOGGED_IN);
     }
@@ -3205,17 +3683,85 @@ Slot::login(SessionHandleSuffix handleSu
 
     if (state & GOV_CARD) {
 	selectCACApplet(0, true);
-    } else {
+    } else if ((state & P15_CARD)== 0) {
+	/* p15 does the select in attemptLogin */
 	selectApplet();
     }
 
     if (isVersion1Key) {
-	attemptLogin((const char *)pPin);
-    } else if (state & GOV_CARD) {
+	attemptCoolKeyLogin((const char *)pPin);
+    } else  {
+	attemptLogin(user, false);
+    }
+}
+
+void
+Slot::attemptLogin(CK_USER_TYPE user, bool flushPin) {
+    if (state & GOV_CARD) {
 	attemptCACLogin();
+    } else if (state & P15_CARD) {
+	attemptP15Login(user);
     } else {
 	oldAttemptLogin();
     }
+    if (flushPin && (user == CKU_CONTEXT_SPECIFIC)) {
+	contextPinCache.clearPin();
+    }
+}
+void dump(const char *label, const CKYBuffer *buf);
+
+void
+Slot::attemptP15Login(CK_USER_TYPE user)
+{
+    PinCache *pinCachePtr  = userPinCache(user);
+    const CKYBuffer *path;
+
+    if (user == CKU_USER) {
+	loggedIn = false;
+    }
+    pinCachePtr->invalidate();
+
+    CKYStatus status;
+    CKYISOStatus result;
+
+    if ((user >= MAX_AUTH_USERS) || (auth[user] == NULL)) {
+	throw PKCS11Exception(CKR_USER_TYPE_INVALID,
+			"No PKCS #15 auth object for user %d\n", user);
+    }
+
+    path = auth[user]->getObjectPath().getPath();
+    status = selectPath(auth[user]->getObjectPath().getPath(), &result);
+    if( status == CKYSCARDERR )  {
+	handleConnectionError();
+    }
+    if (status != CKYSUCCESS) {
+	throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet select return 0x%04x",
+								result);
+    }
+
+    status = P15Applet_VerifyPIN(conn, 
+		(const char *)CKYBuffer_Data(pinCachePtr->get()), 
+		auth[user]->getPinInfo(), &result);
+    if( status == CKYSCARDERR ) {
+	handleConnectionError();
+    }
+    switch( result ) {
+      case CKYISO_SUCCESS:
+        break;
+      case 0x6983:
+	pinCachePtr->clearPin();
+        throw PKCS11Exception(CKR_PIN_LOCKED);
+      default:
+	pinCachePtr->clearPin();
+	if ((result & 0xff00) == 0x6300) {
+            throw PKCS11Exception(CKR_PIN_INCORRECT);
+	}
+        throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet returned 0x%04x", 
+								result);
+    }
+    pinCachePtr->validate();
+    if (user == CKU_USER)
+	loggedIn = true;
 }
 
 void
@@ -3252,6 +3798,7 @@ Slot::attemptCACLogin()
     loggedIn = true;
 }
 
+
 void
 Slot::oldAttemptLogin()
 {
@@ -3286,7 +3833,7 @@ Slot::oldAttemptLogin()
 
 // should already be in a transaction, and applet selected
 void
-Slot::attemptLogin(const char *pin)
+Slot::attemptCoolKeyLogin(const char *pin)
 {
     CKYStatus status;
     CKYISOStatus result;
@@ -3362,7 +3909,7 @@ Slot::logout(SessionHandleSuffix suffix)
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
 
-    if (state & GOV_CARD) {
+    if (state & (GOV_CARD|P15_CARD)) {
 	CACLogout();
 	return;
     }
@@ -3601,31 +4148,26 @@ Slot::ensureValidSession(SessionHandleSu
 // from 0-9.
 //
 CKYByte
-Slot::objectHandleToKeyNum(CK_OBJECT_HANDLE hKey)
+Slot::objectToKeyNum(const PKCS11Object *key)
 {
-    ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
-        ObjectHandleMatch(hKey));
-
-    if( iter == tokenObjects.end() ) {
-        // no such object
-        throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
-    }
+    unsigned long id = key->getMuscleObjID();
 
-    if( getObjectClass(iter->getMuscleObjID()) != 'k' ) {
+    if( getObjectClass(id) != 'k' ) {
         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
     }
-    unsigned short keyNum = getObjectIndex(iter->getMuscleObjID());
+    unsigned short keyNum = getObjectIndex(id);
     if( keyNum > 9 ) {
         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
     }
     return keyNum & 0xFF;
 }
 
-PKCS11Object::KeyType
-Slot::getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey)
+PKCS11Object *
+Slot::getKeyFromHandle(CK_OBJECT_HANDLE hKey)
 {
     ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(),
         ObjectHandleMatch(hKey));
+    PKCS11Object &obj = (PKCS11Object &)*iter;
 
     if( iter == tokenObjects.end() ) {
          throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
@@ -3635,7 +4177,7 @@ Slot::getKeyTypeFromHandle(CK_OBJECT_HAN
         throw PKCS11Exception(CKR_KEY_HANDLE_INVALID);
     }
 
-    return iter->getKeyType();
+    return &obj;
 }
 
 void
@@ -3648,9 +4190,7 @@ Slot::signInit(SessionHandleSuffix suffi
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
 
-    PKCS11Object::KeyType  keyType = getKeyTypeFromHandle(hKey);
-
-    session->signatureState.initialize(objectHandleToKeyNum(hKey), keyType);
+    session->signatureState.initialize(getKeyFromHandle(hKey));
 }
 
 void
@@ -3663,9 +4203,7 @@ Slot::decryptInit(SessionHandleSuffix su
         throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID);
     }
 
-    PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey);
-
-    session->decryptionState.initialize(objectHandleToKeyNum(hKey), keyType);
+    session->decryptionState.initialize(getKeyFromHandle(hKey));
 }
 
 /**
@@ -3935,7 +4473,7 @@ Slot::sign(SessionHandleSuffix suffix, C
 
     CryptOpState sigState = dummyParams.getOpState(*session);
 
-    PKCS11Object::KeyType keyType = sigState.keyType;
+    PKCS11Object::KeyType keyType = sigState.key->getKeyType();
    
     if ( keyType == PKCS11Object::unknown) {
         throw PKCS11Exception(CKR_DATA_INVALID);
@@ -3982,9 +4520,9 @@ Slot::cryptRSA(SessionHandleSuffix suffi
     }
     CryptOpState& opState = params.getOpState(*session);
     CKYBuffer *result = &opState.result;
-    CKYByte keyNum = opState.keyNum;
+    PKCS11Object  *key = opState.key;
 
-    unsigned int keySize = getRSAKeySize(keyNum);
+    unsigned int keySize = getRSAKeySize(key);
 
     if (keySize != CryptParams::DEFAULT_KEY_SIZE)
         params.setKeySize(keySize);
@@ -4008,8 +4546,8 @@ Slot::cryptRSA(SessionHandleSuffix suffi
   	}
 	try {
 	    params.padInput(&inputPad, &input);
-            performRSAOp(&output, &inputPad, params.getKeySize(), 
-								keyNum, params.getDirection());
+            performRSAOp(&output, &inputPad, params.getKeySize(), key, 
+							params.getDirection());
 	    params.unpadOutput(result, &output);
 	    CKYBuffer_FreeData(&input);
 	    CKYBuffer_FreeData(&inputPad);
@@ -4072,9 +4610,9 @@ void Slot::signECC(SessionHandleSuffix s
     }
     CryptOpState& opState = params.getOpState(*session);
     CKYBuffer *result = &opState.result;
-    CKYByte keyNum = opState.keyNum;
+    PKCS11Object  *key = opState.key;
 
-    unsigned int keySize = getECCKeySize(keyNum);
+    unsigned int keySize = getECCKeySize(key);
 
     if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE)
         params.setKeySize(keySize);
@@ -4100,7 +4638,7 @@ void Slot::signECC(SessionHandleSuffix s
             throw PKCS11Exception(CKR_HOST_MEMORY);
         }
         try {
-            performECCSignature(&output, &input, params.getKeySize(), keyNum);
+            performECCSignature(&output, &input, params.getKeySize(), key);
             params.unpadOutput(result, &output);
             CKYBuffer_FreeData(&input);
             CKYBuffer_FreeData(&output);
@@ -4122,8 +4660,26 @@ void Slot::signECC(SessionHandleSuffix s
 }
 
 void
+Slot::selectKey(const PKCS11Object *key, bool retry)
+{
+    /* P15 cards need to be reselected on retry because P15 must select
+     * on authentication. PIV, CAC and Coolkeys do not */
+    if (retry && ((state & GOV_CARD) || ((state & P15_CARD) == 0))) {
+	return;
+    }
+    if (state & GOV_CARD) {
+	selectCACApplet(objectToKeyNum(key), true);
+    } else if (state & P15_CARD) {
+	selectPath(key->getObjectPath().getPath(), NULL);
+    } else {
+	selectApplet();
+    }
+    return;
+}
+
+void
 Slot::performECCSignature(CKYBuffer *output, const CKYBuffer *input, 
-						unsigned int keySize, CKYByte keyNum)
+			unsigned int keySize, const PKCS11Object *key)
 {
 
     /* establish a transaction */
@@ -4135,22 +4691,23 @@ Slot::performECCSignature(CKYBuffer *out
         throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
     }
 
-    if (state & GOV_CARD) {
-	selectCACApplet(keyNum, true);
-    } else {
-	selectApplet();
-    }
-
     CKYISOStatus result;
-    int loginAttempted = 0;
+    bool loginAttempted = false;
 
 retry:
+    selectKey(key, loginAttempted);
+
     if (state & PIV_CARD) {
-        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result);
+        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, 
+					input, output, &result);
     } else if (state & CAC_CARD) {
         status = CACApplet_SignDecrypt(conn, input, output, &result);
+    } else if (state & P15_CARD) {
+	status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
+				CKY_DIR_ENCRYPT, input, output, &result); 
+	
     } else {
-        status = CKYApplet_ComputeECCSignature(conn, keyNum, input, NULL, output, getNonce(), &result);
+        status = CKYApplet_ComputeECCSignature(conn, objectToKeyNum(key), 					input, NULL, output, getNonce(), &result);
     }
     /* map the ISO not logged in code to the coolkey one */
     if ((result == CKYISO_CONDITION_NOT_SATISFIED) ||
@@ -4166,19 +4723,16 @@ retry:
         if (result == CKYISO_DATA_INVALID) {
             throw PKCS11Exception(CKR_DATA_INVALID);
         }
-        /* version0 keys could be logged out in the middle by someone else,
-           reauthenticate... This code can go away when we depricate.
-           version0 applets.
-        */
+        /* keys could be logged out in the middle by someone else,
+         *  reauthenticate... coolkey version 1 bypasses this issue by
+         *  allowing multiple applications separate login states at once.
+         */
         if (!isVersion1Key && !loginAttempted  &&
+				userPinCache(key->getUser())->isValid() &&
                     (result == CKYISO_UNAUTHORIZED)) {
             /* try to reauthenticate  */
 	    try {
-		if (state & GOV_CARD) {
-		    attemptCACLogin();
-		} else {
-		    oldAttemptLogin();
-		}
+		attemptLogin(key->getUser(),true);
             } catch(PKCS11Exception& ) {
                 /* attemptLogin can throw things like CKR_PIN_INCORRECT
                   that don't make sense from a crypto operation. This is
@@ -4199,8 +4753,9 @@ retry:
 
 
 void
-Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, unsigned int keySize,
-					CKYByte keyNum, CKYByte direction)
+Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, 
+		unsigned int keySize, const PKCS11Object *key, 
+		CKYByte direction)
 {
     if ( mECC ) {
         throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
@@ -4212,26 +4767,25 @@ Slot::performRSAOp(CKYBuffer *output, co
     Transaction trans;
     CKYStatus status = trans.begin(conn);
     if( status != CKYSUCCESS ) handleConnectionError();
-
-    //
-    // select the applet
-    //
-    if (state & GOV_CARD) {
-	selectCACApplet(keyNum, true);
-    } else {
-	selectApplet();
-    }
-
     CKYISOStatus result;
-    int loginAttempted = 0;
+    bool loginAttempted = false;
+
 retry:
+    selectKey(key, loginAttempted);
+
+
     if (state & PIV_CARD) {
-        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result);
+        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, 
+				input, output, &result);
     } else if (state & CAC_CARD) {
         status = CACApplet_SignDecrypt(conn, input, output, &result);
+    } else if (state & P15_CARD) {
+	status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
+				direction, input, output, &result);
     } else {
-        status = CKYApplet_ComputeCrypt(conn, keyNum, CKY_RSA_NO_PAD, direction,
-		input, NULL, output, getNonce(), &result);
+        status = CKYApplet_ComputeCrypt(conn, objectToKeyNum(key), 
+		CKY_RSA_NO_PAD, direction, input, NULL, output, 
+		getNonce(), &result);
     } 
 
     /* map the ISO not logged in code to the coolkey one */
@@ -4250,15 +4804,12 @@ retry:
 	// version0 keys could be logged out in the middle by someone else,
 	// reauthenticate... This code can go away when we depricate.
         // version0 applets.
-	if (!isVersion1Key && !loginAttempted  && pinCache.isValid() &&
+	if (!isVersion1Key && !loginAttempted  && 
+				userPinCache(key->getUser())->isValid() &&
 					(result == CKYISO_UNAUTHORIZED)) {
 	    // try to reauthenticate 
 	    try {
-		if (state & GOV_CARD) {
-		    attemptCACLogin();
-		} else {
-		    oldAttemptLogin();
-		}
+		attemptLogin(key->getUser(), true);
 	    } catch(PKCS11Exception& ) {
 		// attemptLogin can throw things like CKR_PIN_INCORRECT
 		// that don't make sense from a crypto operation. This is
@@ -4278,7 +4829,7 @@ void
 Slot::seedRandom(SessionHandleSuffix suffix, CK_BYTE_PTR pData,
         CK_ULONG ulDataLen)
 {
-    if (state & GOV_CARD) {
+    if (state & (GOV_CARD|P15_CARD)) {
 	/* should throw unsupported */
 	throw PKCS11Exception(CKR_DEVICE_ERROR);
     }
@@ -4330,7 +4881,7 @@ void
 Slot::generateRandom(SessionHandleSuffix suffix, const CK_BYTE_PTR pData,
         CK_ULONG ulDataLen)
 {
-    if (state & GOV_CARD) {
+    if (state & (GOV_CARD|P15_CARD)) {
 	/* should throw unsupported */
 	throw PKCS11Exception(CKR_DEVICE_ERROR);
     }
@@ -4364,61 +4915,44 @@ Slot::generateRandom(SessionHandleSuffix
 
 #define MAX_NUM_KEYS 8
 unsigned int
-Slot::getRSAKeySize(CKYByte keyNum)
+Slot::getRSAKeySize(PKCS11Object *key)
 {
     unsigned int keySize = CryptParams::DEFAULT_KEY_SIZE;
     int modSize = 0;
 
-    if(keyNum >= MAX_NUM_KEYS) {
-        return keySize;
-    }
-
-    ObjectConstIter iter;
-    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
-        KeyNumMatch(keyNum,*this));
-
-    if( iter == tokenObjects.end() ) {
-        return keySize;
+    modSize = key->getKeySize();
+    if (modSize != 0) {
+	return modSize;
     }
 
-    CKYBuffer const *modulus = iter->getAttribute(CKA_MODULUS);
+    CKYBuffer const *modulus = key->getAttribute(CKA_MODULUS);
 
     if(modulus) {
         modSize = CKYBuffer_Size(modulus);
         if(CKYBuffer_GetChar(modulus,0) == 0x0) {
             modSize--;
         }
-        if(modSize > 0)
+        if(modSize > 0) {
             keySize = modSize * 8;
+	    key->setKeySize(keySize);
+	}
     }
 
     return keySize;
 }
 
 unsigned int
-Slot::getECCKeySize(CKYByte keyNum)
-{
-    return calcECCKeySize(keyNum);
-} 
-
-unsigned int
-Slot::calcECCKeySize(CKYByte keyNum)
+Slot::getECCKeySize(PKCS11Object *key)
 {
     unsigned int keySize = CryptParams::ECC_DEFAULT_KEY_SIZE;
+    unsigned int objKeySize = 0;
 
-    if(keyNum >= MAX_NUM_KEYS) {
-        return keySize;
-    }
-
-    ObjectConstIter iter;
-    iter = find_if(tokenObjects.begin(), tokenObjects.end(),
-        KeyNumMatch(keyNum,*this));
-
-    if( iter == tokenObjects.end() ) {
-        return keySize;
+    objKeySize = key->getKeySize();
+    if (objKeySize != 0) {
+	return objKeySize;
     }
 
-    CKYBuffer const *eccParams = iter->getAttribute(CKA_EC_PARAMS);
+    CKYBuffer const *eccParams = key->getAttribute(CKA_EC_PARAMS);
 
     if (eccParams == NULL) {
         return keySize;
@@ -4457,6 +4991,7 @@ Slot::calcECCKeySize(CKYByte keyNum)
 
         if ( match == 1 ) {
             keySize =  curveBytesNamePair[i].length;
+	    key->setKeySize(keySize);
             return keySize;
         }
 
@@ -4476,10 +5011,9 @@ Slot::derive(SessionHandleSuffix suffix,
     ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE);
     SessionIter session = findSession(suffix);
 
-    PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hBaseKey);
-
-    session->keyAgreementState.initialize(objectHandleToKeyNum(hBaseKey), keyType);
-    deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey, params);
+    session->keyAgreementState.initialize(getKeyFromHandle(hBaseKey));
+    deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, 
+		phKey, params);
 
 }
 
@@ -4515,9 +5049,8 @@ void Slot::deriveECC(SessionHandleSuffix
 
     CryptOpState& opState = params.getOpState(*session);
     CKYBuffer *result = &opState.result;
-    CKYByte keyNum = opState.keyNum;
 
-    unsigned int keySize = getECCKeySize(keyNum);
+    unsigned int keySize = getECCKeySize(opState.key);
 
     if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE)
         params.setKeySize(keySize);
@@ -4542,10 +5075,11 @@ void Slot::deriveECC(SessionHandleSuffix
 
     if( CKYBuffer_Size(result) == 0 ) {
         try {
-            performECCKeyAgreement(deriveMech, &publicDataBuffer, &secretKeyBuffer,
-			 keyNum, params.getKeySize());
+            performECCKeyAgreement(deriveMech, &publicDataBuffer, 
+			&secretKeyBuffer, opState.key, params.getKeySize());
             CK_OBJECT_HANDLE keyObjectHandle = generateUnusedObjectHandle();
-            secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, pTemplate, ulAttributeCount);
+            secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, 
+						pTemplate, ulAttributeCount);
         } catch(PKCS11Exception& e) {
             CKYBuffer_FreeData(&secretKeyBuffer);
             CKYBuffer_FreeData(&publicDataBuffer);
@@ -4557,15 +5091,15 @@ void Slot::deriveECC(SessionHandleSuffix
    CKYBuffer_FreeData(&publicDataBuffer);
 
    if ( secret ) {
-
        *phKey = secret->getHandle();
         delete secret;
    }
 }
 
 void
-Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, CKYBuffer *publicDataBuffer, 
-			     CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize)
+Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
+	CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, 
+	const PKCS11Object *key, unsigned int keySize)
 {
     if (!mECC) {
        throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
@@ -4575,25 +5109,26 @@ Slot::performECCKeyAgreement(CK_MECHANIS
     CKYStatus status = trans.begin(conn);
     if( status != CKYSUCCESS ) handleConnectionError();
 
-    if (state & GOV_CARD) {
-	selectCACApplet(keyNum, true);
-    } else {
-	selectApplet();
-    }
-
     CKYISOStatus result;
-    int loginAttempted = 0;
+    bool loginAttempted = false;
 
 retry:
+    selectKey(key, loginAttempted);
 
     if (state & PIV_CARD) {
-        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, publicDataBuffer, 
-			secretKeyBuffer, &result);
+        status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, 
+			publicDataBuffer, secretKeyBuffer, &result);
     } else if (state & CAC_CARD) {
-        status = CACApplet_SignDecrypt(conn, publicDataBuffer, secretKeyBuffer, &result);
+        status = CACApplet_SignDecrypt(conn, publicDataBuffer, 
+			secretKeyBuffer, &result);
+    } else if (state & P15_CARD) {
+	/*status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8,
+				publicDataBuffer, secretKeyBuffer, &result); */
+	throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED);
     } else {
-    	status = CKYApplet_ComputeECCKeyAgreement(conn, keyNum, 
-			publicDataBuffer , NULL, secretKeyBuffer, getNonce(), &result);
+    	status = CKYApplet_ComputeECCKeyAgreement(conn, objectToKeyNum(key), 
+			publicDataBuffer , NULL, secretKeyBuffer, 
+			getNonce(), &result);
     }
     /* map the ISO not logged in code to the coolkey one */
     if ((result == CKYISO_CONDITION_NOT_SATISFIED) ||
@@ -4610,13 +5145,10 @@ retry:
             throw PKCS11Exception(CKR_DATA_INVALID);
         }
         if (!isVersion1Key && !loginAttempted  &&
-            (result == CKYISO_UNAUTHORIZED)) {
+				userPinCache(key->getUser())->isValid() &&
+            				(result == CKYISO_UNAUTHORIZED)) {
 	    try {
-		if (state & GOV_CARD) {
-		    attemptCACLogin();
-		} else {
-		    oldAttemptLogin();
-		}
+		attemptLogin(key->getUser(), true);
 	    } catch(PKCS11Exception& ) {
               throw PKCS11Exception(CKR_DEVICE_ERROR);
 	    }
diff -up ./src/coolkey/slot.h.p15 ./src/coolkey/slot.h
--- ./src/coolkey/slot.h.p15	2015-07-06 10:27:55.772827229 -0700
+++ ./src/coolkey/slot.h	2015-07-06 10:27:55.786826965 -0700
@@ -183,7 +183,7 @@ struct PinCache {
 	CKYBuffer_Replace(&cachedPin, 0, (const CKYByte *)newPin, pinLen);
 	CKYBuffer_AppendChar(&cachedPin, 0);
     }
-    void clearPin() { CKYBuffer_Zero(&cachedPin); }
+    void clearPin() { CKYBuffer_Zero(&cachedPin); valid = false; }
     void invalidate() { valid = false; }
     void validate() { valid = true; }
     const CKYBuffer *get() const { return &cachedPin; }
@@ -209,29 +209,26 @@ class CryptOpState {
   public:
     enum State { NOT_INITIALIZED, IN_PROCESS, FINALIZED };
     State state;
-    CKYByte keyNum;
     CKYBuffer result;
-    PKCS11Object::KeyType keyType;
+    PKCS11Object *key;
 
-    CryptOpState() : state(NOT_INITIALIZED), keyNum(0), keyType(PKCS11Object::unknown)
+    CryptOpState() : state(NOT_INITIALIZED), key(NULL)
 				{ CKYBuffer_InitEmpty(&result); }
     CryptOpState(const CryptOpState &cpy) : 
-				state(cpy.state), keyNum(cpy.keyNum), keyType(cpy.keyType) { 
+				state(cpy.state), key(cpy.key) { 
 	CKYBuffer_InitFromCopy(&result, &cpy.result);
     }
     CryptOpState &operator=(const CryptOpState &cpy) {
 	state = cpy.state,
-	keyNum = cpy.keyNum;
-        keyType = cpy.keyType;
+        key = cpy.key;
 	CKYBuffer_Replace(&result, 0, CKYBuffer_Data(&cpy.result),
 				CKYBuffer_Size(&cpy.result));
 	return *this;
     }
     ~CryptOpState() { CKYBuffer_FreeData(&result); }
-    void initialize(CKYByte keyNum, PKCS11Object::KeyType theKeyType) {
+    void initialize(PKCS11Object *theKey) {
         state = IN_PROCESS;
-        this->keyNum = keyNum;
-        this->keyType = theKeyType;
+        this->key = theKey;
         CKYBuffer_Resize(&result, 0);
     }
 };
@@ -298,6 +295,7 @@ class CryptParams {
 };
 
 #define MAX_CERT_SLOTS 3
+#define MAX_AUTH_USERS 3
 class Slot {
 
   public:
@@ -308,7 +306,8 @@ class Slot {
         APPLET_SELECTABLE = 0x08,
         APPLET_PERSONALIZED = 0x10,
         CAC_CARD = 0x20,
-        PIV_CARD = 0x40
+        PIV_CARD = 0x40,
+	P15_CARD = 0x80
     };
     enum {
 	NONCE_SIZE = 8
@@ -321,6 +320,7 @@ class Slot {
     char *readerName;
     char *personName;
     char *manufacturer;
+    char *tokenManufacturer;
     //char *model;
     CK_VERSION hwVersion;
     CK_VERSION tokenFWVersion;
@@ -329,6 +329,7 @@ class Slot {
     CKYCardConnection* conn;
     unsigned long state; // = UNKNOWN
     PinCache pinCache;
+    PinCache contextPinCache;
     bool loggedIn;
     bool reverify;
     bool nonceValid;
@@ -349,6 +350,14 @@ class Slot {
     int pivContainer;
     int pivKey;
     bool mECC;
+    unsigned short p15aid;
+    unsigned short p15odfAddr;
+    unsigned short p15tokenInfoAddr;
+    unsigned int p15Instance;
+    CKYBuffer p15AID;
+    CKYBuffer p15tokenInfo;
+    CKYBuffer p15odf;
+    CKYBuffer p15serialNumber;
     //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 };
 
 #ifdef USE_SHMEM
@@ -367,6 +376,7 @@ class Slot {
 
     void closeAllSessions();
     SessionHandleSuffix generateNewSession(Session::Type type);
+    PK15Object *auth[MAX_AUTH_USERS];
 
     bool cardStateMayHaveChanged();
     void connectToToken();
@@ -418,48 +428,63 @@ class Slot {
                               bool throwException);
     CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize);
 
+    CKYStatus getP15Params();
     void selectApplet();
     void selectCACApplet(CKYByte instance,bool do_disconnect);
+    void selectKey(const PKCS11Object *key, bool retry);
+    CKYStatus selectPath(const CKYBuffer *path, CKYISOStatus *adpurc);
+    CKYStatus readFromPath(const PK15ObjectPath &obj, CKYBuffer *file);
     void unloadObjects();
     void loadCACObjects();
     void loadCACCert(CKYByte instance);
     void loadObjects();
     void loadReaderObject();
 
-    void attemptLogin(const char *pin);
+    void attemptCoolKeyLogin(const char *pin);
+    void attemptLogin(CK_USER_TYPE user, bool flushPin);
+    void attemptP15Login(CK_USER_TYPE user);
     void attemptCACLogin();
     void oldAttemptLogin();
     void oldLogout(void);
     void CACLogout(void);
+    PinCache *userPinCache(CK_USER_TYPE user) {
+	return ((user == CKU_CONTEXT_SPECIFIC) && (state & P15_CARD)) ?
+		&contextPinCache : &pinCache; }
+
+    void parseEF_ODF(void);
+    void parseEF_TokenInfo(void);
+    CKYStatus parseEF_Directory(const CKYByte *data, CKYSize size, 
+				PK15ObjectType type);
+    unsigned int PK15Instance(void) { return p15Instance++; }
 
     void readMuscleObject(CKYBuffer *obj, unsigned long objID, 
 							unsigned int objSize);
 
     void performSignature(CKYBuffer *sig, const CKYBuffer *unpaddedInput, 
-							CKYByte keyNum);
-    void performDecryption(CKYBuffer *data, const CKYBuffer *input, CKYByte keyNum);
+						const PKCS11Object *key);
+    void performDecryption(CKYBuffer *data, const CKYBuffer *input, 
+						const PKCS11Object *key);
 
     void cryptRSA(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
         CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
         CK_ULONG_PTR pulOutputLen, CryptParams& params);
 
-    void performRSAOp(CKYBuffer *out, const CKYBuffer *input, unsigned int keySize,
-						CKYByte keyNum, CKYByte direction);
+    void performRSAOp(CKYBuffer *out, const CKYBuffer *input, 
+	unsigned int keySize, const PKCS11Object *key, CKYByte direction);
 
     void signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput,
         CK_ULONG ulInputLen, CK_BYTE_PTR pOutput,
         CK_ULONG_PTR pulOutputLen, CryptParams& params);
 
     void performECCSignature(CKYBuffer *out, const CKYBuffer *input, 
-					unsigned int keySize, CKYByte keyNum);
+				unsigned int keySize, const PKCS11Object *key);
     void performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, 
-        CKYBuffer *publicDataBuffer, 
-        CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize);
+        CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, 
+	const PKCS11Object *key, 	unsigned int keySize);
 
     void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu);
 
-    CKYByte objectHandleToKeyNum(CK_OBJECT_HANDLE hKey);
-    unsigned int calcECCKeySize(CKYByte keyNum);
+    CKYByte objectToKeyNum(const PKCS11Object *key);
     Slot(const Slot &cpy)
 #ifdef USE_SHMEM
 	: shmem(readerName)
@@ -491,10 +516,10 @@ class Slot {
     }
 
     // actually get the size of a key in bits from the card
-    unsigned int getRSAKeySize(CKYByte keyNum);
-    unsigned int getECCKeySize(CKYByte keyNum);
+    unsigned int getRSAKeySize(PKCS11Object *key);
+    unsigned int getECCKeySize(PKCS11Object *key);
 
-    PKCS11Object::KeyType  getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey);
+    PKCS11Object *getKeyFromHandle(CK_OBJECT_HANDLE hKey);
 
     SessionHandleSuffix openSession(Session::Type type);
     void closeSession(SessionHandleSuffix handleSuffix);
@@ -504,8 +529,8 @@ class Slot {
     void getSessionInfo(SessionHandleSuffix handleSuffix,
         CK_SESSION_INFO_PTR pInfo);
 
-    void login(SessionHandleSuffix handleSuffix, CK_UTF8CHAR_PTR pPin,
-        CK_ULONG ulPinLen);
+    void login(SessionHandleSuffix handleSuffix, CK_USER_TYPE user,
+	CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);
 
     void logout(SessionHandleSuffix suffix);
 
@@ -604,8 +629,8 @@ class SlotList {
     void getSessionInfo(CK_SESSION_HANDLE sessionHandle,
         CK_SESSION_INFO_PTR pInfo);
 
-    void login(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin,
-        CK_ULONG ulPinLen);
+    void login(CK_SESSION_HANDLE hSession, CK_USER_TYPE user,
+	CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen);
 
     void logout(CK_SESSION_HANDLE hSession);
 
diff -up ./src/libckyapplet/cky_applet.c.p15 ./src/libckyapplet/cky_applet.c
--- ./src/libckyapplet/cky_applet.c.p15	2015-07-06 10:27:55.775827172 -0700
+++ ./src/libckyapplet/cky_applet.c	2015-07-06 10:27:55.787826946 -0700
@@ -19,6 +19,7 @@
 
 #include <stdio.h>
 #include "cky_applet.h"
+#include <string.h>
 
 #define MIN(x, y) ((x) < (y) ? (x) : (y))
 
@@ -51,6 +52,12 @@ CACAppletFactory_SelectFile(CKYAPDU *apd
 }
 
 CKYStatus
+P15AppletFactory_SelectFile(CKYAPDU *apdu, const void *param)
+{
+    return CKYAPDUFactory_SelectFile(apdu, 0, 0, (const CKYBuffer *)param);
+}
+
+CKYStatus
 CKYAppletFactory_SelectCardManager(CKYAPDU *apdu, const void *param)
 {
     return CKYAPDUFactory_SelectCardManager(apdu);
@@ -269,17 +276,10 @@ PIVAppletFactory_SignDecrypt(CKYAPDU *ap
 }
 
 CKYStatus
-CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
+P15AppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
 {
-    const char *pin=(const char *)param;
-    return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin);
-}
-
-CKYStatus
-PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param)
-{
-    const char *pin=(const char *)param;
-    return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin);
+    const P15AppletArgVerifyPIN *vps = (const P15AppletArgVerifyPIN *)param;
+    return P15APDUFactory_VerifyPIN(apdu, vps->pinRef, vps->pinVal);
 }
 
 CKYStatus
@@ -323,6 +323,39 @@ CKYAppletFactory_LogoutAllV0(CKYAPDU *ap
    return CKYAPDU_SetSendData(apdu, data, sizeof(data));
 }
 
+CKYStatus
+P15AppletFactory_ReadRecord(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgReadRecord *rrs = (const P15AppletArgReadRecord *)param;
+    return P15APDUFactory_ReadRecord(apdu, rrs->record,
+					rrs->short_ef, rrs->flags, rrs->size);
+}
+
+CKYStatus
+P15AppletFactory_ReadBinary(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgReadBinary *res = (const P15AppletArgReadBinary *)param;
+    return P15APDUFactory_ReadBinary(apdu, res->offset,
+					res->short_ef, res->flags, res->size);
+}
+
+CKYStatus
+P15AppletFactory_ManageSecurityEnvironment(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgManageSecurityEnvironment *mse = 
+		(const P15AppletArgManageSecurityEnvironment *)param;
+    return P15APDUFactory_ManageSecurityEnvironment(apdu, mse->p1, 
+						mse->p2, mse->keyRef);
+}
+
+CKYStatus
+P15AppletFactory_PerformSecurityOperation(CKYAPDU *apdu, const void *param)
+{
+    const P15AppletArgPerformSecurityOperation *pso = 
+		(const P15AppletArgPerformSecurityOperation *)param;
+    return P15APDUFactory_PerformSecurityOperation(apdu, pso->dir, pso->chain,
+						   pso->retLen, pso->data);
+}
 /*****************************************************************
  *
  * Generic Fill routines used by several calls in common
@@ -975,9 +1008,6 @@ CKYApplet_ComputeECCSignature(CKYCardCon
     const CKYBuffer *data, CKYBuffer *sig,
     CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC)
 {
-    int         use2APDUs = 0;
-    int         use_dl_object =  0; 
-    short       dataSize = 0;
     CKYStatus ret = CKYAPDUFAIL;
     CKYAppletArgComputeECCSignature ccd;
     CKYBuffer    empty;
@@ -1052,6 +1082,11 @@ done:
     return ret;
 }
 
+const P15PinInfo CACPinInfo = 
+   { P15PinInitialized|P15PinNeedsPadding, P15PinUTF8, 0, 8, 8, 0, 0xff };
+const P15PinInfo PIVPinInfo = 
+   { P15PinLocal|P15PinInitialized|P15PinNeedsPadding, 
+					   P15PinUTF8, 0, 8, 8, 0, 0xff };
 /*
  * do a CAC VerifyPIN
  */
@@ -1059,23 +1094,8 @@ CKYStatus
 CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local,
 		    CKYISOStatus *apduRC)
 {
-    CKYStatus ret;
-    CKYISOStatus status;
-    if (apduRC == NULL) {
-	apduRC = &status;
-    }
-
-    ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN :
-			    CACAppletFactory_VerifyPIN, pin, NULL, 
-			    0, CKYAppletFill_Null, 
-			    NULL, apduRC);
-    /* it's unfortunate that the same code that means 'more data to follow' for
-     * GetCertificate also means, auth failure, you only have N more attempts
-     * left in the verify PIN call */
-    if ((*apduRC & CKYISO_MORE_MASK) == CKYISO_MORE) {
-	ret = CKYAPDUFAIL;
-    }
-    return ret;
+    return P15Applet_VerifyPIN(conn, pin, 
+				local ? &PIVPinInfo: &CACPinInfo, apduRC);
 }
 
 
@@ -1165,6 +1185,214 @@ CACApplet_ReadFile(CKYCardConnection *co
     return ret;
 }
 
+/*
+ *  Select a EF
+ */
+CKYStatus
+P15Applet_SelectFile(CKYCardConnection *conn, unsigned short ef,
+						 CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer efBuf;
+    CKYBuffer_InitEmpty(&efBuf);
+    CKYBuffer_AppendShort(&efBuf, ef);
+    ret = CKYApplet_HandleAPDU(conn, P15AppletFactory_SelectFile, &efBuf,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+    CKYBuffer_FreeData(&efBuf);
+    return ret;
+}
+
+CKYStatus
+P15Applet_SelectRootFile(CKYCardConnection *conn, unsigned short ef,
+						 CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYBuffer efBuf;
+    CKYBuffer_InitEmpty(&efBuf);
+    CKYBuffer_AppendShort(&efBuf, ef);
+    ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &efBuf,
+		 NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+    CKYBuffer_FreeData(&efBuf);
+    return ret;
+}
+
+CKYStatus
+P15Applet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+			const P15PinInfo *pinInfo, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    CKYISOStatus status;
+    CKYSize size;
+    CKYBuffer encodedPin;
+    P15AppletArgVerifyPIN vps;
+
+    CKYBuffer_InitEmpty(&encodedPin);
+
+    if (apduRC == NULL) {
+	apduRC = &status;
+    }
+
+    size = strlen(pin);
+    if (pinInfo->pinFlags & P15PinNeedsPadding) {
+	if (size > pinInfo->storedLength) {
+	    size = pinInfo->storedLength;
+	}
+	ret=CKYBuffer_Reserve(&encodedPin, pinInfo->storedLength);
+	if (ret != CKYSUCCESS) { goto fail; }
+    }
+    /* This is where we would do upcase processing for the case insensitive 
+     * flag. It's also where we would do mapping for bcd pins */
+    ret = CKYBuffer_Replace(&encodedPin, 0, (const CKYByte *)pin, size);
+    if (ret != CKYSUCCESS) { goto fail; }
+    if (pinInfo->pinFlags & P15PinNeedsPadding) {
+	int i; 
+	int padSize = pinInfo->storedLength - size;
+	for (i=0; i < padSize; i++) {
+	    CKYBuffer_AppendChar(&encodedPin, pinInfo->padChar);
+	}
+    }
+
+    vps.pinRef = pinInfo->pinRef | 
+     ((pinInfo->pinFlags & P15PinLocal) ? ISO_LOGIN_LOCAL : ISO_LOGIN_GLOBAL);
+    vps.pinVal = &encodedPin;
+    ret = CKYApplet_HandleAPDU(conn, P15AppletFactory_VerifyPIN, &vps, NULL, 
+			    0, CKYAppletFill_Null, 
+			    NULL, apduRC);
+    /* it's unfortunate that the same code that means 'more data to follow' for
+     * GetCertificate also means, auth failure, you only have N more attempts
+     * left in the verify PIN call */
+    if ((*apduRC & CKYISO_MORE_MASK) == CKYISO_MORE) {
+	ret = CKYAPDUFAIL;
+    }
+fail:
+    CKYBuffer_FreeData(&encodedPin);
+    return ret;
+}
+
+
+/*
+ * Read Record
+ */
+CKYStatus
+P15Applet_ReadRecord(CKYCardConnection *conn, CKYByte record, CKYByte short_ef,
+		CKYByte flags, CKYByte size, CKYBuffer *data, CKYISOStatus *apduRC)
+{
+    P15AppletArgReadRecord rrd;
+
+    rrd.record = record;
+    rrd.short_ef = short_ef;
+    rrd.flags = flags;
+    rrd.size = size;
+    return CKYApplet_HandleAPDU(conn, P15AppletFactory_ReadRecord, &rrd, NULL,
+	CKY_SIZE_UNKNOWN, CKYAppletFill_ReplaceBuffer, data, apduRC);
+}
+
+static CKYStatus
+P15Applet_ManageSecurityEnvironment(CKYCardConnection *conn, CKYByte key,
+				 CKYByte direction, CKYByte p1, 
+				 CKYISOStatus *apduRC)
+{
+    P15AppletArgManageSecurityEnvironment mse;
+
+    mse.p1 = p1; /* this appears to be where most cards disagree */
+    mse.p2 = (direction == CKY_DIR_DECRYPT) ? ISO_MSE_KEA : ISO_MSE_SIGN;
+    mse.keyRef = key; /* should be CKYBuffer in the future? */
+    return CKYApplet_HandleAPDU(conn, 
+		P15AppletFactory_ManageSecurityEnvironment, &mse, NULL,
+		CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+}
+
+CKYStatus
+P15Applet_SignDecrypt(CKYCardConnection *conn, CKYByte key, 
+		unsigned int keySize, CKYByte direction, 
+		const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC)
+{
+    CKYStatus ret;
+    P15AppletArgPerformSecurityOperation pso;
+    CKYSize dataSize = CKYBuffer_Size(data);
+    CKYOffset offset = 0;
+    CKYBuffer tmp;
+    int length = dataSize;
+    int appendLength = length;
+    int hasPad = 0;
+
+    /* Hack, lie and say we are always doing encipherment */
+    direction = CKY_DIR_DECRYPT;
+    CKYBuffer_Resize(result,0);
+    /*
+     * first set the security environment
+     */
+    ret = P15Applet_ManageSecurityEnvironment(conn, key, direction, 
+			ISO_MSE_SET|ISO_MSE_QUAL_COMPUTE, apduRC);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+
+    CKYBuffer_InitEmpty(&tmp);
+
+    pso.data = &tmp;
+    pso.dir = direction;
+    if (direction == CKY_DIR_DECRYPT) {
+	length++;
+	CKYBuffer_AppendChar(&tmp, 0x00); /* pad byte */
+	hasPad = 1;
+    }
+    if (CKYCardConnection_GetProtocol(conn) == SCARD_PROTOCOL_T0) {
+	ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE);
+	if (ret != CKYSUCCESS) {
+	    goto done;
+	}
+	for(offset = 0; length > CKY_MAX_WRITE_CHUNK_SIZE; 
+					hasPad = 0,
+					offset += CKY_MAX_WRITE_CHUNK_SIZE, 
+					length -= CKY_MAX_WRITE_CHUNK_SIZE) {
+	    pso.chain = 1;
+	    pso.retLen = 0;
+	    CKYBuffer_AppendBuffer(&tmp, data, offset, 
+	     hasPad ? (CKY_MAX_WRITE_CHUNK_SIZE-1) : CKY_MAX_WRITE_CHUNK_SIZE);
+            ret = CKYApplet_HandleAPDU(conn, 
+			P15AppletFactory_PerformSecurityOperation, &pso, NULL, 
+			CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC);
+	    if (ret != CKYSUCCESS) {
+		goto done;
+	    }
+	    CKYBuffer_Resize(&tmp, 0);
+	}
+	appendLength = length;
+    } else {
+	ret = CKYBuffer_Reserve(&tmp, length);
+    }
+    CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength);
+    pso.chain = 0;
+    pso.retLen = dataSize;
+
+    ret = CKYApplet_HandleAPDU(conn, 
+		P15AppletFactory_PerformSecurityOperation, &pso, NULL, 
+	CKY_SIZE_UNKNOWN, CKYAppletFill_ReplaceBuffer, result, apduRC);
+
+done:
+    CKYBuffer_FreeData(&tmp);
+    return ret;
+}
+
+/*
+ * Read Binary
+ */
+CKYStatus
+P15Applet_ReadBinary(CKYCardConnection *conn, unsigned short offset,
+			CKYByte short_ef, CKYByte flags, CKYByte size, 
+			CKYBuffer *data, CKYISOStatus *apduRC)
+{
+    P15AppletArgReadBinary red;
+
+    red.offset = offset;
+    red.short_ef = short_ef;
+    red.flags = flags;
+    red.size = size;
+    return CKYApplet_HandleAPDU(conn, P15AppletFactory_ReadBinary, &red, NULL,
+	CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, data, apduRC);
+}
+
 CKYStatus 
 CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, 
 			CKYSize *nextSize, CKYISOStatus *apduRC)
@@ -1632,6 +1860,7 @@ CKYApplet_ReadObjectFull(CKYCardConnecti
     return ret;
 }
 
+
 /*
  * Write Object
  * This makes multiple APDU calls to write the entire object.
diff -up ./src/libckyapplet/cky_applet.h.p15 ./src/libckyapplet/cky_applet.h
--- ./src/libckyapplet/cky_applet.h.p15	2015-07-06 10:27:55.776827153 -0700
+++ ./src/libckyapplet/cky_applet.h	2015-07-06 10:27:55.788826927 -0700
@@ -204,6 +204,7 @@ typedef struct _CKYAppletArgReadObject {
     CKYByte         size;
 } CKYAppletArgReadObject;
 
+
 typedef struct _CKYAppletArgWriteObject {
     unsigned long objectID;
     CKYOffset     offset;
@@ -262,6 +263,39 @@ typedef struct _PIVAppletRespSignDecrypt
      CKYBuffer  *buf;
 } PIVAppletRespSignDecrypt;
 
+typedef struct _P15AppletArgReadRecord {
+    CKYByte	    record;
+    CKYByte         short_ef;
+    CKYByte         flags;
+    CKYByte         size;
+} P15AppletArgReadRecord;
+
+typedef struct _P15AppletArgReadBinary {
+    unsigned short    offset;
+    CKYByte         short_ef;
+    CKYByte         flags;
+    CKYByte         size;
+} P15AppletArgReadBinary;
+
+typedef struct _P15AppletArgVerifyPIN {
+    const  CKYBuffer *pinVal;
+    CKYByte pinRef;
+} P15AppletArgVerifyPIN;
+
+typedef struct _P15AppletArgManageSecurityEnvironment {
+    CKYByte p1;
+    CKYByte p2;
+    CKYByte keyRef;
+}
+ P15AppletArgManageSecurityEnvironment;
+
+typedef struct _P15AppletArgPerformSecurityOperation {
+    CKYByte dir;
+    int     chain;
+    CKYSize retLen;
+    const CKYBuffer *data;
+} P15AppletArgPerformSecurityOperation;
+
 /* fills in an APDU from a structure -- form of all the generic factories*/
 typedef CKYStatus (*CKYAppletFactory)(CKYAPDU *apdu, const void *param);
 /* fills in an a structure from a response -- form of all the fill structures*/
@@ -514,6 +548,7 @@ CKYStatus CACApplet_ReadFile(CKYCardConn
 CKYStatus CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef,
 			     CKYISOStatus *apduRC);
 
+
 /* must happen with PKI applet selected */
 CKYStatus CACApplet_SignDecrypt(CKYCardConnection *conn, const CKYBuffer *data,
 		CKYBuffer *result, CKYISOStatus *apduRC);
@@ -539,6 +574,26 @@ CKYStatus PIVApplet_SignDecrypt(CKYCardC
 				   unsigned int keySize, int derive,
                                    const CKYBuffer *data, CKYBuffer *result, 
                                    CKYISOStatus *apduRC);
+
+/* PKCS Commands 15 */
+CKYStatus P15Applet_SelectFile(CKYCardConnection *conn, unsigned short ef,
+			     CKYISOStatus *apduRC);
+CKYStatus P15Applet_SelectRootFile(CKYCardConnection *conn, unsigned short ef,
+			     CKYISOStatus *apduRC);
+CKYStatus P15Applet_ReadRecord(CKYCardConnection *conn, CKYByte record, 
+		CKYByte short_ef, CKYByte flags, CKYByte size, CKYBuffer *data, 
+		CKYISOStatus *apduRC);
+CKYStatus P15Applet_ReadBinary(CKYCardConnection *conn, unsigned short offset, 
+		CKYByte short_ef, CKYByte flags, CKYByte size, CKYBuffer *data, 
+		CKYISOStatus *apduRC);
+CKYStatus P15Applet_VerifyPIN(CKYCardConnection *conn, const char *pin, 
+			const P15PinInfo *pinInfo, CKYISOStatus *apduRC);
+
+CKYStatus P15Applet_SignDecrypt(CKYCardConnection *conn, CKYByte key,
+				   unsigned int keySize, CKYByte direction,
+                                   const CKYBuffer *data, CKYBuffer *result, 
+                                   CKYISOStatus *apduRC);
+
 /*
  * There are 3 read commands:
  *  
diff -up ./src/libckyapplet/cky_base.c.p15 ./src/libckyapplet/cky_base.c
--- ./src/libckyapplet/cky_base.c.p15	2015-07-06 10:27:55.776827153 -0700
+++ ./src/libckyapplet/cky_base.c	2015-07-06 10:27:55.788826927 -0700
@@ -651,21 +651,38 @@ CKYStatus
 CKYAPDU_SetSendData(CKYAPDU *apdu, const CKYByte *data, CKYSize len)
 {
     CKYStatus ret;
+    CKYOffset offset = 0;
 
-    if (len > CKYAPDU_MAX_DATA_LEN) {
-	return CKYDATATOOLONG;
-    }
+    /* Encode with T1 if necessary */
 
-    ret = CKYBuffer_Resize(&apdu->apduBuf, len + CKYAPDU_HEADER_LEN);
-    if (ret != CKYSUCCESS) {
-	return ret;
+    if (len < CKYAPDU_MAX_DATA_LEN) {
+	offset = 0;
+        ret = CKYBuffer_Resize(&apdu->apduBuf, len+offset+CKYAPDU_HEADER_LEN);
+	if (ret != CKYSUCCESS ) {
+	    return ret;
+	}
+    	ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) len);
+    } else if (len < CKYAPDU_MAX_T1_DATA_LEN) {
+	offset = 2;
+        ret = CKYBuffer_Resize(&apdu->apduBuf, len+offset+CKYAPDU_HEADER_LEN);
+	if (ret != CKYSUCCESS ) {
+	    return ret;
+	}
+    	ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) 0);
+	if (ret != CKYSUCCESS) {
+	    return ret;
+	}
+        ret = CKYBuffer_SetShort(&apdu->apduBuf,CKY_LC_OFFSET+1,
+							(unsigned short)len);
+    } else {
+	return CKYDATATOOLONG;
     }
-    ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET,
-				len == CKYAPDU_MAX_DATA_LEN ? 0: (CKYByte) len);
+	
     if (ret != CKYSUCCESS) {
 	return ret;
     }
-    return CKYBuffer_Replace(&apdu->apduBuf, CKYAPDU_HEADER_LEN, data, len);
+    return CKYBuffer_Replace(&apdu->apduBuf, 
+				CKYAPDU_HEADER_LEN + offset , data, len);
 }
 
 CKYStatus
@@ -685,15 +702,15 @@ CKYAPDU_AppendSendData(CKYAPDU *apdu, co
     }
 
     dataLen = CKYBuffer_Size(&apdu->apduBuf) + len - CKYAPDU_HEADER_LEN;
-    if (dataLen > CKYAPDU_MAX_DATA_LEN) {
+    /* only handles T0 encoding, not T1 encoding */
+    if (dataLen >= CKYAPDU_MAX_DATA_LEN) {
 	return CKYDATATOOLONG;
     }
     ret = CKYBuffer_AppendData(&apdu->apduBuf, data, len);
     if (ret != CKYSUCCESS) {
 	return ret;
     }
-    return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET,
-			dataLen == CKYAPDU_MAX_DATA_LEN ? 0 : (CKYByte) dataLen);
+    return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) dataLen);
 }
 
 CKYStatus
@@ -714,11 +731,100 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY
 }
 
 CKYStatus
+CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen)
+{
+    CKYStatus ret;
+
+    if (recvlen <= CKYAPDU_MAX_DATA_LEN) {
+	return APDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff));
+    }
+    ret = CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_HEADER_LEN+2);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, 0);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    return CKYBuffer_SetShort(&apdu->apduBuf, CKY_LE_OFFSET+1, recvlen);
+}
+
+CKYStatus
+CKYAPDU_SetReceiveLength(CKYAPDU *apdu, CKYSize recvlen)
+{
+    if (recvlen <= CKYAPDU_MAX_T1_DATA_LEN) {
+	return CKYAPDU_SetShortReceiveLen(apdu, (unsigned short) 
+						(recvlen & 0xffff));
+    }
+    return CKYDATATOOLONG;
+}
+
+/*
+ *  Append Le, If Le=0, treat it as 256 (CKYAPD_MAX_DATA_LEN)
+ */
+CKYStatus
 CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen)
 {
+    /* If we already have a data buffer, make sure that we aren't already
+     * using T1 encoding */
+    if (CKYBuffer_Size(&apdu->apduBuf) > CKYAPDU_MIN_LEN) {
+	if (CKYBuffer_GetChar(&apdu->apduBuf, CKY_LC_OFFSET) == 0) {
+	    /* we are using T1 encoding, use AppendShort*/
+	    return CKYBuffer_AppendShort(&apdu->apduBuf, 
+		recvlen ? (unsigned short) recvlen: CKYAPDU_MAX_DATA_LEN);
+	}
+    }
     return CKYBuffer_AppendChar(&apdu->apduBuf, recvlen);
 }
 
+/*
+ * Append a short Le. If Le be encoded with just T0, do so. If Le=0 treat
+ * it as 65536 (CKYAPDU_MAX_T1_DATA_LEN)
+ */
+CKYStatus
+CKYAPDU_AppendShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen)
+{
+    CKYStatus ret;
+    /* If we already have a data buffer, it's encoding affects ours */
+    if (CKYBuffer_Size(&apdu->apduBuf) > CKYAPDU_MIN_LEN) {
+	/* CKY_LC_OFFSET == 0 means T1, otherwise it's T0 */
+	if (CKYBuffer_GetChar(&apdu->apduBuf, CKY_LC_OFFSET) != 0) {
+	    /* remember 0 is 65536 here */
+	    if ((recvlen == 0) || (recvlen > CKYAPDU_MAX_DATA_LEN)) {
+		/* we can't a encode T1 receive length if we already have a
+ 		 * T0 encoded buffer data */
+		return CKYDATATOOLONG;
+	    }
+	    /* T0 encoding */
+	    return CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)recvlen&0xff);
+	}
+	/* T1 encoding */
+	return CKYBuffer_AppendShort(&apdu->apduBuf, recvlen);
+    }
+    /* if length fits in a bit and we aren't forced into T1 encoding, use
+     * T0 */
+    if ((recvlen != 0) && (recvlen <= CKYAPDU_MAX_DATA_LEN)) {
+	return CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)recvlen&0xff);
+    }
+    /* write the T1 encoding marker */
+    ret = CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)0);
+    if (ret != CKYSUCCESS) {
+	return ret;
+    }
+    /* T1 encoded length */
+    return CKYBuffer_AppendShort(&apdu->apduBuf, recvlen);
+}
+
+CKYStatus
+CKYAPDU_AppendReceiveLength(CKYAPDU *apdu, CKYSize recvlen)
+{
+    if (recvlen > CKYAPDU_MAX_T1_DATA_LEN) {
+	return CKYDATATOOLONG;
+    }
+    return CKYAPDU_AppendShortReceiveLen(apdu, 
+					(unsigned short)(recvlen & 0xffff));
+}
+
 
 void
 CKY_SetName(const char *p)
diff -up ./src/libckyapplet/cky_base.h.p15 ./src/libckyapplet/cky_base.h
--- ./src/libckyapplet/cky_base.h.p15	2015-07-06 10:27:55.777827135 -0700
+++ ./src/libckyapplet/cky_base.h	2015-07-06 10:27:55.789826908 -0700
@@ -32,6 +32,8 @@ typedef unsigned char CKYByte;
 /* Bool type */
 typedef unsigned char CKYBool;
 
+typedef unsigned long CKYBitFlags;
+
 #define CKYBUFFER_PUBLIC \
     unsigned long reserved1;\
     unsigned long reserved2;\
@@ -93,6 +95,8 @@ typedef enum {
 			 * (command) sent. ADPUIOStatus has more info on
 			 * why the APDU failed */
     CKYINVALIDARGS,      /* Caller passed in bad args */
+    CKYINVALIDDATA,	/* Data supplied was invalid */
+    CKYUNSUPPORTED,	/* Requested Operation or feature is not supported */
 } CKYStatus;
 
 /*
@@ -107,12 +111,56 @@ typedef enum {
 #define CKY_LE_OFFSET	4
 
 #define CKYAPDU_MAX_DATA_LEN	256
+#define CKYAPDU_MAX_T1_DATA_LEN	65536
 #define CKYAPDU_MIN_LEN		4
 #define CKYAPDU_HEADER_LEN	5
 #define CKYAPDU_MAX_LEN		(CKYAPDU_HEADER_LEN+CKYAPDU_MAX_DATA_LEN)
 #define CKY_MAX_ATR_LEN		32
 #define CKY_OUTRAGEOUS_MALLOC_SIZE (1024*1024)
 
+#define P15FlagsPrivate           0x00000001
+#define P15FlagsModifiable        0x00000002
+
+#define P15UsageEncrypt           0x00000001
+#define P15UsageDecrypt           0x00000002
+#define P15UsageSign              0x00000004
+#define P15UsageSignRecover       0x00000008
+#define P15UsageWrap              0x00000010
+#define P15UsageUnwrap            0x00000020
+#define P15UsageVerify            0x00000040
+#define P15UsageVerifyRecover     0x00000080
+#define P15UsageDerive            0x00000100
+#define P15UsageNonRepudiation    0x00000200
+
+#define P15AccessSensitive        0x00000001
+#define P15AccessExtractable      0x00000002
+#define P15AccessAlwaysSenstive   0x00000004
+#define P15AccessNeverExtractable 0x00000008
+#define P15AccessLocal            0x00000010
+
+#define P15PinCaseSensitive       0x00000001
+#define P15PinLocal               0x00000002
+#define P15PinChangeDisabled      0x00000004
+#define P15PinUnblockDisabled     0x00000008
+#define P15PinInitialized         0x00000010
+#define P15PinNeedsPadding        0x00000020
+#define P15PinUnblockingPin       0x00000040
+#define P15PinSOPin               0x00000080
+#define P15PinDisableAllowed      0x00000100
+
+typedef enum {P15PinBCD=0, P15PinASCIINum=1, P15PinUTF8=2} P15PinType;
+
+typedef struct _P15PinInfo {
+    CKYBitFlags   pinFlags;
+    P15PinType    pinType;
+    CKYByte       minLength;
+    CKYByte       storedLength;
+    unsigned long maxLength;
+    CKYByte       pinRef;
+    CKYByte       padChar;
+} P15PinInfo;
+     
+
 /*
  * allow direct inclusion in C++ files 
  */
@@ -278,7 +326,11 @@ CKYStatus CKYAPDU_AppendSendDataBuffer(C
 /* set Le in the APDU header to the amount of bytes expected to be
  * returned. */
 CKYStatus CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKYByte recvlen);
+CKYStatus CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen);
+CKYStatus CKYAPDU_SetReceiveLength(CKYAPDU *apdu, CKYSize recvlen);
 CKYStatus CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen);
+CKYStatus CKYAPDU_AppendShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen);
+CKYStatus CKYAPDU_AppendReceiveLength(CKYAPDU *apdu, CKYSize recvlen);
 
 /* set the parent loadmodule name */
 void CKY_SetName(const char *name);
diff -up ./src/libckyapplet/cky_card.c.p15 ./src/libckyapplet/cky_card.c
--- ./src/libckyapplet/cky_card.c.p15	2015-07-06 10:27:55.778827116 -0700
+++ ./src/libckyapplet/cky_card.c	2015-07-06 10:27:55.790826889 -0700
@@ -910,6 +910,7 @@ ckyCardConnection_init(CKYCardConnection
     conn->protocol = SCARD_PROTOCOL_T0;
 }
 
+
 CKYCardConnection *
 CKYCardConnection_Create(const CKYCardContext *ctx)
 {
@@ -984,6 +985,12 @@ CKYCardConnection_IsConnected(const CKYC
     return (conn->cardHandle != 0);
 }
 
+unsigned long
+CKYCardConnection_GetProtocol(const CKYCardConnection *conn)
+{
+    return conn->protocol;
+}
+
 CKYStatus 
 ckyCardConnection_reconnectRaw(CKYCardConnection *conn, unsigned long init)
 {
@@ -996,6 +1003,7 @@ ckyCardConnection_reconnectRaw(CKYCardCo
 	conn->lastError = rv;
 	return CKYSCARDERR;
     }
+    conn->protocol = protocol;
     return CKYSUCCESS;
 }
 
diff -up ./src/libckyapplet/cky_card.h.p15 ./src/libckyapplet/cky_card.h
--- ./src/libckyapplet/cky_card.h.p15	2015-07-06 10:27:55.766827342 -0700
+++ ./src/libckyapplet/cky_card.h	2015-07-06 10:27:55.790826889 -0700
@@ -116,6 +116,7 @@ CKYStatus CKYCardConnection_ExchangeAPDU
 CKYStatus CKYCardConnection_Connect(CKYCardConnection *connection, 
 					const char *readerName);
 CKYStatus CKYCardConnection_Disconnect(CKYCardConnection *connection);
+unsigned long CKYCardConnection_GetProtocol(const CKYCardConnection *conn);
 CKYBool CKYCardConnection_IsConnected(const CKYCardConnection *connection);
 CKYStatus CKYCardConnection_Reconnect(CKYCardConnection *connection);
 CKYStatus CKYCardConnection_GetStatus(CKYCardConnection *connection,
diff -up ./src/libckyapplet/cky_factory.c.p15 ./src/libckyapplet/cky_factory.c
--- ./src/libckyapplet/cky_factory.c.p15	2015-07-06 10:27:55.778827116 -0700
+++ ./src/libckyapplet/cky_factory.c	2015-07-06 10:27:55.791826870 -0700
@@ -29,7 +29,7 @@ CKYAPDUFactory_SelectFile(CKYAPDU *apdu,
 			  const CKYBuffer *AID)
 {
     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
-    CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE);
+    CKYAPDU_SetINS(apdu, ISO_INS_SELECT_FILE);
     CKYAPDU_SetP1(apdu, p1);
     CKYAPDU_SetP2(apdu, p2);
     return CKYAPDU_SetSendDataBuffer(apdu, AID);
@@ -40,7 +40,7 @@ CKYAPDUFactory_SelectCardManager(CKYAPDU
 {
     CKYByte c = 0;
     CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
-    CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE);
+    CKYAPDU_SetINS(apdu, ISO_INS_SELECT_FILE);
     CKYAPDU_SetP1(apdu, 0x04);
     CKYAPDU_SetP2(apdu, 0x00);
     /* I can't find the documentation for this, but if you pass an empty
@@ -57,7 +57,7 @@ CKYStatus
 CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu)
 {
     CKYAPDU_SetCLA(apdu, CKY_CLASS_GLOBAL_PLATFORM);
-    CKYAPDU_SetINS(apdu, CKY_INS_GET_DATA);
+    CKYAPDU_SetINS(apdu, ISO_INS_GET_DATA);
     CKYAPDU_SetP1(apdu, 0x9f);
     CKYAPDU_SetP2(apdu, 0x7f);
     return CKYAPDU_SetReceiveLen(apdu, CKY_SIZE_GET_CPLCDATA);
@@ -707,6 +707,7 @@ fail:
     CKYBuffer_FreeData(&buf);
     return ret;
 }
+
 CKYStatus
 CACAPDUFactory_GetProperties(CKYAPDU *apdu)
 {
@@ -718,37 +719,6 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap
 }
 
 CKYStatus
-CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const char *pin)
-{
-    CKYStatus ret;
-    CKYSize size;
-
-    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
-    CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN);
-    CKYAPDU_SetP1(apdu, 0x00);
-    CKYAPDU_SetP2(apdu, keyRef);
-    /* no pin, send an empty buffer */
-    if (!pin) {
-    	return CKYAPDU_SetReceiveLen(apdu, 0);
-    }
-
-    /* all CAC pins are 8 bytes exactly. If to long, truncate it */
-    size = strlen(pin);
-    if (size > 8) {
-	size = 8;
-    }
-    ret = CKYAPDU_SetSendData(apdu, (unsigned char *) pin, size);
-    /* if too short, pad it */
-    if ((ret == CKYSUCCESS) && (size < 8)) {
-	static const unsigned char pad[]= { 0xff , 0xff, 0xff ,0xff, 
-				   0xff, 0xff, 0xff, 0xff };
-	return CKYAPDU_AppendSendData(apdu, pad, 8-size);
-    }
-    return ret;
-
-}
-
-CKYStatus
 PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, 
 			   CKYByte key, int len, const CKYBuffer *data)
 {
@@ -807,3 +777,109 @@ fail:
     return ret;
 }
 
+CKYStatus
+P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const CKYBuffer *pin)
+{
+    CKYStatus ret;
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN);
+    CKYAPDU_SetP1(apdu, 0x00);
+    CKYAPDU_SetP2(apdu, keyRef);
+    /* no pin, send an empty buffer */
+    if (CKYBuffer_Size(pin) == 0) {
+    	return CKYAPDU_SetReceiveLen(apdu, 0);
+    }
+
+    /* all CAC pins are 8 bytes exactly. If to long, truncate it */
+    ret = CKYAPDU_SetSendDataBuffer(apdu, pin);
+    return ret;
+
+}
+
+CKYStatus
+P15APDUFactory_ReadRecord(CKYAPDU *apdu, CKYByte record, CKYByte short_ef, 
+					CKYByte flags, CKYByte count)
+{
+    CKYByte control;
+
+    control = (short_ef << 3) & 0xf8;
+    control |= flags & 0x07;
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_READ_RECORD);
+    CKYAPDU_SetP1(apdu, record);
+    CKYAPDU_SetP2(apdu, control);
+    return CKYAPDU_SetReceiveLen(apdu, count);
+}
+
+CKYStatus
+P15APDUFactory_ReadBinary(CKYAPDU *apdu, unsigned short offset, 
+			CKYByte short_ef, CKYByte flags, CKYByte count)
+{
+    CKYByte p1 = 0,p2 = 0;
+    unsigned short max_offset = 0;
+
+    if (flags & P15_USE_SHORT_EF) {
+	max_offset = 0xff;
+	p1 = P15_USE_SHORT_EF | (short_ef & 0x7);
+	p2 = offset & 0xff;
+    } else {
+	max_offset = 0x7fff;
+	p1 = (offset >> 8) & 0x7f;
+	p2 = offset & 0xff;
+    }
+    if (offset > max_offset) {
+	return CKYINVALIDARGS;
+    }
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_READ_BINARY);
+    CKYAPDU_SetP1(apdu, p1);
+    CKYAPDU_SetP2(apdu, p2);
+    return CKYAPDU_SetReceiveLen(apdu, count);
+}
+
+CKYStatus
+P15APDUFactory_ManageSecurityEnvironment(CKYAPDU *apdu, CKYByte p1, CKYByte p2,
+					CKYByte keyRef)
+{
+    CKYByte param[3];
+
+    CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_MANAGE_SECURITY_ENVIRONMENT);
+    CKYAPDU_SetP1(apdu, p1);
+    CKYAPDU_SetP2(apdu, p2);
+    param[0] = 0x83;
+    param[1] = 1;
+    param[2] = keyRef;
+    return CKYAPDU_SetSendData(apdu, param, sizeof param);
+}
+
+CKYStatus
+P15APDUFactory_PerformSecurityOperation(CKYAPDU *apdu, CKYByte dir,
+			int chain, CKYSize retLen, const CKYBuffer *data)
+{
+    CKYByte p1,p2;
+    CKYStatus ret;
+
+    CKYAPDU_SetCLA(apdu, chain ? CKY_CLASS_ISO7816_CHAIN :
+				  CKY_CLASS_ISO7816);
+    CKYAPDU_SetINS(apdu, ISO_INS_PERFORM_SECURITY_OPERATION);
+    if (dir == CKY_DIR_DECRYPT) {
+	p1 = ISO_PSO_DECRYPT_P1;
+	p2 = ISO_PSO_DECRYPT_P2;
+    } else {
+	p1 = ISO_PSO_SIGN_P1;
+	p2 = ISO_PSO_SIGN_P2;
+    }
+    CKYAPDU_SetP1(apdu, p1);
+    CKYAPDU_SetP2(apdu, p2);
+    ret =  CKYAPDU_SetSendDataBuffer(apdu, data);
+    if (ret == CKYSUCCESS && (chain == 0) && retLen != 0) {
+	ret = CKYAPDU_AppendReceiveLength(apdu, retLen);
+    }
+    return ret;
+}
+
+
diff -up ./src/libckyapplet/cky_factory.h.p15 ./src/libckyapplet/cky_factory.h
--- ./src/libckyapplet/cky_factory.h.p15	2015-07-06 10:27:55.779827097 -0700
+++ ./src/libckyapplet/cky_factory.h	2015-07-06 10:27:55.791826870 -0700
@@ -35,8 +35,31 @@
  * Applet Instruction Bytes
  */
 /* Card Manager */
-#define CKY_INS_SELECT_FILE	0xa4
-#define CKY_INS_GET_DATA 	0xca
+#define ISO_INS_SELECT_FILE	0xa4
+#define ISO_INS_GET_DATA 	0xca
+#define ISO_INS_READ_BINARY 	0xb0
+#define ISO_INS_READ_RECORD 	0xb2
+#define ISO_INS_MANAGE_SECURITY_ENVIRONMENT 0x22
+#define ISO_INS_PERFORM_SECURITY_OPERATION 0x2a
+
+/* ISO Parameters: */
+#define ISO_LOGIN_LOCAL		0x80
+#define ISO_LOGIN_GLOBAL	0x00
+#define ISO_MSE_SET		0x01
+#define ISO_MSE_STORE		0xf2
+#define ISO_MSE_RESTORE		0xf3
+#define ISO_MSE_ERASE		0xf4
+#define ISO_MSE_QUAL_VERIFY	0x80
+#define ISO_MSE_QUAL_COMPUTE	0x40
+#define ISO_MSE_AUTH		0xa4
+#define ISO_MSE_SIGN		0xb6
+#define ISO_MSE_KEA		0xb8
+#define ISO_PSO_SIGN_P1		0x9e
+#define ISO_PSO_SIGN_P2		0x9a
+#define ISO_PSO_ENCRYPT_P1	0x86
+#define ISO_PSO_ENCRYPT_P2	0x80
+#define ISO_PSO_DECRYPT_P1	0x80
+#define ISO_PSO_DECRYPT_P2	0x86
 
 /* deprecated */
 #define CKY_INS_SETUP    	0x2A
@@ -84,6 +107,7 @@
 #define CKY_INS_SEC_READ_IOBUF	0x08
 #define CKY_INS_SEC_START_ENROLLMENT	0x0C
 
+
 /* CAC */
 #define CAC_INS_GET_CERTIFICATE 0x36
 #define CAC_INS_SIGN_DECRYPT	0x42
@@ -94,11 +118,8 @@
 #define CAC_SIZE_GET_PROPERTIES	48
 #define CAC_P1_STEP		0x80
 #define CAC_P1_FINAL		0x00
-#define CAC_LOGIN_GLOBAL	0x00
 
 /* PIV */
-#define PIV_LOGIN_LOCAL		0x80
-#define PIV_LOGIN_GLOBAL	CAC_LOGIN_GLOBAL
 #define PIV_INS_GEN_AUTHENTICATE 0x87
 
 /*
@@ -121,7 +142,7 @@
 /* functions */
 #define CKY_CIPHER_INIT		1
 #define CKY_CIPHER_PROCESS	2
-#define CKY_CIPHER_FINAL		3
+#define CKY_CIPHER_FINAL	3
 #define CKY_CIPHER_ONE_STEP	4  /* init and final in one APDU */
 
 /* modes */
@@ -173,6 +194,18 @@
 #define CKY_CARDM_MANAGER_LOCKED          0x7f
 #define CKY_CARDM_MANAGER_TERMINATED      0xff
 
+/* Read Record Flags */
+#define P15_READ_P1          0x4
+#define P15_READ_P1_TO_LAST  0x5
+#define P15_READ_LAST_TO_P1  0x6
+#define P15_READ_FIRST       0x0
+#define P15_READ_LAST        0x1
+#define P15_READ_NEXT        0x2
+#define P15_READ_PREV        0x3
+
+/* Read Binary Flags */
+#define P15_USE_SHORT_EF    0x80
+
 /*
  * The following factories 'Fill in' APDUs for each of the
  * functions described below. Nonces are not automatically added.
@@ -234,17 +267,28 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C
 
 CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, 
 				     const CKYBuffer *data);
-CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef,
-				   const char *pin);
 CKYStatus CACAPDUFactory_GetCertificate(CKYAPDU *apdu, CKYSize size);
 CKYStatus CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, 
 				  CKYByte type, CKYByte count);
 CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu);
+
 CKYStatus PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, 
 				CKYByte count);
 CKYStatus PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, 
                            CKYByte key, int len, const CKYBuffer *data);
 
+CKYStatus P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef,
+			   const CKYBuffer *pin);
+CKYStatus P15APDUFactory_ReadRecord(CKYAPDU *apdu, CKYByte record, 
+			   CKYByte short_ef, CKYByte flags, CKYByte count);
+CKYStatus P15APDUFactory_ReadBinary(CKYAPDU *apdu, unsigned short offset, 
+			   CKYByte short_ef, CKYByte flags, CKYByte count);
+CKYStatus P15APDUFactory_ManageSecurityEnvironment(CKYAPDU *apdu, 
+			   CKYByte p1, CKYByte p2, CKYByte key);
+CKYStatus P15APDUFactory_PerformSecurityOperation(CKYAPDU *apdu, CKYByte dir,
+			   int chain, CKYSize retLen, const CKYBuffer *data);
+
+
 CKY_END_PROTOS
 
 #endif /* CKY_FACTORY_H */