From a2763b048b8382dc091f7ac8c96b7a05a481199e Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 16 2018 12:48:42 +0000 Subject: import coolkey-1.1.0-37.51.el7_5 --- diff --git a/.coolkey.metadata b/.coolkey.metadata new file mode 100644 index 0000000..9c8cdc5 --- /dev/null +++ b/.coolkey.metadata @@ -0,0 +1 @@ +54136decf9dfd091c8b231cb77dac97db95e1866 SOURCES/coolkey-1.1.0.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f56700c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/coolkey-1.1.0.tar.gz diff --git a/README.md b/README.md deleted file mode 100644 index 0e7897f..0000000 --- a/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 - -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/coolkey-1.1.0-alt-tokens-2.patch b/SOURCES/coolkey-1.1.0-alt-tokens-2.patch new file mode 100644 index 0000000..de7c8c1 --- /dev/null +++ b/SOURCES/coolkey-1.1.0-alt-tokens-2.patch @@ -0,0 +1,141 @@ +diff -up ./src/coolkey/slot.cpp.alt-tokens-2 ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.alt-tokens-2 2018-06-25 17:58:23.472185284 -0700 ++++ ./src/coolkey/slot.cpp 2018-06-25 18:02:29.714918126 -0700 +@@ -415,8 +415,9 @@ Slot::Slot(const char *readerName_, Log + tokenManufacturer(NULL), + slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), + isVersion1Key(false), needLogin(false), fullTokenName(false), +- mCoolkey(false), mOldCAC(false), mCACLocalLogin(false), +- pivContainer(-1), pivKey(-1), maxCacCerts(MAX_CERT_SLOTS), ++ mCoolkey(false), mOldCAC(false), mCACLocalLogin(false), mCAC_ACA(false), ++ pivContainer(-1), pivKey(-1), ++ minCacCerts(0), maxCacCerts(MAX_CERT_SLOTS), + algs(ALG_NONE), p15aid(0), p15odfAddr(0), p15tokenInfoAddr(0), + p15Instance(0), + #ifdef USE_SHMEM +@@ -782,9 +783,11 @@ Slot::connectToToken() + state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; + isVersion1Key = 0; + needLogin = true; ++ minCacCerts = 0; + maxCacCerts = MAX_CERT_SLOTS; + mCoolkey = 0; + mOldCAC = 0; ++ mCAC_ACA = 0; + mCACLocalLogin = getPIVLoginType(); + return; + } +@@ -924,23 +927,29 @@ Slot::getCACAid() + CKYBuffer_Resize(&cardAID[i],0); + } + ++ mCAC_ACA=false; + status = CACApplet_SelectCCC(conn,NULL); + if (status != CKYSUCCESS) { + /* are we an old CAC */ +- status = CACApplet_SelectPKI(conn, &cardAID[0], 0, NULL); +- if (status != CKYSUCCESS) { +- /* no, just fail */ +- return status; +- } +- /* yes, fill in the old applets */ +- mOldCAC = true; +- maxCacCerts = 1; +- for (i=1; i< MAX_CERT_SLOTS; i++) { ++ maxCacCerts = 0; ++ minCacCerts = -1; ++ status = CACApplet_SelectACA(conn,NULL); ++ if (status == CKYSUCCESS) { ++ mCAC_ACA = true; ++ } ++ for (i=0; i< MAX_CERT_SLOTS; i++) { + status = CACApplet_SelectPKI(conn, &cardAID[i], i, NULL); + if (status == CKYSUCCESS) { ++ if (minCacCerts == -1) { ++ minCacCerts = i; ++ } + maxCacCerts = i+1; + } + } ++ if (minCacCerts == -1) { ++ return status; ++ } ++ mOldCAC = true; + return CKYSUCCESS; + } + /* definately not an old CAC */ +@@ -997,6 +1006,7 @@ Slot::getCACAid() + if (certSlot == 0) { + status = CKYAPDUFAIL; /* probably neeed a beter error code */ + } ++ minCacCerts = 0; + maxCacCerts = certSlot; + + done: +@@ -3840,7 +3850,16 @@ Slot::login(SessionHandleSuffix handleSu + if(status != CKYSUCCESS ) handleConnectionError(); + + if (state & GOV_CARD) { +- selectCACApplet(0, true); ++ if (mCAC_ACA) { ++ status = CACApplet_SelectACA(conn,NULL); ++ if ( status == CKYSCARDERR ) handleConnectionError(); ++ if ( status != CKYSUCCESS) { ++ disconnect(); ++ throw PKCS11Exception(CKR_DEVICE_REMOVED); ++ } ++ } else { ++ selectCACApplet(minCacCerts, true); ++ } + } else if ((state & P15_CARD)== 0) { + /* p15 does the select in attemptLogin */ + selectApplet(); +diff -up ./src/coolkey/slot.h.alt-tokens-2 ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.alt-tokens-2 2018-06-25 17:58:23.473185283 -0700 ++++ ./src/coolkey/slot.h 2018-06-25 17:58:23.475185280 -0700 +@@ -356,8 +356,10 @@ class Slot { + bool mCoolkey; + bool mOldCAC; + bool mCACLocalLogin; ++ bool mCAC_ACA; + int pivContainer; + int pivKey; ++ int minCacCerts; + int maxCacCerts; + SlotAlgs algs; + unsigned short p15aid; +diff -up ./src/libckyapplet/cky_applet.c.alt-tokens-2 ./src/libckyapplet/cky_applet.c +--- ./src/libckyapplet/cky_applet.c.alt-tokens-2 2018-06-25 17:58:23.473185283 -0700 ++++ ./src/libckyapplet/cky_applet.c 2018-06-25 17:58:23.475185280 -0700 +@@ -626,6 +626,19 @@ CACApplet_SelectCCC(CKYCardConnection *c + return ret; + } + ++static CKYByte cacACAid[] = {0xa0, 0x00, 0x00, 0x00, 0x79, 0x10, 0x00 }; ++CKYStatus ++CACApplet_SelectACA(CKYCardConnection *conn, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYBuffer CAC_CM_AID; ++ CKYBuffer_InitFromData(&CAC_CM_AID, cacACAid, sizeof(cacACAid)); ++ ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID, ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++ CKYBuffer_FreeData(&CAC_CM_AID); ++ return ret; ++} ++ + CKYStatus + CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef, + CKYISOStatus *apduRC) +diff -up ./src/libckyapplet/cky_applet.h.alt-tokens-2 ./src/libckyapplet/cky_applet.h +--- ./src/libckyapplet/cky_applet.h.alt-tokens-2 2018-06-25 17:58:23.457185300 -0700 ++++ ./src/libckyapplet/cky_applet.h 2018-06-25 17:58:23.475185280 -0700 +@@ -539,6 +539,8 @@ CKYStatus CACApplet_SelectCardManager(CK + CKYISOStatus *apduRC); + /* Select the CAC CC container. Can happen with either applet selected */ + CKYStatus CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC); ++/* Select the CAC ACA container. Can happen with either applet selected */ ++CKYStatus CACApplet_SelectACA(CKYCardConnection *conn, CKYISOStatus *apduRC); + /* Select an old CAC applet and fill in the cardAID */ + CKYStatus CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cardAid, + CKYByte instance, CKYISOStatus *apduRC); diff --git a/SOURCES/coolkey-1.1.0-cardos-5-3.patch b/SOURCES/coolkey-1.1.0-cardos-5-3.patch new file mode 100644 index 0000000..5f570ea --- /dev/null +++ b/SOURCES/coolkey-1.1.0-cardos-5-3.patch @@ -0,0 +1,324 @@ +diff -up ./src/coolkey/object.cpp.cardos-5-3 ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.cardos-5-3 2017-03-16 17:14:02.415338726 -0700 ++++ ./src/coolkey/object.cpp 2017-03-16 17:14:02.419338794 -0700 +@@ -32,7 +32,7 @@ const CKYByte eccOID[] = {0x2a,0x86,0x48 + void dump(const char *label, const CKYBuffer *buf) + { + CKYSize i; +- CKYSize size = CKYBuffer_Size(buf); ++ CKYSize size = buf ? CKYBuffer_Size(buf) : 0; + #define ROW_LENGTH 60 + char string[ROW_LENGTH+1]; + char *bp = &string[0]; +diff -up ./src/coolkey/object.h.cardos-5-3 ./src/coolkey/object.h +--- ./src/coolkey/object.h.cardos-5-3 2017-03-16 17:14:02.415338726 -0700 ++++ ./src/coolkey/object.h 2017-03-16 17:14:02.419338794 -0700 +@@ -200,9 +200,11 @@ class PKCS11Object { + CK_USER_TYPE getUser(void) const { return user; } + void setKeyType(KeyType theType) { keyType = theType; } + void setKeySize(unsigned int keySize_) { keySize = keySize_; } ++ void setUser(CK_USER_TYPE user_) { user = user_; } + const CKYBuffer *getAuthId(void) const { return &authId; } + const CKYBuffer *getPinAuthId(void) const { return &pinAuthId; } + const PK15ObjectPath &getObjectPath() const { return objectPath; } ++ void setObjectPath(const PK15ObjectPath &newPath) { objectPath = newPath; } + void completeKey(const PKCS11Object &cert); + }; + +@@ -308,6 +310,7 @@ class PK15Object : public PKCS11Object { + bool isLocal(void) const { return + (pinInfo.pinFlags & P15PinLocal) ? true : false; } + const P15PinInfo *getPinInfo(void) const { return &pinInfo; } ++ void setPinRef(CK_BYTE pinRef) { pinInfo.pinRef = pinRef; } + }; + + class Reader : public PKCS11Object { +diff -up ./src/coolkey/slot.cpp.cardos-5-3 ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.cardos-5-3 2017-03-16 17:14:02.416338743 -0700 ++++ ./src/coolkey/slot.cpp 2017-03-17 13:48:26.661205327 -0700 +@@ -41,6 +41,7 @@ + #define PRINTF(args) + #endif + // #define DISPLAY_WHOLE_GET_DATA 1 ++void dump(const char *label, const CKYBuffer *buf); + + + // The Cyberflex Access 32k egate ATR +@@ -467,6 +468,8 @@ Slot::Slot(const char *readerName_, Log + } + CKYBuffer_InitEmpty(&cardATR); + CKYBuffer_InitEmpty(&mCUID); ++ CKYBuffer_InitEmpty(&candidateUserAuthId); ++ CKYBuffer_InitEmpty(&candidateContextSpecificAuthId); + for (int i=0; i < MAX_CERT_SLOTS; i++) { + CKYBuffer_InitEmpty(&cardAID[i]); + } +@@ -540,6 +543,8 @@ Slot::~Slot() + CKYBuffer_FreeData(&nonce); + CKYBuffer_FreeData(&cardATR); + CKYBuffer_FreeData(&mCUID); ++ CKYBuffer_FreeData(&candidateUserAuthId); ++ CKYBuffer_FreeData(&candidateContextSpecificAuthId); + CKYBuffer_FreeData(&p15AID); + CKYBuffer_FreeData(&p15odf); + CKYBuffer_FreeData(&p15tokenInfo); +@@ -1272,6 +1277,41 @@ class ObjectKeyCKAIDMatch { + } + }; + ++#ifdef DEBUG ++void ++dumpPin(const char *label, const PK15Object *pin) ++{ ++ const P15PinInfo *pinInfo = pin->getPinInfo(); ++ const PK15ObjectPath &pinPath=pin->getObjectPath(); ++ unsigned int pin_type = (unsigned int) pinInfo->pinType; ++ const char *pin_name[3] = {"BCD", "ASCIINum", "UTF8" }; ++ ++ printf("Pin Object %s\n",label); ++ printf(" Pin flags=0x%08lx\n",pinInfo->pinFlags); ++ printf(" Pin type=%d (%s)\n",(int) pin_type, ++ pin_type < 3U ? pin_name[pin_type] : ++ "Invalid"); ++ printf(" Pin length= %d (%d - %d)\n",(int)pinInfo->storedLength, ++ (int)pinInfo->minLength, ++ (int)pinInfo->maxLength); ++ printf(" Pin pad = 0x%02x,<%c>\n", pinInfo->padChar, pinInfo->padChar); ++ printf(" Pin Ref = 0x%02x\n", pinInfo->pinRef); ++ printf(" Pin Path index = %ld\n",(long)pinPath.getIndex()); ++ printf(" Pin Path size = %ld\n",(long)pinPath.getLength()); ++ dump(" Pin Path:",pinPath.getPath()); ++} ++ ++void ++dumpPath(const char *label, const PK15ObjectPath &path) ++{ ++ printf(" Path for %s\n", label); ++ printf(" index = %ld\n",(long)path.getIndex()); ++ printf(" size = %ld\n",(long)path.getLength()); ++ dump(" objPath:",path.getPath()); ++} ++#endif ++ ++ + CKYStatus + Slot::parseEF_Directory(const CKYByte *current, + CKYSize size, PK15ObjectType type) +@@ -1326,7 +1366,22 @@ Slot::parseEF_Directory(const CKYByte *c + auth[CKU_SO] = new PK15Object(obj); + } + } else if (auth[CKU_USER] == NULL) { ++ const CKYBuffer *authid = obj.getPinAuthId(); + auth[CKU_USER] = new PK15Object(obj); ++ if ((CKYBuffer_Size(&candidateUserAuthId) != 0) ++ && !CKYBuffer_IsEqual(authid, &candidateUserAuthId)) { ++ /* validate our candidates */ ++ if ((CKYBuffer_Size(&candidateContextSpecificAuthId) ++ == 0) || (CKYBuffer_IsEqual( ++ &candidateContextSpecificAuthId, authid))) { ++ CKYBuffer_Replace(&candidateContextSpecificAuthId,0, ++ CKYBuffer_Data(&candidateUserAuthId), ++ CKYBuffer_Size(&candidateUserAuthId)); ++ } ++ CKYBuffer_Replace(&candidateUserAuthId, 0, ++ CKYBuffer_Data(authid), CKYBuffer_Size(authid)); ++ } ++ + } else if (auth[CKU_CONTEXT_SPECIFIC] == NULL) { + ObjectIter iter; + const CKYBuffer *authid = obj.getPinAuthId(); +@@ -1339,6 +1394,8 @@ Slot::parseEF_Directory(const CKYByte *c + if( CKYBuffer_IsEqual(iter->getAuthId(),authid)) { + iter->setAttributeBool(CKA_ALWAYS_AUTHENTICATE, + TRUE); ++ iter->setUser(CKU_CONTEXT_SPECIFIC); ++printf("Setting Context Specific pin on key\n"); + } + } + } +@@ -1349,7 +1406,19 @@ Slot::parseEF_Directory(const CKYByte *c + { + ObjectConstIter iter; + const CKYBuffer *id; ++ const CKYBuffer *authid; + ++ authid = obj.getAuthId(); ++ if (authid) { ++ if (CKYBuffer_Size(&candidateUserAuthId) == 0) { ++ CKYBuffer_Replace(&candidateUserAuthId, 0, ++ CKYBuffer_Data(authid), CKYBuffer_Size(authid)); ++ } else if (!CKYBuffer_IsEqual(&candidateUserAuthId, ++ authid)) { ++ CKYBuffer_Replace(&candidateContextSpecificAuthId,0, ++ CKYBuffer_Data(authid), CKYBuffer_Size(authid)); ++ } ++ } + id = obj.getAttribute(CKA_ID); + if ((!id) || (CKYBuffer_Size(id) != 1)) { + break; +@@ -1386,6 +1455,31 @@ Slot::parseEF_Directory(const CKYByte *c + tokenObjects.push_back(obj); + } while ( false ); + } ++ ++ /* handle the case where we have context specific with the same user pin */ ++ if ((type == PK15AuthObj) ++ && (CKYBuffer_Size(&candidateContextSpecificAuthId) != 0) ++ && (auth[CKU_CONTEXT_SPECIFIC] == NULL)) { ++ ObjectIter iter; ++ ++ /* these should put on the individual keys */ ++ auth[CKU_CONTEXT_SPECIFIC] = new PK15Object(*auth[CKU_USER]); ++ /* set the pin ref for the context specific auth */ ++ auth[CKU_CONTEXT_SPECIFIC]->setPinRef( ++ (CK_BYTE) CKYBuffer_GetChar(&candidateContextSpecificAuthId,0)); ++ for( iter = tokenObjects.begin(); iter != tokenObjects.end(); ++iter) { ++ const CKYBuffer *authid = iter->getAuthId(); ++ if(authid && ++ CKYBuffer_IsEqual(authid,&candidateContextSpecificAuthId)) { ++ iter->setAttributeBool(CKA_ALWAYS_AUTHENTICATE, TRUE); ++ iter->setUser(CKU_CONTEXT_SPECIFIC); ++ /* auth[CKU_CONTEXT_SPECIFIC]-> ++ setObjectPath(iter->getObjectPath()); */ ++ } ++ } ++ ++ ++ } + CKYBuffer_FreeData(&file); + return CKYSUCCESS; + } +@@ -2221,6 +2315,12 @@ Slot::unloadObjects() + tokenManufacturer = NULL; + } + CKYBuffer_Resize(&p15serialNumber,0); ++ CKYBuffer_Resize(&candidateUserAuthId,0); ++ CKYBuffer_Resize(&candidateContextSpecificAuthId,0); ++ for (int i=0; i < MAX_AUTH_USERS; i++) { ++ if (auth[i]) delete auth[i]; ++ auth[i]=NULL; ++ } + } + + #ifdef USE_SHMEM +@@ -3766,7 +3866,6 @@ Slot::attemptLogin(CK_USER_TYPE user, bo + contextPinCache.clearPin(); + } + } +-void dump(const char *label, const CKYBuffer *buf); + + void + Slot::attemptP15Login(CK_USER_TYPE user) +@@ -3794,7 +3893,6 @@ Slot::attemptP15Login(CK_USER_TYPE user) + throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet select return 0x%04x", + result); + } +- + status = P15Applet_VerifyPIN(conn, + (const char *)CKYBuffer_Data(pinCachePtr->get()), + auth[user]->getPinInfo(), &result); +@@ -4636,7 +4734,14 @@ Slot::cryptRSA(SessionHandleSuffix suffi + params.padInput(&inputPad, &input); + performRSAOp(&output, &inputPad, params.getKeySize(), key, + params.getDirection()); +- params.unpadOutput(result, &output); ++ if (CKYBuffer_Size(&output) < CKYBuffer_Size(&inputPad)) { ++ /* if the size is smaller than the input, treat it as ++ * unpadded */ ++ CKYBuffer_Replace(result, 0, CKYBuffer_Data(&output), ++ CKYBuffer_Size(&output)); ++ } else { ++ params.unpadOutput(result, &output); ++ } + CKYBuffer_FreeData(&input); + CKYBuffer_FreeData(&inputPad); + CKYBuffer_FreeData(&output); +@@ -4787,9 +4892,8 @@ retry: + } else if (state & CAC_CARD) { + status = CACApplet_SignDecrypt(conn, input, output, &result); + } else if (state & P15_CARD) { +- status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8, ++ status = P15Applet_SignDecrypt(conn, key->getKeyRef(), (keySize/8)*2, + CKY_DIR_ENCRYPT, input, output, &result); +- + } else { + status = CKYApplet_ComputeECCSignature(conn, objectToKeyNum(key), input, NULL, output, getNonce(), &result); + } +@@ -4861,8 +4965,32 @@ retry: + } else if (state & CAC_CARD) { + status = CACApplet_SignDecrypt(conn, input, output, &result); + } else if (state & P15_CARD) { +- status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8, +- direction, input, output, &result); ++ if (direction == CKY_DIR_DECRYPT) { ++ status = P15Applet_SignDecrypt(conn, key->getKeyRef(), ++ keySize/8, direction, input, output, &result); ++ } else { ++ CKYBuffer unpadInput; ++ CKYBuffer_InitEmpty(&unpadInput); ++ stripRSAPadding(&unpadInput, input); /* will throw exception ++ * on error */ ++ status = P15Applet_SignDecrypt(conn, key->getKeyRef(), keySize/8, ++ direction, &unpadInput, output, &result); ++ CKYBuffer_FreeData(&unpadInput); ++ /* if it didn't work, try full padded the input first */ ++ if ((status != CKYSUCCESS) ++ && (result != CKYISO_CONDITION_NOT_SATISFIED) ++ && (result != CKYISO_SECURITY_NOT_SATISFIED)) { ++ status = P15Applet_SignDecrypt(conn, key->getKeyRef(), ++ keySize/8, direction, input, output, &result); ++ } ++ /* finally just lie and try to "decrypt" the buffer */ ++ if ((status != CKYSUCCESS) ++ && (result != CKYISO_CONDITION_NOT_SATISFIED) ++ && (result != CKYISO_SECURITY_NOT_SATISFIED)) { ++ status = P15Applet_SignDecrypt(conn, key->getKeyRef(), ++ keySize/8, CKY_DIR_DECRYPT, input, output, &result); ++ } ++ } + } else { + status = CKYApplet_ComputeCrypt(conn, objectToKeyNum(key), + CKY_RSA_NO_PAD, direction, input, NULL, output, +@@ -4883,8 +5011,7 @@ retry: + throw PKCS11Exception(CKR_DATA_INVALID); + } + // version0 keys could be logged out in the middle by someone else, +- // reauthenticate... This code can go away when we depricate. +- // version0 applets. ++ // reauthenticate... + if (!isVersion1Key && !loginAttempted && + userPinCache(key->getUser())->isValid() && + (result == CKYISO_UNAUTHORIZED)) { +diff -up ./src/coolkey/slot.h.cardos-5-3 ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.cardos-5-3 2017-03-16 17:14:02.417338760 -0700 ++++ ./src/coolkey/slot.h 2017-03-16 17:14:02.421338828 -0700 +@@ -368,6 +368,8 @@ class Slot { + CKYBuffer p15tokenInfo; + CKYBuffer p15odf; + CKYBuffer p15serialNumber; ++ CKYBuffer candidateUserAuthId; ++ CKYBuffer candidateContextSpecificAuthId; + //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 }; + + #ifdef USE_SHMEM +diff -up ./src/libckyapplet/cky_applet.c.cardos-5-3 ./src/libckyapplet/cky_applet.c +--- ./src/libckyapplet/cky_applet.c.cardos-5-3 2017-03-16 17:14:02.404338539 -0700 ++++ ./src/libckyapplet/cky_applet.c 2017-03-16 17:14:02.422338845 -0700 +@@ -1316,8 +1316,6 @@ P15Applet_SignDecrypt(CKYCardConnection + int appendLength = length; + int hasPad = 0; + +- /* Hack, lie and say we are always doing encipherment */ +- direction = CKY_DIR_DECRYPT; + CKYBuffer_Resize(result,0); + /* + * first set the security environment +@@ -1367,7 +1365,7 @@ P15Applet_SignDecrypt(CKYCardConnection + } + CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength); + pso.chain = 0; +- pso.retLen = dataSize; ++ pso.retLen = keySize; + + ret = CKYApplet_HandleAPDU(conn, + P15AppletFactory_PerformSecurityOperation, &pso, NULL, diff --git a/SOURCES/coolkey-1.1.0-fail-on-bad-mechanisms.patch b/SOURCES/coolkey-1.1.0-fail-on-bad-mechanisms.patch new file mode 100644 index 0000000..c0d1c43 --- /dev/null +++ b/SOURCES/coolkey-1.1.0-fail-on-bad-mechanisms.patch @@ -0,0 +1,109 @@ +diff -up ./src/coolkey/coolkey.cpp.fail-on-bad-mechanisms ./src/coolkey/coolkey.cpp +--- ./src/coolkey/coolkey.cpp.fail-on-bad-mechanisms 2016-06-16 14:36:05.934755563 -0700 ++++ ./src/coolkey/coolkey.cpp 2016-06-16 14:36:05.945755372 -0700 +@@ -77,7 +77,8 @@ rsaMechanismList[] = { + + static const MechInfo + ecMechanismList[] = { +- {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDSA_SHA1, {256, 521, CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} } ++ {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}}, ++ {CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} } + }; + + unsigned int numRSAMechanisms = sizeof(rsaMechanismList)/sizeof(MechInfo); +diff -up ./src/coolkey/slot.cpp.fail-on-bad-mechanisms ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.fail-on-bad-mechanisms 2016-06-16 14:36:05.943755407 -0700 ++++ ./src/coolkey/slot.cpp 2016-06-16 15:07:40.255882660 -0700 +@@ -4185,11 +4185,30 @@ Slot::signInit(SessionHandleSuffix suffi + { + refreshTokenState(); + SessionIter session = findSession(suffix); ++ PKCS11Object *key = getKeyFromHandle(hKey); + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } ++ if (pMechanism == NULL) { ++ throw PKCS11Exception(CKR_ARGUMENTS_BAD); ++ } ++ ++ switch (pMechanism->mechanism) { ++ case CKM_RSA_PKCS: ++ if (key->getKeyType() != Key::rsa) { ++ throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT); ++ } ++ break; ++ case CKM_ECDSA: ++ if (key->getKeyType() != Key::ecc) { ++ throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT); ++ } ++ break; ++ default: ++ throw PKCS11Exception(CKR_MECHANISM_INVALID); ++ } + +- session->signatureState.initialize(getKeyFromHandle(hKey)); ++ session->signatureState.initialize(key); + } + + void +@@ -4198,11 +4217,24 @@ Slot::decryptInit(SessionHandleSuffix su + { + refreshTokenState(); + SessionIter session = findSession(suffix); ++ PKCS11Object *key = getKeyFromHandle(hKey); + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } ++ if (pMechanism == NULL) { ++ throw PKCS11Exception(CKR_ARGUMENTS_BAD); ++ } ++ switch (pMechanism->mechanism) { ++ case CKM_RSA_PKCS: ++ if (key->getKeyType() != Key::rsa) { ++ throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT); ++ } ++ break; ++ default: ++ throw PKCS11Exception(CKR_MECHANISM_INVALID); ++ } + +- session->decryptionState.initialize(getKeyFromHandle(hKey)); ++ session->decryptionState.initialize(key); + } + + /** +@@ -5008,8 +5040,23 @@ Slot::derive(SessionHandleSuffix suffix, + + ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE); + SessionIter session = findSession(suffix); ++ PKCS11Object *key=getKeyFromHandle(hBaseKey); + +- session->keyAgreementState.initialize(getKeyFromHandle(hBaseKey)); ++ if (pMechanism == NULL ) { ++ throw PKCS11Exception(CKR_ARGUMENTS_BAD); ++ } ++ ++ switch (pMechanism->mechanism) { ++ case CKM_ECDH1_DERIVE: ++ if (key->getKeyType() != Key::ecc) { ++ throw PKCS11Exception(CKR_KEY_TYPE_INCONSISTENT); ++ } ++ break; ++ default: ++ throw PKCS11Exception(CKR_MECHANISM_INVALID); ++ } ++ ++ session->keyAgreementState.initialize(key); + deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, + phKey, params); + +@@ -5018,9 +5065,6 @@ Slot::derive(SessionHandleSuffix suffix, + void Slot::deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params) + { +- if (pMechanism == NULL ) { +- throw PKCS11Exception(CKR_ARGUMENTS_BAD); +- } + + CK_ECDH1_DERIVE_PARAMS *mechParams = NULL; + diff --git a/SOURCES/coolkey-1.1.0-fix-spurious-event.patch b/SOURCES/coolkey-1.1.0-fix-spurious-event.patch new file mode 100644 index 0000000..739901a --- /dev/null +++ b/SOURCES/coolkey-1.1.0-fix-spurious-event.patch @@ -0,0 +1,11 @@ +diff -up ./src/coolkey/slot.cpp.fix-spurious ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.fix-spurious 2014-09-26 15:31:17.277958895 -0700 ++++ ./src/coolkey/slot.cpp 2014-09-26 15:34:33.218313227 -0700 +@@ -1412,6 +1412,7 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + #endif + } while ((status == CKYSUCCESS) || + (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT) || ++ (CKYCardContext_GetLastError(context) == SCARD_E_UNKNOWN_READER) || + (CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE) || + (CKYCardContext_GetLastError(context) == SCARD_E_NO_SERVICE) || + (CKYCardContext_GetLastError(context) == SCARD_E_SERVICE_STOPPED) ); diff --git a/SOURCES/coolkey-1.1.0-max-cpu-bug.patch b/SOURCES/coolkey-1.1.0-max-cpu-bug.patch new file mode 100644 index 0000000..aab85f7 --- /dev/null +++ b/SOURCES/coolkey-1.1.0-max-cpu-bug.patch @@ -0,0 +1,12 @@ +diff -up ./src/coolkey/slot.cpp.max-cpu-bug ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.max-cpu-bug 2016-06-30 14:36:10.502785885 -0700 ++++ ./src/coolkey/slot.cpp 2016-06-30 14:36:15.812876256 -0700 +@@ -1875,6 +1875,8 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + if (status != CKYSUCCESS) { + if ((CKYCardContext_GetLastError(context) == + SCARD_E_READER_UNAVAILABLE) || ++ (CKYCardContext_GetLastError(context) == ++ SCARD_E_UNKNOWN_READER) || + (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) { + OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY); + } diff --git a/SOURCES/coolkey-1.1.0-more-keys.patch b/SOURCES/coolkey-1.1.0-more-keys.patch new file mode 100644 index 0000000..fb94ec3 --- /dev/null +++ b/SOURCES/coolkey-1.1.0-more-keys.patch @@ -0,0 +1,61 @@ +diff -up ./src/coolkey/slot.cpp.more_keys ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.more_keys 2016-06-16 11:50:01.027432856 -0700 ++++ ./src/coolkey/slot.cpp 2016-06-16 11:50:13.267224824 -0700 +@@ -32,7 +32,8 @@ + + #define MIN(x, y) ((x) < (y) ? (x) : (y)) + +- ++#define MAX_NUM_KEYS 32 ++#define MAX_NUM_CERTS 32 + + #ifdef DEBUG + #define PRINTF(args) printf args +@@ -3458,7 +3459,7 @@ Slot::loadObjects() + } else if( type == 'c' ) { + // cert attribute object. find the DER encoding + unsigned short certnum = getObjectIndex(iter->obj.objectID); +- if( certnum > 9 ) { ++ if( certnum > MAX_NUM_CERTS ) { + //invalid object id + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Invalid object id %08x",iter->obj.objectID); +@@ -4154,7 +4155,7 @@ Slot::objectToKeyNum(const PKCS11Object + throw PKCS11Exception(CKR_KEY_HANDLE_INVALID); + } + unsigned short keyNum = getObjectIndex(id); +- if( keyNum > 9 ) { ++ if( keyNum > MAX_NUM_KEYS ) { + throw PKCS11Exception(CKR_KEY_HANDLE_INVALID); + } + return keyNum & 0xFF; +@@ -4911,7 +4912,6 @@ Slot::generateRandom(SessionHandleSuffix + } + } + +-#define MAX_NUM_KEYS 8 + unsigned int + Slot::getRSAKeySize(PKCS11Object *key) + { +diff -up ./src/coolkey/slot.h.more_keys ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.more_keys 2016-06-16 11:50:08.627303984 -0700 ++++ ./src/coolkey/slot.h 2016-06-16 11:54:08.872153180 -0700 +@@ -512,7 +512,17 @@ class Slot { + return (char) (objectID >> 24) & 0xff; + } + unsigned short getObjectIndex(unsigned long objectID) const { +- return (char )((objectID >> 16) & 0xff) - '0'; ++ char char_index = (char) ((objectID >> 16) & 0xff); ++ if (char_index >= '0' && char_index <= '9') { ++ return char_index - '0'; ++ } ++ if (char_index >= 'A' && char_index <= 'Z') { ++ return char_index - 'A' + 10; ++ } ++ if (char_index >= 'a' && char_index <= 'z') { ++ return char_index - 'a' + 26 + 10; ++ } ++ return 0x0100 + char_index; + } + + // actually get the size of a key in bits from the card diff --git a/SOURCES/coolkey-1.1.0-noapplet.patch b/SOURCES/coolkey-1.1.0-noapplet.patch new file mode 100644 index 0000000..dc931fe --- /dev/null +++ b/SOURCES/coolkey-1.1.0-noapplet.patch @@ -0,0 +1,18 @@ +diff -up ./src/coolkey/slot.cpp.noapplet ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.noapplet 2013-09-30 14:30:40.069595018 -0700 ++++ ./src/coolkey/slot.cpp 2013-09-30 14:31:27.488595000 -0700 +@@ -762,13 +762,7 @@ Slot::connectToToken() + CKYCardConnection_GetLastError(conn)); + disconnect(); + } +- /* CARD is a PIV card */ +- state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; +- isVersion1Key = 0; +- needLogin = 1; +- mCoolkey = 0; +- mOldCAC = 0; +- mCACLocalLogin = getPIVLoginType(); ++ /* CARD is unknown */ + return; + } + state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; diff --git a/SOURCES/coolkey-1.1.0-p15-coverity.patch b/SOURCES/coolkey-1.1.0-p15-coverity.patch new file mode 100644 index 0000000..4118e9c --- /dev/null +++ b/SOURCES/coolkey-1.1.0-p15-coverity.patch @@ -0,0 +1,210 @@ +diff -up ./src/coolkey/object.cpp.p15-coverity ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.p15-coverity 2015-07-06 18:02:34.604191118 -0700 ++++ ./src/coolkey/object.cpp 2015-07-06 19:06:04.432062377 -0700 +@@ -1558,7 +1558,7 @@ unsigned long GetBits(const CKYByte *ent + /* turn the flags into an int */ + for (i=0; i < entrySize; i++) { + CKYByte c = rev[entry[i]]; +- bits = bits | (c << i*8); ++ bits = bits | (((unsigned long)c) << (i*8)); + } + return bits | bitFlag; + } +@@ -1585,8 +1585,8 @@ CKYStatus PK15ObjectPath::setObjectPath( + if (entry == NULL) { return CKYINVALIDDATA; } + tagSize = entry - current; + current += entrySize + tagSize; ++ if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; } + size -= (entrySize +tagSize); +- if (size < 0) { return CKYINVALIDDATA; } + status = CKYBuffer_Replace(&path, 0, entry, entrySize); + if (status != CKYSUCCESS) { + return status; +@@ -1598,8 +1598,8 @@ CKYStatus PK15ObjectPath::setObjectPath( + if (entry == NULL) { return CKYINVALIDDATA; } + tagSize = entry - current; + current += entrySize + tagSize; ++ if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; } + size -= (entrySize +tagSize); +- if (size < 0) { return CKYINVALIDDATA; } + if (entrySize > 5) { return CKYINVALIDDATA; } + for (index = 0, i=0; i < entrySize; i++) { + index = (index << 8) + (unsigned int) entry[i]; +@@ -1612,8 +1612,8 @@ CKYStatus PK15ObjectPath::setObjectPath( + if (entry == NULL) { return CKYINVALIDDATA; } + tagSize = entry - current; + current += entrySize + tagSize; ++ if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; } + size -= (entrySize +tagSize); +- if (size < 0) { return CKYINVALIDDATA; } + if (entrySize > 5) { return CKYINVALIDDATA; } + for (length = 0, i=0; i < entrySize; i++) { + length = (length << 8) + (unsigned int) entry[i]; +@@ -1741,8 +1741,8 @@ set_key_type: + /* point current to the next section (cass attributes) */ + tagSize = commonAttributes - current; + current += commonSize + tagSize; ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } + currentSize -= (commonSize +tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } + + /* get the CKA_LABEL */ + if (commonAttributes[0] != ASN1_UTF8_STRING) { return CKYINVALIDDATA; } +@@ -1835,8 +1835,8 @@ PK15Object::completeCertObject(const CKY + /* point current to the next section (type attributes) */ + tagSize = commonCertAttributes - current; + current += commonSize + tagSize; ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } + currentSize -= (commonSize +tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } + + /* get the id */ + if (commonCertAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; } +@@ -1907,8 +1907,8 @@ PK15Object::completeAuthObject(const CKY + if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; } + tagSize = commonAuthAttributes - current; + current += commonSize + tagSize; ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } + currentSize -= (commonSize + tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } + if (commonAuthAttributes[0] != ASN1_OCTET_STRING) { + return CKYINVALIDDATA; + } +@@ -1930,8 +1930,8 @@ PK15Object::completeAuthObject(const CKY + if (commonAuthAttributes == NULL) { return CKYINVALIDDATA; } + tagSize = commonAuthAttributes - current; + current += commonSize + tagSize; +- currentSize -= (commonSize +tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } ++ currentSize -= (commonSize + tagSize); + /* + * parse the Pin Auth Attributes + * pinFlags BIT_STRING +@@ -2093,8 +2093,8 @@ PK15Object::completeKeyObject(const CKYB + /* point current to the next section (sublcass attributes) */ + tagSize = commonKeyAttributes - current; + current += commonSize + tagSize; +- currentSize -= (commonSize +tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } ++ currentSize -= (commonSize + tagSize); + + /* get the id */ + if (commonKeyAttributes[0] != ASN1_OCTET_STRING) { return CKYINVALIDDATA; } +@@ -2263,8 +2263,8 @@ CKYStatus PK15Object::completePrivKeyObj + /* point current to the next section (type attributes) */ + tagSize = commonPrivKeyAttributes - current; + current += commonSize + tagSize; ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } + currentSize -= (commonSize +tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } + + /* subjectName */ + if (commonPrivKeyAttributes[0] == ASN1_SEQUENCE) { +@@ -2385,8 +2385,8 @@ PK15Object::completePubKeyObject(const C + /* point current to the next section (type attributes) */ + tagSize = commonPubKeyAttributes - current; + current += commonSize + tagSize; +- currentSize -= (commonSize +tagSize); +- if (currentSize < 0) { return CKYINVALIDDATA; } ++ if (currentSize < (commonSize + tagSize)) { return CKYINVALIDDATA; } ++ currentSize -= (commonSize + tagSize); + + /* subjectName */ + if (commonPubKeyAttributes[0] == ASN1_SEQUENCE) { +@@ -2535,8 +2535,8 @@ PK15Object::completeRawPublicKey(const C + if (entry == NULL) { return CKYINVALIDDATA; } + tagSize = entry - current; + current += entrySize + tagSize; ++ if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; } + size -= (entrySize +tagSize); +- if (size < 0) { return CKYINVALIDDATA; } + if ((entry[0] == 0) && (entrySize > 1)) { + entry++; entrySize--; + } +@@ -2548,8 +2548,8 @@ PK15Object::completeRawPublicKey(const C + if (entry == NULL) { return CKYINVALIDDATA; } + tagSize = entry - current; + current += entrySize + tagSize; +- size -= (entrySize +tagSize); +- if (size < 0) { return CKYINVALIDDATA; } ++ if (size < (entrySize + tagSize)) { return CKYINVALIDDATA; } ++ size -= (entrySize + tagSize); + if ((entry[0] == 0) && (entrySize > 1)) { + entry++; entrySize--; + } +@@ -2682,11 +2682,11 @@ DEREncodedTokenInfo::DEREncodedTokenInfo + if (entry == NULL) return; + tagSize = entry - current; + current += tagSize + entrySize; ++ if (size < tagSize + entrySize) return; + size -= tagSize + entrySize; + if (entrySize < 1) { + version = *entry; + } +- if (size < 0) return; + + /* get the serial number */ + if (current[0] != ASN1_OCTET_STRING) { return ; } +@@ -2729,6 +2729,8 @@ DEREncodedTokenInfo::DEREncodedTokenInfo + } + + /* parsing flags */ ++#ifdef notdef ++ /* we arn't using this right now, keep it for future reference */ + if (current[0] == ASN1_BIT_STRING) { + /* recordinfo parsing would go here */ + unsigned long bits; +@@ -2739,6 +2741,7 @@ DEREncodedTokenInfo::DEREncodedTokenInfo + size -= tagSize + entrySize; + bits = GetBits(entry, entrySize,8,2); + } ++#endif + return; + } + +diff -up ./src/coolkey/slot.cpp.p15-coverity ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.p15-coverity 2015-07-06 18:02:34.606191081 -0700 ++++ ./src/coolkey/slot.cpp 2015-07-06 18:02:34.610191006 -0700 +@@ -3714,7 +3714,6 @@ void + Slot::attemptP15Login(CK_USER_TYPE user) + { + PinCache *pinCachePtr = userPinCache(user); +- const CKYBuffer *path; + + if (user == CKU_USER) { + loggedIn = false; +@@ -3729,7 +3728,6 @@ Slot::attemptP15Login(CK_USER_TYPE user) + "No PKCS #15 auth object for user %d\n", user); + } + +- path = auth[user]->getObjectPath().getPath(); + status = selectPath(auth[user]->getObjectPath().getPath(), &result); + if( status == CKYSCARDERR ) { + handleConnectionError(); +diff -up ./src/libckyapplet/cky_applet.c.p15-coverity ./src/libckyapplet/cky_applet.c +--- ./src/libckyapplet/cky_applet.c.p15-coverity 2015-07-06 18:02:34.606191081 -0700 ++++ ./src/libckyapplet/cky_applet.c 2015-07-06 18:02:34.610191006 -0700 +@@ -1361,6 +1361,9 @@ P15Applet_SignDecrypt(CKYCardConnection + appendLength = length; + } else { + ret = CKYBuffer_Reserve(&tmp, length); ++ if (ret != CKYSUCCESS) { ++ goto done; ++ } + } + CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength); + pso.chain = 0; +diff -up ./src/libckyapplet/cky_base.c.p15-coverity ./src/libckyapplet/cky_base.c +--- ./src/libckyapplet/cky_base.c.p15-coverity 2015-07-06 18:02:34.607191062 -0700 ++++ ./src/libckyapplet/cky_base.c 2015-07-06 18:02:34.610191006 -0700 +@@ -736,7 +736,7 @@ CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu + CKYStatus ret; + + if (recvlen <= CKYAPDU_MAX_DATA_LEN) { +- return APDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff)); ++ return CKYAPDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff)); + } + ret = CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_HEADER_LEN+2); + if (ret != CKYSUCCESS) { diff --git a/SOURCES/coolkey-1.1.0-p15.patch b/SOURCES/coolkey-1.1.0-p15.patch new file mode 100644 index 0000000..f1b269f --- /dev/null +++ b/SOURCES/coolkey-1.1.0-p15.patch @@ -0,0 +1,4379 @@ +diff -up ./src/coolkey/coolkey.cpp.p15 ./src/coolkey/coolkey.cpp +--- ./src/coolkey/coolkey.cpp.p15 2015-07-06 10:27:55.769827286 -0700 ++++ ./src/coolkey/coolkey.cpp 2015-07-06 10:27:55.784827002 -0700 +@@ -599,13 +599,10 @@ C_Login(CK_SESSION_HANDLE hSession, CK_U + } + try { + log->log("C_Login called\n"); +- if( userType != CKU_USER ) { +- throw PKCS11Exception(CKR_USER_TYPE_INVALID); +- } + if( pPin == NULL ) { + throw PKCS11Exception(CKR_ARGUMENTS_BAD); + } +- slotList->login(hSession, pPin, ulPinLen); ++ slotList->login(hSession, userType, pPin, ulPinLen); + return CKR_OK; + } catch(PKCS11Exception &e) { + e.log(log); +diff -up ./src/coolkey/object.cpp.p15 ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.p15 2015-07-06 10:27:55.770827267 -0700 ++++ ./src/coolkey/object.cpp 2015-07-06 10:27:55.785826984 -0700 +@@ -19,9 +19,9 @@ + + #include "mypkcs11.h" + #include "PKCS11Exception.h" +-#include "object.h" + #include + #include ++#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, ¤tSize, 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 +@@ -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& 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 + #include "cky_applet.h" ++#include + + #define MIN(x, y) ((x) < (y) ? (x) : (y)) + +@@ -51,6 +52,12 @@ CACAppletFactory_SelectFile(CKYAPDU *apd + } + + CKYStatus ++P15AppletFactory_SelectFile(CKYAPDU *apdu, const void *param) ++{ ++ return CKYAPDUFactory_SelectFile(apdu, 0, 0, (const CKYBuffer *)param); ++} ++ ++CKYStatus + CKYAppletFactory_SelectCardManager(CKYAPDU *apdu, const void *param) + { + return CKYAPDUFactory_SelectCardManager(apdu); +@@ -269,17 +276,10 @@ PIVAppletFactory_SignDecrypt(CKYAPDU *ap + } + + CKYStatus +-CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) ++P15AppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) + { +- const char *pin=(const char *)param; +- return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin); +-} +- +-CKYStatus +-PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) +-{ +- const char *pin=(const char *)param; +- return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin); ++ const P15AppletArgVerifyPIN *vps = (const P15AppletArgVerifyPIN *)param; ++ return P15APDUFactory_VerifyPIN(apdu, vps->pinRef, vps->pinVal); + } + + CKYStatus +@@ -323,6 +323,39 @@ CKYAppletFactory_LogoutAllV0(CKYAPDU *ap + return CKYAPDU_SetSendData(apdu, data, sizeof(data)); + } + ++CKYStatus ++P15AppletFactory_ReadRecord(CKYAPDU *apdu, const void *param) ++{ ++ const P15AppletArgReadRecord *rrs = (const P15AppletArgReadRecord *)param; ++ return P15APDUFactory_ReadRecord(apdu, rrs->record, ++ rrs->short_ef, rrs->flags, rrs->size); ++} ++ ++CKYStatus ++P15AppletFactory_ReadBinary(CKYAPDU *apdu, const void *param) ++{ ++ const P15AppletArgReadBinary *res = (const P15AppletArgReadBinary *)param; ++ return P15APDUFactory_ReadBinary(apdu, res->offset, ++ res->short_ef, res->flags, res->size); ++} ++ ++CKYStatus ++P15AppletFactory_ManageSecurityEnvironment(CKYAPDU *apdu, const void *param) ++{ ++ const P15AppletArgManageSecurityEnvironment *mse = ++ (const P15AppletArgManageSecurityEnvironment *)param; ++ return P15APDUFactory_ManageSecurityEnvironment(apdu, mse->p1, ++ mse->p2, mse->keyRef); ++} ++ ++CKYStatus ++P15AppletFactory_PerformSecurityOperation(CKYAPDU *apdu, const void *param) ++{ ++ const P15AppletArgPerformSecurityOperation *pso = ++ (const P15AppletArgPerformSecurityOperation *)param; ++ return P15APDUFactory_PerformSecurityOperation(apdu, pso->dir, pso->chain, ++ pso->retLen, pso->data); ++} + /***************************************************************** + * + * Generic Fill routines used by several calls in common +@@ -975,9 +1008,6 @@ CKYApplet_ComputeECCSignature(CKYCardCon + const CKYBuffer *data, CKYBuffer *sig, + CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) + { +- int use2APDUs = 0; +- int use_dl_object = 0; +- short dataSize = 0; + CKYStatus ret = CKYAPDUFAIL; + CKYAppletArgComputeECCSignature ccd; + CKYBuffer empty; +@@ -1052,6 +1082,11 @@ done: + return ret; + } + ++const P15PinInfo CACPinInfo = ++ { P15PinInitialized|P15PinNeedsPadding, P15PinUTF8, 0, 8, 8, 0, 0xff }; ++const P15PinInfo PIVPinInfo = ++ { P15PinLocal|P15PinInitialized|P15PinNeedsPadding, ++ P15PinUTF8, 0, 8, 8, 0, 0xff }; + /* + * do a CAC VerifyPIN + */ +@@ -1059,23 +1094,8 @@ CKYStatus + CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local, + CKYISOStatus *apduRC) + { +- CKYStatus ret; +- CKYISOStatus status; +- if (apduRC == NULL) { +- apduRC = &status; +- } +- +- ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN : +- CACAppletFactory_VerifyPIN, pin, NULL, +- 0, CKYAppletFill_Null, +- NULL, apduRC); +- /* it's unfortunate that the same code that means 'more data to follow' for +- * GetCertificate also means, auth failure, you only have N more attempts +- * left in the verify PIN call */ +- if ((*apduRC & CKYISO_MORE_MASK) == CKYISO_MORE) { +- ret = CKYAPDUFAIL; +- } +- return ret; ++ return P15Applet_VerifyPIN(conn, pin, ++ local ? &PIVPinInfo: &CACPinInfo, apduRC); + } + + +@@ -1165,6 +1185,214 @@ CACApplet_ReadFile(CKYCardConnection *co + return ret; + } + ++/* ++ * Select a EF ++ */ ++CKYStatus ++P15Applet_SelectFile(CKYCardConnection *conn, unsigned short ef, ++ CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYBuffer efBuf; ++ CKYBuffer_InitEmpty(&efBuf); ++ CKYBuffer_AppendShort(&efBuf, ef); ++ ret = CKYApplet_HandleAPDU(conn, P15AppletFactory_SelectFile, &efBuf, ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++ CKYBuffer_FreeData(&efBuf); ++ return ret; ++} ++ ++CKYStatus ++P15Applet_SelectRootFile(CKYCardConnection *conn, unsigned short ef, ++ CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYBuffer efBuf; ++ CKYBuffer_InitEmpty(&efBuf); ++ CKYBuffer_AppendShort(&efBuf, ef); ++ ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &efBuf, ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++ CKYBuffer_FreeData(&efBuf); ++ return ret; ++} ++ ++CKYStatus ++P15Applet_VerifyPIN(CKYCardConnection *conn, const char *pin, ++ const P15PinInfo *pinInfo, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYISOStatus status; ++ CKYSize size; ++ CKYBuffer encodedPin; ++ P15AppletArgVerifyPIN vps; ++ ++ CKYBuffer_InitEmpty(&encodedPin); ++ ++ if (apduRC == NULL) { ++ apduRC = &status; ++ } ++ ++ size = strlen(pin); ++ if (pinInfo->pinFlags & P15PinNeedsPadding) { ++ if (size > pinInfo->storedLength) { ++ size = pinInfo->storedLength; ++ } ++ ret=CKYBuffer_Reserve(&encodedPin, pinInfo->storedLength); ++ if (ret != CKYSUCCESS) { goto fail; } ++ } ++ /* This is where we would do upcase processing for the case insensitive ++ * flag. It's also where we would do mapping for bcd pins */ ++ ret = CKYBuffer_Replace(&encodedPin, 0, (const CKYByte *)pin, size); ++ if (ret != CKYSUCCESS) { goto fail; } ++ if (pinInfo->pinFlags & P15PinNeedsPadding) { ++ int i; ++ int padSize = pinInfo->storedLength - size; ++ for (i=0; i < padSize; i++) { ++ CKYBuffer_AppendChar(&encodedPin, pinInfo->padChar); ++ } ++ } ++ ++ vps.pinRef = pinInfo->pinRef | ++ ((pinInfo->pinFlags & P15PinLocal) ? ISO_LOGIN_LOCAL : ISO_LOGIN_GLOBAL); ++ vps.pinVal = &encodedPin; ++ ret = CKYApplet_HandleAPDU(conn, P15AppletFactory_VerifyPIN, &vps, NULL, ++ 0, CKYAppletFill_Null, ++ NULL, apduRC); ++ /* it's unfortunate that the same code that means 'more data to follow' for ++ * GetCertificate also means, auth failure, you only have N more attempts ++ * left in the verify PIN call */ ++ if ((*apduRC & CKYISO_MORE_MASK) == CKYISO_MORE) { ++ ret = CKYAPDUFAIL; ++ } ++fail: ++ CKYBuffer_FreeData(&encodedPin); ++ return ret; ++} ++ ++ ++/* ++ * Read Record ++ */ ++CKYStatus ++P15Applet_ReadRecord(CKYCardConnection *conn, CKYByte record, CKYByte short_ef, ++ CKYByte flags, CKYByte size, CKYBuffer *data, CKYISOStatus *apduRC) ++{ ++ P15AppletArgReadRecord rrd; ++ ++ rrd.record = record; ++ rrd.short_ef = short_ef; ++ rrd.flags = flags; ++ rrd.size = size; ++ return CKYApplet_HandleAPDU(conn, P15AppletFactory_ReadRecord, &rrd, NULL, ++ CKY_SIZE_UNKNOWN, CKYAppletFill_ReplaceBuffer, data, apduRC); ++} ++ ++static CKYStatus ++P15Applet_ManageSecurityEnvironment(CKYCardConnection *conn, CKYByte key, ++ CKYByte direction, CKYByte p1, ++ CKYISOStatus *apduRC) ++{ ++ P15AppletArgManageSecurityEnvironment mse; ++ ++ mse.p1 = p1; /* this appears to be where most cards disagree */ ++ mse.p2 = (direction == CKY_DIR_DECRYPT) ? ISO_MSE_KEA : ISO_MSE_SIGN; ++ mse.keyRef = key; /* should be CKYBuffer in the future? */ ++ return CKYApplet_HandleAPDU(conn, ++ P15AppletFactory_ManageSecurityEnvironment, &mse, NULL, ++ CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++} ++ ++CKYStatus ++P15Applet_SignDecrypt(CKYCardConnection *conn, CKYByte key, ++ unsigned int keySize, CKYByte direction, ++ const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ P15AppletArgPerformSecurityOperation pso; ++ CKYSize dataSize = CKYBuffer_Size(data); ++ CKYOffset offset = 0; ++ CKYBuffer tmp; ++ int length = dataSize; ++ int appendLength = length; ++ int hasPad = 0; ++ ++ /* Hack, lie and say we are always doing encipherment */ ++ direction = CKY_DIR_DECRYPT; ++ CKYBuffer_Resize(result,0); ++ /* ++ * first set the security environment ++ */ ++ ret = P15Applet_ManageSecurityEnvironment(conn, key, direction, ++ ISO_MSE_SET|ISO_MSE_QUAL_COMPUTE, apduRC); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ ++ CKYBuffer_InitEmpty(&tmp); ++ ++ pso.data = &tmp; ++ pso.dir = direction; ++ if (direction == CKY_DIR_DECRYPT) { ++ length++; ++ CKYBuffer_AppendChar(&tmp, 0x00); /* pad byte */ ++ hasPad = 1; ++ } ++ if (CKYCardConnection_GetProtocol(conn) == SCARD_PROTOCOL_T0) { ++ ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE); ++ if (ret != CKYSUCCESS) { ++ goto done; ++ } ++ for(offset = 0; length > CKY_MAX_WRITE_CHUNK_SIZE; ++ hasPad = 0, ++ offset += CKY_MAX_WRITE_CHUNK_SIZE, ++ length -= CKY_MAX_WRITE_CHUNK_SIZE) { ++ pso.chain = 1; ++ pso.retLen = 0; ++ CKYBuffer_AppendBuffer(&tmp, data, offset, ++ hasPad ? (CKY_MAX_WRITE_CHUNK_SIZE-1) : CKY_MAX_WRITE_CHUNK_SIZE); ++ ret = CKYApplet_HandleAPDU(conn, ++ P15AppletFactory_PerformSecurityOperation, &pso, NULL, ++ CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++ if (ret != CKYSUCCESS) { ++ goto done; ++ } ++ CKYBuffer_Resize(&tmp, 0); ++ } ++ appendLength = length; ++ } else { ++ ret = CKYBuffer_Reserve(&tmp, length); ++ } ++ CKYBuffer_AppendBuffer(&tmp, data, offset, appendLength); ++ pso.chain = 0; ++ pso.retLen = dataSize; ++ ++ ret = CKYApplet_HandleAPDU(conn, ++ P15AppletFactory_PerformSecurityOperation, &pso, NULL, ++ CKY_SIZE_UNKNOWN, CKYAppletFill_ReplaceBuffer, result, apduRC); ++ ++done: ++ CKYBuffer_FreeData(&tmp); ++ return ret; ++} ++ ++/* ++ * Read Binary ++ */ ++CKYStatus ++P15Applet_ReadBinary(CKYCardConnection *conn, unsigned short offset, ++ CKYByte short_ef, CKYByte flags, CKYByte size, ++ CKYBuffer *data, CKYISOStatus *apduRC) ++{ ++ P15AppletArgReadBinary red; ++ ++ red.offset = offset; ++ red.short_ef = short_ef; ++ red.flags = flags; ++ red.size = size; ++ return CKYApplet_HandleAPDU(conn, P15AppletFactory_ReadBinary, &red, NULL, ++ CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, data, apduRC); ++} ++ + CKYStatus + CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, + CKYSize *nextSize, CKYISOStatus *apduRC) +@@ -1632,6 +1860,7 @@ CKYApplet_ReadObjectFull(CKYCardConnecti + return ret; + } + ++ + /* + * Write Object + * This makes multiple APDU calls to write the entire object. +diff -up ./src/libckyapplet/cky_applet.h.p15 ./src/libckyapplet/cky_applet.h +--- ./src/libckyapplet/cky_applet.h.p15 2015-07-06 10:27:55.776827153 -0700 ++++ ./src/libckyapplet/cky_applet.h 2015-07-06 10:27:55.788826927 -0700 +@@ -204,6 +204,7 @@ typedef struct _CKYAppletArgReadObject { + CKYByte size; + } CKYAppletArgReadObject; + ++ + typedef struct _CKYAppletArgWriteObject { + unsigned long objectID; + CKYOffset offset; +@@ -262,6 +263,39 @@ typedef struct _PIVAppletRespSignDecrypt + CKYBuffer *buf; + } PIVAppletRespSignDecrypt; + ++typedef struct _P15AppletArgReadRecord { ++ CKYByte record; ++ CKYByte short_ef; ++ CKYByte flags; ++ CKYByte size; ++} P15AppletArgReadRecord; ++ ++typedef struct _P15AppletArgReadBinary { ++ unsigned short offset; ++ CKYByte short_ef; ++ CKYByte flags; ++ CKYByte size; ++} P15AppletArgReadBinary; ++ ++typedef struct _P15AppletArgVerifyPIN { ++ const CKYBuffer *pinVal; ++ CKYByte pinRef; ++} P15AppletArgVerifyPIN; ++ ++typedef struct _P15AppletArgManageSecurityEnvironment { ++ CKYByte p1; ++ CKYByte p2; ++ CKYByte keyRef; ++} ++ P15AppletArgManageSecurityEnvironment; ++ ++typedef struct _P15AppletArgPerformSecurityOperation { ++ CKYByte dir; ++ int chain; ++ CKYSize retLen; ++ const CKYBuffer *data; ++} P15AppletArgPerformSecurityOperation; ++ + /* fills in an APDU from a structure -- form of all the generic factories*/ + typedef CKYStatus (*CKYAppletFactory)(CKYAPDU *apdu, const void *param); + /* fills in an a structure from a response -- form of all the fill structures*/ +@@ -514,6 +548,7 @@ CKYStatus CACApplet_ReadFile(CKYCardConn + CKYStatus CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef, + CKYISOStatus *apduRC); + ++ + /* must happen with PKI applet selected */ + CKYStatus CACApplet_SignDecrypt(CKYCardConnection *conn, const CKYBuffer *data, + CKYBuffer *result, CKYISOStatus *apduRC); +@@ -539,6 +574,26 @@ CKYStatus PIVApplet_SignDecrypt(CKYCardC + unsigned int keySize, int derive, + const CKYBuffer *data, CKYBuffer *result, + CKYISOStatus *apduRC); ++ ++/* PKCS Commands 15 */ ++CKYStatus P15Applet_SelectFile(CKYCardConnection *conn, unsigned short ef, ++ CKYISOStatus *apduRC); ++CKYStatus P15Applet_SelectRootFile(CKYCardConnection *conn, unsigned short ef, ++ CKYISOStatus *apduRC); ++CKYStatus P15Applet_ReadRecord(CKYCardConnection *conn, CKYByte record, ++ CKYByte short_ef, CKYByte flags, CKYByte size, CKYBuffer *data, ++ CKYISOStatus *apduRC); ++CKYStatus P15Applet_ReadBinary(CKYCardConnection *conn, unsigned short offset, ++ CKYByte short_ef, CKYByte flags, CKYByte size, CKYBuffer *data, ++ CKYISOStatus *apduRC); ++CKYStatus P15Applet_VerifyPIN(CKYCardConnection *conn, const char *pin, ++ const P15PinInfo *pinInfo, CKYISOStatus *apduRC); ++ ++CKYStatus P15Applet_SignDecrypt(CKYCardConnection *conn, CKYByte key, ++ unsigned int keySize, CKYByte direction, ++ const CKYBuffer *data, CKYBuffer *result, ++ CKYISOStatus *apduRC); ++ + /* + * There are 3 read commands: + * +diff -up ./src/libckyapplet/cky_base.c.p15 ./src/libckyapplet/cky_base.c +--- ./src/libckyapplet/cky_base.c.p15 2015-07-06 10:27:55.776827153 -0700 ++++ ./src/libckyapplet/cky_base.c 2015-07-06 10:27:55.788826927 -0700 +@@ -651,21 +651,38 @@ CKYStatus + CKYAPDU_SetSendData(CKYAPDU *apdu, const CKYByte *data, CKYSize len) + { + CKYStatus ret; ++ CKYOffset offset = 0; + +- if (len > CKYAPDU_MAX_DATA_LEN) { +- return CKYDATATOOLONG; +- } ++ /* Encode with T1 if necessary */ + +- ret = CKYBuffer_Resize(&apdu->apduBuf, len + CKYAPDU_HEADER_LEN); +- if (ret != CKYSUCCESS) { +- return ret; ++ if (len < CKYAPDU_MAX_DATA_LEN) { ++ offset = 0; ++ ret = CKYBuffer_Resize(&apdu->apduBuf, len+offset+CKYAPDU_HEADER_LEN); ++ if (ret != CKYSUCCESS ) { ++ return ret; ++ } ++ ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) len); ++ } else if (len < CKYAPDU_MAX_T1_DATA_LEN) { ++ offset = 2; ++ ret = CKYBuffer_Resize(&apdu->apduBuf, len+offset+CKYAPDU_HEADER_LEN); ++ if (ret != CKYSUCCESS ) { ++ return ret; ++ } ++ ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) 0); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ ret = CKYBuffer_SetShort(&apdu->apduBuf,CKY_LC_OFFSET+1, ++ (unsigned short)len); ++ } else { ++ return CKYDATATOOLONG; + } +- ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, +- len == CKYAPDU_MAX_DATA_LEN ? 0: (CKYByte) len); ++ + if (ret != CKYSUCCESS) { + return ret; + } +- return CKYBuffer_Replace(&apdu->apduBuf, CKYAPDU_HEADER_LEN, data, len); ++ return CKYBuffer_Replace(&apdu->apduBuf, ++ CKYAPDU_HEADER_LEN + offset , data, len); + } + + CKYStatus +@@ -685,15 +702,15 @@ CKYAPDU_AppendSendData(CKYAPDU *apdu, co + } + + dataLen = CKYBuffer_Size(&apdu->apduBuf) + len - CKYAPDU_HEADER_LEN; +- if (dataLen > CKYAPDU_MAX_DATA_LEN) { ++ /* only handles T0 encoding, not T1 encoding */ ++ if (dataLen >= CKYAPDU_MAX_DATA_LEN) { + return CKYDATATOOLONG; + } + ret = CKYBuffer_AppendData(&apdu->apduBuf, data, len); + if (ret != CKYSUCCESS) { + return ret; + } +- return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, +- dataLen == CKYAPDU_MAX_DATA_LEN ? 0 : (CKYByte) dataLen); ++ return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LC_OFFSET, (CKYByte) dataLen); + } + + CKYStatus +@@ -714,11 +731,100 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY + } + + CKYStatus ++CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen) ++{ ++ CKYStatus ret; ++ ++ if (recvlen <= CKYAPDU_MAX_DATA_LEN) { ++ return APDU_SetReceiveLen(apdu, (CKYByte)(recvlen & 0xff)); ++ } ++ ret = CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_HEADER_LEN+2); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ ret = CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, 0); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ return CKYBuffer_SetShort(&apdu->apduBuf, CKY_LE_OFFSET+1, recvlen); ++} ++ ++CKYStatus ++CKYAPDU_SetReceiveLength(CKYAPDU *apdu, CKYSize recvlen) ++{ ++ if (recvlen <= CKYAPDU_MAX_T1_DATA_LEN) { ++ return CKYAPDU_SetShortReceiveLen(apdu, (unsigned short) ++ (recvlen & 0xffff)); ++ } ++ return CKYDATATOOLONG; ++} ++ ++/* ++ * Append Le, If Le=0, treat it as 256 (CKYAPD_MAX_DATA_LEN) ++ */ ++CKYStatus + CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen) + { ++ /* If we already have a data buffer, make sure that we aren't already ++ * using T1 encoding */ ++ if (CKYBuffer_Size(&apdu->apduBuf) > CKYAPDU_MIN_LEN) { ++ if (CKYBuffer_GetChar(&apdu->apduBuf, CKY_LC_OFFSET) == 0) { ++ /* we are using T1 encoding, use AppendShort*/ ++ return CKYBuffer_AppendShort(&apdu->apduBuf, ++ recvlen ? (unsigned short) recvlen: CKYAPDU_MAX_DATA_LEN); ++ } ++ } + return CKYBuffer_AppendChar(&apdu->apduBuf, recvlen); + } + ++/* ++ * Append a short Le. If Le be encoded with just T0, do so. If Le=0 treat ++ * it as 65536 (CKYAPDU_MAX_T1_DATA_LEN) ++ */ ++CKYStatus ++CKYAPDU_AppendShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen) ++{ ++ CKYStatus ret; ++ /* If we already have a data buffer, it's encoding affects ours */ ++ if (CKYBuffer_Size(&apdu->apduBuf) > CKYAPDU_MIN_LEN) { ++ /* CKY_LC_OFFSET == 0 means T1, otherwise it's T0 */ ++ if (CKYBuffer_GetChar(&apdu->apduBuf, CKY_LC_OFFSET) != 0) { ++ /* remember 0 is 65536 here */ ++ if ((recvlen == 0) || (recvlen > CKYAPDU_MAX_DATA_LEN)) { ++ /* we can't a encode T1 receive length if we already have a ++ * T0 encoded buffer data */ ++ return CKYDATATOOLONG; ++ } ++ /* T0 encoding */ ++ return CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)recvlen&0xff); ++ } ++ /* T1 encoding */ ++ return CKYBuffer_AppendShort(&apdu->apduBuf, recvlen); ++ } ++ /* if length fits in a bit and we aren't forced into T1 encoding, use ++ * T0 */ ++ if ((recvlen != 0) && (recvlen <= CKYAPDU_MAX_DATA_LEN)) { ++ return CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)recvlen&0xff); ++ } ++ /* write the T1 encoding marker */ ++ ret = CKYBuffer_AppendChar(&apdu->apduBuf, (CKYByte)0); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ /* T1 encoded length */ ++ return CKYBuffer_AppendShort(&apdu->apduBuf, recvlen); ++} ++ ++CKYStatus ++CKYAPDU_AppendReceiveLength(CKYAPDU *apdu, CKYSize recvlen) ++{ ++ if (recvlen > CKYAPDU_MAX_T1_DATA_LEN) { ++ return CKYDATATOOLONG; ++ } ++ return CKYAPDU_AppendShortReceiveLen(apdu, ++ (unsigned short)(recvlen & 0xffff)); ++} ++ + + void + CKY_SetName(const char *p) +diff -up ./src/libckyapplet/cky_base.h.p15 ./src/libckyapplet/cky_base.h +--- ./src/libckyapplet/cky_base.h.p15 2015-07-06 10:27:55.777827135 -0700 ++++ ./src/libckyapplet/cky_base.h 2015-07-06 10:27:55.789826908 -0700 +@@ -32,6 +32,8 @@ typedef unsigned char CKYByte; + /* Bool type */ + typedef unsigned char CKYBool; + ++typedef unsigned long CKYBitFlags; ++ + #define CKYBUFFER_PUBLIC \ + unsigned long reserved1;\ + unsigned long reserved2;\ +@@ -93,6 +95,8 @@ typedef enum { + * (command) sent. ADPUIOStatus has more info on + * why the APDU failed */ + CKYINVALIDARGS, /* Caller passed in bad args */ ++ CKYINVALIDDATA, /* Data supplied was invalid */ ++ CKYUNSUPPORTED, /* Requested Operation or feature is not supported */ + } CKYStatus; + + /* +@@ -107,12 +111,56 @@ typedef enum { + #define CKY_LE_OFFSET 4 + + #define CKYAPDU_MAX_DATA_LEN 256 ++#define CKYAPDU_MAX_T1_DATA_LEN 65536 + #define CKYAPDU_MIN_LEN 4 + #define CKYAPDU_HEADER_LEN 5 + #define CKYAPDU_MAX_LEN (CKYAPDU_HEADER_LEN+CKYAPDU_MAX_DATA_LEN) + #define CKY_MAX_ATR_LEN 32 + #define CKY_OUTRAGEOUS_MALLOC_SIZE (1024*1024) + ++#define P15FlagsPrivate 0x00000001 ++#define P15FlagsModifiable 0x00000002 ++ ++#define P15UsageEncrypt 0x00000001 ++#define P15UsageDecrypt 0x00000002 ++#define P15UsageSign 0x00000004 ++#define P15UsageSignRecover 0x00000008 ++#define P15UsageWrap 0x00000010 ++#define P15UsageUnwrap 0x00000020 ++#define P15UsageVerify 0x00000040 ++#define P15UsageVerifyRecover 0x00000080 ++#define P15UsageDerive 0x00000100 ++#define P15UsageNonRepudiation 0x00000200 ++ ++#define P15AccessSensitive 0x00000001 ++#define P15AccessExtractable 0x00000002 ++#define P15AccessAlwaysSenstive 0x00000004 ++#define P15AccessNeverExtractable 0x00000008 ++#define P15AccessLocal 0x00000010 ++ ++#define P15PinCaseSensitive 0x00000001 ++#define P15PinLocal 0x00000002 ++#define P15PinChangeDisabled 0x00000004 ++#define P15PinUnblockDisabled 0x00000008 ++#define P15PinInitialized 0x00000010 ++#define P15PinNeedsPadding 0x00000020 ++#define P15PinUnblockingPin 0x00000040 ++#define P15PinSOPin 0x00000080 ++#define P15PinDisableAllowed 0x00000100 ++ ++typedef enum {P15PinBCD=0, P15PinASCIINum=1, P15PinUTF8=2} P15PinType; ++ ++typedef struct _P15PinInfo { ++ CKYBitFlags pinFlags; ++ P15PinType pinType; ++ CKYByte minLength; ++ CKYByte storedLength; ++ unsigned long maxLength; ++ CKYByte pinRef; ++ CKYByte padChar; ++} P15PinInfo; ++ ++ + /* + * allow direct inclusion in C++ files + */ +@@ -278,7 +326,11 @@ CKYStatus CKYAPDU_AppendSendDataBuffer(C + /* set Le in the APDU header to the amount of bytes expected to be + * returned. */ + CKYStatus CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKYByte recvlen); ++CKYStatus CKYAPDU_SetShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen); ++CKYStatus CKYAPDU_SetReceiveLength(CKYAPDU *apdu, CKYSize recvlen); + CKYStatus CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen); ++CKYStatus CKYAPDU_AppendShortReceiveLen(CKYAPDU *apdu, unsigned short recvlen); ++CKYStatus CKYAPDU_AppendReceiveLength(CKYAPDU *apdu, CKYSize recvlen); + + /* set the parent loadmodule name */ + void CKY_SetName(const char *name); +diff -up ./src/libckyapplet/cky_card.c.p15 ./src/libckyapplet/cky_card.c +--- ./src/libckyapplet/cky_card.c.p15 2015-07-06 10:27:55.778827116 -0700 ++++ ./src/libckyapplet/cky_card.c 2015-07-06 10:27:55.790826889 -0700 +@@ -910,6 +910,7 @@ ckyCardConnection_init(CKYCardConnection + conn->protocol = SCARD_PROTOCOL_T0; + } + ++ + CKYCardConnection * + CKYCardConnection_Create(const CKYCardContext *ctx) + { +@@ -984,6 +985,12 @@ CKYCardConnection_IsConnected(const CKYC + return (conn->cardHandle != 0); + } + ++unsigned long ++CKYCardConnection_GetProtocol(const CKYCardConnection *conn) ++{ ++ return conn->protocol; ++} ++ + CKYStatus + ckyCardConnection_reconnectRaw(CKYCardConnection *conn, unsigned long init) + { +@@ -996,6 +1003,7 @@ ckyCardConnection_reconnectRaw(CKYCardCo + conn->lastError = rv; + return CKYSCARDERR; + } ++ conn->protocol = protocol; + return CKYSUCCESS; + } + +diff -up ./src/libckyapplet/cky_card.h.p15 ./src/libckyapplet/cky_card.h +--- ./src/libckyapplet/cky_card.h.p15 2015-07-06 10:27:55.766827342 -0700 ++++ ./src/libckyapplet/cky_card.h 2015-07-06 10:27:55.790826889 -0700 +@@ -116,6 +116,7 @@ CKYStatus CKYCardConnection_ExchangeAPDU + CKYStatus CKYCardConnection_Connect(CKYCardConnection *connection, + const char *readerName); + CKYStatus CKYCardConnection_Disconnect(CKYCardConnection *connection); ++unsigned long CKYCardConnection_GetProtocol(const CKYCardConnection *conn); + CKYBool CKYCardConnection_IsConnected(const CKYCardConnection *connection); + CKYStatus CKYCardConnection_Reconnect(CKYCardConnection *connection); + CKYStatus CKYCardConnection_GetStatus(CKYCardConnection *connection, +diff -up ./src/libckyapplet/cky_factory.c.p15 ./src/libckyapplet/cky_factory.c +--- ./src/libckyapplet/cky_factory.c.p15 2015-07-06 10:27:55.778827116 -0700 ++++ ./src/libckyapplet/cky_factory.c 2015-07-06 10:27:55.791826870 -0700 +@@ -29,7 +29,7 @@ CKYAPDUFactory_SelectFile(CKYAPDU *apdu, + const CKYBuffer *AID) + { + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); +- CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE); ++ CKYAPDU_SetINS(apdu, ISO_INS_SELECT_FILE); + CKYAPDU_SetP1(apdu, p1); + CKYAPDU_SetP2(apdu, p2); + return CKYAPDU_SetSendDataBuffer(apdu, AID); +@@ -40,7 +40,7 @@ CKYAPDUFactory_SelectCardManager(CKYAPDU + { + CKYByte c = 0; + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); +- CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE); ++ CKYAPDU_SetINS(apdu, ISO_INS_SELECT_FILE); + CKYAPDU_SetP1(apdu, 0x04); + CKYAPDU_SetP2(apdu, 0x00); + /* I can't find the documentation for this, but if you pass an empty +@@ -57,7 +57,7 @@ CKYStatus + CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu) + { + CKYAPDU_SetCLA(apdu, CKY_CLASS_GLOBAL_PLATFORM); +- CKYAPDU_SetINS(apdu, CKY_INS_GET_DATA); ++ CKYAPDU_SetINS(apdu, ISO_INS_GET_DATA); + CKYAPDU_SetP1(apdu, 0x9f); + CKYAPDU_SetP2(apdu, 0x7f); + return CKYAPDU_SetReceiveLen(apdu, CKY_SIZE_GET_CPLCDATA); +@@ -707,6 +707,7 @@ fail: + CKYBuffer_FreeData(&buf); + return ret; + } ++ + CKYStatus + CACAPDUFactory_GetProperties(CKYAPDU *apdu) + { +@@ -718,37 +719,6 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap + } + + CKYStatus +-CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const char *pin) +-{ +- CKYStatus ret; +- CKYSize size; +- +- CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); +- CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN); +- CKYAPDU_SetP1(apdu, 0x00); +- CKYAPDU_SetP2(apdu, keyRef); +- /* no pin, send an empty buffer */ +- if (!pin) { +- return CKYAPDU_SetReceiveLen(apdu, 0); +- } +- +- /* all CAC pins are 8 bytes exactly. If to long, truncate it */ +- size = strlen(pin); +- if (size > 8) { +- size = 8; +- } +- ret = CKYAPDU_SetSendData(apdu, (unsigned char *) pin, size); +- /* if too short, pad it */ +- if ((ret == CKYSUCCESS) && (size < 8)) { +- static const unsigned char pad[]= { 0xff , 0xff, 0xff ,0xff, +- 0xff, 0xff, 0xff, 0xff }; +- return CKYAPDU_AppendSendData(apdu, pad, 8-size); +- } +- return ret; +- +-} +- +-CKYStatus + PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, + CKYByte key, int len, const CKYBuffer *data) + { +@@ -807,3 +777,109 @@ fail: + return ret; + } + ++CKYStatus ++P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const CKYBuffer *pin) ++{ ++ CKYStatus ret; ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN); ++ CKYAPDU_SetP1(apdu, 0x00); ++ CKYAPDU_SetP2(apdu, keyRef); ++ /* no pin, send an empty buffer */ ++ if (CKYBuffer_Size(pin) == 0) { ++ return CKYAPDU_SetReceiveLen(apdu, 0); ++ } ++ ++ /* all CAC pins are 8 bytes exactly. If to long, truncate it */ ++ ret = CKYAPDU_SetSendDataBuffer(apdu, pin); ++ return ret; ++ ++} ++ ++CKYStatus ++P15APDUFactory_ReadRecord(CKYAPDU *apdu, CKYByte record, CKYByte short_ef, ++ CKYByte flags, CKYByte count) ++{ ++ CKYByte control; ++ ++ control = (short_ef << 3) & 0xf8; ++ control |= flags & 0x07; ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, ISO_INS_READ_RECORD); ++ CKYAPDU_SetP1(apdu, record); ++ CKYAPDU_SetP2(apdu, control); ++ return CKYAPDU_SetReceiveLen(apdu, count); ++} ++ ++CKYStatus ++P15APDUFactory_ReadBinary(CKYAPDU *apdu, unsigned short offset, ++ CKYByte short_ef, CKYByte flags, CKYByte count) ++{ ++ CKYByte p1 = 0,p2 = 0; ++ unsigned short max_offset = 0; ++ ++ if (flags & P15_USE_SHORT_EF) { ++ max_offset = 0xff; ++ p1 = P15_USE_SHORT_EF | (short_ef & 0x7); ++ p2 = offset & 0xff; ++ } else { ++ max_offset = 0x7fff; ++ p1 = (offset >> 8) & 0x7f; ++ p2 = offset & 0xff; ++ } ++ if (offset > max_offset) { ++ return CKYINVALIDARGS; ++ } ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, ISO_INS_READ_BINARY); ++ CKYAPDU_SetP1(apdu, p1); ++ CKYAPDU_SetP2(apdu, p2); ++ return CKYAPDU_SetReceiveLen(apdu, count); ++} ++ ++CKYStatus ++P15APDUFactory_ManageSecurityEnvironment(CKYAPDU *apdu, CKYByte p1, CKYByte p2, ++ CKYByte keyRef) ++{ ++ CKYByte param[3]; ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, ISO_INS_MANAGE_SECURITY_ENVIRONMENT); ++ CKYAPDU_SetP1(apdu, p1); ++ CKYAPDU_SetP2(apdu, p2); ++ param[0] = 0x83; ++ param[1] = 1; ++ param[2] = keyRef; ++ return CKYAPDU_SetSendData(apdu, param, sizeof param); ++} ++ ++CKYStatus ++P15APDUFactory_PerformSecurityOperation(CKYAPDU *apdu, CKYByte dir, ++ int chain, CKYSize retLen, const CKYBuffer *data) ++{ ++ CKYByte p1,p2; ++ CKYStatus ret; ++ ++ CKYAPDU_SetCLA(apdu, chain ? CKY_CLASS_ISO7816_CHAIN : ++ CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, ISO_INS_PERFORM_SECURITY_OPERATION); ++ if (dir == CKY_DIR_DECRYPT) { ++ p1 = ISO_PSO_DECRYPT_P1; ++ p2 = ISO_PSO_DECRYPT_P2; ++ } else { ++ p1 = ISO_PSO_SIGN_P1; ++ p2 = ISO_PSO_SIGN_P2; ++ } ++ CKYAPDU_SetP1(apdu, p1); ++ CKYAPDU_SetP2(apdu, p2); ++ ret = CKYAPDU_SetSendDataBuffer(apdu, data); ++ if (ret == CKYSUCCESS && (chain == 0) && retLen != 0) { ++ ret = CKYAPDU_AppendReceiveLength(apdu, retLen); ++ } ++ return ret; ++} ++ ++ +diff -up ./src/libckyapplet/cky_factory.h.p15 ./src/libckyapplet/cky_factory.h +--- ./src/libckyapplet/cky_factory.h.p15 2015-07-06 10:27:55.779827097 -0700 ++++ ./src/libckyapplet/cky_factory.h 2015-07-06 10:27:55.791826870 -0700 +@@ -35,8 +35,31 @@ + * Applet Instruction Bytes + */ + /* Card Manager */ +-#define CKY_INS_SELECT_FILE 0xa4 +-#define CKY_INS_GET_DATA 0xca ++#define ISO_INS_SELECT_FILE 0xa4 ++#define ISO_INS_GET_DATA 0xca ++#define ISO_INS_READ_BINARY 0xb0 ++#define ISO_INS_READ_RECORD 0xb2 ++#define ISO_INS_MANAGE_SECURITY_ENVIRONMENT 0x22 ++#define ISO_INS_PERFORM_SECURITY_OPERATION 0x2a ++ ++/* ISO Parameters: */ ++#define ISO_LOGIN_LOCAL 0x80 ++#define ISO_LOGIN_GLOBAL 0x00 ++#define ISO_MSE_SET 0x01 ++#define ISO_MSE_STORE 0xf2 ++#define ISO_MSE_RESTORE 0xf3 ++#define ISO_MSE_ERASE 0xf4 ++#define ISO_MSE_QUAL_VERIFY 0x80 ++#define ISO_MSE_QUAL_COMPUTE 0x40 ++#define ISO_MSE_AUTH 0xa4 ++#define ISO_MSE_SIGN 0xb6 ++#define ISO_MSE_KEA 0xb8 ++#define ISO_PSO_SIGN_P1 0x9e ++#define ISO_PSO_SIGN_P2 0x9a ++#define ISO_PSO_ENCRYPT_P1 0x86 ++#define ISO_PSO_ENCRYPT_P2 0x80 ++#define ISO_PSO_DECRYPT_P1 0x80 ++#define ISO_PSO_DECRYPT_P2 0x86 + + /* deprecated */ + #define CKY_INS_SETUP 0x2A +@@ -84,6 +107,7 @@ + #define CKY_INS_SEC_READ_IOBUF 0x08 + #define CKY_INS_SEC_START_ENROLLMENT 0x0C + ++ + /* CAC */ + #define CAC_INS_GET_CERTIFICATE 0x36 + #define CAC_INS_SIGN_DECRYPT 0x42 +@@ -94,11 +118,8 @@ + #define CAC_SIZE_GET_PROPERTIES 48 + #define CAC_P1_STEP 0x80 + #define CAC_P1_FINAL 0x00 +-#define CAC_LOGIN_GLOBAL 0x00 + + /* PIV */ +-#define PIV_LOGIN_LOCAL 0x80 +-#define PIV_LOGIN_GLOBAL CAC_LOGIN_GLOBAL + #define PIV_INS_GEN_AUTHENTICATE 0x87 + + /* +@@ -121,7 +142,7 @@ + /* functions */ + #define CKY_CIPHER_INIT 1 + #define CKY_CIPHER_PROCESS 2 +-#define CKY_CIPHER_FINAL 3 ++#define CKY_CIPHER_FINAL 3 + #define CKY_CIPHER_ONE_STEP 4 /* init and final in one APDU */ + + /* modes */ +@@ -173,6 +194,18 @@ + #define CKY_CARDM_MANAGER_LOCKED 0x7f + #define CKY_CARDM_MANAGER_TERMINATED 0xff + ++/* Read Record Flags */ ++#define P15_READ_P1 0x4 ++#define P15_READ_P1_TO_LAST 0x5 ++#define P15_READ_LAST_TO_P1 0x6 ++#define P15_READ_FIRST 0x0 ++#define P15_READ_LAST 0x1 ++#define P15_READ_NEXT 0x2 ++#define P15_READ_PREV 0x3 ++ ++/* Read Binary Flags */ ++#define P15_USE_SHORT_EF 0x80 ++ + /* + * The following factories 'Fill in' APDUs for each of the + * functions described below. Nonces are not automatically added. +@@ -234,17 +267,28 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C + + CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, + const CKYBuffer *data); +-CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, +- const char *pin); + CKYStatus CACAPDUFactory_GetCertificate(CKYAPDU *apdu, CKYSize size); + CKYStatus CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, + CKYByte type, CKYByte count); + CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu); ++ + CKYStatus PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, + CKYByte count); + CKYStatus PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, + CKYByte key, int len, const CKYBuffer *data); + ++CKYStatus P15APDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, ++ const CKYBuffer *pin); ++CKYStatus P15APDUFactory_ReadRecord(CKYAPDU *apdu, CKYByte record, ++ CKYByte short_ef, CKYByte flags, CKYByte count); ++CKYStatus P15APDUFactory_ReadBinary(CKYAPDU *apdu, unsigned short offset, ++ CKYByte short_ef, CKYByte flags, CKYByte count); ++CKYStatus P15APDUFactory_ManageSecurityEnvironment(CKYAPDU *apdu, ++ CKYByte p1, CKYByte p2, CKYByte key); ++CKYStatus P15APDUFactory_PerformSecurityOperation(CKYAPDU *apdu, CKYByte dir, ++ int chain, CKYSize retLen, const CKYBuffer *data); ++ ++ + CKY_END_PROTOS + + #endif /* CKY_FACTORY_H */ diff --git a/SOURCES/coolkey-1.1.0-rhel7-alt-cac.patch b/SOURCES/coolkey-1.1.0-rhel7-alt-cac.patch new file mode 100644 index 0000000..f352f42 --- /dev/null +++ b/SOURCES/coolkey-1.1.0-rhel7-alt-cac.patch @@ -0,0 +1,858 @@ +diff -up ./src/coolkey/coolkey.cpp.alt-cac ./src/coolkey/coolkey.cpp +--- ./src/coolkey/coolkey.cpp.alt-cac 2016-12-01 15:37:49.106167768 -0800 ++++ ./src/coolkey/coolkey.cpp 2016-12-01 15:37:49.113167892 -0800 +@@ -80,9 +80,16 @@ ecMechanismList[] = { + {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}}, + {CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} } + }; ++static const MechInfo ++allMechanismList[] = { ++ {CKM_RSA_PKCS, { 1024, 4096, CKF_HW | CKF_SIGN | CKF_DECRYPT } }, ++ {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}}, ++ {CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} } ++}; + + unsigned int numRSAMechanisms = sizeof(rsaMechanismList)/sizeof(MechInfo); + unsigned int numECMechanisms = sizeof(ecMechanismList)/sizeof(MechInfo); ++unsigned int numAllMechanisms = sizeof(allMechanismList)/sizeof(MechInfo); + + /* ------------------------------------------------------------ */ + +@@ -382,13 +389,22 @@ C_GetMechanismList(CK_SLOT_ID slotID, CK + return CKR_TOKEN_NOT_PRESENT; + } + +- if ( slot->getIsECC()) { ++ switch (slot->getAlgs()) { ++ case ALG_ECC|ALG_RSA: ++ mechanismList = allMechanismList; ++ numMechanisms = numAllMechanisms; ++ break; ++ case ALG_ECC: + mechanismList = ecMechanismList; + numMechanisms = numECMechanisms; +- } else { ++ break; ++ case ALG_NONE: ++ case ALG_RSA: ++ default: + mechanismList = rsaMechanismList; + numMechanisms = numRSAMechanisms; +- } ++ break; ++ } + + if( pMechanismList != NULL ) { + if( *pulCount < numMechanisms ) { +@@ -438,13 +454,22 @@ C_GetMechanismInfo(CK_SLOT_ID slotID, CK + return CKR_TOKEN_NOT_PRESENT; + } + +- if ( slot->getIsECC()) { ++ switch (slot->getAlgs()) { ++ case ALG_ECC|ALG_RSA: ++ mechanismList = allMechanismList; ++ numMechanisms = numAllMechanisms; ++ break; ++ case ALG_ECC: + mechanismList = ecMechanismList; + numMechanisms = numECMechanisms; +- } else { ++ break; ++ case ALG_NONE: ++ case ALG_RSA: ++ default: + mechanismList = rsaMechanismList; + numMechanisms = numRSAMechanisms; +- } ++ break; ++ } + + for(unsigned int i=0; i < numMechanisms; ++i ) { + if( mechanismList[i].mech == type ) { +diff -up ./src/coolkey/object.cpp.alt-cac ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.alt-cac 2016-12-01 15:37:49.097167608 -0800 ++++ ./src/coolkey/object.cpp 2016-12-01 15:37:49.114167910 -0800 +@@ -1232,7 +1232,7 @@ Reader::Reader(unsigned long muscleObjID + } + + +-CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert) : ++CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert,bool isPIV) : + PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16, + instance | 0x400) + { +@@ -1242,7 +1242,9 @@ CACPrivKey::CACPrivKey(CKYByte instance, + + /* So we know what the key is supposed to be used for based on + * the instance */ +- if (instance == 2) { ++ /* instance 2 is usually a decryption cert. >2 are usually old decryption ++ * certs */ ++ if (instance == 2 || (instance > (isPIV ? 3 : 2))) { + decrypt = TRUE; + } + +@@ -1305,8 +1307,8 @@ CACPrivKey::CACPrivKey(CKYByte instance, + CKYBuffer_FreeData(¶m2); + } + +-CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert) : +- PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16, ++CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert, bool isPIV) : ++ PKCS11Object( ((int)'k') << 24 | ((int)(instance+'a')) << 16, + instance | 0x500) + { + CKYBuffer id; +@@ -1315,7 +1317,7 @@ CACPubKey::CACPubKey(CKYByte instance, c + + /* So we know what the key is supposed to be used for based on + * the instance */ +- if (instance == 2) { ++ if (instance == 2 || (instance > (isPIV ? 3 : 2))) { + encrypt = TRUE; + } + +@@ -1359,6 +1361,9 @@ CACPubKey::CACPubKey(CKYByte instance, c + setAttribute(CKA_EC_POINT, ¶m1); + setAttribute(CKA_EC_PARAMS, ¶m2); + setAttributeULong(CKA_KEY_TYPE, CKK_EC); ++ setAttributeBool(CKA_VERIFY_RECOVER, FALSE); ++ setAttributeBool(CKA_ENCRYPT, FALSE); ++ setAttributeBool(CKA_DERIVE, encrypt); + break; + default: + break; +@@ -1376,6 +1381,26 @@ static const char *CAC_Label[] = { + "CAC ID Certificate", + "CAC Email Signature Certificate", + "CAC Email Encryption Certificate", ++ "CAC Cert 3", ++ "CAC Cert 4", ++ "CAC Cert 5", ++ "CAC Cert 6", ++ "CAC Cert 7", ++ "CAC Cert 8", ++ "CAC Cert 9", ++}; ++ ++static const char *PIV_Label[] = { ++ "PIV ID Certificate", ++ "PIV Email Signature Certificate", ++ "PIV Email Encryption Certificate", ++ "PIV Card Authentication Certificate", ++ "PIV Cert 4", ++ "PIV Cert 5", ++ "PIV Cert 6", ++ "PIV Cert 7", ++ "PIV Cert 8", ++ "PIV Cert 9", + }; + + static const unsigned char CN_DATA[] = { 0x55, 0x4, 0x3 }; +@@ -1454,7 +1479,7 @@ GetUserName(const CKYBuffer *dn) + return string; + } + +-CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert) : ++CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert, bool isPIV) : + PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, + instance | 0x600) + { +@@ -1471,7 +1496,7 @@ CACCert::CACCert(CKYByte instance, const + setAttribute(CKA_ID, &id); + CKYBuffer_FreeData(&id); + setAttributeULong(CKA_CERTIFICATE_TYPE, CKC_X_509); +- setAttribute(CKA_LABEL, CAC_Label[instance]); ++ setAttribute(CKA_LABEL, isPIV ? PIV_Label[instance] : CAC_Label[instance]); + + CKYBuffer derSerial; CKYBuffer_InitEmpty(&derSerial); + CKYBuffer derSubject; CKYBuffer_InitEmpty(&derSubject); +diff -up ./src/coolkey/object.h.alt-cac ./src/coolkey/object.h +--- ./src/coolkey/object.h.alt-cac 2016-12-01 15:37:49.087167430 -0800 ++++ ./src/coolkey/object.h 2016-12-01 15:37:49.115167928 -0800 +@@ -219,17 +219,17 @@ class Cert : public PKCS11Object { + + class CACPrivKey : public PKCS11Object { + public: +- CACPrivKey(CKYByte instance, const PKCS11Object &cert); ++ CACPrivKey(CKYByte instance, const PKCS11Object &cert, bool isPIV); + }; + + class CACPubKey : public PKCS11Object { + public: +- CACPubKey(CKYByte instance, const PKCS11Object &cert); ++ CACPubKey(CKYByte instance, const PKCS11Object &cert, bool isPIV); + }; + + class CACCert : public PKCS11Object { + public: +- CACCert(CKYByte instance, const CKYBuffer *derCert); ++ CACCert(CKYByte instance, const CKYBuffer *derCert, bool isPIV); + }; + + typedef enum { PK15StateInit, PK15StateNeedObject, +diff -up ./src/coolkey/slot.cpp.alt-cac ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.alt-cac 2016-12-01 15:37:49.110167839 -0800 ++++ ./src/coolkey/slot.cpp 2016-12-01 15:57:37.307994776 -0800 +@@ -415,8 +415,9 @@ Slot::Slot(const char *readerName_, Log + slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), + isVersion1Key(false), needLogin(false), fullTokenName(false), + mCoolkey(false), mOldCAC(false), mCACLocalLogin(false), +- pivContainer(-1), pivKey(-1), mECC(false), p15aid(0), p15odfAddr(0), +- p15tokenInfoAddr(0), p15Instance(0), ++ pivContainer(-1), pivKey(-1), maxCacCerts(MAX_CERT_SLOTS), ++ algs(ALG_NONE), p15aid(0), p15odfAddr(0), p15tokenInfoAddr(0), ++ p15Instance(0), + #ifdef USE_SHMEM + shmem(readerName_), + #endif +@@ -776,6 +777,7 @@ Slot::connectToToken() + state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; + isVersion1Key = 0; + needLogin = true; ++ maxCacCerts = MAX_CERT_SLOTS; + mCoolkey = 0; + mOldCAC = 0; + mCACLocalLogin = getPIVLoginType(); +@@ -927,8 +929,12 @@ Slot::getCACAid() + } + /* yes, fill in the old applets */ + mOldCAC = true; ++ maxCacCerts = 1; + for (i=1; i< MAX_CERT_SLOTS; i++) { +- CACApplet_SelectPKI(conn, &cardAID[i], i, NULL); ++ status = CACApplet_SelectPKI(conn, &cardAID[i], i, NULL); ++ if (status == CKYSUCCESS) { ++ maxCacCerts = i+1; ++ } + } + return CKYSUCCESS; + } +@@ -986,6 +992,7 @@ Slot::getCACAid() + if (certSlot == 0) { + status = CKYAPDUFAIL; /* probably neeed a beter error code */ + } ++ maxCacCerts = certSlot; + + done: + CKYBuffer_FreeData(&tBuf); +@@ -2168,12 +2175,11 @@ Slot::addKeyObject(list& o + } + keyObj.completeKey(*iter); + +- /* For now this is how we determine what type of key. +- Also for now, allow only one or the other */ ++ /* use key object to determine what algorithms we support */ + if ( keyObj.getKeyType() == PKCS11Object::ecc) { +- mECC = true; ++ algs = (SlotAlgs) (algs | ALG_ECC); + } else { +- mECC = false; ++ algs = (SlotAlgs) (algs | ALG_RSA); + } + + } +@@ -2205,7 +2211,7 @@ Slot::addCertObject(list& + void + Slot::unloadObjects() + { +- mECC = false; ++ algs = ALG_NONE; + tokenObjects.clear(); + free(personName); + personName = NULL; +@@ -2269,29 +2275,42 @@ Slot::unloadObjects() + // Shared memory segments are fixed size (equal to the object memory size of + // the token). + // ++// ++// ++ ++struct SlotDataPair { ++ unsigned long dataOffset; ++ unsigned long dataSize; ++}; + + struct SlotSegmentHeader { + unsigned short version; + unsigned short headerSize; + unsigned char valid; +- unsigned char reserved; ++ unsigned char firstCacCert; + unsigned char cuid[10]; +- unsigned short reserved2; ++ ++ unsigned short reserved; + unsigned short dataVersion; + unsigned short dataHeaderOffset; + unsigned short dataOffset; + unsigned long dataHeaderSize; + unsigned long dataSize; +- unsigned long cert2Offset; +- unsigned long cert2Size; ++ unsigned long nextDataOffset; ++ SlotDataPair cacCerts[MAX_CERT_SLOTS]; + }; + ++const unsigned char NOT_A_CAC=0xff; /* place in firstCacCert field */ ++const unsigned short CAC_DATA_VERSION=2; ++ ++ + #define MAX_OBJECT_STORE_SIZE 15000 + // + // previous development versions used a segment prefix of + // "coolkeypk11s" + // +-#define SEGMENT_PREFIX "coolkeypk11s" ++#define SEGMENT_PREFIX "coolkeypk11t" // update segment since the old cache was ++ // incompatible + #define CAC_FAKE_CUID "CAC Certs" + SlotMemSegment::SlotMemSegment(const char *readerName): + segmentAddr(NULL), segmentSize(0), segment(NULL) +@@ -2320,9 +2339,8 @@ SlotMemSegment::SlotMemSegment(const cha + return; + } + +- SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr; + if (needInit) { +- segmentHeader->valid = 0; ++ clearValid(0); + } + segmentSize = segment->getSHMemSize(); + } +@@ -2396,6 +2414,18 @@ SlotMemSegment::getDataVersion() const + return segmentHeader->dataVersion; + } + ++unsigned char ++SlotMemSegment::getFirstCacCert() const ++{ ++ if (!segment) { ++ return NOT_A_CAC; ++ } ++ ++ SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr; ++ ++ return segmentHeader->firstCacCert; ++} ++ + void + SlotMemSegment::setVersion(unsigned short version) + { +@@ -2419,6 +2449,18 @@ SlotMemSegment::setDataVersion(unsigned + segmentHeader->dataVersion = version; + } + ++void ++SlotMemSegment::setFirstCacCert(unsigned char firstCacCert) ++{ ++ if (!segment) { ++ return; ++ } ++ ++ SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr; ++ ++ segmentHeader->firstCacCert = firstCacCert; ++} ++ + bool + SlotMemSegment::isValid() const + { +@@ -2493,23 +2535,13 @@ SlotMemSegment::readCACCert(CKYBuffer *o + int size; + CKYByte *data; + +- switch (instance) { +- case 0: +- data = (CKYByte *) &segmentAddr[segmentHeader->dataHeaderOffset]; +- size = segmentHeader->dataHeaderSize; +- break; +- case 1: +- data = (CKYByte *) &segmentAddr[segmentHeader->dataOffset]; +- size = segmentHeader->dataSize; +- break; +- case 2: +- data = (CKYByte *) &segmentAddr[segmentHeader->cert2Offset]; +- size = segmentHeader->cert2Size; +- break; +- default: ++ if (instance >= MAX_CERT_SLOTS) { + CKYBuffer_Resize(objData, 0); + return; + } ++ data = (CKYByte *) &segmentAddr[segmentHeader->cacCerts[instance] ++ .dataOffset]; ++ size = segmentHeader->cacCerts[instance].dataSize; + CKYBuffer_Replace(objData, 0, data, size); + } + +@@ -2523,30 +2555,20 @@ SlotMemSegment::writeCACCert(const CKYBu + SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr; + int size = CKYBuffer_Size(data); + CKYByte *shmData; +- switch (instance) { +- case 0: +- segmentHeader->headerSize = sizeof *segmentHeader; +- segmentHeader->dataHeaderOffset = sizeof *segmentHeader; +- segmentHeader->dataHeaderSize = size; +- segmentHeader->dataOffset = segmentHeader->dataHeaderOffset + size; +- segmentHeader->dataSize = 0; +- segmentHeader->cert2Offset = segmentHeader->dataOffset; +- segmentHeader->cert2Size = 0; +- shmData = (CKYByte *) &segmentAddr[segmentHeader->dataHeaderOffset]; +- break; +- case 1: +- segmentHeader->dataSize = size; +- segmentHeader->cert2Offset = segmentHeader->dataOffset + size; +- segmentHeader->cert2Size = 0; +- shmData = (CKYByte *) &segmentAddr[segmentHeader->dataOffset]; +- break; +- case 2: +- segmentHeader->cert2Size = size; +- shmData = (CKYByte *) &segmentAddr[segmentHeader->cert2Offset]; +- break; +- default: ++ ++ if (instance >= MAX_CERT_SLOTS) { + return; + } ++ ++ if (segmentHeader->firstCacCert == NOT_A_CAC) { ++ segmentHeader->firstCacCert = instance; ++ } ++ unsigned long dataOffset = segmentHeader->nextDataOffset; ++ segmentHeader->cacCerts[instance].dataOffset = dataOffset; ++ segmentHeader->nextDataOffset += size; ++ segmentHeader->cacCerts[instance].dataSize = size; ++ shmData = (CKYByte *) &segmentAddr[dataOffset]; ++ + memcpy(shmData, CKYBuffer_Data(data), size); + } + +@@ -2558,15 +2580,18 @@ SlotMemSegment::clearValid(CKYByte insta + return; + } + SlotSegmentHeader *segmentHeader = (SlotSegmentHeader *)segmentAddr; +- switch (instance) { +- case 0: +- segmentHeader->headerSize = 0; +- segmentHeader->dataHeaderSize = 0; +- /* fall through */ +- case 1: +- segmentHeader->dataSize = 0; ++ ++ segmentHeader->headerSize = sizeof *segmentHeader; ++ segmentHeader->dataHeaderOffset = sizeof *segmentHeader; ++ segmentHeader->dataHeaderSize = 0; ++ segmentHeader->dataSize = 0; ++ for (int i=0; i < MAX_CERT_SLOTS; i++) { ++ segmentHeader->cacCerts[i].dataSize = 0; + } ++ segmentHeader->dataOffset = sizeof *segmentHeader; ++ segmentHeader->nextDataOffset = sizeof *segmentHeader; + segmentHeader->valid = 0; ++ segmentHeader->firstCacCert = NOT_A_CAC; + } + + void +@@ -2882,8 +2907,7 @@ berProcess(CKYBuffer *buf, int matchTag, + + + CKYStatus +-Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, +- bool throwException) ++Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize) + { + CKYStatus status; + CKYISOStatus apduRC; +@@ -2897,9 +2921,6 @@ Slot::readCACCertificateFirst(CKYBuffer + CKYBuffer_InitEmpty(&certInfo); + CKYBuffer_Resize(cert, 0); + status = PIVApplet_GetCertificate(conn, cert, pivContainer, &apduRC); +- if (throwException && (status != CKYSUCCESS)) { +- handleConnectionError(); +- } + /* actually, on success, we need to parse the certificate and find the + * propper tag */ + if (status == CKYSUCCESS) { +@@ -2940,10 +2961,10 @@ Slot::readCACCertificateFirst(CKYBuffer + if (mOldCAC) { + /* get the first 100 bytes of the cert */ + status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC); +- if (throwException && (status != CKYSUCCESS)) { +- handleConnectionError(); ++ if (status == CKYSUCCESS) { ++ return status; + } +- return status; ++ /* try to use CACApplet_ReadFile before we give up */ + } + + CKYBuffer tBuf; +@@ -2959,11 +2980,11 @@ Slot::readCACCertificateFirst(CKYBuffer + + /* handle the new CAC card read */ + /* read the TLV */ +- status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, NULL); ++ status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, &apduRC); + if (status != CKYSUCCESS) { + goto done; + } +- status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, NULL); ++ status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, &apduRC); + if (status != CKYSUCCESS) { + goto done; + } +@@ -3199,14 +3220,12 @@ Slot::loadCACCert(CKYByte instance) + CKYStatus status = CKYSUCCESS; + CKYBuffer cert; + CKYBuffer rawCert; +- CKYBuffer shmCert; + CKYSize nextSize; + + OSTime time = OSTimeNow(); + + CKYBuffer_InitEmpty(&cert); + CKYBuffer_InitEmpty(&rawCert); +- CKYBuffer_InitEmpty(&shmCert); + + // + // not all CAC cards have all the PKI instances +@@ -3215,78 +3234,24 @@ Slot::loadCACCert(CKYByte instance) + try { + selectCACApplet(instance, false); + } catch(PKCS11Exception& e) { +- // all CAC's must have instance '0', throw the error it +- // they don't. +- if (instance == 0) throw e; +- // If the CAC doesn't have instance '2', and we were updating +- // the shared memory, set it to valid now. +- if ((instance == 2) && !shmem.isValid()) { +- shmem.setValid(); +- } + return; + } + + log->log("CAC Cert %d: select CAC applet: %d ms\n", + instance, OSTimeNow() - time); + +- if (instance == 0) { +- readCACCertificateFirst(&rawCert, &nextSize, true); +- +- if(CKYBuffer_Size(&rawCert) <= 1) { +- handleConnectionError(); +- } +- log->log("CAC Cert %d: fetch CAC Cert: %d ms\n", +- instance, OSTimeNow() - time); +- } +- +- unsigned short dataVersion = 1; +- CKYBool needRead = 1; +- + /* see if it matches the shared memory */ +- if (shmem.isValid() && shmem.getDataVersion() == dataVersion) { +- shmem.readCACCert(&shmCert, instance); +- CKYSize certSize = CKYBuffer_Size(&rawCert); +- CKYSize shmCertSize = CKYBuffer_Size(&shmCert); +- const CKYByte *shmData = CKYBuffer_Data(&shmCert); +- +- if (instance != 0) { +- needRead = 0; +- } +- +- if (shmCertSize >= certSize) { +- if (memcmp(shmData, CKYBuffer_Data(&rawCert), certSize) == 0) { +- /* yes it does, no need to read the rest of the cert, use +- * the cache */ +- CKYBuffer_Replace(&rawCert, 0, shmData, shmCertSize); +- needRead = 0; +- } +- } +- if (!needRead && (shmCertSize == 0)) { ++ if (shmem.isValid() && shmem.getDataVersion() == CAC_DATA_VERSION) { ++ shmem.readCACCert(&rawCert, instance); ++ if (CKYBuffer_Size(&rawCert) == 0) { + /* no cert of this type, just return */ + return; + } +- } +- CKYBuffer_FreeData(&shmCert); +- +- if (needRead) { +- /* it doesn't, read the new cert and update the cache */ +- if (instance == 0) { +- shmem.clearValid(0); +- shmem.setVersion(SHMEM_VERSION); +- shmem.setDataVersion(dataVersion); +- } else { +- status = readCACCertificateFirst(&rawCert, &nextSize, false); +- +- if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) { +- /* CAC only requires the Certificate in pki '0' */ +- /* if pki '1' or '2' are empty, treat it as a non-fatal error*/ +- if (instance == 2) { +- /* we've attempted to read all the certs, shared memory +- * is now valid */ +- shmem.setValid(); +- } +- return; +- } ++ } else { ++ status = readCACCertificateFirst(&rawCert, &nextSize); ++ if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) { ++ /*this cert doesn't exist, go to the next one */ ++ return; + } + + if (nextSize) { +@@ -3298,9 +3263,6 @@ Slot::loadCACCert(CKYByte instance) + handleConnectionError(); + } + shmem.writeCACCert(&rawCert, instance); +- if (instance == 2) { +- shmem.setValid(); +- } + } + + +@@ -3368,14 +3330,17 @@ Slot::loadCACCert(CKYByte instance) + log->log("CAC Cert %d: Cert has been uncompressed: %d ms\n", + instance, OSTimeNow() - time); + +- CACCert certObj(instance, &cert); +- CACPrivKey privKey(instance, certObj); +- CACPubKey pubKey(instance, certObj); ++ bool isPIV = (bool)((state & PIV_CARD) == PIV_CARD); ++ CACCert certObj(instance, &cert, isPIV); ++ CACPrivKey privKey(instance, certObj, isPIV); ++ CACPubKey pubKey(instance, certObj, isPIV); + tokenObjects.push_back(privKey); + tokenObjects.push_back(pubKey); + tokenObjects.push_back(certObj); + if ( pubKey.getKeyType() == PKCS11Object::ecc) { +- mECC = 1; ++ algs = (SlotAlgs) (algs | ALG_ECC); ++ } else { ++ algs = (SlotAlgs) (algs | ALG_RSA); + } + + if (personName == NULL) { +@@ -3388,6 +3353,94 @@ Slot::loadCACCert(CKYByte instance) + } + + void ++Slot::initCACShMem(void) ++{ ++ bool failed = false; ++ ++ unsigned char firstCert = shmem.getFirstCacCert(); ++ ++ log->log("init CACShMem: \n"); ++ /* check to make sure the shared memory is initialized with a CAC card */ ++ if (shmem.isValid() && shmem.getDataVersion() == CAC_DATA_VERSION ++ && firstCert != NOT_A_CAC) { ++ CKYBuffer rawCert; ++ CKYBuffer shmCert; ++ CKYSize nextSize; ++ ++ log->log("init CACShMem: valid CAC cache found firstCert = %d\n", ++ firstCert); ++ CKYBuffer_InitEmpty(&rawCert); ++ CKYBuffer_InitEmpty(&shmCert); ++ ++ ++ /* yes, see if it's this cac card by comparing the first cert ++ * in the chain */ ++ ++ /* see if the first cert is in the expected slot */ ++ try { ++ selectCACApplet(firstCert, false); ++ } catch(PKCS11Exception& e) { ++ failed = true; ++ log->log("init CACShMem: applet select failed firstCert = %d\n", ++ firstCert); ++ } ++ if (!failed) { ++ CKYStatus status = readCACCertificateFirst(&rawCert, &nextSize); ++ if ((status != CKYSUCCESS) || CKYBuffer_Size(&rawCert) <= 1) { ++ failed = true; ++ log->log("init CACShMem: read Cert failed firstCert = %d\n", ++ firstCert); ++ } ++ } ++ if (!failed) { ++ shmem.readCACCert(&shmCert, firstCert); ++ CKYSize certSize = CKYBuffer_Size(&rawCert); ++ CKYSize shmCertSize = CKYBuffer_Size(&shmCert); ++ const CKYByte *shmData = CKYBuffer_Data(&shmCert); ++ ++ if (shmCertSize >= certSize) { ++ if (memcmp(shmData, CKYBuffer_Data(&rawCert), certSize) == 0) { ++ /* this card is cached, go on and use the cache */ ++ log->log("init CACShMem: entries match, using cache\n"); ++ CKYBuffer_FreeData(&rawCert); ++ CKYBuffer_FreeData(&shmCert); ++ return; ++ } ++ } ++ log->log("init CACShMem: no entry match certSize=%d" ++ " shmCertSize=%d\n",certSize, shmCertSize); ++ } ++ CKYBuffer_FreeData(&rawCert); ++ CKYBuffer_FreeData(&shmCert); ++ } ++ ++ log->log("init CACShMem: starting new cache valid=%d version=%d " ++ " firstCert=%d\n",shmem.isValid(), shmem.getDataVersion(), ++ firstCert); ++ /* cache is either invalid or for another card, start initializing it */ ++ shmem.clearValid(0); ++ shmem.setVersion(SHMEM_VERSION); ++ shmem.setDataVersion(CAC_DATA_VERSION); ++} ++ ++void ++Slot::verifyCACShMem(void) ++{ ++ /* if the memory is valid, then nothing to do */ ++ if (shmem.isValid()) { ++ return; ++ } ++ /* if we didn't find any cert fail */ ++ if (shmem.getFirstCacCert() == NOT_A_CAC) { ++ shmem.clearValid(0); ++ disconnect(); ++ throw PKCS11Exception(CKR_DEVICE_REMOVED); ++ } ++ /* we're all set, let others see our results */ ++ shmem.setValid(); ++} ++ ++void + Slot::loadObjects() + { + // throw away all token objects! +@@ -3406,9 +3459,11 @@ Slot::loadObjects() + std::list::iterator iter; + + if (state & GOV_CARD) { +- loadCACCert(0); +- loadCACCert(1); +- loadCACCert(2); ++ initCACShMem(); ++ for (int i=0; i < maxCacCerts; i++) { ++ loadCACCert(i); ++ } ++ verifyCACShMem(); + status = trans.end(); + loadReaderObject(); + return; +@@ -4720,10 +4775,6 @@ Slot::performECCSignature(CKYBuffer *out + CKYStatus status = trans.begin(conn); + if( status != CKYSUCCESS ) handleConnectionError(); + +- if (!mECC) { +- throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); +- } +- + CKYISOStatus result; + bool loginAttempted = false; + +@@ -4790,9 +4841,6 @@ Slot::performRSAOp(CKYBuffer *output, co + unsigned int keySize, const PKCS11Object *key, + CKYByte direction) + { +- if ( mECC ) { +- throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); +- } + + // + // establish a transaction +@@ -5145,10 +5193,6 @@ Slot::performECCKeyAgreement(CK_MECHANIS + CKYBuffer *publicDataBuffer, CKYBuffer *secretKeyBuffer, + const PKCS11Object *key, unsigned int keySize) + { +- if (!mECC) { +- throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); +- } +- + Transaction trans; + CKYStatus status = trans.begin(conn); + if( status != CKYSUCCESS ) handleConnectionError(); +diff -up ./src/coolkey/slot.h.alt-cac ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.alt-cac 2016-12-01 15:37:49.104167732 -0800 ++++ ./src/coolkey/slot.h 2016-12-01 15:37:49.117167964 -0800 +@@ -79,9 +79,11 @@ public: + bool CUIDIsEqual(const CKYBuffer *cuid) const; + unsigned short getVersion() const; + unsigned short getDataVersion() const; ++ unsigned char getFirstCacCert() const; + void setCUID(const CKYBuffer *cuid); + void setVersion(unsigned short version); + void setDataVersion(unsigned short version); ++ void setFirstCacCert(unsigned char firstCacCert); + bool isValid() const; + int size() const; + const unsigned char *getCUID() const; +@@ -90,6 +92,7 @@ public: + void setSize(int size); + void readData(CKYBuffer *data) const; + void writeData(const CKYBuffer *data); ++ void initCACHeaders(void); + void readCACCert(CKYBuffer *data, CKYByte instance) const; + void writeCACCert(const CKYBuffer *data, CKYByte instance); + void clearValid(CKYByte instance); +@@ -294,7 +297,13 @@ class CryptParams { + const CKYBuffer *paddedOutput) const = 0; + }; + +-#define MAX_CERT_SLOTS 3 ++#define MAX_CERT_SLOTS 10 ++typedef enum { ++ ALG_NONE= 0x0, ++ ALG_ECC = 0x1, ++ ALG_RSA = 0x2 ++} SlotAlgs; ++ + #define MAX_AUTH_USERS 3 + class Slot { + +@@ -349,7 +358,8 @@ class Slot { + bool mCACLocalLogin; + int pivContainer; + int pivKey; +- bool mECC; ++ int maxCacCerts; ++ SlotAlgs algs; + unsigned short p15aid; + unsigned short p15odfAddr; + unsigned short p15tokenInfoAddr; +@@ -424,8 +434,7 @@ class Slot { + list fetchSeparateObjects(); + + CKYStatus getCACAid(); +- CKYStatus readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, +- bool throwException); ++ CKYStatus readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize); + CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize); + + CKYStatus getP15Params(); +@@ -485,6 +494,8 @@ class Slot { + void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu); + + CKYByte objectToKeyNum(const PKCS11Object *key); ++ void initCACShMem(void); ++ void verifyCACShMem(void); + Slot(const Slot &cpy) + #ifdef USE_SHMEM + : shmem(readerName) +@@ -580,7 +591,7 @@ class Slot { + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params); + +- bool getIsECC() { return mECC; } ++ SlotAlgs getAlgs() { return algs; } + }; + + class SlotList { diff --git a/SOURCES/coolkey-cac-1.patch b/SOURCES/coolkey-cac-1.patch new file mode 100644 index 0000000..ea70187 --- /dev/null +++ b/SOURCES/coolkey-cac-1.patch @@ -0,0 +1,28 @@ +diff -up ./src/coolkey/object.cpp.cac-1 ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.cac-1 2010-06-23 04:46:35.726198827 -0700 ++++ ./src/coolkey/object.cpp 2010-06-23 04:47:28.073827862 -0700 +@@ -505,6 +505,10 @@ dataStart(const CKYByte *buf, unsigned i + unsigned char tag; + unsigned int used_length= 0; + ++ if(!buf) { ++ return NULL; ++ } ++ + tag = buf[used_length++]; + + /* blow out when we come to the end */ +diff -up ./src/coolkey/slot.cpp.cac-1 ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.cac-1 2010-06-23 04:46:22.718371631 -0700 ++++ ./src/coolkey/slot.cpp 2010-06-23 04:57:04.417774402 -0700 +@@ -2192,6 +2192,10 @@ Slot::readCACCertificateFirst(CKYBuffer + if (throwException && (status != CKYSUCCESS)) { + handleConnectionError(); + } ++ ++ if(CKYBuffer_Size(cert) == 0) { ++ handleConnectionError(); ++ } + return status; + } + diff --git a/SOURCES/coolkey-cac.patch b/SOURCES/coolkey-cac.patch new file mode 100644 index 0000000..a2a9732 --- /dev/null +++ b/SOURCES/coolkey-cac.patch @@ -0,0 +1,1013 @@ +diff -up ./src/coolkey/slot.cpp.cac ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.cac 2010-06-16 13:43:51.477181000 -0700 ++++ ./src/coolkey/slot.cpp 2010-06-16 13:43:51.535179000 -0700 +@@ -372,7 +372,7 @@ Slot::Slot(const char *readerName_, Log + : log(log_), readerName(NULL), personName(NULL), manufacturer(NULL), + slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), + isVersion1Key(false), needLogin(false), fullTokenName(false), +- mCoolkey(false), ++ mCoolkey(false), mOldCAC(false), + #ifdef USE_SHMEM + shmem(readerName_), + #endif +@@ -412,6 +412,9 @@ Slot::Slot(const char *readerName_, Log + } + CKYBuffer_InitEmpty(&cardATR); + CKYBuffer_InitEmpty(&mCUID); ++ for (int i=0; i < MAX_CERT_SLOTS; i++) { ++ CKYBuffer_InitEmpty(&cardAID[i]); ++ } + } catch(PKCS11Exception &) { + if (conn) { + CKYCardConnection_Destroy(conn); +@@ -479,6 +482,9 @@ Slot::~Slot() + CKYBuffer_FreeData(&nonce); + CKYBuffer_FreeData(&cardATR); + CKYBuffer_FreeData(&mCUID); ++ for (int i=0; i < MAX_CERT_SLOTS; i++) { ++ CKYBuffer_FreeData(&cardAID[i]); ++ } + } + + template +@@ -671,15 +677,9 @@ Slot::connectToToken() + status = CKYApplet_SelectCoolKeyManager(conn, NULL); + if (status != CKYSUCCESS) { + log->log("CoolKey Select failed 0x%x\n", status); +- status = CACApplet_SelectPKI(conn, 0, NULL); ++ status = getCACAid(); + if (status != CKYSUCCESS) { +- log->log("CAC Select failed 0x%x\n", status); +- if (status == CKYSCARDERR) { +- log->log("CAC Card Failure 0x%x\n", +- CKYCardConnection_GetLastError(conn)); +- disconnect(); +- } +- return; ++ goto loser; + } + state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; + /* skip the read of the cuid. We really don't need it and, +@@ -690,6 +690,15 @@ Slot::connectToToken() + needLogin = 1; + mCoolkey = 0; + return; ++ ++loser: ++ log->log("CAC Select failed 0x%x\n", status); ++ if (status == CKYSCARDERR) { ++ log->log("CAC Card Failure 0x%x\n", ++ CKYCardConnection_GetLastError(conn)); ++ disconnect(); ++ } ++ return; + } + mCoolkey = 1; + log->log("time connect: Select Applet %d ms\n", OSTimeNow() - time); +@@ -771,17 +780,111 @@ Slot::disconnect() + invalidateLogin(false); + } + ++CKYStatus ++Slot::getCACAid() ++{ ++ CKYBuffer tBuf; ++ CKYBuffer vBuf; ++ CKYSize tlen, vlen; ++ CKYOffset toffset, voffset; ++ int certSlot = 0; ++ int i,length = 0; ++ CKYStatus status; ++ ++ CKYBuffer_InitEmpty(&tBuf); ++ CKYBuffer_InitEmpty(&vBuf); ++ ++ /* clear out the card AID's */ ++ for (i=0; i < MAX_CERT_SLOTS; i++) { ++ CKYBuffer_Resize(&cardAID[i],0); ++ } ++ ++ status = CACApplet_SelectCCC(conn,NULL); ++ if (status != CKYSUCCESS) { ++ /* are we an old CAC */ ++ status = CACApplet_SelectPKI(conn, &cardAID[0], 0, NULL); ++ if (status != CKYSUCCESS) { ++ /* no, just fail */ ++ return status; ++ } ++ /* yes, fill in the old applets */ ++ mOldCAC = true; ++ for (i=1; i< MAX_CERT_SLOTS; i++) { ++ CACApplet_SelectPKI(conn, &cardAID[i], i, NULL); ++ } ++ return CKYSUCCESS; ++ } ++ /* definately not an old CAC */ ++ mOldCAC = false; ++ ++ /* read the TLV */ ++ status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, NULL); ++ if (status != CKYSUCCESS) { ++ goto done; ++ } ++ status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, NULL); ++ if (status != CKYSUCCESS) { ++ goto done; ++ } ++ tlen = CKYBuffer_Size(&tBuf); ++ vlen = CKYBuffer_Size(&vBuf); ++ ++ for(toffset = 2, voffset=2; ++ certSlot < MAX_CERT_SLOTS && toffset < tlen && voffset < vlen ; ++ voffset += length) { ++ ++ CKYByte tag = CKYBuffer_GetChar(&tBuf, toffset); ++ length = CKYBuffer_GetChar(&tBuf, toffset+1); ++ toffset += 2; ++ if (length == 0xff) { ++ length = CKYBuffer_GetShortLE(&tBuf, toffset); ++ toffset +=2; ++ } ++ if (tag != CAC_TAG_CARDURL) { ++ continue; ++ } ++ /* CARDURL tags must be at least 10 bytes long */ ++ if (length < 10) { ++ continue; ++ } ++ /* check the app type, should be TLV_APP_PKI */ ++ if (CKYBuffer_GetChar(&vBuf, voffset+5) != CAC_TLV_APP_PKI) { ++ continue; ++ } ++ status = CKYBuffer_AppendBuffer(&cardAID[certSlot], &vBuf, voffset, 5); ++ if (status != CKYSUCCESS) { ++ goto done; ++ } ++ status = CKYBuffer_AppendBuffer(&cardAID[certSlot], &vBuf, ++ voffset+8, 2); ++ if (status != CKYSUCCESS) { ++ goto done; ++ } ++ cardEF[certSlot] = CKYBuffer_GetShortLE(&vBuf, voffset+6); ++ ++ certSlot++; ++ } ++ status = CKYSUCCESS; ++ if (certSlot == 0) { ++ status = CKYAPDUFAIL; /* probably neeed a beter error code */ ++ } ++ ++done: ++ CKYBuffer_FreeData(&tBuf); ++ CKYBuffer_FreeData(&vBuf); ++ return status; ++} ++ + void + Slot::refreshTokenState() + { + if( cardStateMayHaveChanged() ) { +-log->log("card changed\n"); ++ log->log("card changed\n"); + invalidateLogin(true); + closeAllSessions(); + unloadObjects(); + connectToToken(); + +- + if( state & APPLET_PERSONALIZED ) { + try { + loadObjects(); +@@ -1019,7 +1122,7 @@ Slot::makeModelString(char *model, int m + + struct _manList { + unsigned short type; +- char *string; ++ const char *string; + }; + + static const struct _manList manList[] = { +@@ -1280,13 +1383,30 @@ void + Slot::selectCACApplet(CKYByte instance) + { + CKYStatus status; +- status = CACApplet_SelectPKI(conn, instance, NULL); ++ CKYBuffer *aid = &cardAID[instance]; ++ ++ if (CKYBuffer_Size(aid) == 0) { ++ disconnect(); ++ throw PKCS11Exception(CKR_DEVICE_REMOVED); ++ return; ++ } ++ ++ status = CKYApplet_SelectFile(conn, aid, NULL); + if ( status == CKYSCARDERR ) handleConnectionError(); + if ( status != CKYSUCCESS) { + // could not select applet: this just means it's not there + disconnect(); + throw PKCS11Exception(CKR_DEVICE_REMOVED); + } ++ if (mOldCAC) { ++ return; ++ } ++ status = CACApplet_SelectFile(conn, cardEF[instance], NULL); ++ if ( status == CKYSCARDERR ) handleConnectionError(); ++ if ( status != CKYSUCCESS) { ++ disconnect(); ++ throw PKCS11Exception(CKR_DEVICE_REMOVED); ++ } + } + // assume we are already in a transaction + void +@@ -2059,10 +2179,85 @@ Slot::fetchCombinedObjects(const CKYBuff + return objInfoList; + } + ++CKYStatus ++Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, ++ bool throwException) ++{ ++ CKYStatus status; ++ CKYISOStatus apduRC; ++ ++ if (mOldCAC) { ++ /* get the first 100 bytes of the cert */ ++ status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC); ++ if (throwException && (status != CKYSUCCESS)) { ++ handleConnectionError(); ++ } ++ return status; ++ } ++ ++ CKYBuffer tBuf; ++ CKYBuffer vBuf; ++ CKYSize tlen, vlen; ++ CKYOffset toffset, voffset; ++ int length = 0; ++ ++ CKYBuffer_InitEmpty(&tBuf); ++ CKYBuffer_InitEmpty(&vBuf); ++ CKYBuffer_Resize(cert, 0); ++ ++ /* handle the new CAC card read */ ++ /* read the TLV */ ++ status = CACApplet_ReadFile(conn, CAC_TAG_FILE, &tBuf, NULL); ++ if (status != CKYSUCCESS) { ++ goto done; ++ } ++ status = CACApplet_ReadFile(conn, CAC_VALUE_FILE, &vBuf, NULL); ++ if (status != CKYSUCCESS) { ++ goto done; ++ } ++ tlen = CKYBuffer_Size(&tBuf); ++ vlen = CKYBuffer_Size(&vBuf); ++ ++ /* look for the Cert out of the TLV */ ++ for(toffset = 2, voffset=2; toffset < tlen && voffset < vlen ; ++ voffset += length) { ++ ++ CKYByte tag = CKYBuffer_GetChar(&tBuf, toffset); ++ length = CKYBuffer_GetChar(&tBuf, toffset+1); ++ toffset += 2; ++ if (length == 0xff) { ++ length = CKYBuffer_GetShortLE(&tBuf, toffset); ++ toffset +=2; ++ } ++ if (tag != CAC_TAG_CERTIFICATE) { ++ continue; ++ } ++ CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length); ++ break; ++ } ++ status = CKYSUCCESS; ++ ++done: ++ CKYBuffer_FreeData(&tBuf); ++ CKYBuffer_FreeData(&vBuf); ++ return status; ++} ++ ++/* ++ * only necessary for old CAC cards. New CAC cards have to read the ++ * whole cert in anyway above.... ++ */ ++CKYStatus ++Slot::readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize) ++{ ++ CKYISOStatus apduRC; ++ assert(mOldCAC); ++ return CACApplet_GetCertificateAppend(conn, cert, nextSize, &apduRC); ++} ++ + void + Slot::loadCACCert(CKYByte instance) + { +- CKYISOStatus apduRC; + CKYStatus status = CKYSUCCESS; + CKYBuffer cert; + CKYBuffer rawCert; +@@ -2097,12 +2292,7 @@ Slot::loadCACCert(CKYByte instance) + instance, OSTimeNow() - time); + + if (instance == 0) { +- /* get the first 100 bytes of the cert */ +- status = CACApplet_GetCertificateFirst(conn, &rawCert, +- &nextSize, &apduRC); +- if (status != CKYSUCCESS) { +- handleConnectionError(); +- } ++ readCACCertificateFirst(&rawCert, &nextSize, true); + log->log("CAC Cert %d: fetch CAC Cert: %d ms\n", + instance, OSTimeNow() - time); + } +@@ -2143,8 +2333,7 @@ Slot::loadCACCert(CKYByte instance) + shmem.setVersion(SHMEM_VERSION); + shmem.setDataVersion(dataVersion); + } else { +- status = CACApplet_GetCertificateFirst(conn, &rawCert, +- &nextSize, &apduRC); ++ status = readCACCertificateFirst(&rawCert, &nextSize, false); + + if (status != CKYSUCCESS) { + /* CAC only requires the Certificate in pki '0' */ +@@ -2159,8 +2348,7 @@ Slot::loadCACCert(CKYByte instance) + } + + if (nextSize) { +- status = CACApplet_GetCertificateAppend(conn, &rawCert, +- nextSize, &apduRC); ++ status = readCACCertificateAppend(&rawCert, nextSize); + } + log->log("CAC Cert %d: Fetch rest : %d ms\n", + instance, OSTimeNow() - time); +@@ -2176,9 +2364,10 @@ Slot::loadCACCert(CKYByte instance) + + log->log("CAC Cert %d: Cert has been read: %d ms\n", + instance, OSTimeNow() - time); +- if (CKYBuffer_GetChar(&rawCert,0) == 1) { ++ if (!mOldCAC || CKYBuffer_GetChar(&rawCert,0) == 1) { + CKYSize guessFinalSize = CKYBuffer_Size(&rawCert); + CKYSize certSize = 0; ++ CKYOffset offset = mOldCAC ? 1 : 0; + int zret = Z_MEM_ERROR; + + do { +@@ -2189,7 +2378,8 @@ Slot::loadCACCert(CKYByte instance) + } + certSize = guessFinalSize; + zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize, +- CKYBuffer_Data(&rawCert)+1, CKYBuffer_Size(&rawCert)-1); ++ CKYBuffer_Data(&rawCert)+offset, ++ CKYBuffer_Size(&rawCert)-offset); + } while (zret == Z_BUF_ERROR); + + if (zret != Z_OK) { +@@ -2526,7 +2716,7 @@ Slot::attemptCACLogin() + switch( result ) { + case CKYISO_SUCCESS: + break; +- case 6981: ++ case 0x6981: + throw PKCS11Exception(CKR_PIN_LOCKED); + default: + if ((result & 0xff00) == 0x6300) { +diff -up ./src/coolkey/slot.h.cac ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.cac 2010-06-16 13:43:51.344185000 -0700 ++++ ./src/coolkey/slot.h 2010-06-16 13:43:51.546179000 -0700 +@@ -294,6 +294,7 @@ class CryptParams { + const CKYBuffer *paddedOutput) const = 0; + }; + ++#define MAX_CERT_SLOTS 3 + class Slot { + + public: +@@ -328,6 +329,8 @@ class Slot { + CKYBuffer nonce; + CKYBuffer cardATR; + CKYBuffer mCUID; ++ CKYBuffer cardAID[MAX_CERT_SLOTS]; ++ unsigned short cardEF[MAX_CERT_SLOTS]; + bool isVersion1Key; + bool needLogin; + long publicFree; +@@ -335,6 +338,7 @@ class Slot { + long privateFree; + bool fullTokenName; + bool mCoolkey; ++ bool mOldCAC; + + //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 }; + +@@ -398,6 +402,11 @@ class Slot { + list fetchCombinedObjects(const CKYBuffer *header); + list fetchSeparateObjects(); + ++ CKYStatus getCACAid(); ++ CKYStatus readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, ++ bool throwException); ++ CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize); ++ + void selectApplet(); + void selectCACApplet(CKYByte instance); + void unloadObjects(); +diff -up ./src/libckyapplet/cky_applet.c.cac ./src/libckyapplet/cky_applet.c +--- ./src/libckyapplet/cky_applet.c.cac 2010-06-16 13:43:51.357181000 -0700 ++++ ./src/libckyapplet/cky_applet.c 2010-06-16 14:47:41.305529000 -0700 +@@ -41,7 +41,13 @@ + CKYStatus + CKYAppletFactory_SelectFile(CKYAPDU *apdu, const void *param) + { +- return CKYAPDUFactory_SelectFile(apdu,(const CKYBuffer *)param); ++ return CKYAPDUFactory_SelectFile(apdu, 4, 0, (const CKYBuffer *)param); ++} ++ ++CKYStatus ++CACAppletFactory_SelectFile(CKYAPDU *apdu, const void *param) ++{ ++ return CKYAPDUFactory_SelectFile(apdu, 2, 12, (const CKYBuffer *)param); + } + + CKYStatus +@@ -225,10 +231,17 @@ CKYAppletFactory_GetBuiltinACL(CKYAPDU * + } + + CKYStatus +-CACAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param) ++CACAppletFactory_SignDecryptStep(CKYAPDU *apdu, const void *param) ++{ ++ const CKYBuffer *buf=(CKYBuffer *)param; ++ return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_STEP, buf); ++} ++ ++CKYStatus ++CACAppletFactory_SignDecryptFinal(CKYAPDU *apdu, const void *param) + { + const CKYBuffer *buf=(CKYBuffer *)param; +- return CACAPDUFactory_SignDecrypt(apdu, buf); ++ return CACAPDUFactory_SignDecrypt(apdu, CAC_P1_FINAL, buf); + } + + CKYStatus +@@ -246,6 +259,13 @@ CACAppletFactory_GetCertificate(CKYAPDU + } + + CKYStatus ++CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param) ++{ ++ const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param; ++ return CACAPDUFactory_ReadFile(apdu, rfs->offset, rfs->type, rfs->count); ++} ++ ++CKYStatus + CACAppletFactory_GetProperties(CKYAPDU *apdu, const void *param) + { + return CACAPDUFactory_GetProperties(apdu); +@@ -457,7 +477,7 @@ CKYApplet_SelectFile(CKYCardConnection * + CKYISOStatus *apduRC) + { + return CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, AID, NULL, +- 0, CKYAppletFill_Null, NULL, apduRC); ++ CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); + } + + static CKYByte coolkeyid[] = {0x62, 0x76, 0x01, 0xff, 0x00, 0x00, 0x00 }; +@@ -477,22 +497,23 @@ CKYApplet_SelectCoolKeyManager(CKYCardCo + return ret; + } + +-static CKYByte CACPKIid[] = {0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; ++static CKYByte CACPKIid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01 }; + /* + * Select the CoolKey applet. Must happen after we start a transaction and + * before we issue any applet specific command. + */ + CKYStatus +-CACApplet_SelectPKI(CKYCardConnection *conn, CKYByte instance, +- CKYISOStatus *apduRC) ++CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cacAID, ++ CKYByte instance, CKYISOStatus *apduRC) + { + CKYStatus ret; +- CKYBuffer CACPKIAID; +- CKYBuffer_InitFromData(&CACPKIAID, CACPKIid, sizeof(CACPKIid)); +- CKYBuffer_SetChar(&CACPKIAID, 6, instance); +- ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CACPKIAID, ++ CKYBuffer_AppendData(cacAID, CACPKIid, sizeof(CACPKIid)); ++ CKYBuffer_AppendChar(cacAID, instance); ++ ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, cacAID, + NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); +- CKYBuffer_FreeData(&CACPKIAID); ++ if (ret != CKYSUCCESS) { ++ CKYBuffer_Resize(cacAID, 0); ++ } + return ret; + } + +@@ -515,11 +536,38 @@ CACApplet_SelectCardManager(CKYCardConne + CKYBuffer CAC_CM_AID; + CKYBuffer_InitFromData(&CAC_CM_AID, cacmgrid, sizeof(cacmgrid)); + ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID, +- NULL, 0, CKYAppletFill_Null, NULL, apduRC); ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); + CKYBuffer_FreeData(&CAC_CM_AID); + return ret; + } + ++static CKYByte cacCCCid[] = {0xa0, 0x00, 0x00, 0x01, 0x16, 0xdb, 0x00 }; ++CKYStatus ++CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYBuffer CAC_CM_AID; ++ CKYBuffer_InitFromData(&CAC_CM_AID, cacCCCid, sizeof(cacCCCid)); ++ ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, &CAC_CM_AID, ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++ CKYBuffer_FreeData(&CAC_CM_AID); ++ return ret; ++} ++ ++CKYStatus ++CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef, ++ CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYBuffer efBuf; ++ CKYBuffer_InitEmpty(&efBuf); ++ CKYBuffer_AppendShortLE(&efBuf, ef); ++ ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SelectFile, &efBuf, ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_Null, NULL, apduRC); ++ CKYBuffer_FreeData(&efBuf); ++ return ret; ++} ++ + /* + * GetCPLC cluster -- must be called with CM selected + */ +@@ -673,8 +721,8 @@ CKYApplet_ComputeCryptProcess(CKYCardCon + ccd.keyNumber = keyNumber; + ccd.location = location; + ccd.data = data; +- return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, &ccd, +- nonce, 0, CKYAppletFill_Null, NULL, apduRC); ++ return CKYApplet_HandleAPDU(conn, CKYAppletFactory_ComputeCryptProcess, ++ &ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC); + } + + /* computeCrypt returns data in the form : +@@ -832,11 +880,39 @@ CACApplet_SignDecrypt(CKYCardConnection + CKYBuffer *result, CKYISOStatus *apduRC) + { + CKYStatus ret; +- +- ret = CKYApplet_HandleAPDU(conn, +- CACAppletFactory_SignDecrypt, data, NULL, +- CKYBuffer_Size(data), CKYAppletFill_ReplaceBuffer, ++ CKYSize dataSize = CKYBuffer_Size(data); ++ CKYOffset offset = 0; ++ CKYBuffer tmp; ++ ++ CKYBuffer_InitEmpty(&tmp); ++ ++ CKYBuffer_Resize(result, 0); ++ for(offset = 0; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; ++ offset += CKY_MAX_WRITE_CHUNK_SIZE) { ++ CKYBuffer_Resize(&tmp,0); ++ CKYBuffer_AppendBuffer(&tmp, data, offset, CKY_MAX_WRITE_CHUNK_SIZE); ++ ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptStep, ++ &tmp, NULL, CKY_SIZE_UNKNOWN, ++ CKYAppletFill_AppendBuffer, + result, apduRC); ++ if (ret != CKYSUCCESS) { ++ goto done; ++ } ++ } ++ CKYBuffer_Resize(&tmp,0); ++ CKYBuffer_AppendBuffer(&tmp, data, offset, dataSize - offset); ++ ret = CKYApplet_HandleAPDU(conn, CACAppletFactory_SignDecryptFinal, ++ &tmp, NULL, CKY_SIZE_UNKNOWN, ++ CKYAppletFill_AppendBuffer, ++ result, apduRC); ++ ++ if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != dataSize)) { ++ /* RSA returns the same data size as input, didn't happen, so ++ * something is wrong. */ ++ } ++ ++done: ++ CKYBuffer_FreeData(&tmp); + return ret; + } + +@@ -895,6 +971,63 @@ CACApplet_GetCertificate(CKYCardConnecti + } + return ret; + } ++ ++/* ++ * Read a CAC Tag/Value file ++ */ ++CKYStatus ++CACApplet_ReadFile(CKYCardConnection *conn, CKYByte type, CKYBuffer *buffer, ++ CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYISOStatus status; ++ CKYByte maxtransfer; ++ unsigned short offset = 0; ++ unsigned short size; ++ CACAppletArgReadFile rfs; ++ ++ CKYBuffer_Resize(buffer,0); ++ if (apduRC == NULL) { ++ apduRC = &status; ++ } ++ rfs.offset = 0; ++ rfs.count = 2; ++ rfs.type = type; ++ ++ /* APDU's are expensive, Grab a big chunk of the file first if possible */ ++ ret = CKYApplet_HandleAPDU(conn, ++ CACAppletFactory_ReadFile, &rfs, NULL, ++ rfs.count, CKYAppletFill_AppendBuffer, ++ buffer, apduRC); ++ /* file is probably smaller than 100 bytes, get the actual size first */ ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ size = CKYBuffer_GetShortLE(buffer, 0) + 2 /* include the length itself */; ++ maxtransfer = CKY_MAX_READ_CHUNK_SIZE; ++ /* get the rest of the buffer if necessary */ ++ for (offset = CKYBuffer_Size(buffer); size > offset; ++ offset = CKYBuffer_Size(buffer)) { ++ rfs.offset = offset; ++ rfs.count = MIN(size - offset, maxtransfer); ++ ret = CKYApplet_HandleAPDU(conn, ++ CACAppletFactory_ReadFile, &rfs, NULL, ++ rfs.count, CKYAppletFill_AppendBuffer, ++ buffer, apduRC); ++ if (ret != CKYSUCCESS) { ++ if (*apduRC == CAC_INVALID_PARAMS) { ++ maxtransfer = maxtransfer/2; ++ if (maxtransfer == 0) { ++ return ret; ++ } ++ } else { ++ return ret; ++ } ++ } ++ } ++ return ret; ++} ++ + CKYStatus + CACApplet_GetCertificateFirst(CKYCardConnection *conn, CKYBuffer *cert, + CKYSize *nextSize, CKYISOStatus *apduRC) +diff -up ./src/libckyapplet/cky_applet.h.cac ./src/libckyapplet/cky_applet.h +--- ./src/libckyapplet/cky_applet.h.cac 2010-06-16 13:43:51.370181000 -0700 ++++ ./src/libckyapplet/cky_applet.h 2010-06-16 13:43:51.572179000 -0700 +@@ -71,6 +71,15 @@ typedef unsigned short CKYISOStatus; /* + #define CKYISO_INTERNAL_ERROR 0x9cff /* Reserved for debugging, + * shouldn't happen */ + ++#define CAC_INVALID_PARAMS 0x6a83 ++#define CAC_TAG_FILE 1 ++#define CAC_VALUE_FILE 2 ++ ++ ++#define CAC_TAG_CARDURL 0xf3 ++#define CAC_TAG_CERTIFICATE 0x70 ++#define CAC_TLV_APP_PKI 0x04 ++ + /* + * Pin Constants as used by our applet + */ +@@ -209,6 +218,12 @@ typedef struct _CKYAppletArgComputeCrypt + const CKYBuffer *sig; + } CKYAppletArgComputeCrypt; + ++typedef struct _CACAppletArgReadFile { ++ CKYByte type; ++ CKYByte count; ++ unsigned short offset; ++} CACAppletArgReadFile; ++ + /* fills in an APDU from a structure -- form of all the generic factories*/ + typedef CKYStatus (*CKYAppletFactory)(CKYAPDU *apdu, const void *param); + /* fills in an a structure from a response -- form of all the fill structures*/ +@@ -451,9 +466,17 @@ CKYStatus CKYApplet_DeleteObject(CKYCard + /* Select the CAC card manager. Can happen with either applet selected */ + CKYStatus CACApplet_SelectCardManager(CKYCardConnection *conn, + CKYISOStatus *apduRC); +-/* Can happen with either applet selected */ +-CKYStatus CACApplet_SelectPKI(CKYCardConnection *conn, CKYByte instance, +- CKYISOStatus *apduRC); ++/* Select the CAC CC container. Can happen with either applet selected */ ++CKYStatus CACApplet_SelectCCC(CKYCardConnection *conn, CKYISOStatus *apduRC); ++/* Select an old CAC applet and fill in the cardAID */ ++CKYStatus CACApplet_SelectPKI(CKYCardConnection *conn, CKYBuffer *cardAid, ++ CKYByte instance, CKYISOStatus *apduRC); ++/* read a TLV file */ ++CKYStatus CACApplet_ReadFile(CKYCardConnection *conn, CKYByte type, ++ CKYBuffer *buffer, CKYISOStatus *apduRC); ++CKYStatus CACApplet_SelectFile(CKYCardConnection *conn, unsigned short ef, ++ CKYISOStatus *apduRC); ++ + /* must happen with PKI applet selected */ + CKYStatus CACApplet_SignDecrypt(CKYCardConnection *conn, const CKYBuffer *data, + CKYBuffer *result, CKYISOStatus *apduRC); +diff -up ./src/libckyapplet/cky_base.c.cac ./src/libckyapplet/cky_base.c +--- ./src/libckyapplet/cky_base.c.cac 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_base.c 2010-06-16 13:43:51.583179000 -0700 +@@ -220,6 +220,22 @@ CKYBuffer_AppendShort(CKYBuffer *buf, un + return CKYSUCCESS; + } + ++/* append a short in network order */ ++CKYStatus ++CKYBuffer_AppendShortLE(CKYBuffer *buf, unsigned short val) ++{ ++ CKYStatus ret; ++ ++ ret = CKYBuffer_Reserve(buf, buf->len + 2); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ buf->data[buf->len+1] = (CKYByte) ((val >> 8) & 0xff); ++ buf->data[buf->len+0] = (CKYByte) ((val >> 0) & 0xff); ++ buf->len += 2; ++ return CKYSUCCESS; ++} ++ + /* append a long in applet order */ + CKYStatus + CKYBuffer_AppendLong(CKYBuffer *buf, unsigned long val) +@@ -238,6 +254,24 @@ CKYBuffer_AppendLong(CKYBuffer *buf, uns + return CKYSUCCESS; + } + ++/* append a long in applet order */ ++CKYStatus ++CKYBuffer_AppendLongLE(CKYBuffer *buf, unsigned long val) ++{ ++ CKYStatus ret; ++ ++ ret = CKYBuffer_Reserve(buf, buf->len + 4); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ buf->data[buf->len+3] = (CKYByte) ((val >> 24) & 0xff); ++ buf->data[buf->len+2] = (CKYByte) ((val >> 16) & 0xff); ++ buf->data[buf->len+1] = (CKYByte) ((val >> 8) & 0xff); ++ buf->data[buf->len+0] = (CKYByte) ((val >> 0) & 0xff); ++ buf->len += 4; ++ return CKYSUCCESS; ++} ++ + CKYStatus + CKYBuffer_Replace(CKYBuffer *buf, CKYOffset offset, const CKYByte *data, CKYSize len) + { +@@ -351,6 +385,22 @@ CKYBuffer_SetShort(CKYBuffer *buf, CKYOf + } + + CKYStatus ++CKYBuffer_SetShortLE(CKYBuffer *buf, CKYOffset offset, unsigned short val) ++{ ++ CKYStatus ret; ++ ++ if (buf->len < offset+2) { ++ ret = CKYBuffer_Resize(buf,offset+2); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ } ++ buf->data[offset+1] = (CKYByte) ((val >> 8) & 0xff); ++ buf->data[offset+0] = (CKYByte) ((val >> 0) & 0xff); ++ return CKYSUCCESS; ++} ++ ++CKYStatus + CKYBuffer_SetLong(CKYBuffer *buf, CKYOffset offset, unsigned long val) + { + CKYStatus ret; +@@ -368,6 +418,24 @@ CKYBuffer_SetLong(CKYBuffer *buf, CKYOff + return CKYSUCCESS; + } + ++CKYStatus ++CKYBuffer_SetLongLE(CKYBuffer *buf, CKYOffset offset, unsigned long val) ++{ ++ CKYStatus ret; ++ ++ if (buf->len < offset+4) { ++ ret = CKYBuffer_Resize(buf,offset+4); ++ if (ret != CKYSUCCESS) { ++ return ret; ++ } ++ } ++ buf->data[offset+3] = (CKYByte) ((val >> 24) & 0xff); ++ buf->data[offset+2] = (CKYByte) ((val >> 16) & 0xff); ++ buf->data[offset+1] = (CKYByte) ((val >> 8) & 0xff); ++ buf->data[offset+0] = (CKYByte) ((val >> 0) & 0xff); ++ return CKYSUCCESS; ++} ++ + CKYByte + CKYBuffer_GetChar(const CKYBuffer *buf, CKYOffset offset) + { +@@ -388,6 +456,18 @@ CKYBuffer_GetShort(const CKYBuffer *buf, + val |= ((unsigned short)buf->data[offset+1]) << 0; + return val; + } ++ ++unsigned short ++CKYBuffer_GetShortLE(const CKYBuffer *buf, CKYOffset offset) ++{ ++ unsigned short val; ++ if (buf->len < offset+2) { ++ return 0; ++ } ++ val = ((unsigned short)buf->data[offset+1]) << 8; ++ val |= ((unsigned short)buf->data[offset+0]) << 0; ++ return val; ++} + + unsigned long + CKYBuffer_GetLong(const CKYBuffer *buf, CKYOffset offset) +@@ -402,6 +482,20 @@ CKYBuffer_GetLong(const CKYBuffer *buf, + val |= ((unsigned long)buf->data[offset+3]) << 0; + return val; + } ++ ++unsigned long ++CKYBuffer_GetLongLE(const CKYBuffer *buf, CKYOffset offset) ++{ ++ unsigned long val; ++ if (buf->len < offset+4) { ++ return 0; ++ } ++ val = ((unsigned long)buf->data[offset+3]) << 24; ++ val |= ((unsigned long)buf->data[offset+2]) << 16; ++ val |= ((unsigned long)buf->data[offset+1]) << 8; ++ val |= ((unsigned long)buf->data[offset+0]) << 0; ++ return val; ++} + + CKYStatus + CKYBuffer_Resize(CKYBuffer *buf, CKYSize newLen) +diff -up ./src/libckyapplet/cky_base.h.cac ./src/libckyapplet/cky_base.h +--- ./src/libckyapplet/cky_base.h.cac 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_base.h 2010-06-16 13:43:51.592179000 -0700 +@@ -170,9 +170,15 @@ CKYStatus CKYBuffer_AppendChar(CKYBuffer + /* append a short in applet order */ + CKYStatus CKYBuffer_AppendShort(CKYBuffer *buf, unsigned short val); + ++/* append a short in little endian order */ ++CKYStatus CKYBuffer_AppendShortLE(CKYBuffer *buf, unsigned short val); ++ + /* append a long in applet order */ + CKYStatus CKYBuffer_AppendLong(CKYBuffer *buf, unsigned long val); + ++/* append a long in little endian order */ ++CKYStatus CKYBuffer_AppendLongLE(CKYBuffer *buf, unsigned long val); ++ + /* append data. the data starts at data and extends len bytes */ + CKYStatus CKYBuffer_AppendData(CKYBuffer *buf, const CKYByte *data, CKYSize len); + +@@ -210,12 +216,18 @@ CKYStatus CKYBuffer_SetChars(CKYBuffer * + CKYStatus CKYBuffer_SetShort(CKYBuffer *buf, CKYOffset offset, unsigned short val); + CKYStatus CKYBuffer_SetLong(CKYBuffer *buf, CKYOffset offset, unsigned long val); + ++/* These functions work in little endian order */ ++CKYStatus CKYBuffer_SetShortLE(CKYBuffer *buf, CKYOffset offset, unsigned short val); ++CKYStatus CKYBuffer_SetLongLE(CKYBuffer *buf, CKYOffset offset, unsigned long val); + /* read a character from offset. If offset is beyond the end of the buffer, + * then the function returns '0' */ + CKYByte CKYBuffer_GetChar(const CKYBuffer *buf, CKYOffset offset); + /* These functions work in applet order */ + unsigned short CKYBuffer_GetShort(const CKYBuffer *buf, CKYOffset offset); + unsigned long CKYBuffer_GetLong(const CKYBuffer *buf, CKYOffset offset); ++/* These functions work in little endian order */ ++unsigned short CKYBuffer_GetShortLE(const CKYBuffer *buf, CKYOffset offset); ++unsigned long CKYBuffer_GetLongLE(const CKYBuffer *buf, CKYOffset offset); + + /* clear out all the data in a buffer */ + void CKYBuffer_Zero(CKYBuffer *buf); +diff -up ./src/libckyapplet/cky_factory.c.cac ./src/libckyapplet/cky_factory.c +--- ./src/libckyapplet/cky_factory.c.cac 2010-06-16 13:43:51.393185000 -0700 ++++ ./src/libckyapplet/cky_factory.c 2010-06-16 14:48:08.885473000 -0700 +@@ -25,12 +25,13 @@ + * special commands can be issued at any time + */ + CKYStatus +-CKYAPDUFactory_SelectFile(CKYAPDU *apdu, const CKYBuffer *AID) ++CKYAPDUFactory_SelectFile(CKYAPDU *apdu, CKYByte p1, CKYByte p2, ++ const CKYBuffer *AID) + { + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); + CKYAPDU_SetINS(apdu, CKY_INS_SELECT_FILE); +- CKYAPDU_SetP1(apdu, 0x04); +- CKYAPDU_SetP2(apdu, 0x00); ++ CKYAPDU_SetP1(apdu, p1); ++ CKYAPDU_SetP2(apdu, p2); + return CKYAPDU_SetSendDataBuffer(apdu, AID); + } + +@@ -131,6 +132,7 @@ fail: + return ret; + } + ++ + CKYStatus + CKYAPDUFactory_ComputeCryptFinal(CKYAPDU *apdu, CKYByte keyNumber, + CKYByte location, const CKYBuffer *data, const CKYBuffer *sig) +@@ -572,11 +574,11 @@ CKYAPDUFactory_GetBuiltinACL(CKYAPDU *ap + } + + CKYStatus +-CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, const CKYBuffer *data) ++CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, const CKYBuffer *data) + { + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); + CKYAPDU_SetINS(apdu, CAC_INS_SIGN_DECRYPT); +- CKYAPDU_SetP1(apdu, 0x00); ++ CKYAPDU_SetP1(apdu, type); + CKYAPDU_SetP2(apdu, 0x00); + return CKYAPDU_SetSendDataBuffer(apdu, data); + } +@@ -592,6 +594,36 @@ CACAPDUFactory_GetCertificate(CKYAPDU *a + } + + CKYStatus ++CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, ++ CKYByte type, CKYByte count) ++{ ++ CKYStatus ret; ++ CKYBuffer buf; ++ ++ CKYBuffer_InitEmpty(&buf); ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_GLOBAL_PLATFORM); ++ CKYAPDU_SetINS(apdu, CAC_INS_READ_FILE); ++ CKYAPDU_SetP1(apdu, (offset >> 8) & 0xff); ++ CKYAPDU_SetP2(apdu, offset & 0xff); ++ ret = CKYBuffer_Reserve(&buf, 2); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendChar(&buf, type); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendChar(&buf, count); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); ++fail: ++ CKYBuffer_FreeData(&buf); ++ return ret; ++} ++ ++CKYStatus + CACAPDUFactory_GetProperties(CKYAPDU *apdu) + { + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); +diff -up ./src/libckyapplet/cky_factory.h.cac ./src/libckyapplet/cky_factory.h +--- ./src/libckyapplet/cky_factory.h.cac 2010-06-16 13:43:51.402181000 -0700 ++++ ./src/libckyapplet/cky_factory.h 2010-06-16 14:43:20.867049000 -0700 +@@ -86,7 +86,11 @@ + #define CAC_INS_SIGN_DECRYPT 0x42 + #define CAC_INS_VERIFY_PIN 0x20 + #define CAC_INS_GET_PROPERTIES 0x56 ++#define CAC_INS_READ_FILE 0x52 ++ + #define CAC_SIZE_GET_PROPERTIES 48 ++#define CAC_P1_STEP 0x80 ++#define CAC_P1_FINAL 0x00 + + /* + * Fixed return sized from various commands +@@ -169,7 +173,8 @@ + CKY_BEGIN_PROTOS + + /* function based factorys */ +-CKYStatus CKYAPDUFactory_SelectFile(CKYAPDU *apdu, const CKYBuffer *AID); ++CKYStatus CKYAPDUFactory_SelectFile(CKYAPDU *apdu, CKYByte p1, CKYByte p2, ++ const CKYBuffer *AID); + CKYStatus CKYAPDUFactory_SelectCardManager(CKYAPDU *apdu); + CKYStatus CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu); + CKYStatus CKYAPDUFactory_ListKeys(CKYAPDU *apdu, CKYByte sequence); +@@ -211,9 +216,12 @@ CKYStatus CKYAPDUFactory_SeedRandom(CKYA + CKYStatus CKYAPDUFactory_GetIssuerInfo(CKYAPDU *apdu); + CKYStatus CKYAPDUFactory_GetBuiltinACL(CKYAPDU *apdu); + +-CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, const CKYBuffer *data); ++CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, ++ const CKYBuffer *data); + CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin); + CKYStatus CACAPDUFactory_GetCertificate(CKYAPDU *apdu, CKYSize size); ++CKYStatus CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, ++ CKYByte type, CKYByte count); + CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu); + + CKY_END_PROTOS diff --git a/SOURCES/coolkey-cache-dir-move.patch b/SOURCES/coolkey-cache-dir-move.patch new file mode 100644 index 0000000..1b5eaff --- /dev/null +++ b/SOURCES/coolkey-cache-dir-move.patch @@ -0,0 +1,177 @@ +Index: src/coolkey/machdep.cpp +=================================================================== +RCS file: /cvs/dirsec/coolkey/src/coolkey/machdep.cpp,v +retrieving revision 1.4 +diff -u -r1.4 machdep.cpp +--- src/coolkey/machdep.cpp 14 Feb 2007 00:46:28 -0000 1.4 ++++ src/coolkey/machdep.cpp 15 Aug 2007 01:41:11 -0000 +@@ -185,12 +185,20 @@ + #define MAP_INHERIT 0 + #endif + ++#ifndef BASEPATH ++#ifdef MAC ++#define BASEPATH "/var" ++#else ++#define BASEPATH "/var/cache" ++#endif ++#endif ++ + #ifdef FULL_CLEANUP + #define RESERVED_OFFSET 256 +-#define MEMSEGPATH "/tmp/.pk11ipc" ++#define MEMSEGPATH BASEPATH"/coolkey-lock" + #else + #define RESERVED_OFFSET 0 +-#define MEMSEGPATH "/tmp/.pk11ipc1" ++#define MEMSEGPATH BASEPATH"/coolkey" + #endif + + struct SHMemData { +@@ -208,11 +216,6 @@ + #ifdef FULL_CLEANUP + flock(fd,LOCK_EX); + unsigned long ref = --(*(unsigned long *)addr); +-#ifdef notdef +- if (ref == 0) { +- unlink(path); +- } +-#endif + flock(fd, LOCK_UN); + #endif + munmap(addr,size+RESERVED_OFFSET); +@@ -225,6 +228,73 @@ + } + } + ++/* ++ * The cache directory is shared and accessible by anyone, make ++ * sure the cache file we are opening is really a valid cache file. ++ */ ++int safe_open(char *path, int flags, int mode, int size) ++{ ++ struct stat buf; ++ int fd, ret; ++ ++ fd = open (path, flags|O_NOFOLLOW, mode); ++ ++ if (fd < 0) { ++ return fd; ++ } ++ ++ ret = fstat(fd, &buf); ++ if (ret < 0) { ++ close (fd); ++ return ret; ++ } ++ ++ /* our cache files are pretty specific, make sure we are looking ++ * at the correct one */ ++ ++ /* first, we should own the file ourselves, don't open a file ++ * that someone else wanted us to see. */ ++ if (buf.st_uid != getuid()) { ++ close(fd); ++ errno = EACCES; ++ return -1; ++ } ++ ++ /* next, there should only be one link in this file. Don't ++ * use this code to trash another file */ ++ if (buf.st_nlink != 1) { ++ close(fd); ++ errno = EMLINK; ++ return -1; ++ } ++ ++ /* next, This better be a regular file */ ++ if (!S_ISREG(buf.st_mode)) { ++ close(fd); ++ errno = EACCES; ++ return -1; ++ } ++ ++ /* if the permissions don't match, something is wrong */ ++ if ((buf.st_mode & 03777) != mode) { ++ close(fd); ++ errno = EACCES; ++ return -1; ++ } ++ ++ /* finally the file should be the correct size. This ++ * check isn't so much to protect from an attack, as it is to ++ * detect a corrupted cache file */ ++ if (buf.st_size != size) { ++ close(fd); ++ errno = EACCES; ++ return -1; ++ } ++ ++ /* OK, the file checked out, ok to continue */ ++ return fd; ++} ++ + SHMem::SHMem(): shmemData(0) {} + + SHMem * +@@ -248,7 +318,7 @@ + return NULL; + } + int mask = umask(0); +- int ret = mkdir (MEMSEGPATH, 0777); ++ int ret = mkdir (MEMSEGPATH, 01777); + umask(mask); + if ((ret == -1) && (errno != EEXIST)) { + delete shmemData; +@@ -264,21 +334,16 @@ + shmemData->path[sizeof(MEMSEGPATH)-1] = '/'; + strcpy(&shmemData->path[sizeof(MEMSEGPATH)],name); + +- int mode = 0777; +- if (strcmp(name,"token_names") != 0) { +- /* each user gets his own uid array */ +- sprintf(uid_str, "-%u",getuid()); +- strcat(shmemData->path,uid_str); +- mode = 0700; +- } ++ sprintf(uid_str, "-%u",getuid()); ++ strcat(shmemData->path,uid_str); ++ int mode = 0600; ++ + shmemData->fd = open(shmemData->path, + O_CREAT|O_RDWR|O_EXCL|O_APPEND|O_EXLOCK, mode); +- if (shmemData->fd < 0) { +- needInit = false; +- shmemData->fd = open(shmemData->path,O_RDWR|O_EXLOCK, mode); +- } else { ++ if (shmemData->fd >= 0) { + char *buf; + int len = size+RESERVED_OFFSET; ++ int ret; + + buf = (char *)calloc(1,len); + if (!buf) { +@@ -289,8 +354,22 @@ + delete shmemData; + return NULL; + } +- write(shmemData->fd,buf,len); ++ ret = write(shmemData->fd,buf,len); ++ if (ret != len) { ++ unlink(shmemData->path); ++#ifdef FULL_CLEANUP ++ flock(shmemData->fd, LOCK_UN); ++#endif ++ delete shmemData; ++ return NULL; ++ } ++ + free(buf); ++ } else if (errno == EEXIST) { ++ needInit = false; ++ ++ shmemData->fd = safe_open(shmemData->path,O_RDWR|O_EXLOCK, mode, ++ size+RESERVED_OFFSET); + } + if (shmemData->fd < 0) { + delete shmemData; diff --git a/SOURCES/coolkey-fix-token-removal-failure.patch b/SOURCES/coolkey-fix-token-removal-failure.patch new file mode 100644 index 0000000..7f8a954 --- /dev/null +++ b/SOURCES/coolkey-fix-token-removal-failure.patch @@ -0,0 +1,86 @@ +Fix insertion/removal detection + +pcsc now errors out of the SCardGetStatusChange call with +SCARD_E_UNKNOWN_READER if any of the passed readers aren't known. +This includes readers that were very recently forgotton about because +a user just disconnected them. + +(See + http://anonscm.debian.org/viewvc/pcsclite/trunk/PCSC/src/winscard_clnt.c?r1=5858&r2=5881 +for the change to pcsc) + +Unfortunately, this means SECMOD_WaitForAnyTokenEvent will fail with a +SC_NO_EVENT error if a user removes their smartcard at the wrong time. + +This patch changes coolkey to detect removed readers before calling +SCardGetStatusChange, so that it can handle the removal itself. + +diff -up coolkey-1.1.0/src/coolkey/slot.cpp.fix coolkey-1.1.0/src/coolkey/slot.cpp +--- coolkey-1.1.0/src/coolkey/slot.cpp.fix 2013-05-22 16:23:41.728846957 -0400 ++++ coolkey-1.1.0/src/coolkey/slot.cpp 2013-05-22 17:09:59.813958927 -0400 +@@ -279,24 +279,22 @@ SlotList::updateReaderList() + * don't recognize. + */ + +- /* first though, let's check to see if any previously removed readers have +- * come back from the dead. If the ignored bit has been set, we do not need +- * it any more. +- */ ++ /* Iterate through all the readers to see if we need to make unavailable any ++ * freshly removed readers. Also, see if any previously removed ++ * readers have come back from the dead and don't need to be ignored. ++ */ + + const char *curReaderName = NULL; + unsigned long knownState = 0; + for(int ri = 0 ; ri < numReaders; ri ++) { +- + knownState = CKYReader_GetKnownState(&readerStates[ri]); +- if( !(knownState & SCARD_STATE_IGNORE)) { +- continue; +- } +- ++ + curReaderName = CKYReader_GetReaderName(&readerStates[ri]); + if(readerNameExistsInList(curReaderName,&readerNames)) { + CKYReader_SetKnownState(&readerStates[ri], knownState & ~SCARD_STATE_IGNORE); +- ++ } else { ++ if (!(knownState & SCARD_STATE_UNAVAILABLE)) ++ CKYReader_SetKnownState(&readerStates[ri], knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED); + } + } + +@@ -1238,6 +1236,32 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + throw; + } + ++ /* Before round-tripping to the daemon for the duration of the ++ * timeout, first see if we lost any readers, and pick a slot ++ * from that set to return ++ */ ++ for (i=0; i < numReaders; i++) { ++ unsigned long knownState = CKYReader_GetKnownState(&readerStates[i]); ++ ++ if ((knownState & SCARD_STATE_UNAVAILABLE) && ++ (knownState & SCARD_STATE_CHANGED)) { ++ CKYReader_SetKnownState(&readerStates[i], knownState & ~SCARD_STATE_CHANGED); ++ readerListLock.releaseLock(); ++ *slotp = slotIndexToID(i); ++ found = TRUE; ++ break; ++ } ++ } ++ ++ if (found) { ++ break; ++ } ++ ++ if (shuttingDown) { ++ readerListLock.releaseLock(); ++ break; ++ } ++ + if (myNumReaders != numReaders) { + if (myReaderStates) { + delete [] myReaderStates; diff --git a/SOURCES/coolkey-gcc43.patch b/SOURCES/coolkey-gcc43.patch new file mode 100644 index 0000000..5e41b5a --- /dev/null +++ b/SOURCES/coolkey-gcc43.patch @@ -0,0 +1,54 @@ +diff -up ./src/coolkey/slot.cpp.coolkey-gcc43 ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.coolkey-gcc43 2008-02-13 18:01:45.000000000 -0800 ++++ ./src/coolkey/slot.cpp 2008-02-13 18:03:05.000000000 -0800 +@@ -25,7 +25,6 @@ + #include "PKCS11Exception.h" + #include + #include "slot.h" +-#include + #include "zlib.h" + #include "params.h" + +@@ -33,7 +32,6 @@ + + #define MIN(x, y) ((x) < (y) ? (x) : (y)) + +-using std::auto_ptr; + + + #ifdef DEBUG +diff -up ./src/coolkey/machdep.cpp.coolkey-gcc43 ./src/coolkey/machdep.cpp +--- ./src/coolkey/machdep.cpp.coolkey-gcc43 2008-02-13 18:02:06.000000000 -0800 ++++ ./src/coolkey/machdep.cpp 2008-02-13 18:04:04.000000000 -0800 +@@ -33,6 +33,8 @@ + #include + #include + #include ++#include ++#include + #endif + + #ifdef _WIN32 +diff -up ./src/coolkey/log.cpp.coolkey-gcc43 ./src/coolkey/log.cpp +--- ./src/coolkey/log.cpp.coolkey-gcc43 2008-02-13 18:01:55.000000000 -0800 ++++ ./src/coolkey/log.cpp 2008-02-13 18:03:37.000000000 -0800 +@@ -18,6 +18,8 @@ + * ***** END COPYRIGHT BLOCK *****/ + + #include ++#include ++#include + #include "mypkcs11.h" + #include + #include +diff -up ./src/coolkey/object.cpp.coolkey-gcc43 ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.coolkey-gcc43 2008-02-13 18:02:20.000000000 -0800 ++++ ./src/coolkey/object.cpp 2008-02-13 18:04:22.000000000 -0800 +@@ -21,6 +21,7 @@ + #include "PKCS11Exception.h" + #include "object.h" + #include ++#include + + using std::find_if; + diff --git a/SOURCES/coolkey-latest.patch b/SOURCES/coolkey-latest.patch new file mode 100644 index 0000000..bc10eb8 --- /dev/null +++ b/SOURCES/coolkey-latest.patch @@ -0,0 +1,685 @@ +diff -up ./src/coolkey/slot.cpp.coolkey-latest ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.coolkey-latest 2009-09-11 13:58:24.423487305 -0700 ++++ ./src/coolkey/slot.cpp 2009-09-11 14:04:30.813488220 -0700 +@@ -203,6 +203,29 @@ SlotList::readerExists(const char *reade + return FALSE; + } + ++bool ++SlotList::readerNameExistsInList(const char *readerName,CKYReaderNameList *readerNameList) ++{ ++ if( !readerName || !readerNameList) { ++ return FALSE; ++ } ++ ++ int i = 0; ++ int readerNameCnt = CKYReaderNameList_GetCount(*readerNameList); ++ ++ const char *curReaderName = NULL; ++ for(i=0; i < readerNameCnt; i++) { ++ curReaderName = CKYReaderNameList_GetValue(*readerNameList,i); ++ ++ if(!strcmp(curReaderName,readerName)) { ++ return TRUE; ++ } ++ ++ } ++ ++ return FALSE; ++} ++ + /* + * you need to hold the ReaderList Lock before you can update the ReaderList + */ +@@ -256,6 +279,27 @@ SlotList::updateReaderList() + * don't recognize. + */ + ++ /* first though, let's check to see if any previously removed readers have ++ * come back from the dead. If the ignored bit has been set, we do not need ++ * it any more. ++ */ ++ ++ const char *curReaderName = NULL; ++ unsigned long knownState = 0; ++ for(int ri = 0 ; ri < numReaders; ri ++) { ++ ++ knownState = CKYReader_GetKnownState(&readerStates[ri]); ++ if( !(knownState & SCARD_STATE_IGNORE)) { ++ continue; ++ } ++ ++ curReaderName = CKYReader_GetReaderName(&readerStates[ri]); ++ if(readerNameExistsInList(curReaderName,&readerNames)) { ++ CKYReader_SetKnownState(&readerStates[ri], knownState & ~SCARD_STATE_IGNORE); ++ ++ } ++ } ++ + const char *newReadersData[MAX_READER_DELTA]; + const char **newReaders = &newReadersData[0]; + unsigned int newReaderCount = 0; +@@ -528,7 +572,7 @@ SlotList::getSlotList(CK_BBOOL tokenPres + void + Slot::connectToToken() + { +- CKYStatus status; ++ CKYStatus status = CKYSCARDERR; + OSTime time = OSTimeNow(); + + mCoolkey = 0; +@@ -537,13 +581,31 @@ Slot::connectToToken() + + // try to connect to the card + if( ! CKYCardConnection_IsConnected(conn) ) { +- status = CKYCardConnection_Connect(conn, readerName); +- if( status != CKYSUCCESS ) { +- log->log("Unable to connect to token\n"); ++ int i = 0; ++ //for cranky readers try again a few more times ++ while( i++ < 5 && status != CKYSUCCESS ) ++ { ++ status = CKYCardConnection_Connect(conn, readerName); ++ if( status != CKYSUCCESS && ++ CKYCardConnection_GetLastError(conn) == SCARD_E_PROTO_MISMATCH ) ++ { ++ log->log("Unable to connect to token status %d ConnGetGetLastError %x .\n",status,CKYCardConnection_GetLastError(conn)); ++ ++ } ++ else ++ { ++ break; ++ } ++ OSSleep(100000); ++ } ++ ++ if( status != CKYSUCCESS) ++ { + state = UNKNOWN; + return; + } + } ++ + log->log("time connect: Connect Time %d ms\n", OSTimeNow() - time); + if (!slotInfoFound) { + readSlotInfo(); +@@ -562,15 +624,10 @@ Slot::connectToToken() + state = CARD_PRESENT; + } + +- if ( CKYBuffer_DataIsEqual(&cardATR, ATR, sizeof (ATR)) || +- CKYBuffer_DataIsEqual(&cardATR, ATR1, sizeof(ATR1)) || +- CKYBuffer_DataIsEqual(&cardATR, ATR2, sizeof(ATR2)) ) { +- +- if (Params::hasParam("noAppletOK")) +- { +- state |= APPLET_SELECTABLE; +- mCoolkey = 1; +- } ++ if (Params::hasParam("noAppletOK")) ++ { ++ state |= APPLET_SELECTABLE; ++ mCoolkey = 1; + } + + /* support CAC card. identify the card based on applets, not the ATRS */ +@@ -631,7 +688,7 @@ Slot::connectToToken() + * unfriendly */ + isVersion1Key = 0; + needLogin = 1; +- ++ mCoolkey = 0; + return; + } + mCoolkey = 1; +@@ -1077,6 +1134,7 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + } + throw; + } ++ + if (myNumReaders != numReaders) { + if (myReaderStates) { + delete [] myReaderStates; +@@ -1103,6 +1161,7 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + } + } + } ++ + if (found || (flag == CKF_DONT_BLOCK) || shuttingDown) { + break; + } +@@ -1272,6 +1331,19 @@ class ObjectHandleMatch { + } + }; + ++class KeyNumMatch { ++ private: ++ CKYByte keyNum; ++ const Slot &slot; ++ public: ++ KeyNumMatch(CKYByte keyNum_, const Slot &s) : keyNum(keyNum_), slot(s) { } ++ bool operator() (const PKCS11Object& obj) { ++ unsigned long objID = obj.getMuscleObjID(); ++ return (slot.getObjectClass(objID) == 'k') ++ && (slot.getObjectIndex(objID) == keyNum); ++ } ++}; ++ + class ObjectCertCKAIDMatch { + private: + CKYByte cka_id; +@@ -3007,8 +3079,9 @@ Slot::sign(SessionHandleSuffix suffix, C + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) + { ++ RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE); + cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen, +- RSASignatureParams(CryptParams::FIXED_KEY_SIZE)); ++ params); + } + + void +@@ -3016,14 +3089,15 @@ Slot::decrypt(SessionHandleSuffix suffix + CK_ULONG ulDataLen, CK_BYTE_PTR pDecryptedData, + CK_ULONG_PTR pulDecryptedDataLen) + { ++ RSADecryptParams params(CryptParams::DEFAULT_KEY_SIZE); + cryptRSA(suffix, pData, ulDataLen, pDecryptedData, pulDecryptedDataLen, +- RSADecryptParams(CryptParams::FIXED_KEY_SIZE)); ++ params); + } + + void + Slot::cryptRSA(SessionHandleSuffix suffix, CK_BYTE_PTR pInput, + CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, +- CK_ULONG_PTR pulOutputLen, const CryptParams& params) ++ CK_ULONG_PTR pulOutputLen, CryptParams& params) + { + refreshTokenState(); + SessionIter session = findSession(suffix); +@@ -3041,6 +3115,11 @@ Slot::cryptRSA(SessionHandleSuffix suffi + CKYBuffer *result = &opState.result; + CKYByte keyNum = opState.keyNum; + ++ unsigned int keySize = getKeySize(keyNum); ++ ++ if(keySize != CryptParams::DEFAULT_KEY_SIZE) ++ params.setKeySize(keySize); ++ + if( CKYBuffer_Size(result) == 0 ) { + // we haven't already peformed the decryption, so do it now. + if( pInput == NULL || ulInputLen == 0) { +@@ -3243,3 +3322,36 @@ Slot::generateRandom(SessionHandleSuffix + throw PKCS11Exception(CKR_DEVICE_ERROR); + } + } ++ ++#define MAX_NUM_KEYS 8 ++unsigned int ++Slot::getKeySize(CKYByte keyNum) ++{ ++ unsigned int keySize = CryptParams::DEFAULT_KEY_SIZE; ++ int modSize = 0; ++ ++ if(keyNum >= MAX_NUM_KEYS) { ++ return keySize; ++ } ++ ++ ObjectConstIter iter; ++ iter = find_if(tokenObjects.begin(), tokenObjects.end(), ++ KeyNumMatch(keyNum,*this)); ++ ++ if( iter == tokenObjects.end() ) { ++ return keySize; ++ } ++ ++ CKYBuffer const *modulus = iter->getAttribute(CKA_MODULUS); ++ ++ if(modulus) { ++ modSize = CKYBuffer_Size(modulus); ++ if(CKYBuffer_GetChar(modulus,0) == 0x0) { ++ modSize--; ++ } ++ if(modSize > 0) ++ keySize = modSize * 8; ++ } ++ ++ return keySize; ++} +diff -up ./src/coolkey/slot.h.coolkey-latest ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.coolkey-latest 2006-06-09 11:39:11.000000000 -0700 ++++ ./src/coolkey/slot.h 2009-09-11 13:58:24.462488099 -0700 +@@ -270,10 +270,9 @@ class CryptParams { + protected: + unsigned int getKeySize() const { return keySize; } + public: +- // !!!XXX hack. The right way to get the key size is to get all the +- // key information from the token with MSCListKeys, the same way +- // we get all the object information with MSCListObjects. +- enum { FIXED_KEY_SIZE = 1024 }; ++ // set the actual key size obtained from the card ++ void setKeySize(unsigned int newKeySize) { keySize = newKeySize; } ++ enum { DEFAULT_KEY_SIZE = 1024 }; + + + CryptParams(unsigned int keySize_) : keySize(keySize_) { } +@@ -422,7 +421,7 @@ class Slot { + + void cryptRSA(SessionHandleSuffix suffix, CK_BYTE_PTR pInput, + CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, +- CK_ULONG_PTR pulOutputLen, const CryptParams& params); ++ CK_ULONG_PTR pulOutputLen, CryptParams& params); + + void performRSAOp(CKYBuffer *out, const CKYBuffer *input, CKYByte keyNum, + CKYByte direction); +@@ -460,6 +459,8 @@ class Slot { + return (char )((objectID >> 16) & 0xff) - '0'; + } + ++ // actually get the size of a key in bits from the card ++ unsigned int getKeySize(CKYByte keyNum); + + SessionHandleSuffix openSession(Session::Type type); + void closeSession(SessionHandleSuffix handleSuffix); +@@ -527,6 +528,8 @@ class SlotList { + * has called 'C_GetSlotList' with a NULL parameter */ + void updateReaderList(); + ++ /* see if a reader name exists in a caller provided reader name list. */ ++ bool readerNameExistsInList(const char *readerName,CKYReaderNameList *readerNameList ); + bool readerExists(const char *readerName, unsigned int *hint = 0); + public: + SlotList(Log *log); +diff -up ./src/libckyapplet/cky_applet.c.coolkey-latest ./src/libckyapplet/cky_applet.c +--- ./src/libckyapplet/cky_applet.c.coolkey-latest 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_applet.c 2009-09-11 13:58:24.464487796 -0700 +@@ -134,6 +134,13 @@ CKYAppletFactory_Logout(CKYAPDU *apdu, c + /* Future add WriteObject */ + + CKYStatus ++CKYAppletFactory_WriteObject(CKYAPDU *apdu, const void *param) ++{ ++ const CKYAppletArgWriteObject *wos = (const CKYAppletArgWriteObject *)param; ++ return CKYAPDUFactory_WriteObject(apdu,wos->objectID,wos->offset,wos->size,wos->data); ++} ++ ++CKYStatus + CKYAppletFactory_CreateObject(CKYAPDU *apdu, const void *param) + { + const CKYAppletArgCreateObject *cos=(const CKYAppletArgCreateObject *)param; +@@ -192,7 +199,6 @@ CKYAppletFactory_GetLifeCycleV2(CKYAPDU + { + return CKYAPDUFactory_GetLifeCycleV2(apdu); + } +- + CKYStatus + CKYAppletFactory_GetRandom(CKYAPDU *apdu, const void *param) + { +@@ -725,24 +731,48 @@ CKYApplet_ComputeCrypt(CKYCardConnection + CKYAppletArgComputeCrypt ccd; + CKYBuffer empty; + CKYISOStatus status; ++ short dataSize = 0; + int use2APDUs = 0; ++ int use_dl_object = CKYBuffer_Size(data) > 200 ; + + CKYBuffer_InitEmpty(&empty); + ccd.keyNumber = keyNumber; + ccd.mode = mode; + ccd.direction = direction; +- ccd.location = CKY_DL_APDU; ++ ccd.location = use_dl_object ? CKY_DL_OBJECT : CKY_DL_APDU; + + if (!apduRC) + apduRC = &status; + ++ if (use_dl_object) { ++ CKYBuffer sizeBuf; ++ ++ CKYBuffer_InitEmpty(&sizeBuf); ++ CKYBuffer_AppendShort(&sizeBuf, CKYBuffer_Size(data)); ++ ++ ret = CKYApplet_WriteObjectFull(conn, 0xffffffff, ++ 0, CKYBuffer_Size(&sizeBuf), nonce, ++ &sizeBuf, apduRC); ++ ++ CKYBuffer_FreeData(&sizeBuf); ++ if( ret != CKYSUCCESS) ++ goto fail; ++ ++ ret = CKYApplet_WriteObjectFull(conn, 0xffffffff, ++ 2, CKYBuffer_Size(data), nonce, ++ data, apduRC); ++ ++ if(ret != CKYSUCCESS) ++ goto fail; ++ } ++ + if (mode == CKY_RSA_NO_PAD) { +- ccd.data = data; ++ ccd.data = use_dl_object ? &empty : data; + ccd.sig = sig; + ret = CKYApplet_HandleAPDU(conn, + CKYAppletFactory_ComputeCryptOneStep, &ccd, nonce, + CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, +- result, apduRC); ++ use_dl_object ? NULL : result, apduRC); + if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { + use2APDUs = 1; /* maybe it's an old applet */ + } +@@ -759,13 +789,38 @@ CKYApplet_ComputeCrypt(CKYCardConnection + CKYAppletFactory_ComputeCryptInit, &ccd, nonce, + 0, CKYAppletFill_Null, NULL, apduRC); + if (ret == CKYSUCCESS) { +- ccd.data = data; ++ ccd.data = use_dl_object ? &empty : data; + ret = CKYApplet_HandleAPDU(conn, + CKYAppletFactory_ComputeCryptFinal, &ccd, nonce, + CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeCryptFinal, +- result, apduRC); ++ use_dl_object ? NULL : result, apduRC); + } + } ++ ++ if (use_dl_object && ret == CKYSUCCESS) { ++ CKYBuffer sizeOutBuf; ++ CKYBuffer_InitEmpty(&sizeOutBuf); ++ ++ ret = CKYApplet_ReadObjectFull(conn,0xffffffff, ++ 0, 2, ++ nonce,&sizeOutBuf,apduRC); ++ ++ if(ret != CKYSUCCESS) { ++ CKYBuffer_FreeData(&sizeOutBuf); ++ goto fail; ++ } ++ ++ dataSize = CKYBuffer_GetShort(&sizeOutBuf, 0); ++ ++ CKYBuffer_FreeData(&sizeOutBuf); ++ ++ ret = CKYApplet_ReadObjectFull(conn,0xffffffff, ++ 2, dataSize, ++ nonce,result,apduRC); ++ } ++ ++fail: ++ + return ret; + } + +@@ -1036,6 +1091,44 @@ CKYApplet_ReadObjectFull(CKYCardConnecti + } + + /* ++ * Write Object ++ * This makes multiple APDU calls to write the entire object. ++ * ++ */ ++ ++CKYStatus ++CKYApplet_WriteObjectFull(CKYCardConnection *conn, unsigned long objectID, ++ CKYOffset offset, CKYSize size, const CKYBuffer *nonce, ++ const CKYBuffer *data, CKYISOStatus *apduRC) ++{ ++ ++ CKYBuffer chunk; ++ CKYOffset srcOffset = 0; ++ CKYAppletArgWriteObject wod; ++ CKYStatus ret = CKYSUCCESS; ++ ++ wod.objectID = objectID; ++ wod.offset = offset; ++ do { ++ wod.size = (CKYByte) MIN(size, 220); ++ ret = CKYBuffer_InitFromBuffer(&chunk, data, ++ srcOffset, wod.size); ++ if(ret == CKYSUCCESS) { ++ wod.data = &chunk; ++ ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_WriteObject, &wod, ++ nonce, 0, CKYAppletFill_Null, NULL, apduRC); ++ size -= wod.size; ++ wod.offset += wod.size; ++ srcOffset += wod.size; ++ CKYBuffer_FreeData(&chunk); ++ } ++ ++ } while ((size > 0) && (ret == CKYSUCCESS)); ++ ++ return ret; ++} ++ ++/* + * List Object cluster + */ + static CKYStatus +diff -up ./src/libckyapplet/cky_applet.h.coolkey-latest ./src/libckyapplet/cky_applet.h +--- ./src/libckyapplet/cky_applet.h.coolkey-latest 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_applet.h 2009-09-11 13:58:24.466487772 -0700 +@@ -192,6 +192,14 @@ typedef struct _CKYAppletArgReadObject { + CKYByte size; + } CKYAppletArgReadObject; + ++typedef struct _CKYAppletArgWriteObject { ++ unsigned long objectID; ++ CKYOffset offset; ++ CKYByte size; ++ CKYBuffer *data; ++ ++} CKYAppletArgWriteObject; ++ + typedef struct _CKYAppletArgComputeCrypt { + CKYByte keyNumber; + CKYByte mode; +@@ -250,6 +258,8 @@ CKYStatus CKYAppletFactory_ListPINs(CKYA + /* param == CKYByte * (pointer to pinNumber) */ + CKYStatus CKYAppletFactory_Logout(CKYAPDU *apdu, const void *param); + /* Future add WriteObject */ ++/* parm == CKYAppletArgWriteObject */ ++CKYStatus CKYAppletFactory_WriteObject(CKYAPDU *apdu, const void *param); + /* param == CKYAppletArgCreateObject */ + CKYStatus CKYAppletFactory_CreateObject(CKYAPDU *apdu, const void *param); + /* param == CKYAppletArgDeleteObject */ +@@ -482,6 +492,17 @@ CKYStatus CKYApplet_ReadObjectAppend(CKY + CKYStatus CKYApplet_ReadObjectFull(CKYCardConnection *conn, + unsigned long objectID, CKYOffset offset, CKYSize size, + const CKYBuffer *nonce, CKYBuffer *data, CKYISOStatus *apduRC); ++/* ++ * There is 1 write command: ++ * CKYApplet_WriteObjectFull can write an entire data object. It makes multiple ++ * apdu calls in order to write the full amount into the buffer. The buffer is ++ * overwritten. ++*/ ++ ++CKYStatus CKYApplet_WriteObjectFull(CKYCardConnection *conn, ++ unsigned long objectID, CKYOffset offset, CKYSize size, ++ const CKYBuffer *nonce, const CKYBuffer *data, CKYISOStatus *apduRC); ++ + CKYStatus CKYApplet_ListObjects(CKYCardConnection *conn, CKYByte seq, + CKYAppletRespListObjects *lop, CKYISOStatus *apduRC); + CKYStatus CKYApplet_GetStatus(CKYCardConnection *conn, +diff -up ./src/libckyapplet/cky_card.c.coolkey-latest ./src/libckyapplet/cky_card.c +--- ./src/libckyapplet/cky_card.c.coolkey-latest 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_card.c 2009-09-11 13:58:24.468487469 -0700 +@@ -129,6 +129,7 @@ typedef struct _SCard { + SCardGetStatusChangeFn SCardGetStatusChange; + SCardCancelFn SCardCancel; + SCARD_IO_REQUEST *SCARD_PCI_T0_; ++ SCARD_IO_REQUEST *SCARD_PCI_T1_; + } SCard; + + #define GET_ADDRESS(library, scard, name) \ +@@ -195,6 +196,12 @@ ckySCard_Init(void) + if( status != CKYSUCCESS ) { + goto fail; + } ++ ++ status = ckyShLibrary_getAddress( library, ++ (void**) &scard->SCARD_PCI_T1_, MAKE_DLL_SYMBOL(g_rgSCardT1Pci)); ++ if( status != CKYSUCCESS ) { ++ goto fail; ++ } + return scard; + + fail: +@@ -884,6 +891,7 @@ struct _CKYCardConnection { + SCARDHANDLE cardHandle; + unsigned long lastError; + CKYBool inTransaction; ++ unsigned long protocol; + }; + + static void +@@ -894,6 +902,7 @@ ckyCardConnection_init(CKYCardConnection + conn->cardHandle = 0; + conn->lastError = 0; + conn->inTransaction = 0; ++ conn->protocol = SCARD_PROTOCOL_T0; + } + + CKYCardConnection * +@@ -934,14 +943,13 @@ CKYCardConnection_Connect(CKYCardConnect + { + CKYStatus ret; + unsigned long rv; +- unsigned long protocol; + + ret = CKYCardConnection_Disconnect(conn); + if (ret != CKYSUCCESS) { + return ret; + } + rv = conn->scard->SCardConnect( conn->ctx->context, readerName, +- SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &conn->cardHandle, &protocol); ++ SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &conn->cardHandle, &conn->protocol); + if (rv != SCARD_S_SUCCESS) { + conn->lastError = rv; + return CKYSCARDERR; +@@ -978,7 +986,7 @@ ckyCardConnection_reconnectRaw(CKYCardCo + unsigned long protocol; + + rv = conn->scard->SCardReconnect(conn->cardHandle, +- SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, init, &protocol); ++ SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 , init, &protocol); + if (rv != SCARD_S_SUCCESS) { + conn->lastError = rv; + return CKYSCARDERR; +@@ -1039,10 +1047,17 @@ CKYCardConnection_TransmitAPDU(CKYCardCo + return ret; + } + +- rv = conn->scard->SCardTransmit(conn->cardHandle, +- conn->scard->SCARD_PCI_T0_, +- CKYBuffer_Data(&apdu->apduBuf), CKYBuffer_Size(&apdu->apduBuf), +- NULL, response->data, &response->len); ++ if( conn->protocol == SCARD_PROTOCOL_T0 ) { ++ rv = conn->scard->SCardTransmit(conn->cardHandle, ++ conn->scard->SCARD_PCI_T0_, ++ CKYBuffer_Data(&apdu->apduBuf), CKYBuffer_Size(&apdu->apduBuf), ++ NULL, response->data, &response->len); ++ } else { ++ rv = conn->scard->SCardTransmit(conn->cardHandle, ++ conn->scard->SCARD_PCI_T1_, ++ CKYBuffer_Data(&apdu->apduBuf), CKYBuffer_Size(&apdu->apduBuf), ++ NULL, response->data, &response->len); ++ } + + if (rv != SCARD_S_SUCCESS) { + conn->lastError =rv; +diff -up ./src/libckyapplet/cky_factory.c.coolkey-latest ./src/libckyapplet/cky_factory.c +--- ./src/libckyapplet/cky_factory.c.coolkey-latest 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_factory.c 2009-09-11 13:58:24.470495267 -0700 +@@ -190,8 +190,11 @@ CKYAPDUFactory_ComputeCryptOneStep(CKYAP + CKYSize len; + CKYBuffer buf; + +- if (!idata || !(len = CKYBuffer_Size(idata)) || location != CKY_DL_APDU) +- return ret; ++ if (!idata) ++ return ret; ++ ++ if (!(len = CKYBuffer_Size(idata)) && location != CKY_DL_OBJECT) ++ return ret; + + CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); + CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_CRYPT); +@@ -314,8 +317,6 @@ CKYAPDUFactory_Logout(CKYAPDU *apdu, CKY + return CKYSUCCESS; + } + +-/* Future add WriteObject */ +- + CKYStatus + CKYAPDUFactory_CreateObject(CKYAPDU *apdu, unsigned long objectID, CKYSize size, + unsigned short readACL, unsigned short writeACL, unsigned short deleteACL) +@@ -419,6 +420,58 @@ fail: + } + + CKYStatus ++CKYAPDUFactory_WriteObject(CKYAPDU *apdu, unsigned long objectID, ++ CKYOffset offset,CKYSize size,CKYBuffer *data) ++{ ++ CKYBuffer buf; ++ CKYStatus ret = CKYSUCCESS; ++ unsigned short dataSize = 0; ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); ++ CKYAPDU_SetINS(apdu, CKY_INS_WRITE_OBJ); ++ CKYAPDU_SetP1(apdu, 0x00); ++ CKYAPDU_SetP2(apdu, 0x00); ++ CKYBuffer_InitEmpty(&buf); ++ ++ dataSize = (unsigned short) CKYBuffer_Size(data); ++ ++ if(!dataSize) { ++ ret = CKYINVALIDARGS; ++ goto fail; ++ } ++ ++ ret = CKYBuffer_AppendLong(&buf,objectID); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendLong(&buf,offset); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendChar(&buf, size); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ++ ret = CKYAPDU_SetSendDataBuffer(apdu,&buf); ++ ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, data); ++ ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ++fail: ++ CKYBuffer_FreeData(&buf); ++ return ret; ++ ++} ++ ++CKYStatus + CKYAPDUFactory_ListObjects(CKYAPDU *apdu, CKYByte sequence) + { + CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); +diff -up ./src/libckyapplet/cky_factory.h.coolkey-latest ./src/libckyapplet/cky_factory.h +--- ./src/libckyapplet/cky_factory.h.coolkey-latest 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_factory.h 2009-09-11 13:58:24.472487421 -0700 +@@ -190,7 +190,8 @@ CKYStatus CKYAPDUFactory_ChangePIN(CKYAP + const char *oldPin, const char *newPin); + CKYStatus CKYAPDUFactory_ListPINs(CKYAPDU *apdu); + CKYStatus CKYAPDUFactory_Logout(CKYAPDU *apdu, CKYByte pinNumber); +- ++CKYStatus CKYAPDUFactory_WriteObject(CKYAPDU *apdu, unsigned long objectID, ++ CKYOffset offset,CKYSize size,CKYBuffer *data); + /* Future add WriteObject */ + CKYStatus CKYAPDUFactory_CreateObject(CKYAPDU *apdu, unsigned long objectID, + CKYSize size, unsigned short readACL, unsigned short writeACL, diff --git a/SOURCES/coolkey-pcsc-lite-fix.patch b/SOURCES/coolkey-pcsc-lite-fix.patch new file mode 100644 index 0000000..f528676 --- /dev/null +++ b/SOURCES/coolkey-pcsc-lite-fix.patch @@ -0,0 +1,69 @@ +diff -up ./src/coolkey/slot.cpp.reader-state-fix ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.reader-state-fix 2010-09-08 13:25:14.479109000 -0700 ++++ ./src/coolkey/slot.cpp 2010-09-08 13:25:14.506109000 -0700 +@@ -2185,6 +2185,7 @@ Slot::readCACCertificateFirst(CKYBuffer + { + CKYStatus status; + CKYISOStatus apduRC; ++ *nextSize = 0; + + if (mOldCAC) { + /* get the first 100 bytes of the cert */ +diff -up ./src/libckyapplet/cky_card.c.reader-state-fix ./src/libckyapplet/cky_card.c +--- ./src/libckyapplet/cky_card.c.reader-state-fix 2010-09-08 14:05:10.859321000 -0700 ++++ ./src/libckyapplet/cky_card.c 2010-09-08 14:05:42.792257000 -0700 +@@ -27,7 +27,6 @@ + + #ifndef WINAPI + #define WINAPI +-typedef SCARD_READERSTATE *LPSCARD_READERSTATE; + #endif + + #ifndef SCARD_E_NO_READERS_AVAILABLE +diff -up ./src/libckyapplet/cky_card.h.reader-state-fix ./src/libckyapplet/cky_card.h +--- ./src/libckyapplet/cky_card.h.reader-state-fix 2006-06-09 11:44:17.000000000 -0700 ++++ ./src/libckyapplet/cky_card.h 2010-09-08 13:25:14.518109000 -0700 +@@ -41,23 +41,23 @@ CKYLIST_DECLARE(CKYReaderName, char *) + CKYLIST_DECLARE(CKYCardConnection, CKYCardConnection *) + + CKY_BEGIN_PROTOS +-void CKYReader_Init(SCARD_READERSTATE_A *reader); +-void CKYReader_FreeData(SCARD_READERSTATE_A *reader); ++void CKYReader_Init(SCARD_READERSTATE *reader); ++void CKYReader_FreeData(SCARD_READERSTATE *reader); + + /* +- * "Accessors": for SCARD_READERSTATE_A structure as a class. +- * These functions take an SCARD_READERSTATE_A which can also be referenced ++ * "Accessors": for SCARD_READERSTATE structure as a class. ++ * These functions take an SCARD_READERSTATE which can also be referenced + * directly. + */ +-CKYStatus CKYReader_SetReaderName(SCARD_READERSTATE_A *reader, const char *name); +-const char *CKYReader_GetReaderName(const SCARD_READERSTATE_A *reader); +-CKYStatus CKYReader_SetKnownState(SCARD_READERSTATE_A *reader, ++CKYStatus CKYReader_SetReaderName(SCARD_READERSTATE *reader, const char *name); ++const char *CKYReader_GetReaderName(const SCARD_READERSTATE *reader); ++CKYStatus CKYReader_SetKnownState(SCARD_READERSTATE *reader, + unsigned long state); +-unsigned long CKYReader_GetKnownState(const SCARD_READERSTATE_A *reader); +-unsigned long CKYReader_GetEventState(const SCARD_READERSTATE_A *reader); +-CKYStatus CKYReader_GetATR(const SCARD_READERSTATE_A *reader, CKYBuffer *buf); ++unsigned long CKYReader_GetKnownState(const SCARD_READERSTATE *reader); ++unsigned long CKYReader_GetEventState(const SCARD_READERSTATE *reader); ++CKYStatus CKYReader_GetATR(const SCARD_READERSTATE *reader, CKYBuffer *buf); + /* create an array of READERSTATEs from a LIST of Readers */ +-SCARD_READERSTATE_A *CKYReader_CreateArray(const CKYReaderNameList readerNames, ++SCARD_READERSTATE *CKYReader_CreateArray(const CKYReaderNameList readerNames, + unsigned long *readerCount); + /* frees the reader, then the full array */ + void CKYReader_DestroyArray(SCARD_READERSTATE *reader, unsigned long count); +@@ -88,7 +88,7 @@ CKYStatus CKYCardContext_FindReadersByAT + const CKYBuffer *targetATR); + /* return if any of the readers in our array has changed in status */ + CKYStatus CKYCardContext_WaitForStatusChange(CKYCardContext *context, +- SCARD_READERSTATE_A *readers, ++ SCARD_READERSTATE *readers, + unsigned long readerCount, + unsigned long timeout); + /* cancel any current operation (such as wait for status change) on this diff --git a/SOURCES/coolkey-piv-ecc-el7.patch b/SOURCES/coolkey-piv-ecc-el7.patch new file mode 100644 index 0000000..1ca6f61 --- /dev/null +++ b/SOURCES/coolkey-piv-ecc-el7.patch @@ -0,0 +1,4792 @@ +diff -up ./src/coolkey/coolkey.cpp.piv-ecc ./src/coolkey/coolkey.cpp +--- ./src/coolkey/coolkey.cpp.piv-ecc 2013-09-08 15:50:33.085428102 -0700 ++++ ./src/coolkey/coolkey.cpp 2013-09-08 15:50:33.119428673 -0700 +@@ -34,7 +34,6 @@ + #include "cky_base.h" + #include "params.h" + +-#define NULL 0 + + /* static module data -------------------------------- */ + +@@ -70,11 +69,19 @@ typedef struct { + /********************************************************************** + ************************** MECHANISM TABLE *************************** + **********************************************************************/ +-static MechInfo +-mechanismList[] = { ++ ++static const MechInfo ++rsaMechanismList[] = { + {CKM_RSA_PKCS, { 1024, 4096, CKF_HW | CKF_SIGN | CKF_DECRYPT } } + }; +-static unsigned int numMechanisms = sizeof(mechanismList)/sizeof(MechInfo); ++ ++static const MechInfo ++ecMechanismList[] = { ++ {CKM_ECDSA,{256,521,CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDSA_SHA1, {256, 521, CKF_HW | CKF_SIGN | CKF_EC_F_P}},{ CKM_ECDH1_DERIVE,{256, 521, CKF_HW | CKF_DERIVE | CKF_EC_F_P} } ++}; ++ ++unsigned int numRSAMechanisms = sizeof(rsaMechanismList)/sizeof(MechInfo); ++unsigned int numECMechanisms = sizeof(ecMechanismList)/sizeof(MechInfo); + + /* ------------------------------------------------------------ */ + +@@ -166,7 +173,6 @@ NOTSUPPORTED(C_GenerateKey, (CK_SESSION_ + NOTSUPPORTED(C_GenerateKeyPair, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_ATTRIBUTE_PTR,CK_ULONG,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR,CK_OBJECT_HANDLE_PTR)) + NOTSUPPORTED(C_WrapKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_OBJECT_HANDLE,CK_BYTE_PTR,CK_ULONG_PTR)) + NOTSUPPORTED(C_UnwrapKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_BYTE_PTR,CK_ULONG,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR)) +-NOTSUPPORTED(C_DeriveKey, (CK_SESSION_HANDLE,CK_MECHANISM_PTR,CK_OBJECT_HANDLE,CK_ATTRIBUTE_PTR,CK_ULONG,CK_OBJECT_HANDLE_PTR)) + NOTSUPPORTED(C_GetFunctionStatus, (CK_SESSION_HANDLE)) + NOTSUPPORTED(C_CancelFunction, (CK_SESSION_HANDLE)) + +@@ -200,6 +206,10 @@ SUPPORTED(C_SeedRandom, seedRandom, + SUPPORTED(C_GenerateRandom, generateRandom, + (CK_SESSION_HANDLE hSession ,CK_BYTE_PTR data,CK_ULONG dataLen), + (hSession, data, dataLen)) ++SUPPORTED(C_DeriveKey,derive, ++ (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey ), ++ (hSession, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey)) + + /* non-specialized functions supported with the slot directly */ + +@@ -249,7 +259,7 @@ C_Initialize(CK_VOID_PTR pInitArgs) + log = new DummyLog(); + } + log->log("Initialize called, hello %d\n", 5); +- CKY_SetName("coolkey"); ++ CKY_SetName((char *) "coolkey"); + slotList = new SlotList(log); + initialized = TRUE; + return CKR_OK; +@@ -347,6 +357,11 @@ CK_RV + C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, + CK_ULONG_PTR pulCount) + { ++ ++ const MechInfo *mechanismList = NULL; ++ unsigned int numMechanisms = 0; ++ ++ + if( ! initialized ) { + return CKR_CRYPTOKI_NOT_INITIALIZED; + } +@@ -359,11 +374,21 @@ C_GetMechanismList(CK_SLOT_ID slotID, CK + } + + slotList->validateSlotID(slotID); +- if( ! slotList->getSlot( +- slotIDToIndex(slotID))->isTokenPresent() ) { ++ ++ Slot *slot = slotList->getSlot(slotIDToIndex(slotID)); ++ ++ if( ! slot || ! slot->isTokenPresent() ) { + return CKR_TOKEN_NOT_PRESENT; + } + ++ if ( slot->getIsECC()) { ++ mechanismList = ecMechanismList; ++ numMechanisms = numECMechanisms; ++ } else { ++ mechanismList = rsaMechanismList; ++ numMechanisms = numRSAMechanisms; ++ } ++ + if( pMechanismList != NULL ) { + if( *pulCount < numMechanisms ) { + rv = CKR_BUFFER_TOO_SMALL; +@@ -390,19 +415,36 @@ CK_RV + C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR pInfo) + { ++ const MechInfo *mechanismList = NULL; ++ unsigned int numMechanisms = 0; ++ + if( ! initialized ) { + return CKR_CRYPTOKI_NOT_INITIALIZED; + } ++ ++ + try { + log->log("C_GetMechanismInfo called\n"); + if( pInfo == NULL ) { + throw PKCS11Exception(CKR_ARGUMENTS_BAD); + } + slotList->validateSlotID(slotID); +- if( ! slotList->getSlot(slotIDToIndex(slotID))->isTokenPresent() ) { ++ ++ ++ Slot *slot = slotList->getSlot(slotIDToIndex(slotID)); ++ ++ if( ! slot || ! slot->isTokenPresent() ) { + return CKR_TOKEN_NOT_PRESENT; + } + ++ if ( slot->getIsECC()) { ++ mechanismList = ecMechanismList; ++ numMechanisms = numECMechanisms; ++ } else { ++ mechanismList = rsaMechanismList; ++ numMechanisms = numRSAMechanisms; ++ } ++ + for(unsigned int i=0; i < numMechanisms; ++i ) { + if( mechanismList[i].mech == type ) { + *pInfo = mechanismList[i].info; +diff -up ./src/coolkey/machdep.cpp.piv-ecc ./src/coolkey/machdep.cpp +--- ./src/coolkey/machdep.cpp.piv-ecc 2013-09-08 15:50:33.085428102 -0700 ++++ ./src/coolkey/machdep.cpp 2013-09-08 15:50:33.119428673 -0700 +@@ -368,6 +368,7 @@ SHMem::initSegment(const char *name, int + #ifdef FULL_CLEANUP + flock(shmemData->fd, LOCK_UN); + #endif ++ free(buf); + delete shmemData; + return NULL; + } +diff -up ./src/coolkey/object.cpp.piv-ecc ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.piv-ecc 2013-09-08 15:50:33.104428421 -0700 ++++ ./src/coolkey/object.cpp 2013-09-08 15:50:33.121428706 -0700 +@@ -25,12 +25,44 @@ + + using std::find_if; + ++const CKYByte rsaOID[] = {0x2A,0x86,0x48,0x86,0xF7,0x0D, 0x01, 0x01,0x1}; ++const CKYByte eccOID[] = {0x2a,0x86,0x48,0xce,0x3d,0x02,0x01}; ++ ++#ifdef DEBUG ++void dump(CKYBuffer *buf) ++{ ++ CKYSize i; ++ CKYSize size = CKYBuffer_Size(buf); ++#define ROW_LENGTH 60 ++ char string[ROW_LENGTH+1]; ++ char *bp = &string[0]; ++ CKYByte c; ++ ++ for (i=0; i < size; i++) { ++ if (i && ((i % (ROW_LENGTH-1)) == 0) ) { ++ *bp = 0; ++ printf(" %s\n",string); ++ bp = &string[0]; ++ } ++ c = CKYBuffer_GetChar(buf, i); ++ printf("%02x ",c); ++ *bp++ = (c < ' ') ? '.' : ((c & 0x80) ? '*' : c); ++ } ++ *bp = 0; ++ for (i= (i % (ROW_LENGTH-1)); i && (i < ROW_LENGTH); i++) { ++ printf(" "); ++ } ++ printf(" %s\n",string); ++ fflush(stdout); ++} ++#endif ++ + + bool AttributeMatch::operator()(const PKCS11Attribute& cmp) + { + return (attr->type == cmp.getType()) && +- CKYBuffer_DataIsEqual(cmp.getValue(), +- (const CKYByte *)attr->pValue, attr->ulValueLen); ++ CKYBuffer_DataIsEqual(cmp.getValue(), ++ (const CKYByte *)attr->pValue, attr->ulValueLen); + } + + class AttributeTypeMatch +@@ -45,14 +77,14 @@ class AttributeTypeMatch + }; + + PKCS11Object::PKCS11Object(unsigned long muscleObjID_,CK_OBJECT_HANDLE handle_) +- : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL) ++ : muscleObjID(muscleObjID_), handle(handle_), label(NULL), name(NULL), keyType(unknown) + { + CKYBuffer_InitEmpty(&pubKey); + } + + PKCS11Object::PKCS11Object(unsigned long muscleObjID_, const CKYBuffer *data, + CK_OBJECT_HANDLE handle_) : muscleObjID(muscleObjID_), handle(handle_), +- label(NULL), name(NULL) ++ label(NULL), name(NULL), keyType(unknown) + { + CKYBuffer_InitEmpty(&pubKey); + +@@ -63,9 +95,98 @@ PKCS11Object::PKCS11Object(unsigned long + "PKCS #11 actual object id does not match stated id"); + } + if (type == 0) { +- parseOldObject(data); ++ parseOldObject(data); + } else if (type == 1) { +- parseNewObject(data); ++ parseNewObject(data); ++ } ++} ++ ++SecretKey::SecretKey(unsigned long muscleObjID_, CK_OBJECT_HANDLE handle_, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) ++ : PKCS11Object(muscleObjID_, handle_) ++{ ++ static CK_OBJECT_CLASS objClass = CKO_SECRET_KEY; ++ static CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; ++ static CK_BBOOL value = 0x1; ++ ++ if ( secretKeyBuffer == NULL) ++ return; ++ ++ /* Rifle through the input template */ ++ ++ CK_ATTRIBUTE_TYPE type; ++ CK_ATTRIBUTE attr; ++ CK_ULONG valueLength = 0; ++ ++ for(int i = 0; i < (int) ulAttributeCount; i++) { ++ attr = pTemplate[i]; ++ type = attr.type; ++ ++ if ( type == CKA_VALUE_LEN) { ++ //CK_ULONG ulValueLen = attr.ulValueLen; ++ valueLength = *((CK_ULONG *)attr.pValue); ++ } else { ++ ++ CKYBuffer val; ++ CKYBuffer_InitFromData(&val,(const CK_BYTE *) attr.pValue, attr.ulValueLen); ++ setAttribute( type, &val); ++ CKYBuffer_FreeData(&val); ++ } ++ } ++ ++ adjustToKeyValueLength( secretKeyBuffer, valueLength ); ++ ++ /* Fall backs. */ ++ ++ if(!attributeExists(CKA_CLASS)) ++ setAttributeULong(CKA_CLASS, objClass); ++ ++ if(!attributeExists(CKA_KEY_TYPE)) ++ setAttributeULong(CKA_KEY_TYPE, keyType); ++ ++ if(!attributeExists(CKA_TOKEN)) ++ setAttributeBool(CKA_TOKEN, value); ++ ++ if(!attributeExists(CKA_DERIVE)) ++ setAttributeBool(CKA_DERIVE, value); ++ ++ /* Actual value */ ++ setAttribute(CKA_VALUE, secretKeyBuffer); ++ ++} ++ ++void SecretKey::adjustToKeyValueLength(CKYBuffer * secretKeyBuffer,CK_ULONG valueLength) ++{ ++ const CK_LONG MAX_DIFF = 200; /* Put some bounds on this value */ ++ ++ if ( !secretKeyBuffer ) { ++ return; ++ } ++ ++ CKYBuffer scratch; ++ CK_ULONG actual_length = CKYBuffer_Size(secretKeyBuffer); ++ ++ CK_LONG diff = 0; ++ diff = (CK_LONG) valueLength - actual_length; ++ ++ if ( diff == 0 ) { ++ return; ++ } ++ ++ if ( diff > 0 && diff < MAX_DIFF ) { /*check for silly values */ ++ /* prepend with zeroes */ ++ CKYBuffer_InitFromLen(&scratch, diff); ++ CKYBuffer_AppendCopy(&scratch, secretKeyBuffer); ++ ++ CKYBuffer_FreeData(secretKeyBuffer); ++ CKYBuffer_InitFromCopy(secretKeyBuffer, &scratch); ++ CKYBuffer_FreeData(&scratch); ++ ++ } else if (diff < 0 ) { ++ /* truncate most significant bytes */ ++ CKYBuffer_InitFromData(&scratch, CKYBuffer_Data(secretKeyBuffer)-diff, valueLength); ++ CKYBuffer_FreeData(secretKeyBuffer); ++ CKYBuffer_InitFromCopy(secretKeyBuffer, &scratch); ++ CKYBuffer_FreeData(&scratch); + } + } + +@@ -95,29 +216,29 @@ PKCS11Object::parseOldObject(const CKYBu + attrib.setType(CKYBuffer_GetLong(data, idx)); + idx += 4; + unsigned int attrLen = CKYBuffer_GetShort(data, idx); +- idx += 2; ++ idx += 2; + if( attrLen > CKYBuffer_Size(data) +- || (idx + attrLen > CKYBuffer_Size(data)) ) { ++ || (idx + attrLen > CKYBuffer_Size(data)) ) { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Invalid attribute length %d\n", attrLen); + } +- /* these two types are ints, read them back from +- * the card in host order */ +- if ((attrib.getType() == CKA_CLASS) || +- (attrib.getType() == CKA_CERTIFICATE_TYPE) || +- (attrib.getType() == CKA_KEY_TYPE)) { +- /* ulongs are 4 bytes on the token, even if they are 8 or +- * more in the pkcs11 module */ +- if (attrLen != 4) { ++ /* these two types are ints, read them back from ++ * the card in host order */ ++ if ((attrib.getType() == CKA_CLASS) || ++ (attrib.getType() == CKA_CERTIFICATE_TYPE) || ++ (attrib.getType() == CKA_KEY_TYPE)) { ++ /* ulongs are 4 bytes on the token, even if they are 8 or ++ * more in the pkcs11 module */ ++ if (attrLen != 4) { + throw PKCS11Exception(CKR_DEVICE_ERROR, + "Invalid attribute length %d\n", attrLen); +- } +- CK_ULONG value = makeLEUInt(data,idx); ++ } ++ CK_ULONG value = makeLEUInt(data,idx); + +- attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); +- } else { +- attrib.setValue(CKYBuffer_Data(data)+idx, attrLen); +- } ++ attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); ++ } else { ++ attrib.setValue(CKYBuffer_Data(data)+idx, attrLen); ++ } + idx += attrLen; + attributes.push_back(attrib); + } +@@ -177,33 +298,33 @@ PKCS11Object::expandAttributes(unsigned + unsigned long i; + + if (!attributeExists(CKA_ID)) { +- PKCS11Attribute attrib; +- attrib.setType(CKA_ID); +- attrib.setValue(&cka_id, 1); ++ PKCS11Attribute attrib; ++ attrib.setType(CKA_ID); ++ attrib.setValue(&cka_id, 1); + attributes.push_back(attrib); + } + /* unpack the class */ + if (!attributeExists(CKA_CLASS)) { +- PKCS11Attribute attrib; +- attrib.setType(CKA_CLASS); +- attrib.setValue((CKYByte *)&objectType, sizeof(CK_ULONG)); ++ PKCS11Attribute attrib; ++ attrib.setType(CKA_CLASS); ++ attrib.setValue((CKYByte *)&objectType, sizeof(CK_ULONG)); + attributes.push_back(attrib); + } + + /* unpack the boolean flags. Note, the default mask is based on + * the class specified in fixedAttrs, not on the real class */ + for (i=1; i < sizeof(unsigned long)*8; i++) { +- unsigned long iMask = 1<< i; +- if ((mask & iMask) == 0) { +- continue; +- } +- if (attributeExists(boolType[i])) { +- continue; +- } +- PKCS11Attribute attrib; +- CKYByte bVal = (fixedAttrs & iMask) != 0; +- attrib.setType(boolType[i]); +- attrib.setValue(&bVal, 1); ++ unsigned long iMask = 1<< i; ++ if ((mask & iMask) == 0) { ++ continue; ++ } ++ if (attributeExists(boolType[i])) { ++ continue; ++ } ++ PKCS11Attribute attrib; ++ CKYByte bVal = (fixedAttrs & iMask) != 0; ++ attrib.setType(boolType[i]); ++ attrib.setValue(&bVal, 1); + attributes.push_back(attrib); + } + } +@@ -224,40 +345,40 @@ PKCS11Object::parseNewObject(const CKYBu + // load up the explicit attributes first + for (j=0, offset = 11; j < attributeCount && offset < size; j++) { + PKCS11Attribute attrib; +- CKYByte attributeDataType = CKYBuffer_GetChar(data, offset+4); +- unsigned int attrLen = 0; ++ CKYByte attributeDataType = CKYBuffer_GetChar(data, offset+4); ++ unsigned int attrLen = 0; + attrib.setType(CKYBuffer_GetLong(data, offset)); + offset += 5; + +- switch(attributeDataType) { +- case DATATYPE_STRING: +- attrLen = CKYBuffer_GetShort(data, offset); +- offset += 2; ++ switch(attributeDataType) { ++ case DATATYPE_STRING: ++ attrLen = CKYBuffer_GetShort(data, offset); ++ offset += 2; + if (attrLen > CKYBuffer_Size(data) +- || (offset + attrLen > CKYBuffer_Size(data)) ) { +- throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Invalid attribute length %d\n", attrLen); ++ || (offset + attrLen > CKYBuffer_Size(data)) ) { ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Invalid attribute length %d\n", attrLen); + } +- attrib.setValue(CKYBuffer_Data(data)+offset, attrLen); +- break; +- case DATATYPE_BOOL_FALSE: +- case DATATYPE_BOOL_TRUE: +- { +- CKYByte bval = attributeDataType & 1; +- attrib.setValue(&bval, 1); +- } +- break; +- case DATATYPE_INTEGER: +- { +- CK_ULONG value = CKYBuffer_GetLong(data, offset); +- attrLen = 4; +- attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); +- } +- break; +- default: +- throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Invalid attribute Data Type %d\n", attributeDataType); +- } ++ attrib.setValue(CKYBuffer_Data(data)+offset, attrLen); ++ break; ++ case DATATYPE_BOOL_FALSE: ++ case DATATYPE_BOOL_TRUE: ++ { ++ CKYByte bval = attributeDataType & 1; ++ attrib.setValue(&bval, 1); ++ } ++ break; ++ case DATATYPE_INTEGER: ++ { ++ CK_ULONG value = CKYBuffer_GetLong(data, offset); ++ attrLen = 4; ++ attrib.setValue((const CKYByte *)&value, sizeof(CK_ULONG)); ++ } ++ break; ++ default: ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Invalid attribute Data Type %d\n", attributeDataType); ++ } + offset += attrLen; + attributes.push_back(attrib); + } +@@ -276,7 +397,7 @@ static const CK_ATTRIBUTE rdr_templat + + bool + PKCS11Object::matchesTemplate(const CK_ATTRIBUTE_PTR pTemplate, +- CK_ULONG ulCount) ++ CK_ULONG ulCount) + const + { + unsigned int i; +@@ -285,10 +406,10 @@ PKCS11Object::matchesTemplate(const CK_A + + #if defined( NSS_HIDE_NONSTANDARD_OBJECTS ) + if (!ulCount) { +- // exclude MOZ reader objects from searches for all objects. +- // To find an MOZ reader object, one must search for it by +- // some matching attribute, such as class. +- iterator iter = find_if(attributes.begin(), attributes.end(), ++ // exclude MOZ reader objects from searches for all objects. ++ // To find an MOZ reader object, one must search for it by ++ // some matching attribute, such as class. ++ iterator iter = find_if(attributes.begin(), attributes.end(), + AttributeMatch(&rdr_template[0])); + return (iter == attributes.end()) ? true : false; + } +@@ -325,7 +446,7 @@ PKCS11Object::getAttribute(CK_ATTRIBUTE_ + AttributeTypeMatch(type)); + + if( iter == attributes.end() ) { +- return NULL; ++ return NULL; + } + return iter->getValue(); + } +@@ -349,8 +470,9 @@ PKCS11Object::getAttributeValue(CK_ATTRI + if( iter == attributes.end() ) { + // no attribute of this type + attrTypeInvalid = true; +- log->log("GetAttributeValue: invalid type 0x%08x on object %x\n", +- pTemplate[i].type, muscleObjID); ++ if ( log ) ++ log->log("GetAttributeValue: invalid type 0x%08x on object %x\n", ++ pTemplate[i].type, muscleObjID); + pTemplate[i].ulValueLen = (CK_ULONG)-1; + continue; + } +@@ -371,7 +493,7 @@ PKCS11Object::getAttributeValue(CK_ATTRI + // the buffer is large enough. return the value and set the exact + // length. + memcpy(pTemplate[i].pValue, CKYBuffer_Data(iter->getValue()), +- CKYBuffer_Size(iter->getValue())); ++ CKYBuffer_Size(iter->getValue())); + pTemplate[i].ulValueLen = CKYBuffer_Size(iter->getValue()); + } + +@@ -406,14 +528,14 @@ PKCS11Object::getLabel() + + // none found + if( iter == attributes.end() ) { +- return ""; ++ return ""; + } + + int size = CKYBuffer_Size(iter->getValue()); + + label = new char [ size + 1 ]; + if (!label) { +- return ""; ++ return ""; + } + memcpy(label, CKYBuffer_Data(iter->getValue()), size); + label[size] = 0; +@@ -431,13 +553,13 @@ PKCS11Object::getClass() + + // none found */ + if( iter == attributes.end() ) { +- return (CK_OBJECT_CLASS) -1; ++ return (CK_OBJECT_CLASS) -1; + } + + int size = CKYBuffer_Size(iter->getValue()); + + if (size != sizeof(objClass)) { +- return (CK_OBJECT_CLASS) -1; ++ return (CK_OBJECT_CLASS) -1; + } + + memcpy(&objClass, CKYBuffer_Data(iter->getValue()), size); +@@ -453,7 +575,7 @@ PKCS11Object::setAttribute(CK_ATTRIBUTE_ + iter = find_if(attributes.begin(), attributes.end(), + AttributeTypeMatch(type)); + if( iter != attributes.end() ) { +- iter->setValue( CKYBuffer_Data(value), CKYBuffer_Size(value)); ++ iter->setValue( CKYBuffer_Data(value), CKYBuffer_Size(value)); + } else { + attributes.push_back(PKCS11Attribute(type, value)); + } +@@ -505,9 +627,15 @@ dataStart(const CKYByte *buf, unsigned i + unsigned char tag; + unsigned int used_length= 0; + ++ *data_length = 0; /* make sure data_length is zero on failure */ ++ + if(!buf) { + return NULL; + } ++ /* there must be at least 2 bytes */ ++ if (length < 2) { ++ return NULL; ++ } + + tag = buf[used_length++]; + +@@ -521,15 +649,22 @@ dataStart(const CKYByte *buf, unsigned i + if (*data_length&0x80) { + int len_count = *data_length & 0x7f; + ++ if (len_count+used_length > length) { ++ return NULL; ++ } ++ + *data_length = 0; + + while (len_count-- > 0) { + *data_length = (*data_length << 8) | buf[used_length++]; + } + } ++ /* paranoia, can't happen */ ++ if (length < used_length) { ++ return NULL; ++ } + + if (*data_length > (length-used_length) ) { +- *data_length = length-used_length; + return NULL; + } + if (includeTag) *data_length += used_length; +@@ -542,16 +677,158 @@ unwrapBitString(const CKYByte *buf, unsi + { + /* for RSA, bit string always has byte number of bits */ + if (buf[0] != 0) { +- return NULL; ++ return NULL; + } + if (len < 1) { +- return NULL; ++ return NULL; + } + *retLen = len -1; + return buf+1; + } + + static SECStatus ++GetECKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length, ++ CCItem *point, CCItem *params) ++{ ++ const CKYByte *buf = spki_data; ++ unsigned int buf_length = spki_length; ++ const CKYByte *algid; ++ unsigned int algidlen; ++ const CKYByte *dummy; ++ unsigned int dummylen; ++ ++ if (!point || !params || !buf) ++ return SECFailure; ++ ++ point->data = NULL; ++ point->len = 0; ++ params->data = NULL; ++ params->len = 0; ++ ++ /* unwrap the algorithm id */ ++ dummy = dataStart(buf,buf_length,&dummylen,false); ++ if (dummy == NULL) return SECFailure; ++ buf_length -= (dummy-buf) + dummylen; ++ buf = dummy + dummylen; ++ /* unwrpped value is in dummy */ ++ algid = dummy; ++ algidlen = dummylen; ++ /* skip past algid oid */ ++ dummy = dataStart(algid, algidlen, &dummylen, false); ++ if (dummy == NULL) return SECFailure; ++ algidlen -= (dummy-algid) + dummylen; ++ algid = dummy + dummylen; ++ params->data = algid; ++ params->len = algidlen; ++ ++ /* unwrap the public key info */ ++ buf = dataStart(buf,buf_length,&buf_length,false); ++ if (buf == NULL) return SECFailure; ++ buf = unwrapBitString(buf,buf_length,&buf_length); ++ if (buf == NULL) return SECFailure; ++ ++ point->data = buf; ++ point->len = buf_length; ++ ++ if(point->data == NULL) return SECFailure; ++ ++ return SECSuccess; ++} ++ ++static bool ++GetKeyOIDMatches(const CKYByte *spki_data, unsigned int length, const CKYByte *oid_data) ++{ ++ bool ret = TRUE; ++ ++ if( spki_data == NULL || oid_data == NULL) { ++ return FALSE; ++ } ++ ++ for ( int i = 0 ; i < (int) length ; i++) { ++ if (spki_data[i] != oid_data[i]) { ++ ret = FALSE; ++ break; ++ } ++ ++ } ++ ++ return ret; ++} ++ ++static SECStatus ++GetKeyAlgorithmId(const CKYByte *spki_data, unsigned int spki_length, ++ CCItem *algorithmId) ++{ ++ ++ const CKYByte *buf = spki_data; ++ unsigned int buf_length = spki_length; ++ ++ if ( algorithmId == NULL) return SECFailure; ++ ++ /* objtain the algorithm id */ ++ algorithmId->data = dataStart(buf,buf_length,&algorithmId->len,false); ++ if (algorithmId->data == NULL) return SECFailure; ++ ++ return SECSuccess; ++ ++} ++ ++static PKCS11Object::KeyType ++GetKeyTypeFromSPKI(const CKYBuffer *key) ++{ ++ CCItem algIdItem; ++ SECStatus ret = GetKeyAlgorithmId(CKYBuffer_Data(key), ++ CKYBuffer_Size(key),&algIdItem); ++ PKCS11Object::KeyType foundType = PKCS11Object::unknown; ++ ++ if ( ret != SECSuccess ) { ++ throw PKCS11Exception(CKR_FUNCTION_FAILED, ++ "Failed to decode key algorithm ID."); ++ } ++ ++ unsigned int length = 0; ++ const CKYByte *keyData = NULL; ++ ++ /* Get actual oid buffer */ ++ ++ keyData = dataStart(algIdItem.data,algIdItem.len,&length, false); ++ if (keyData == NULL) { ++ throw PKCS11Exception(CKR_FUNCTION_FAILED, ++ "Failed to decode key algorithm ID."); ++ } ++ ++ bool match = FALSE; ++ ++ /* Check for outrageous length */ ++ ++ if ( length <= 3 || length >= algIdItem.len) { ++ throw PKCS11Exception(CKR_FUNCTION_FAILED, ++ "Failed to decode key algorithm ID."); ++ } ++ /* check for RSA */ ++ ++ match = GetKeyOIDMatches(keyData, length, rsaOID); ++ ++ if ( match == TRUE ) { ++ foundType = PKCS11Object::rsa; ++ } else { ++ /* check for ECC */ ++ match = GetKeyOIDMatches(keyData, length, eccOID); ++ ++ if ( match == TRUE ) { ++ foundType = PKCS11Object::ecc; ++ } ++ ++ } ++ ++ if ( foundType == PKCS11Object::unknown) { ++ throw PKCS11Exception(CKR_FUNCTION_FAILED, ++ "Failed to decode key algorithm ID."); ++ } ++ return foundType; ++} ++ ++static SECStatus + GetKeyFieldItems(const CKYByte *spki_data,unsigned int spki_length, + CCItem *modulus, CCItem *exponent) + { +@@ -596,7 +873,7 @@ GetKeyFields(const CKYBuffer *spki, CKYB + CCItem modulusItem, exponentItem; + + rv = GetKeyFieldItems(CKYBuffer_Data(spki), CKYBuffer_Size(spki), +- &modulusItem, &exponentItem); ++ &modulusItem, &exponentItem); + + if( rv != SECSuccess ) { + throw PKCS11Exception(CKR_FUNCTION_FAILED, +@@ -607,6 +884,29 @@ GetKeyFields(const CKYBuffer *spki, CKYB + CKYBuffer_Replace(exponent, 0, exponentItem.data, exponentItem.len); + } + ++static void ++GetECKeyFields(const CKYBuffer *spki, CKYBuffer *point, CKYBuffer *params) ++{ ++ SECStatus rv; ++ CCItem pointItem, paramsItem; ++ ++ if (spki == NULL || point == NULL || params == NULL) { ++ throw PKCS11Exception(CKR_FUNCTION_FAILED, ++ "Failed to decode certificate Subject Public KeyInfo!"); ++ } ++ ++ rv = GetECKeyFieldItems(CKYBuffer_Data(spki), CKYBuffer_Size(spki), ++ &pointItem, ¶msItem); ++ ++ if( rv != SECSuccess ) { ++ throw PKCS11Exception(CKR_FUNCTION_FAILED, ++ "Failed to decode certificate Subject Public Key Info!"); ++ } ++ ++ CKYBuffer_Replace(point, 0, pointItem.data, pointItem.len); ++ CKYBuffer_Replace(params, 0, paramsItem.data, paramsItem.len); ++} ++ + Key::Key(unsigned long muscleObjID, const CKYBuffer *data, + CK_OBJECT_HANDLE handle) : PKCS11Object(muscleObjID, data, handle) + { +@@ -616,22 +916,41 @@ Key::Key(unsigned long muscleObjID, cons + CKYBuffer_InitEmpty(&empty); + + if ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY)) { +- /* only CKK_RSA is supported */ +- setAttributeULong(CKA_KEY_TYPE, CKK_RSA); ++ //we may know already what type of key this is. ++ if (attributeExists(CKA_KEY_TYPE)) { ++ CK_ULONG type = 0; ++ CK_ATTRIBUTE aTemplate = {CKA_KEY_TYPE, &type, sizeof(CK_ULONG)}; ++ ++ getAttributeValue(&aTemplate, 1, NULL); ++ ++ if (type == 0x3) { ++ setKeyType(ecc); ++ setAttributeULong(CKA_KEY_TYPE, CKK_EC); ++ } else { ++ setKeyType(rsa); ++ setAttributeULong(CKA_KEY_TYPE, CKK_RSA); ++ } ++ } else { ++ /* default to rsa */ ++ setKeyType(rsa); ++ setAttributeULong(CKA_KEY_TYPE, CKK_RSA); ++ } ++ ++ // Could be RSA or ECC + } else if (objClass == CKO_SECRET_KEY) { +- if (!attributeExists(CKA_LABEL)) { +- setAttribute(CKA_LABEL, &empty); +- } +- if (!attributeExists(CKA_KEY_TYPE)) { +- /* default to DES3 */ +- setAttributeULong(CKA_KEY_TYPE, CKK_DES3); +- } ++ if (!attributeExists(CKA_LABEL)) { ++ setAttribute(CKA_LABEL, &empty); ++ } ++ if (!attributeExists(CKA_KEY_TYPE)) { ++ /* default to DES3 */ ++ setAttributeULong(CKA_KEY_TYPE, CKK_DES3); ++ } + } + if (!attributeExists(CKA_START_DATE)) { +- setAttribute(CKA_START_DATE, &empty); ++ setAttribute(CKA_START_DATE, &empty); + } + if (!attributeExists(CKA_END_DATE)) { +- setAttribute(CKA_END_DATE, &empty); ++ setAttribute(CKA_END_DATE, &empty); + } + } + +@@ -640,32 +959,59 @@ Key::completeKey(const PKCS11Object &cer + { + // infer key attributes from cert + bool modulusExists, exponentExists; +- CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus); +- CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent); ++ bool pointExists, paramsExists; ++ ++ PKCS11Object::KeyType keyType; ++ const CKYBuffer *key = cert.getPubKey(); + + if (!attributeExists(CKA_LABEL)) { +- setAttribute(CKA_LABEL, cert.getAttribute(CKA_LABEL)); ++ setAttribute(CKA_LABEL, cert.getAttribute(CKA_LABEL)); + } ++ ++ CKYBuffer param1; CKYBuffer_InitEmpty(¶m1); ++ CKYBuffer param2; CKYBuffer_InitEmpty(¶m2); + try { +- modulusExists = attributeExists(CKA_MODULUS); +- exponentExists = attributeExists(CKA_PUBLIC_EXPONENT); +- if (!modulusExists || !exponentExists) { +- const CKYBuffer *key = cert.getPubKey(); +- GetKeyFields(key, &modulus, &exponent); +- if (!modulusExists) { +- setAttribute(CKA_MODULUS, &modulus); +- } +- if (!exponentExists) { +- setAttribute(CKA_PUBLIC_EXPONENT, &exponent); +- } +- } ++ keyType = GetKeyTypeFromSPKI(key); ++ setKeyType(keyType); ++ ++ switch (keyType) { ++ case rsa: ++ modulusExists = attributeExists(CKA_MODULUS); ++ exponentExists = attributeExists(CKA_PUBLIC_EXPONENT); ++ if (!modulusExists || !exponentExists) { ++ GetKeyFields(key, ¶m1, ¶m2); ++ if (!modulusExists) { ++ setAttribute(CKA_MODULUS, ¶m1); ++ } ++ if (!exponentExists) { ++ setAttribute(CKA_PUBLIC_EXPONENT, ¶m2); ++ } ++ } ++ break; ++ case ecc: ++ pointExists = attributeExists(CKA_EC_POINT); ++ paramsExists = attributeExists(CKA_EC_PARAMS); ++ ++ if (!pointExists || !paramsExists) { ++ GetECKeyFields(key, ¶m1, ¶m2); ++ if (!pointExists) { ++ setAttribute(CKA_EC_POINT, ¶m1); ++ } ++ if (!paramsExists) { ++ setAttribute(CKA_EC_PARAMS, ¶m2); ++ } ++ } ++ break; ++ default: ++ break; ++ } + } catch (PKCS11Exception &e) { +- CKYBuffer_FreeData(&modulus); +- CKYBuffer_FreeData(&exponent); +- throw e; ++ CKYBuffer_FreeData(¶m1); ++ CKYBuffer_FreeData(¶m2); ++ throw e; + } +- CKYBuffer_FreeData(&modulus); +- CKYBuffer_FreeData(&exponent); ++ CKYBuffer_FreeData(¶m1); ++ CKYBuffer_FreeData(¶m2); + } + + static SECStatus +@@ -737,14 +1083,14 @@ GetCertFieldItems(const CKYByte *dercert + + static void + GetCertFields(const CKYBuffer *derCert, CKYBuffer *derSerial, +- CKYBuffer *derSubject, CKYBuffer *derIssuer, CKYBuffer *subjectKey) ++ CKYBuffer *derSubject, CKYBuffer *derIssuer, CKYBuffer *subjectKey) + { + SECStatus rv; + CCItem issuerItem, serialItem, derSerialItem, subjectItem, + validityItem, subjectKeyItem; + + rv = GetCertFieldItems(CKYBuffer_Data(derCert), CKYBuffer_Size(derCert), +- &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem, ++ &issuerItem, &serialItem, &derSerialItem, &subjectItem, &validityItem, + &subjectKeyItem); + + if( rv != SECSuccess ) { +@@ -769,50 +1115,50 @@ Cert::Cert(unsigned long muscleObjID, co + CK_ULONG certTypeValue = CKC_X_509; + + CKYBuffer_InitFromData(&certType, (CKYByte *)&certTypeValue, +- sizeof(certTypeValue)); ++ sizeof(certTypeValue)); + CKYBuffer_Resize(&pubKey,0); + + try { +- setAttribute(CKA_CERTIFICATE_TYPE, &certType); ++ setAttribute(CKA_CERTIFICATE_TYPE, &certType); + +- if (!attributeExists(CKA_VALUE)) { +- if (derCert) { +- setAttribute(CKA_VALUE, derCert); +- } else { +- throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Missing certificate data from token"); +- } +- } ++ if (!attributeExists(CKA_VALUE)) { ++ if (derCert) { ++ setAttribute(CKA_VALUE, derCert); ++ } else { ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Missing certificate data from token"); ++ } ++ } + +- if (!derCert) { +- derCert = getAttribute(CKA_VALUE); +- if (!derCert) { +- // paranoia, should never happen since we verify the +- // attribute exists above +- throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Missing certificate data from token"); +- } +- } ++ if (!derCert) { ++ derCert = getAttribute(CKA_VALUE); ++ if (!derCert) { ++ // paranoia, should never happen since we verify the ++ // attribute exists above ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Missing certificate data from token"); ++ } ++ } + +- // infer cert attributes ++ // infer cert attributes + +- GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); ++ GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); + +- if (!attributeExists(CKA_SERIAL_NUMBER)) { +- setAttribute(CKA_SERIAL_NUMBER, &derSerial); +- } +- if (!attributeExists(CKA_SUBJECT)) { +- setAttribute(CKA_SUBJECT, &derSubject); +- } +- if (!attributeExists(CKA_ISSUER)) { +- setAttribute(CKA_ISSUER, &derIssuer); +- } ++ if (!attributeExists(CKA_SERIAL_NUMBER)) { ++ setAttribute(CKA_SERIAL_NUMBER, &derSerial); ++ } ++ if (!attributeExists(CKA_SUBJECT)) { ++ setAttribute(CKA_SUBJECT, &derSubject); ++ } ++ if (!attributeExists(CKA_ISSUER)) { ++ setAttribute(CKA_ISSUER, &derIssuer); ++ } + } catch (PKCS11Exception &e) { +- CKYBuffer_FreeData(&certType); +- CKYBuffer_FreeData(&derSerial); +- CKYBuffer_FreeData(&derSubject); +- CKYBuffer_FreeData(&derIssuer); +- throw e; ++ CKYBuffer_FreeData(&certType); ++ CKYBuffer_FreeData(&derSerial); ++ CKYBuffer_FreeData(&derSubject); ++ CKYBuffer_FreeData(&derIssuer); ++ throw e; + } + CKYBuffer_FreeData(&certType); + CKYBuffer_FreeData(&derSerial); +@@ -822,7 +1168,7 @@ Cert::Cert(unsigned long muscleObjID, co + + Reader::Reader(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, + const char *reader, const CKYBuffer *cardATR, bool isCoolkey) : +- PKCS11Object(muscleObjID, handle) ++ PKCS11Object(muscleObjID, handle) + { + setAttributeULong(CKA_CLASS, CKO_MOZ_READER); + setAttribute(CKA_LABEL, reader); +@@ -833,9 +1179,10 @@ Reader::Reader(unsigned long muscleObjID + setAttribute(CKA_MOZ_ATR, cardATR); + } + ++ + CACPrivKey::CACPrivKey(CKYByte instance, const PKCS11Object &cert) : +- PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16, +- instance | 0x400) ++ PKCS11Object( ((int)'k') << 24 | ((int)instance+'0') << 16, ++ instance | 0x400) + { + CKYBuffer id; + CKYBuffer empty; +@@ -844,7 +1191,7 @@ CACPrivKey::CACPrivKey(CKYByte instance, + /* So we know what the key is supposed to be used for based on + * the instance */ + if (instance == 2) { +- decrypt = TRUE; ++ decrypt = TRUE; + } + + CKYBuffer_InitEmpty(&empty); +@@ -863,33 +1210,52 @@ CACPrivKey::CACPrivKey(CKYByte instance, + setAttributeBool(CKA_LOCAL, TRUE); + setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + +- setAttributeBool(CKA_DECRYPT, decrypt); + setAttributeBool(CKA_SIGN, !decrypt); + setAttributeBool(CKA_SIGN_RECOVER, !decrypt); + setAttributeBool(CKA_UNWRAP, FALSE); + setAttributeBool(CKA_SENSITIVE, TRUE); + setAttributeBool(CKA_EXTRACTABLE, FALSE); + +- CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus); +- CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent); ++ CKYBuffer param1; CKYBuffer_InitEmpty(¶m1); ++ CKYBuffer param2; CKYBuffer_InitEmpty(¶m2); + + try { +- const CKYBuffer *key = cert.getPubKey(); +- GetKeyFields(key, &modulus, &exponent); +- setAttribute(CKA_MODULUS, &modulus); +- setAttribute(CKA_PUBLIC_EXPONENT, &exponent); +- } catch (PKCS11Exception &e) { +- CKYBuffer_FreeData(&modulus); +- CKYBuffer_FreeData(&exponent); +- throw e; +- } +- CKYBuffer_FreeData(&modulus); +- CKYBuffer_FreeData(&exponent); ++ const CKYBuffer *key = cert.getPubKey(); ++ keyType = GetKeyTypeFromSPKI(key); ++ setKeyType(keyType); ++ ++ switch (keyType) { ++ case rsa: ++ GetKeyFields(key, ¶m1, ¶m2); ++ setAttribute(CKA_MODULUS, ¶m1); ++ setAttribute(CKA_PUBLIC_EXPONENT, ¶m2); ++ setAttributeULong(CKA_KEY_TYPE, CKK_RSA); ++ setAttributeBool(CKA_DECRYPT, decrypt); ++ setAttributeBool(CKA_DERIVE, FALSE); ++ break; ++ case ecc: ++ GetECKeyFields(key, ¶m1, ¶m2); ++ setAttribute(CKA_EC_POINT, ¶m1); ++ setAttribute(CKA_EC_PARAMS, ¶m2); ++ setAttributeULong(CKA_KEY_TYPE, CKK_EC); ++ setAttributeBool(CKA_DECRYPT, FALSE); ++ setAttributeBool(CKA_DERIVE, decrypt); ++ break; ++ default: ++ break; ++ } ++ } catch (PKCS11Exception &e) { ++ CKYBuffer_FreeData(¶m1); ++ CKYBuffer_FreeData(¶m2); ++ throw e; ++ } ++ CKYBuffer_FreeData(¶m1); ++ CKYBuffer_FreeData(¶m2); + } + + CACPubKey::CACPubKey(CKYByte instance, const PKCS11Object &cert) : +- PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16, +- instance | 0x500) ++ PKCS11Object( ((int)'k') << 24 | ((int)(instance+'5')) << 16, ++ instance | 0x500) + { + CKYBuffer id; + CKYBuffer empty; +@@ -898,7 +1264,7 @@ CACPubKey::CACPubKey(CKYByte instance, c + /* So we know what the key is supposed to be used for based on + * the instance */ + if (instance == 2) { +- encrypt = TRUE; ++ encrypt = TRUE; + } + + CKYBuffer_InitEmpty(&empty); +@@ -915,34 +1281,49 @@ CACPubKey::CACPubKey(CKYByte instance, c + setAttribute(CKA_END_DATE, &empty); + setAttributeBool(CKA_DERIVE, FALSE); + setAttributeBool(CKA_LOCAL, TRUE); +- setAttributeULong(CKA_KEY_TYPE, CKK_RSA); + + setAttributeBool(CKA_ENCRYPT, encrypt); + setAttributeBool(CKA_VERIFY, !encrypt); + setAttributeBool(CKA_VERIFY_RECOVER, !encrypt); + setAttributeBool(CKA_WRAP, FALSE); + +- CKYBuffer modulus; CKYBuffer_InitEmpty(&modulus); +- CKYBuffer exponent; CKYBuffer_InitEmpty(&exponent); ++ CKYBuffer param1; CKYBuffer_InitEmpty(¶m1); ++ CKYBuffer param2; CKYBuffer_InitEmpty(¶m2); + + try { +- const CKYBuffer *key = cert.getPubKey(); +- GetKeyFields(key, &modulus, &exponent); +- setAttribute(CKA_MODULUS, &modulus); +- setAttribute(CKA_PUBLIC_EXPONENT, &exponent); +- } catch (PKCS11Exception &e) { +- CKYBuffer_FreeData(&modulus); +- CKYBuffer_FreeData(&exponent); +- throw e; +- } +- CKYBuffer_FreeData(&modulus); +- CKYBuffer_FreeData(&exponent); ++ const CKYBuffer *key = cert.getPubKey(); ++ keyType = GetKeyTypeFromSPKI(key); ++ setKeyType(keyType); ++ ++ switch (keyType) { ++ case rsa: ++ GetKeyFields(key, ¶m1, ¶m2); ++ setAttribute(CKA_MODULUS, ¶m1); ++ setAttribute(CKA_PUBLIC_EXPONENT, ¶m2); ++ setAttributeULong(CKA_KEY_TYPE, CKK_RSA); ++ break; ++ case ecc: ++ GetECKeyFields(key, ¶m1, ¶m2); ++ setAttribute(CKA_EC_POINT, ¶m1); ++ setAttribute(CKA_EC_PARAMS, ¶m2); ++ setAttributeULong(CKA_KEY_TYPE, CKK_EC); ++ break; ++ default: ++ break; ++ } ++ } catch (PKCS11Exception &e) { ++ CKYBuffer_FreeData(¶m1); ++ CKYBuffer_FreeData(¶m2); ++ throw e; ++ } ++ CKYBuffer_FreeData(¶m1); ++ CKYBuffer_FreeData(¶m2); + } + + static const char *CAC_Label[] = { +- "CAC ID Certificate", +- "CAC Email Signature Certificate", +- "CAC Email Encryption Certificate", ++ "CAC ID Certificate", ++ "CAC Email Signature Certificate", ++ "CAC Email Encryption Certificate", + }; + + static const unsigned char CN_DATA[] = { 0x55, 0x4, 0x3 }; +@@ -959,39 +1340,43 @@ GetCN(const CKYByte *dn, unsigned int dn + if (buf == NULL) return SECFailure; + + while (buf_length) { +- const CKYByte *name; +- unsigned int name_length; +- const CKYByte *oid; +- unsigned int oid_length; +- +- /* unwrap the set */ +- name = dataStart(buf, buf_length, &name_length, false); ++ const CKYByte *name; ++ unsigned int name_length; ++ const CKYByte *oid; ++ unsigned int oid_length; ++ ++ /* unwrap the set */ ++ name = dataStart(buf, buf_length, &name_length, false); ++ if (name == NULL) return SECFailure; + + /* advance to next set */ +- buf_length -= (name-buf) + name_length; +- buf = name + name_length; ++ buf_length -= (name-buf) + name_length; ++ buf = name + name_length; + +- /* unwrap the Sequence */ +- name = dataStart(name, name_length, &name_length, false); ++ /* unwrap the Sequence */ ++ name = dataStart(name, name_length, &name_length, false); ++ if (name == NULL) return SECFailure; + + /* unwrap the oid */ +- oid = dataStart(name, name_length, &oid_length, false); ++ oid = dataStart(name, name_length, &oid_length, false); ++ if (oid == NULL) return SECFailure; + +- /* test the oid */ +- if (oid_length != CN_LENGTH) { +- continue; +- } +- if (memcmp(oid, CN_DATA, CN_LENGTH) != 0) { +- continue; +- } ++ /* test the oid */ ++ if (oid_length != CN_LENGTH) { ++ continue; ++ } ++ if (memcmp(oid, CN_DATA, CN_LENGTH) != 0) { ++ continue; ++ } + +- /* advance to CN */ +- name_length -= (oid-name) + oid_length; +- name = oid + oid_length; +- +- /* unwrap the CN */ +- cn->data = dataStart(name, name_length, &cn->len, false); +- return SECSuccess; ++ /* advance to CN */ ++ name_length -= (oid-name) + oid_length; ++ name = oid + oid_length; ++ ++ /* unwrap the CN */ ++ cn->data = dataStart(name, name_length, &cn->len, false); ++ if (cn->data == NULL) return SECFailure; ++ return SECSuccess; + } + return SECFailure; + } +@@ -1006,11 +1391,11 @@ GetUserName(const CKYBuffer *dn) + rv = GetCN(CKYBuffer_Data(dn), CKYBuffer_Size(dn) , &cn); + + if( rv != SECSuccess ) { +- return NULL; ++ return NULL; + } + string = new char [ cn.len + 1 ]; + if (string == NULL) { +- return NULL; ++ return NULL; + } + memcpy(string, cn.data, cn.len); + string[cn.len] = 0; +@@ -1018,8 +1403,8 @@ GetUserName(const CKYBuffer *dn) + } + + CACCert::CACCert(CKYByte instance, const CKYBuffer *derCert) : +- PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, +- instance | 0x600) ++ PKCS11Object( ((int)'c') << 24 | ((int)instance+'0') << 16, ++ instance | 0x600) + { + CKYBuffer id; + CKYBuffer empty; +@@ -1028,7 +1413,7 @@ CACCert::CACCert(CKYByte instance, const + /* So we know what the key is supposed to be used for based on + * the instance */ + if (instance == 2) { +- decrypt = TRUE; ++ decrypt = TRUE; + } + + CKYBuffer_InitEmpty(&empty); +@@ -1050,19 +1435,19 @@ CACCert::CACCert(CKYByte instance, const + CKYBuffer_Resize(&pubKey,0); + + try { +- setAttribute(CKA_VALUE, derCert); +- // infer cert attributes ++ setAttribute(CKA_VALUE, derCert); ++ // infer cert attributes + +- GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); ++ GetCertFields(derCert, &derSerial, &derSubject, &derIssuer, &pubKey); + +- setAttribute(CKA_SERIAL_NUMBER, &derSerial); +- setAttribute(CKA_SUBJECT, &derSubject); +- setAttribute(CKA_ISSUER, &derIssuer); ++ setAttribute(CKA_SERIAL_NUMBER, &derSerial); ++ setAttribute(CKA_SUBJECT, &derSubject); ++ setAttribute(CKA_ISSUER, &derIssuer); + } catch (PKCS11Exception &e) { +- CKYBuffer_FreeData(&derSerial); +- CKYBuffer_FreeData(&derSubject); +- CKYBuffer_FreeData(&derIssuer); +- throw e; ++ CKYBuffer_FreeData(&derSerial); ++ CKYBuffer_FreeData(&derSubject); ++ CKYBuffer_FreeData(&derIssuer); ++ throw e; + } + + name = GetUserName(&derSubject); /* adopt */ +@@ -1070,3 +1455,100 @@ CACCert::CACCert(CKYByte instance, const + CKYBuffer_FreeData(&derSubject); + CKYBuffer_FreeData(&derIssuer); + } ++ ++DEREncodedSignature::DEREncodedSignature(const CKYBuffer *derSig) ++{ ++ ++ CKYBuffer_InitEmpty(&derEncodedSignature); ++ CKYBuffer_InitFromCopy(&derEncodedSignature, derSig); ++} ++ ++DEREncodedSignature::~DEREncodedSignature() ++{ ++ CKYBuffer_FreeData(&derEncodedSignature); ++} ++ ++int DEREncodedSignature::getRawSignature(CKYBuffer *rawSig, ++ unsigned int keySize) ++{ ++ const CKYByte *buf = NULL; ++ ++ if (rawSig == NULL) { ++ return -1; ++ } ++ ++ if (CKYBuffer_Size(&derEncodedSignature) == 0) { ++ return -1; ++ } ++ ++ CKYBuffer_Zero(rawSig); ++ ++ unsigned int seq_length = 0; ++ unsigned int expected_sig_len = ( (keySize + 7) / 8 ) * 2 ; ++ unsigned int expected_piece_size = expected_sig_len / 2 ; ++ ++ /* unwrap the sequence */ ++ buf = dataStart(CKYBuffer_Data(&derEncodedSignature), CKYBuffer_Size(&derEncodedSignature),&seq_length, false); ++ ++ if (buf == NULL) return -1; ++ ++ // unwrap first multi byte integer ++ ++ unsigned int int_length = 0; ++ const CKYByte *int1Buf = NULL; ++ const CKYByte *int2Buf = NULL; ++ ++ int1Buf = dataStart(buf, seq_length, &int_length, false ); ++ ++ if (int1Buf == NULL) return -1; ++ //advance to next entry ++ ++ if (int_length > expected_piece_size) { ++ ++ unsigned int diff = int_length - expected_piece_size ; ++ ++ /* Make sure we are chopping off zeroes ++ Otherwise give up. */ ++ ++ for (int i = 0 ; i < (int) diff ; i++) { ++ if ( int1Buf[i] != 0) ++ return -1; ++ } ++ ++ int_length -= diff; ++ int1Buf += diff; ++ ++ } ++ ++ seq_length -= (int1Buf -buf) + int_length; ++ buf = int1Buf + int_length; ++ ++ // unwrap second multi byte integer ++ ++ unsigned int second_int_length = 0; ++ ++ int2Buf = dataStart(buf, seq_length, &second_int_length, false); ++ ++ if (int2Buf == NULL) return -1; ++ ++ ++ if (second_int_length > expected_piece_size) { ++ unsigned int diff = second_int_length - expected_piece_size ; ++ ++ /* Make sure we are chopping off zeroes ++ Otherwise give up. */ ++ ++ for (int i = 0 ; i < (int) diff ; i++) { ++ if ( int2Buf[i] != 0) ++ return -1; ++ } ++ ++ second_int_length -= diff; ++ int2Buf += diff; ++ } ++ ++ CKYBuffer_AppendData(rawSig, int1Buf, int_length); ++ CKYBuffer_AppendData(rawSig, int2Buf, second_int_length); ++ ++ return CKYSUCCESS; ++} +diff -up ./src/coolkey/object.h.piv-ecc ./src/coolkey/object.h +--- ./src/coolkey/object.h.piv-ecc 2013-09-08 15:50:33.081428035 -0700 ++++ ./src/coolkey/object.h 2013-09-08 15:50:33.121428706 -0700 +@@ -49,7 +49,7 @@ class PKCS11Attribute { + CKYBuffer_Size(&cpy.value)); + return *this; + } +- PKCS11Attribute() { CKYBuffer_InitEmpty(&value); } ++ PKCS11Attribute() : type(0){ CKYBuffer_InitEmpty(&value); } + PKCS11Attribute(CK_ATTRIBUTE_TYPE type_, const CKYBuffer *value_) + : type(type_) { CKYBuffer_InitFromCopy(&value, value_); } + ~PKCS11Attribute() { CKYBuffer_FreeData(&value); } +@@ -57,6 +57,11 @@ class PKCS11Attribute { + + class PKCS11Object { + public: ++ enum KeyType { ++ rsa, ++ ecc, ++ unknown ++ }; + + typedef list AttributeList; + typedef AttributeList::iterator AttributeIter; +@@ -75,18 +80,20 @@ class PKCS11Object { + PKCS11Object &operator=(PKCS11Object &cpy) { return *this; } //Disallow + + protected : +- CKYBuffer pubKey; + char *name; ++ KeyType keyType; ++ CKYBuffer pubKey; + + public: + PKCS11Object(unsigned long muscleObjID, CK_OBJECT_HANDLE handle); + PKCS11Object(unsigned long muscleObjID, const CKYBuffer *data, + CK_OBJECT_HANDLE handle); +- ~PKCS11Object() { delete [] label; delete [] name; CKYBuffer_FreeData(&pubKey); } ++ ~PKCS11Object() { delete label; delete name; CKYBuffer_FreeData(&pubKey); ++ attributes.clear(); } + + PKCS11Object(const PKCS11Object& cpy) : + attributes(cpy.attributes), muscleObjID(cpy.muscleObjID), +- handle(cpy.handle), label(NULL), name(NULL) { ++ handle(cpy.handle), label(NULL), name(NULL), keyType(cpy.keyType) { + CKYBuffer_InitFromCopy(&pubKey,&cpy.pubKey); } + + +@@ -116,14 +123,15 @@ class PKCS11Object { + const CKYBuffer *getPubKey(void) const { + return &pubKey; + } ++ ++ KeyType getKeyType() const { return keyType;} ++ void setKeyType(KeyType theType) { keyType = theType; } + }; + + class Key : public PKCS11Object { +- + public: + Key(unsigned long muscleObjID, const CKYBuffer *data, CK_OBJECT_HANDLE handle); + void completeKey(const PKCS11Object &cert); +- + }; + + class Cert : public PKCS11Object { +@@ -153,6 +161,25 @@ class Reader : public PKCS11Object { + const char *reader, const CKYBuffer *cardATR, bool isCoolkey); + }; + ++class SecretKey : public PKCS11Object { ++ public: ++ SecretKey(unsigned long muscleObjID, CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount); ++ private: ++ void adjustToKeyValueLength(CKYBuffer * secretKeyBuffer,CK_ULONG valueLength); ++ ++}; ++ ++class DEREncodedSignature { ++ ++ protected : ++ CKYBuffer derEncodedSignature; ++ public: ++ DEREncodedSignature(const CKYBuffer *derSig); ++ ~DEREncodedSignature(); ++ int getRawSignature(CKYBuffer *rawSig, unsigned int keySize); ++ ++}; ++ + class AttributeMatch { + + private: +diff -up ./src/coolkey/pkcs11t.h.piv-ecc ./src/coolkey/pkcs11t.h +--- ./src/coolkey/pkcs11t.h.piv-ecc 2006-06-09 11:39:11.000000000 -0700 ++++ ./src/coolkey/pkcs11t.h 2013-09-08 15:50:33.122428723 -0700 +@@ -1351,4 +1351,41 @@ typedef struct CK_PKCS5_PBKD2_PARAMS { + + typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR; + ++/* The following EC Key Derivation Functions are defined */ ++ ++#define CKD_NULL 0x00000001 ++ ++#define CKD_SHA1_KDF 0x00000002 ++ ++/* CK_ECDH1_DERIVE_PARAMS is new for v2.11. */ ++ ++ typedef CK_ULONG CK_EC_KDF_TYPE; ++ ++/* ++ * CK_ECDH1_DERIVE_PARAMS provides the parameters to the ++ * ++ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms, ++ * ++ * where each party contributes one key pair. ++ * ++ */ ++ ++typedef struct CK_ECDH1_DERIVE_PARAMS { ++ ++ CK_EC_KDF_TYPE kdf; ++ ++ CK_ULONG ulSharedDataLen; ++ ++ CK_BYTE_PTR pSharedData; ++ ++ CK_ULONG ulPublicDataLen; ++ ++ CK_BYTE_PTR pPublicData; ++ ++} CK_ECDH1_DERIVE_PARAMS; ++ ++ ++ ++typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR; ++ + #endif +diff -up ./src/coolkey/slot.cpp.piv-ecc ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.piv-ecc 2013-09-08 15:50:33.112428555 -0700 ++++ ./src/coolkey/slot.cpp 2013-09-08 15:50:33.124428757 -0700 +@@ -54,6 +54,34 @@ const CKYByte ATR2[] = + { 0x3B, 0x6F, 0x00, 0xFF, 0x52, 0x53, 0x41, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x49, 0x44, 0x28, 0x52, 0x29, 0x31, 0x30 }; + ++ ++/* ECC curve information ++ * Provide information for the limited set of curves supported by our smart card(s). ++ * ++ */ ++ ++typedef struct curveBytes2Name { ++ const CKYByte * bytes; ++ const char *curveName; ++ unsigned int length; ++ ++} CurveBytes2Name; ++ ++/* First byte is length of oid byte array. */ ++ ++const CKYByte nistp256[] = { 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; ++const CKYByte nistp384[] = { 0x5, 0x2b, 0x81, 0x04, 0x00, 0x22 }; ++const CKYByte nistp521[] = { 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 }; ++ ++const int numECCurves = 3; ++ ++static CurveBytes2Name curveBytesNamePair[] = ++{ ++ { nistp256, "nistp256", 256 }, ++ { nistp384, "nistp384", 384 }, ++ { nistp521, "nistp521", 521 } ++}; ++ + SlotList::SlotList(Log *log_) : log(log_) + { + // initialize things to NULL so we can recover from an exception +@@ -136,7 +164,11 @@ SlotList::updateSlotList() + throw PKCS11Exception(CKR_HOST_MEMORY); + memset(newSlots, 0, numReaders*sizeof(Slot*)); + +- memcpy(newSlots, slots, sizeof(slots[0]) * numSlots); ++ /* keep coverity happy, even though slot == NULL implies that ++ * numSlots == 0 */ ++ if (slots) { ++ memcpy(newSlots, slots, sizeof(slots[0]) * numSlots); ++ } + + for (unsigned int i=numSlots; i < numReaders; i++) { + newSlots[i] = new +@@ -237,32 +269,19 @@ SlotList::updateReaderList() + + CKYStatus status = CKYCardContext_ListReaders(context, &readerNames); + if ( status != CKYSUCCESS ) { +- throw PKCS11Exception(CKR_GENERAL_ERROR, ++ /* if the service is stopped, treat it as if we have no readers */ ++ if ((CKYCardContext_GetLastError(context) != SCARD_E_NO_SERVICE) && ++ (CKYCardContext_GetLastError(context) != SCARD_E_SERVICE_STOPPED)) { ++ throw PKCS11Exception(CKR_GENERAL_ERROR, + "Failed to list readers: 0x%x\n", + CKYCardContext_GetLastError(context)); ++ } + } + +- if (!readerStates) { ++ if (readerStates == NULL && readerNames != NULL) { + /* fresh Reader State list, just create it */ + readerStates = CKYReader_CreateArray(readerNames, (CKYSize *)&numReaders); + +- /* if we have no readers, make sure we have at least one to keep things +- * happy */ +- if (readerStates == NULL && +- CKYReaderNameList_GetCount(readerNames) == 0) { +- readerStates = (SCARD_READERSTATE *) +- malloc(sizeof(SCARD_READERSTATE)); +- if (readerStates) { +- CKYReader_Init(readerStates); +- status = CKYReader_SetReaderName(readerStates, "E-Gate 0 0"); +- if (status != CKYSUCCESS) { +- CKYReader_DestroyArray(readerStates, 1); +- readerStates = NULL; +- } else { +- numReaders = 1; +- } +- } +- } + CKYReaderNameList_Destroy(readerNames); + + if (readerStates == NULL) { +@@ -272,6 +291,16 @@ SlotList::updateReaderList() + return; + } + ++ if (readerStates == NULL) { ++ /* if we didn't have any readers before and we did get new names, ++ * that is handled above. If we didn't have any readers before, and ++ * we didn't get any names, there is nothing to update. blow out now. ++ * This more efficient and makes coverity happy (since coverity doesn't ++ * know numReaders and readerStates are linked). */ ++ return; ++ } ++ ++ + /* it would be tempting at this point just to see if we have more readers + * then specified previously. The problem with this is it is possible that + * some readers have been deleted, so the only way to tell if we have +@@ -286,18 +315,26 @@ SlotList::updateReaderList() + + const char *curReaderName = NULL; + unsigned long knownState = 0; +- for(int ri = 0 ; ri < numReaders; ri ++) { ++ for(unsigned int ri = 0 ; ri < numReaders; ri ++) { + knownState = CKYReader_GetKnownState(&readerStates[ri]); +- ++ + curReaderName = CKYReader_GetReaderName(&readerStates[ri]); +- if(readerNameExistsInList(curReaderName,&readerNames)) { +- CKYReader_SetKnownState(&readerStates[ri], knownState & ~SCARD_STATE_IGNORE); ++ if(readerNames && readerNameExistsInList(curReaderName,&readerNames)) { ++ CKYReader_SetKnownState(&readerStates[ri], ++ knownState & ~SCARD_STATE_IGNORE); + } else { +- if (!(knownState & SCARD_STATE_UNAVAILABLE)) +- CKYReader_SetKnownState(&readerStates[ri], knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED); +- } ++ if (!(knownState & SCARD_STATE_UNAVAILABLE)) ++ CKYReader_SetKnownState(&readerStates[ri], ++ knownState | SCARD_STATE_UNAVAILABLE | SCARD_STATE_CHANGED); ++ } + } + ++ if (readerNames == NULL) { ++ /* OK we've marked everything unavailable, we clearly ++ * aren't adding any readers, so we can blow out here */ ++ return; ++ } ++ + const char *newReadersData[MAX_READER_DELTA]; + const char **newReaders = &newReadersData[0]; + unsigned int newReaderCount = 0; +@@ -370,7 +407,8 @@ Slot::Slot(const char *readerName_, Log + : log(log_), readerName(NULL), personName(NULL), manufacturer(NULL), + slotInfoFound(false), context(context_), conn(NULL), state(UNKNOWN), + isVersion1Key(false), needLogin(false), fullTokenName(false), +- mCoolkey(false), mOldCAC(false), ++ mCoolkey(false), mOldCAC(false),mCACLocalLogin(false), ++ pivContainer(-1), pivKey(-1), mECC(false), + #ifdef USE_SHMEM + shmem(readerName_), + #endif +@@ -573,6 +611,35 @@ SlotList::getSlotList(CK_BBOOL tokenPres + return rv; + } + ++bool ++Slot::getPIVLoginType(void) ++{ ++ CKYStatus status; ++ CKYISOStatus apduRC; ++ CKYBuffer buffer; ++ bool local = true; ++ ++ CKYBuffer_InitEmpty(&buffer); ++ ++ /* get the discovery object */ ++ status = PIVApplet_GetCertificate(conn, &buffer, 0x7e, &apduRC); ++ if (status != CKYSUCCESS) { ++ /* Discovery object optional, PIV defaults to local */ ++ goto done; ++ } ++ /* techically we probably should parse out the TLVs, but the PIV ++ * specifies exactly what they should be, so we know exactly which ++ * byte to look at */ ++ if ((CKYBuffer_Size(&buffer) >= 20) && ++ (CKYBuffer_GetChar(&buffer,17) == 0x60)) { ++ /* This tells us we should use global login for this piv card */ ++ local = false; ++ } ++done: ++ CKYBuffer_FreeData(&buffer); ++ return true; ++} ++ + void + Slot::connectToToken() + { +@@ -587,6 +654,7 @@ Slot::connectToToken() + if( ! CKYCardConnection_IsConnected(conn) ) { + int i = 0; + //for cranky readers try again a few more times ++ status = CKYSCARDERR; + while( i++ < 5 && status != CKYSUCCESS ) + { + status = CKYCardConnection_Connect(conn, readerName); +@@ -672,12 +740,36 @@ Slot::connectToToken() + // see if the applet is selectable + + log->log("time connnect: Begin transaction %d ms\n", OSTimeNow() - time); ++ status = PIVApplet_Select(conn, NULL); ++ if (status == CKYSUCCESS) { ++ /* CARD is a PIV card */ ++ state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; ++ isVersion1Key = 0; ++ needLogin = 1; ++ mCoolkey = 0; ++ mOldCAC = 0; ++ mCACLocalLogin = getPIVLoginType(); ++ return; ++ } + status = CKYApplet_SelectCoolKeyManager(conn, NULL); + if (status != CKYSUCCESS) { + log->log("CoolKey Select failed 0x%x\n", status); + status = getCACAid(); + if (status != CKYSUCCESS) { +- goto loser; ++ log->log("CAC Select failed 0x%x\n", status); ++ if (status == CKYSCARDERR) { ++ log->log("Card Failure 0x%x\n", ++ CKYCardConnection_GetLastError(conn)); ++ disconnect(); ++ } ++ /* CARD is a PIV card */ ++ state |= PIV_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; ++ isVersion1Key = 0; ++ needLogin = 1; ++ mCoolkey = 0; ++ mOldCAC = 0; ++ mCACLocalLogin = getPIVLoginType(); ++ return; + } + state |= CAC_CARD | APPLET_SELECTABLE | APPLET_PERSONALIZED; + /* skip the read of the cuid. We really don't need it and, +@@ -687,15 +779,7 @@ Slot::connectToToken() + isVersion1Key = 0; + needLogin = 1; + mCoolkey = 0; +- return; +- +-loser: +- log->log("CAC Select failed 0x%x\n", status); +- if (status == CKYSCARDERR) { +- log->log("CAC Card Failure 0x%x\n", +- CKYCardConnection_GetLastError(conn)); +- disconnect(); +- } ++ mCACLocalLogin = false; + return; + } + mCoolkey = 1; +@@ -762,8 +846,8 @@ Slot::invalidateLogin(bool hard) + } + } else { + loggedIn = false; ++ pinCache.invalidate(); + if (hard) { +- pinCache.invalidate(); + pinCache.clearPin(); + } + } +@@ -1202,6 +1286,7 @@ Slot::getTokenInfo(CK_TOKEN_INFO_PTR pTo + + + return CKR_OK; ++ + } + + void +@@ -1222,7 +1307,16 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + bool found = FALSE; + CKYStatus status; + SCARD_READERSTATE *myReaderStates = NULL; ++ static SCARD_READERSTATE pnp = { 0 }; + unsigned int myNumReaders = 0; ++ ++ readerListLock.getLock(); ++ if (pnp.szReader == 0) { ++ CKYReader_Init(&pnp); ++ pnp.szReader = "\\\\?PnP?\\Notification"; ++ } ++ readerListLock.releaseLock(); ++ + #ifndef notdef + do { + readerListLock.getLock(); +@@ -1241,11 +1335,13 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + * from that set to return + */ + for (i=0; i < numReaders; i++) { +- unsigned long knownState = CKYReader_GetKnownState(&readerStates[i]); ++ unsigned long knownState = ++ CKYReader_GetKnownState(&readerStates[i]); + + if ((knownState & SCARD_STATE_UNAVAILABLE) && + (knownState & SCARD_STATE_CHANGED)) { +- CKYReader_SetKnownState(&readerStates[i], knownState & ~SCARD_STATE_CHANGED); ++ CKYReader_SetKnownState(&readerStates[i], ++ knownState & ~SCARD_STATE_CHANGED); + readerListLock.releaseLock(); + *slotp = slotIndexToID(i); + found = TRUE; +@@ -1262,31 +1358,48 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + break; + } + +- if (myNumReaders != numReaders) { ++ if (myNumReaders != numReaders + 1) { + if (myReaderStates) { + delete [] myReaderStates; + } +- myReaderStates = new SCARD_READERSTATE [numReaders]; ++ myReaderStates = new SCARD_READERSTATE [numReaders + 1]; ++ myNumReaders = numReaders + 1; + } +- memcpy(myReaderStates, readerStates, +- sizeof(SCARD_READERSTATE)*numReaders); +- myNumReaders = numReaders; ++ ++ memcpy(myReaderStates, readerStates, ++ sizeof(SCARD_READERSTATE) * numReaders); ++ memcpy(&myReaderStates[numReaders], &pnp, sizeof(pnp)); + readerListLock.releaseLock(); + status = CKYCardContext_WaitForStatusChange(context, +- myReaderStates, myNumReaders, timeout); ++ myReaderStates, myNumReaders, timeout); + if (status == CKYSUCCESS) { +- for (i=0; i < myNumReaders; i++) { +- SCARD_READERSTATE *rsp = &myReaderStates[i]; +- unsigned long eventState = CKYReader_GetEventState(rsp); ++ unsigned long eventState; ++ for (i=0; i < myNumReaders - 1; i++) { ++ eventState = CKYReader_GetEventState(&myReaderStates[i]); + if (eventState & SCARD_STATE_CHANGED) { + readerListLock.getLock(); +- CKYReader_SetKnownState(&readerStates[i], eventState & ~SCARD_STATE_CHANGED); ++ CKYReader_SetKnownState(&readerStates[i], ++ eventState & ~SCARD_STATE_CHANGED); + readerListLock.releaseLock(); + *slotp = slotIndexToID(i); + found = TRUE; + break; + } + } ++ /* No real need to check for an additional card, we already update ++ * the list when we iterate. */ ++ if (!found) { ++ eventState = CKYReader_GetEventState( ++ &myReaderStates[myNumReaders-1]); ++ if (eventState & SCARD_STATE_CHANGED) { ++ readerListLock.getLock(); ++ CKYReader_SetKnownState(&pnp, ++ eventState & ~SCARD_STATE_CHANGED); ++ readerListLock.releaseLock(); ++ log->log("Reader insertion/removal detected\n"); ++ continue; /* get the update */ ++ } ++ } + } + + if (found || (flag == CKF_DONT_BLOCK) || shuttingDown) { +@@ -1294,21 +1407,20 @@ SlotList::waitForSlotEvent(CK_FLAGS flag + } + + #ifndef WIN32 +- if (status != CKYSUCCESS) { +- +- if ( (CKYCardContext_GetLastError(context) == +- SCARD_E_READER_UNAVAILABLE) || +- (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) +- { +- OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY); +- } +- +- +- } ++ /* pcsc-lite needs to make progress or something */ ++ if (status != CKYSUCCESS) { ++ if ((CKYCardContext_GetLastError(context) == ++ SCARD_E_READER_UNAVAILABLE) || ++ (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT)) { ++ OSSleep(timeout*PKCS11_CARD_ERROR_LATENCY); ++ } ++ } + #endif + } while ((status == CKYSUCCESS) || + (CKYCardContext_GetLastError(context) == SCARD_E_TIMEOUT) || +- ( CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE)); ++ (CKYCardContext_GetLastError(context) == SCARD_E_READER_UNAVAILABLE) || ++ (CKYCardContext_GetLastError(context) == SCARD_E_NO_SERVICE) || ++ (CKYCardContext_GetLastError(context) == SCARD_E_SERVICE_STOPPED) ); + #else + do { + OSSleep(100); +@@ -1345,6 +1457,7 @@ Slot::handleConnectionError() + case SCARD_W_REMOVED_CARD: + ckrv = CKR_DEVICE_REMOVED; + break; ++ + default: + ckrv = CKR_DEVICE_ERROR; + break; +@@ -1404,13 +1517,46 @@ Slot::selectApplet() + } + + void +-Slot::selectCACApplet(CKYByte instance) ++Slot::selectCACApplet(CKYByte instance, bool doDisconnect) + { + CKYStatus status; ++ /* PIV containers and keys by instance */ ++ static const int container[] = { ++ 0x5fc105, 0x5fc10a, 0x5fc10b, 0x5fc101, ++ 0x5fc10d, 0x5fc10e, 0x5fc10f, 0x5fc110, ++ 0x5fc111, 0x5fc112, 0x5fc113, 0x5fc114, ++ 0x5fc115, 0x5fc116, 0x5fc117, 0x5fc118, ++ 0x5fc119, 0x5fc11a, 0x5fc11b, 0x5fc11c, ++ 0x5fc11d, 0x5fc11e, 0x5fc11f, 0x5fc120 ++ }; ++ static const int keyRef[] = { ++ 0x9a, 0x9c, 0x9d, 0x9e, ++ 0x82, 0x83, 0x84, 0x85, ++ 0x86, 0x87, 0x88, 0x89, ++ 0x8a, 0x8b, 0x8c, 0x8d, ++ 0x8e, 0x8f, 0x90, 0x91, ++ 0x92, 0x93, 0x94, 0x95 ++ }; ++ ++ if (state & PIV_CARD) { ++ status = PIVApplet_Select(conn, NULL); ++ if (status == CKYSCARDERR) handleConnectionError(); ++ if (status != CKYSUCCESS) { ++ if (doDisconnect) { ++ disconnect(); ++ } ++ throw PKCS11Exception(CKR_DEVICE_REMOVED); ++ } ++ pivContainer = container[instance]; ++ pivKey = keyRef[instance]; ++ return; ++ } + CKYBuffer *aid = &cardAID[instance]; + + if (CKYBuffer_Size(aid) == 0) { +- disconnect(); ++ if (doDisconnect) { ++ disconnect(); ++ } + throw PKCS11Exception(CKR_DEVICE_REMOVED); + return; + } +@@ -1419,7 +1565,9 @@ Slot::selectCACApplet(CKYByte instance) + if ( status == CKYSCARDERR ) handleConnectionError(); + if ( status != CKYSUCCESS) { + // could not select applet: this just means it's not there +- disconnect(); ++ if (doDisconnect) { ++ disconnect(); ++ } + throw PKCS11Exception(CKR_DEVICE_REMOVED); + } + if (mOldCAC) { +@@ -1428,7 +1576,9 @@ Slot::selectCACApplet(CKYByte instance) + status = CACApplet_SelectFile(conn, cardEF[instance], NULL); + if ( status == CKYSCARDERR ) handleConnectionError(); + if ( status != CKYSUCCESS) { +- disconnect(); ++ if (doDisconnect) { ++ disconnect(); ++ } + throw PKCS11Exception(CKR_DEVICE_REMOVED); + } + } +@@ -1521,6 +1671,29 @@ Slot::generateUnusedObjectHandle() + return handle; + } + ++/* Create a short lived Secret Key for ECC key derive. */ ++PKCS11Object * ++Slot::createSecretKeyObject(CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) ++{ ++ ++ if (secretKeyBuffer == NULL ) { ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Can't create secret key object for ECC."); ++ } ++ ++ unsigned long muscleID = 0xfff; ++ PKCS11Object *secret = new SecretKey(muscleID, handle, secretKeyBuffer, pTemplate, ulAttributeCount); ++ ++ if (secret == NULL) { ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Can't create secret key object for ECC."); ++ } ++ ++ tokenObjects.push_back(*secret); ++ ++ return secret; ++} ++ + void + Slot::addKeyObject(list& objectList, const ListObjectInfo& info, + CK_OBJECT_HANDLE handle, bool isCombined) +@@ -1530,24 +1703,32 @@ Slot::addKeyObject(list& o + CK_OBJECT_CLASS objClass = keyObj.getClass(); + const CKYBuffer *id; + +- + if (isCombined && +- ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY))) { +- id = keyObj.getAttribute(CKA_ID); +- if ((!id) || (CKYBuffer_Size(id) != 1)) { +- throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Missing or invalid CKA_ID value"); +- } +- iter = find_if(objectList.begin(), objectList.end(), +- ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0))); +- if ( iter == objectList.end() ) { ++ ((objClass == CKO_PUBLIC_KEY) || (objClass == CKO_PRIVATE_KEY))) { ++ id = keyObj.getAttribute(CKA_ID); ++ if ((!id) || (CKYBuffer_Size(id) != 1)) { ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Missing or invalid CKA_ID value"); ++ } ++ iter = find_if(objectList.begin(), objectList.end(), ++ ObjectCertCKAIDMatch(CKYBuffer_GetChar(id,0))); ++ if ( iter == objectList.end() ) { + // We failed to find a cert with a matching CKA_ID. This + // can happen if the cert is not present on the token, or + // the der encoded cert stored on the token was corrupted. +- throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Failed to find cert with matching CKA_ID value"); +- } +- keyObj.completeKey(*iter); ++ throw PKCS11Exception(CKR_DEVICE_ERROR, ++ "Failed to find cert with matching CKA_ID value"); ++ } ++ keyObj.completeKey(*iter); ++ ++ /* For now this is how we determine what type of key. ++ Also for now, allow only one or the other */ ++ if ( keyObj.getKeyType() == PKCS11Object::ecc) { ++ mECC = true; ++ } else { ++ mECC = false; ++ } ++ + } + objectList.push_back(keyObj); + +@@ -1577,6 +1758,7 @@ Slot::addCertObject(list& + void + Slot::unloadObjects() + { ++ mECC = false; + tokenObjects.clear(); + free(personName); + personName = NULL; +@@ -1970,7 +2152,7 @@ Slot::readCUID(void) + // shared memory is protected by our transaction call on the card + // + CKYStatus status; +- if (state & CAC_CARD) { ++ if (state & GOV_CARD) { + status = CACApplet_SelectCardManager(conn, NULL); + } else { + status = CKYApplet_SelectCardManager(conn, NULL); +@@ -2203,6 +2385,50 @@ Slot::fetchCombinedObjects(const CKYBuff + return objInfoList; + } + ++typedef enum { ++ BER_UNWRAP, ++ BER_NEXT ++} BERop; ++ ++static CKYStatus ++berProcess(CKYBuffer *buf, int matchTag, CKYBuffer *target, BERop type) ++{ ++ unsigned char tag; ++ unsigned int used_length= 0; ++ unsigned int data_length; ++ ++ tag = CKYBuffer_GetChar(buf,used_length++); ++ ++ /* blow out when we come to the end */ ++ if (matchTag && tag != matchTag) { ++ return CKYLIBFAIL; ++ } ++ ++ data_length = CKYBuffer_GetChar(buf,used_length++); ++ ++ if (data_length & 0x80) { ++ int len_count = data_length & 0x7f; ++ ++ data_length = 0; ++ ++ while (len_count-- > 0) { ++ data_length = (data_length << 8) | ++ CKYBuffer_GetChar(buf,used_length++); ++ } ++ } ++ ++ if (data_length > (CKYBuffer_Size(buf)-used_length) ) { ++ return CKYLIBFAIL; ++ } ++ ++ if (type == BER_UNWRAP) { ++ return CKYBuffer_AppendBuffer(target, buf, used_length, data_length); ++ } ++ return CKYBuffer_AppendBuffer(target, buf, used_length+data_length, ++ CKYBuffer_Size(buf)-(used_length+data_length)); ++} ++ ++ + CKYStatus + Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, + bool throwException) +@@ -2211,16 +2437,60 @@ Slot::readCACCertificateFirst(CKYBuffer + CKYISOStatus apduRC; + *nextSize = 0; + ++ if (state & PIV_CARD) { ++ CKYBuffer pivData; ++ CKYBuffer certInfo; ++ ++ CKYBuffer_InitEmpty(&pivData); ++ CKYBuffer_InitEmpty(&certInfo); ++ CKYBuffer_Resize(cert, 0); ++ status = PIVApplet_GetCertificate(conn, cert, pivContainer, &apduRC); ++ if (throwException && (status != CKYSUCCESS)) { ++ handleConnectionError(); ++ } ++ /* actually, on success, we need to parse the certificate and find the ++ * propper tag */ ++ if (status == CKYSUCCESS) { ++ status = berProcess(cert, 0x53, &pivData, BER_UNWRAP); ++ CKYBuffer_Resize(cert, 0); ++ CKYBuffer_AppendChar(cert,0); ++ do { ++ CKYByte tag = CKYBuffer_GetChar(&pivData,0); ++ if (tag == CAC_TAG_CERTIFICATE) { ++ status = berProcess(&pivData, CAC_TAG_CERTIFICATE, ++ cert, BER_UNWRAP); ++ } ++ if (tag == CAC_TAG_CERTINFO) { ++ CKYBuffer_Resize(&certInfo, 0); ++ status = berProcess(&pivData, CAC_TAG_CERTINFO, ++ &certInfo, BER_UNWRAP); ++ if (CKYBuffer_Size(&certInfo) == 1) { ++ CKYBuffer_SetChar(cert,0, ++ CKYBuffer_GetChar(&certInfo,0)); ++ } ++ } ++ if (status == CKYSUCCESS) { ++ CKYBuffer_Resize(&certInfo, 0); ++ status = berProcess(&pivData, 0, &certInfo, BER_NEXT); ++ if (status == CKYSUCCESS) { ++ CKYBuffer_Resize(&pivData,0); ++ status = CKYBuffer_AppendCopy(&pivData,&certInfo); ++ } ++ } ++ } while ((status == CKYSUCCESS) && (CKYBuffer_Size(&pivData) != 0)); ++ CKYBuffer_FreeData(&pivData); ++ CKYBuffer_FreeData(&certInfo); ++ } ++ ++ return status; ++ } ++ + if (mOldCAC) { + /* get the first 100 bytes of the cert */ + status = CACApplet_GetCertificateFirst(conn, cert, nextSize, &apduRC); + if (throwException && (status != CKYSUCCESS)) { + handleConnectionError(); + } +- +- if(CKYBuffer_Size(cert) == 0) { +- handleConnectionError(); +- } + return status; + } + +@@ -2233,6 +2503,7 @@ Slot::readCACCertificateFirst(CKYBuffer + CKYBuffer_InitEmpty(&tBuf); + CKYBuffer_InitEmpty(&vBuf); + CKYBuffer_Resize(cert, 0); ++ CKYBuffer_AppendChar(cert,0); + + /* handle the new CAC card read */ + /* read the TLV */ +@@ -2258,11 +2529,12 @@ Slot::readCACCertificateFirst(CKYBuffer + length = CKYBuffer_GetShortLE(&tBuf, toffset); + toffset +=2; + } +- if (tag != CAC_TAG_CERTIFICATE) { +- continue; ++ if (tag == CAC_TAG_CERTIFICATE) { ++ CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length); ++ } ++ if (tag == CAC_TAG_CERTINFO) { ++ CKYBuffer_SetChar(cert,0,CKYBuffer_GetChar(&vBuf,voffset)); + } +- CKYBuffer_AppendBuffer(cert, &vBuf, voffset, length); +- break; + } + status = CKYSUCCESS; + +@@ -2272,6 +2544,191 @@ done: + return status; + } + ++ ++const static unsigned long crc_table[] = { ++0x00000000,0x77073096,0xee0e612c,0x990951ba, ++0x076dc419,0x706af48f,0xe963a535,0x9e6495a3, ++0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988, ++0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91, ++0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de, ++0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7, ++0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, ++0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5, ++0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172, ++0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b, ++0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940, ++0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59, ++0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116, ++0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, ++0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924, ++0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d, ++0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a, ++0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433, ++0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818, ++0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01, ++0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, ++0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457, ++0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c, ++0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65, ++0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2, ++0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb, ++0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0, ++0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, ++0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086, ++0x5768b525,0x206f85b3,0xb966d409,0xce61e49f, ++0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4, ++0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad, ++0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a, ++0xead54739,0x9dd277af,0x04db2615,0x73dc1683, ++0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, ++0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1, ++0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe, ++0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7, ++0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc, ++0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5, ++0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252, ++0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, ++0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60, ++0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79, ++0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236, ++0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f, ++0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04, ++0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d, ++0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, ++0x9c0906a9,0xeb0e363f,0x72076785,0x05005713, ++0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38, ++0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21, ++0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e, ++0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777, ++0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c, ++0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, ++0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2, ++0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db, ++0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0, ++0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9, ++0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6, ++0xbad03605,0xcdd70693,0x54de5729,0x23d967bf, ++0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, ++0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d ++}; ++ ++static unsigned long ++calc_crc32(const unsigned char *buf, int len) ++{ ++ unsigned long crc = 0xffffffff; ++ int i; ++ ++ for (i=0; i < len; i++) { ++ unsigned char crc_low = crc & 0xff; ++ unsigned long crc_high = crc >> 8; ++ crc = crc_table[crc_low ^ buf[i]] ^ crc_high; ++ } ++ return crc ^ 0xffffffff; ++} ++ ++/* ++ * decompress, handles both gzip and zlib trailers ++ * it also automatically allocates the output buffer and expands it as ++ * necessary. ++ */ ++static int ++decompress(CKYBuffer *out, ++ CKYBuffer *in, CKYOffset offset, CKYSize len) ++{ ++ int zret; ++ CKYStatus status; ++ z_stream stream; ++ int chunk = len *2; ++ int outlen = 0; ++ ++ ++ /* allocate inflate state */ ++ stream.zalloc = Z_NULL; ++ stream.zfree = Z_NULL; ++ stream.opaque = Z_NULL; ++ stream.avail_in = 0; ++ stream.next_in = Z_NULL; ++ zret = inflateInit(&stream); ++ if (zret != Z_OK) ++ return zret; ++ ++ status = CKYBuffer_Reserve(out, outlen); ++ if (status != CKYSUCCESS) { ++ return Z_MEM_ERROR; ++ } ++ ++ stream.avail_in = len; ++ stream.next_in = (Bytef *)(CKYBuffer_Data(in) + offset); ++ ++ do { ++ CKYBuffer_Resize(out, outlen + chunk); ++ stream.avail_out = chunk; ++ ++ stream.next_out = (Bytef *)CKYBuffer_Data(out)+ outlen; ++ ++ zret= inflate(&stream, Z_NO_FLUSH); ++ ++ /* we need the length early so it can be used in error processing */ ++ outlen += chunk - stream.avail_out; ++ ++ /* proccess the error codes */ ++ switch (zret) { ++ case Z_DATA_ERROR: ++ /* a DATA error can occur on either corrupted data, or on gzip. ++ * data. This is because gzip uses CRC32 and zlib used ADLER32 ++ * checksums. We need to check to see if this failure is do to ++ * a gzip header. */ ++ /* 1) a gzip header includes 4 extra bytes containing the length ++ * of the gziped data. This means there must be 4 more bytes ++ * in our input buffer that have not been processed */ ++ if (stream.avail_in != 4) { ++ break; /* not a gzip header */ ++ } ++ /* The last 4 bytes of a gzip header include the uncompressed length ++ * modulo 2^32. Make sure the actual uncompressed length matches ++ * the header. */ ++ if ((outlen & 0xffffffffL) ++ != CKYBuffer_GetLongLE(in, offset+len-4)) { ++ break; /* didn't decode the full length */ ++ } ++ /* At this point it''s pretty likely we have a gzip trailer. Verify ++ * the crc32 values to make sure there hasn't been any corruption. ++ */ ++ if (calc_crc32(CKYBuffer_Data(out), outlen) != ++ CKYBuffer_GetLongLE(in,offset+len-8)) { ++ break; /* CRC didn't match */ ++ } ++ /* This was valid gzip data, and we've successfully uncompressed ++ * it. We're now done. */ ++ zret=Z_STREAM_END; ++ break; ++ case Z_NEED_DICT: ++ /* if we need the dict, it wasn't in the data, ++ * so it's a data error */ ++ zret = Z_DATA_ERROR; ++ break; ++ case Z_OK: ++ /* Z_OK means we need more data, expand the buffer and go again. ++ * if we don't need more buffer space, then the input must have ++ * been truncated, that's a data error */ ++ if (stream.avail_out != 0) { ++ zret = Z_DATA_ERROR; ++ } ++ break; ++ } ++ } while (zret == Z_OK); ++ ++ /* cleanup */ ++ if (zret == Z_STREAM_END) { ++ zret = Z_OK; ++ CKYBuffer_Resize(out, outlen); ++ } else { ++ CKYBuffer_Resize(out, 0); ++ } ++ (void)inflateEnd(&stream); ++ return zret; ++} ++ + /* + * only necessary for old CAC cards. New CAC cards have to read the + * whole cert in anyway above.... +@@ -2304,7 +2761,7 @@ Slot::loadCACCert(CKYByte instance) + // catch the applet selection errors if they don't + // + try { +- selectCACApplet(instance); ++ selectCACApplet(instance, false); + } catch(PKCS11Exception& e) { + // all CAC's must have instance '0', throw the error it + // they don't. +@@ -2322,6 +2779,10 @@ Slot::loadCACCert(CKYByte instance) + + if (instance == 0) { + readCACCertificateFirst(&rawCert, &nextSize, true); ++ ++ if(CKYBuffer_Size(&rawCert) <= 1) { ++ handleConnectionError(); ++ } + log->log("CAC Cert %d: fetch CAC Cert: %d ms\n", + instance, OSTimeNow() - time); + } +@@ -2364,7 +2825,7 @@ Slot::loadCACCert(CKYByte instance) + } else { + status = readCACCertificateFirst(&rawCert, &nextSize, false); + +- if (status != CKYSUCCESS) { ++ if ((status != CKYSUCCESS) || (CKYBuffer_Size(&rawCert) <= 1)) { + /* CAC only requires the Certificate in pki '0' */ + /* if pki '1' or '2' are empty, treat it as a non-fatal error*/ + if (instance == 2) { +@@ -2393,31 +2854,61 @@ Slot::loadCACCert(CKYByte instance) + + log->log("CAC Cert %d: Cert has been read: %d ms\n", + instance, OSTimeNow() - time); +- if (!mOldCAC || CKYBuffer_GetChar(&rawCert,0) == 1) { +- CKYSize guessFinalSize = CKYBuffer_Size(&rawCert); +- CKYSize certSize = 0; +- CKYOffset offset = mOldCAC ? 1 : 0; ++ /* new CACs, and old CACs with the high one bit are compressed, ++ * uncompress them */ ++ if ((CKYBuffer_GetChar(&rawCert,0) & 0x3) == 1) { ++ CKYOffset offset = 1; + int zret = Z_MEM_ERROR; + +- do { +- guessFinalSize *= 2; +- status = CKYBuffer_Resize(&cert, guessFinalSize); +- if (status != CKYSUCCESS) { +- break; ++ /* process the GZIP header if present */ ++ /* header_id = 0x1f, 0x8b. CM=8. If we ever support something other ++ * than CM=8, we need to change the zlib header below. Currently both ++ * gzip and zlib only support CM=8 (DEFLATE) compression */ ++ if ((CKYBuffer_GetChar(&rawCert,1) == 0x1f) && ++ (CKYBuffer_GetChar(&rawCert,2) == 0x8b) && ++ (CKYBuffer_GetChar(&rawCert,3) == 8)) { ++ CKYByte flags = CKYBuffer_GetChar(&rawCert,4); ++ /* this has a gzip header, not raw data. */ ++ offset += 10; /* base size of the gzip header */ ++ if (flags & 4) { /* FEXTRA */ ++ CKYSize len = CKYBuffer_GetShortLE(&rawCert,offset); ++ offset += len; + } +- certSize = guessFinalSize; +- zret = uncompress((Bytef *)CKYBuffer_Data(&cert),&certSize, +- CKYBuffer_Data(&rawCert)+offset, +- CKYBuffer_Size(&rawCert)-offset); +- } while (zret == Z_BUF_ERROR); ++ if (flags & 8) { /* FNAME */ ++ while (CKYBuffer_GetChar(&rawCert,offset) != 0) { ++ offset++; ++ } ++ offset++; ++ } ++ if (flags & 0x10) { /* FComment */ ++ while (CKYBuffer_GetChar(&rawCert,offset) != 0) { ++ offset++; ++ } ++ offset++; ++ } ++ if (flags & 2) { /* FHCRC */ ++ offset += 2; ++ } ++ offset -= 2; ++ ++ /* add zlib header, so libz will be happy */ ++ /* CINFO=7, CM=8, LEVEL=2, DICTFLAG=0, FCHECK= 1c */ ++ /* NOTE: the zlib will fail when procssing the trailer. this is ++ * ok because decompress automatically notices the failure and ++ * and checks the gzip trailer. */ ++ CKYBuffer_SetChar(&rawCert, offset, 0x78); ++ CKYBuffer_SetChar(&rawCert, offset+1, 0x9c); ++ } ++ /* uncompress. This expands cert as necessary. */ ++ zret = decompress(&cert, &rawCert, offset, ++ CKYBuffer_Size(&rawCert)-offset); + + if (zret != Z_OK) { + CKYBuffer_FreeData(&rawCert); + CKYBuffer_FreeData(&cert); + throw PKCS11Exception(CKR_DEVICE_ERROR, +- "Corrupted compressed CAC Cert"); ++ "Corrupted compressed CAC/PIV Cert"); + } +- CKYBuffer_Resize(&cert,certSize); + } else { + CKYBuffer_InitFromBuffer(&cert,&rawCert,1,CKYBuffer_Size(&rawCert)-1); + } +@@ -2431,6 +2922,9 @@ Slot::loadCACCert(CKYByte instance) + tokenObjects.push_back(privKey); + tokenObjects.push_back(pubKey); + tokenObjects.push_back(certObj); ++ if ( pubKey.getKeyType() == PKCS11Object::ecc) { ++ mECC = 1; ++ } + + if (personName == NULL) { + const char *name = certObj.getName(); +@@ -2459,7 +2953,7 @@ Slot::loadObjects() + list objInfoList; + std::list::iterator iter; + +- if (state & CAC_CARD) { ++ if (state & GOV_CARD) { + loadCACCert(0); + loadCACCert(1); + loadCACCert(2); +@@ -2704,6 +3198,7 @@ Slot::login(SessionHandleSuffix handleSu + } + + if (!isVersion1Key) { ++ pinCache.invalidate(); + pinCache.set((const char *)pPin, ulPinLen); + } else if (nonceValid) { + throw PKCS11Exception(CKR_USER_ALREADY_LOGGED_IN); +@@ -2713,15 +3208,15 @@ Slot::login(SessionHandleSuffix handleSu + CKYStatus status = trans.begin(conn); + if(status != CKYSUCCESS ) handleConnectionError(); + +- if (state & CAC_CARD) { +- selectCACApplet(0); ++ if (state & GOV_CARD) { ++ selectCACApplet(0, true); + } else { + selectApplet(); + } + + if (isVersion1Key) { + attemptLogin((const char *)pPin); +- } else if (state & CAC_CARD) { ++ } else if (state & GOV_CARD) { + attemptCACLogin(); + } else { + oldAttemptLogin(); +@@ -2738,7 +3233,8 @@ Slot::attemptCACLogin() + CKYISOStatus result; + + status = CACApplet_VerifyPIN(conn, +- (const char *)CKYBuffer_Data(pinCache.get()), &result); ++ (const char *)CKYBuffer_Data(pinCache.get()), ++ mCACLocalLogin, &result); + if( status == CKYSCARDERR ) { + handleConnectionError(); + } +@@ -2746,8 +3242,10 @@ Slot::attemptCACLogin() + case CKYISO_SUCCESS: + break; + case 0x6981: ++ pinCache.clearPin(); + throw PKCS11Exception(CKR_PIN_LOCKED); + default: ++ pinCache.clearPin(); + if ((result & 0xff00) == 0x6300) { + throw PKCS11Exception(CKR_PIN_INCORRECT); + } +@@ -2776,10 +3274,13 @@ Slot::oldAttemptLogin() + case CKYISO_SUCCESS: + break; + case CKYISO_AUTH_FAILED: ++ pinCache.clearPin(); + throw PKCS11Exception(CKR_PIN_INCORRECT); + case CKYISO_IDENTITY_BLOCKED: ++ pinCache.clearPin(); + throw PKCS11Exception(CKR_PIN_LOCKED); + default: ++ pinCache.clearPin(); + throw PKCS11Exception(CKR_DEVICE_ERROR, "Applet returned 0x%04x", + result); + } +@@ -2866,7 +3367,7 @@ Slot::logout(SessionHandleSuffix suffix) + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } + +- if (state & CAC_CARD) { ++ if (state & GOV_CARD) { + CACLogout(); + return; + } +@@ -2993,7 +3494,7 @@ Slot::getAttributeValue(SessionHandleSuf + ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(), + ObjectHandleMatch(hObject)); + +- if( iter == tokenObjects.end() ) { ++ if ( iter == tokenObjects.end()) { + throw PKCS11Exception(CKR_OBJECT_HANDLE_INVALID); + } + +@@ -3077,6 +3578,21 @@ SlotList::generateRandom(CK_SESSION_HAND + } + + void ++SlotList::derive(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) ++{ ++ ++ CK_SLOT_ID slotID; ++ SessionHandleSuffix suffix; ++ ++ decomposeSessionHandle(hSession, slotID, suffix); ++ ++ slots[slotIDToIndex(slotID)]->derive(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey); ++ ++} ++ ++void + Slot::ensureValidSession(SessionHandleSuffix suffix) + { + if( ! isValidSession(suffix) ) { +@@ -3110,6 +3626,23 @@ Slot::objectHandleToKeyNum(CK_OBJECT_HAN + return keyNum & 0xFF; + } + ++PKCS11Object::KeyType ++Slot::getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey) ++{ ++ ObjectConstIter iter = find_if(tokenObjects.begin(), tokenObjects.end(), ++ ObjectHandleMatch(hKey)); ++ ++ if( iter == tokenObjects.end() ) { ++ throw PKCS11Exception(CKR_KEY_HANDLE_INVALID); ++ } ++ ++ if( getObjectClass(iter->getMuscleObjID()) != 'k' ) { ++ throw PKCS11Exception(CKR_KEY_HANDLE_INVALID); ++ } ++ ++ return iter->getKeyType(); ++} ++ + void + Slot::signInit(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +@@ -3119,7 +3652,10 @@ Slot::signInit(SessionHandleSuffix suffi + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } +- session->signatureState.initialize(objectHandleToKeyNum(hKey)); ++ ++ PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey); ++ ++ session->signatureState.initialize(objectHandleToKeyNum(hKey), keyType); + } + + void +@@ -3131,7 +3667,10 @@ Slot::decryptInit(SessionHandleSuffix su + if( session == sessions.end() ) { + throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); + } +- session->decryptionState.initialize(objectHandleToKeyNum(hKey)); ++ ++ PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hKey); ++ ++ session->decryptionState.initialize(objectHandleToKeyNum(hKey), keyType); + } + + /** +@@ -3240,54 +3779,141 @@ stripRSAPadding(CKYBuffer *stripped, con + } + } + +-class RSASignatureParams : public CryptParams { ++class ECCKeyAgreementParams : public CryptParams { + public: +- RSASignatureParams(unsigned int keysize) : CryptParams(keysize) { } ++ ECCKeyAgreementParams(unsigned int keysize) : CryptParams(keysize) { } + +- CKYByte getDirection() const { return CKY_DIR_ENCRYPT; } ++ CKYByte getDirection() const { return CKY_DIR_NONE;} + + CryptOpState& getOpState(Session& session) const { +- return session.signatureState; ++ return session.keyAgreementState; + } + + void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { +- // RSA_NO_PAD requires RSA PKCS #1 Type 1 padding +- CKYStatus status = CKYBuffer_Resize(paddedInput,getKeySize()/8); +- if (status != CKYSUCCESS) { +- throw PKCS11Exception(CKR_HOST_MEMORY); +- } +- padRSAType1(unpaddedInput, paddedInput); + return; + } + + void + unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { +- // no need to unpad ciphertext +- CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(paddedOutput), +- CKYBuffer_Size(paddedOutput)); +- ++ return; + } ++ + }; + +-class RSADecryptParams: public CryptParams { ++class SignatureParams : public CryptParams { + public: +- RSADecryptParams(unsigned int keysize) : CryptParams(keysize) { } ++ SignatureParams(unsigned int keysize) : CryptParams(keysize) { } + +- CKYByte getDirection() const { return CKY_DIR_DECRYPT; } ++ CKYByte getDirection() const { return CKY_DIR_NONE; } + + CryptOpState& getOpState(Session& session) const { +- return session.decryptionState; ++ return session.signatureState; + } + + void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { +- // no need to unpad ciphertext +- CKYBuffer_Replace(paddedInput, 0, CKYBuffer_Data(unpaddedInput), +- CKYBuffer_Size(unpaddedInput)); ++ return; + } + + void + unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { +- // strip off PKCS #1 padding ++ return; ++ } ++ ++}; ++ ++ ++ ++class ECCSignatureParams : public CryptParams { ++ public: ++ ECCSignatureParams(unsigned int keysize) : CryptParams(keysize) { } ++ ++ CKYByte getDirection() const { return CKY_DIR_NONE; } ++ ++ CryptOpState& getOpState(Session& session) const { ++ return session.signatureState; ++ } ++ ++ void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { ++ return; ++ } ++ ++ void ++ unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { ++ /* Here we will unpack the DER encoding of the signature */ ++ ++ if ( unpaddedOutput == NULL || paddedOutput == NULL) { ++ throw PKCS11Exception(CKR_ARGUMENTS_BAD); ++ } ++ ++ CKYBuffer rawSignature; ++ CKYBuffer_InitEmpty(&rawSignature); ++ ++ DEREncodedSignature sig(paddedOutput); ++ ++ int rv = sig.getRawSignature(&rawSignature, getKeySize() ); ++ ++ if (rv == CKYSUCCESS) { ++ CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(&rawSignature), ++ CKYBuffer_Size(&rawSignature)); ++ } else { ++ throw PKCS11Exception(CKR_DEVICE_ERROR); ++ } ++ ++ CKYBuffer_FreeData(&rawSignature); ++ ++ } ++ ++}; ++ ++ ++class RSASignatureParams : public CryptParams { ++ public: ++ RSASignatureParams(unsigned int keysize) : CryptParams(keysize) { } ++ ++ CKYByte getDirection() const { return CKY_DIR_ENCRYPT; } ++ ++ CryptOpState& getOpState(Session& session) const { ++ return session.signatureState; ++ } ++ ++ void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { ++ // RSA_NO_PAD requires RSA PKCS #1 Type 1 padding ++ CKYStatus status = CKYBuffer_Resize(paddedInput,getKeySize()/8); ++ if (status != CKYSUCCESS) { ++ throw PKCS11Exception(CKR_HOST_MEMORY); ++ } ++ padRSAType1(unpaddedInput, paddedInput); ++ return; ++ } ++ ++ void ++ unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { ++ // no need to unpad ciphertext ++ CKYBuffer_Replace(unpaddedOutput, 0, CKYBuffer_Data(paddedOutput), ++ CKYBuffer_Size(paddedOutput)); ++ ++ } ++}; ++ ++class RSADecryptParams: public CryptParams { ++ public: ++ RSADecryptParams(unsigned int keysize) : CryptParams(keysize) { } ++ ++ CKYByte getDirection() const { return CKY_DIR_DECRYPT; } ++ ++ CryptOpState& getOpState(Session& session) const { ++ return session.decryptionState; ++ } ++ ++ void padInput(CKYBuffer *paddedInput, const CKYBuffer *unpaddedInput) const { ++ // no need to unpad ciphertext ++ CKYBuffer_Replace(paddedInput, 0, CKYBuffer_Data(unpaddedInput), ++ CKYBuffer_Size(unpaddedInput)); ++ } ++ ++ void ++ unpadOutput(CKYBuffer *unpaddedOutput, const CKYBuffer *paddedOutput) const { ++ // strip off PKCS #1 padding + stripRSAPadding( unpaddedOutput, paddedOutput ); + return; + } +@@ -3298,9 +3924,38 @@ Slot::sign(SessionHandleSuffix suffix, C + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) + { +- RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE); +- cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen, +- params); ++ ++ refreshTokenState(); ++ SessionIter session = findSession(suffix); ++ if( session == sessions.end() ) { ++ throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); ++ } ++ ++ if (!isVersion1Key && ! isLoggedIn() ) { ++ throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN); ++ } ++ ++ /* Create a default one just to get the sigState */ ++ SignatureParams dummyParams(CryptParams::DEFAULT_KEY_SIZE); ++ ++ CryptOpState sigState = dummyParams.getOpState(*session); ++ ++ PKCS11Object::KeyType keyType = sigState.keyType; ++ ++ if ( keyType == PKCS11Object::unknown) { ++ throw PKCS11Exception(CKR_DATA_INVALID); ++ } ++ ++ if( keyType == Key::ecc ) { ++ ECCSignatureParams params(CryptParams::ECC_DEFAULT_KEY_SIZE); ++ signECC(suffix, pData, ulDataLen, pSignature, pulSignatureLen, ++ params); ++ ++ } else if (keyType == Key::rsa) { ++ RSASignatureParams params(CryptParams::DEFAULT_KEY_SIZE); ++ cryptRSA(suffix, pData, ulDataLen, pSignature, pulSignatureLen, ++ params); ++ } + } + + void +@@ -3334,9 +3989,9 @@ Slot::cryptRSA(SessionHandleSuffix suffi + CKYBuffer *result = &opState.result; + CKYByte keyNum = opState.keyNum; + +- unsigned int keySize = getKeySize(keyNum); ++ unsigned int keySize = getRSAKeySize(keyNum); + +- if(keySize != CryptParams::DEFAULT_KEY_SIZE) ++ if (keySize != CryptParams::DEFAULT_KEY_SIZE) + params.setKeySize(keySize); + + if( CKYBuffer_Size(result) == 0 ) { +@@ -3358,7 +4013,8 @@ Slot::cryptRSA(SessionHandleSuffix suffi + } + try { + params.padInput(&inputPad, &input); +- performRSAOp(&output, &inputPad, keyNum, params.getDirection()); ++ performRSAOp(&output, &inputPad, params.getKeySize(), ++ keyNum, params.getDirection()); + params.unpadOutput(result, &output); + CKYBuffer_FreeData(&input); + CKYBuffer_FreeData(&inputPad); +@@ -3395,10 +4051,166 @@ Slot::getNonce() + return &nonce; + } + ++void Slot::signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput, ++ CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, ++ CK_ULONG_PTR pulOutputLen, CryptParams& params) ++{ ++ ++ if( pulOutputLen == NULL ) { ++ throw PKCS11Exception(CKR_DATA_INVALID, ++ "output length is NULL"); ++ } ++ ++ refreshTokenState(); ++ SessionIter session = findSession(suffix); ++ if( session == sessions.end() ) { ++ throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); ++ } ++ /* version 1 keys may not need login. We catch the error ++ on the operation. The token will not allow us to sign with ++ a protected key unless we are logged in. ++ can be removed when version 0 support is depricated. ++ */ ++ ++ if (!isVersion1Key && ! isLoggedIn() ) { ++ throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN); ++ } ++ CryptOpState& opState = params.getOpState(*session); ++ CKYBuffer *result = &opState.result; ++ CKYByte keyNum = opState.keyNum; ++ ++ unsigned int keySize = getECCKeySize(keyNum); ++ ++ if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE) ++ params.setKeySize(keySize); ++ ++ if( CKYBuffer_Size(result) == 0 ) { ++ unsigned int maxSize = params.getKeySize()/8; ++ ++ if( pInput == NULL || ulInputLen == 0) { ++ throw PKCS11Exception(CKR_DATA_LEN_RANGE); ++ } ++ if (ulInputLen > maxSize) { ++ //pInput += ulInputLen - maxSize; ++ ulInputLen = maxSize; ++ } ++ ++ CKYBuffer input; ++ CKYBuffer output; ++ CKYBuffer_InitEmpty(&output); ++ CKYStatus status = CKYBuffer_InitFromData(&input, pInput, ulInputLen); ++ ++ if (status != CKYSUCCESS) { ++ CKYBuffer_FreeData(&output); ++ throw PKCS11Exception(CKR_HOST_MEMORY); ++ } ++ try { ++ performECCSignature(&output, &input, params.getKeySize(), keyNum); ++ params.unpadOutput(result, &output); ++ CKYBuffer_FreeData(&input); ++ CKYBuffer_FreeData(&output); ++ } catch(PKCS11Exception& e) { ++ CKYBuffer_FreeData(&input); ++ CKYBuffer_FreeData(&output); ++ throw(e); ++ } ++ } ++ ++ if( pOutput != NULL ) { ++ if( *pulOutputLen < CKYBuffer_Size(result) ) { ++ *pulOutputLen = CKYBuffer_Size(result); ++ throw PKCS11Exception(CKR_BUFFER_TOO_SMALL); ++ } ++ memcpy(pOutput, CKYBuffer_Data(result), CKYBuffer_Size(result)); ++ } ++ *pulOutputLen = CKYBuffer_Size(result); ++} ++ ++void ++Slot::performECCSignature(CKYBuffer *output, const CKYBuffer *input, ++ unsigned int keySize, CKYByte keyNum) ++{ ++ ++ /* establish a transaction */ ++ Transaction trans; ++ CKYStatus status = trans.begin(conn); ++ if( status != CKYSUCCESS ) handleConnectionError(); ++ ++ if (!mECC) { ++ throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); ++ } ++ ++ if (state & GOV_CARD) { ++ selectCACApplet(keyNum, true); ++ } else { ++ selectApplet(); ++ } ++ ++ CKYISOStatus result; ++ int loginAttempted = 0; ++ ++retry: ++ if (state & PIV_CARD) { ++ status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result); ++ } else if (state & CAC_CARD) { ++ status = CACApplet_SignDecrypt(conn, input, output, &result); ++ } else { ++ status = CKYApplet_ComputeECCSignature(conn, keyNum, input, NULL, output, getNonce(), &result); ++ } ++ /* map the ISO not logged in code to the coolkey one */ ++ if ((result == CKYISO_CONDITION_NOT_SATISFIED) || ++ (result == CKYISO_SECURITY_NOT_SATISFIED)) { ++ result = (CKYStatus) CKYISO_UNAUTHORIZED; ++ } ++ ++ if (status != CKYSUCCESS) { ++ if ( status == CKYSCARDERR ) { ++ handleConnectionError(); ++ } ++ ++ if (result == CKYISO_DATA_INVALID) { ++ throw PKCS11Exception(CKR_DATA_INVALID); ++ } ++ /* version0 keys could be logged out in the middle by someone else, ++ reauthenticate... This code can go away when we depricate. ++ version0 applets. ++ */ ++ if (!isVersion1Key && !loginAttempted && ++ (result == CKYISO_UNAUTHORIZED)) { ++ /* try to reauthenticate */ ++ try { ++ if (state & GOV_CARD) { ++ attemptCACLogin(); ++ } else { ++ oldAttemptLogin(); ++ } ++ } catch(PKCS11Exception& ) { ++ /* attemptLogin can throw things like CKR_PIN_INCORRECT ++ that don't make sense from a crypto operation. This is ++ a result of pin caching. We will reformat any login ++ exception to a CKR_DEVICE_ERROR. ++ */ ++ throw PKCS11Exception(CKR_DEVICE_ERROR); ++ } ++ loginAttempted = true; ++ goto retry; /* easier to understand than a while loop in this case. */ ++ } ++ throw PKCS11Exception( result == CKYISO_UNAUTHORIZED ? ++ CKR_USER_NOT_LOGGED_IN : CKR_DEVICE_ERROR); ++ ++ } ++ ++} ++ ++ + void +-Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, ++Slot::performRSAOp(CKYBuffer *output, const CKYBuffer *input, unsigned int keySize, + CKYByte keyNum, CKYByte direction) + { ++ if ( mECC ) { ++ throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); ++ } ++ + // + // establish a transaction + // +@@ -3409,8 +4221,8 @@ Slot::performRSAOp(CKYBuffer *output, co + // + // select the applet + // +- if (state & CAC_CARD) { +- selectCACApplet(keyNum); ++ if (state & GOV_CARD) { ++ selectCACApplet(keyNum, true); + } else { + selectApplet(); + } +@@ -3418,12 +4230,21 @@ Slot::performRSAOp(CKYBuffer *output, co + CKYISOStatus result; + int loginAttempted = 0; + retry: +- if (state & CAC_CARD) { ++ if (state & PIV_CARD) { ++ status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 0, input, output, &result); ++ } else if (state & CAC_CARD) { + status = CACApplet_SignDecrypt(conn, input, output, &result); + } else { + status = CKYApplet_ComputeCrypt(conn, keyNum, CKY_RSA_NO_PAD, direction, + input, NULL, output, getNonce(), &result); + } ++ ++ /* map the ISO not logged in code to the coolkey one */ ++ if ((result == CKYISO_CONDITION_NOT_SATISFIED) || ++ (result == CKYISO_SECURITY_NOT_SATISFIED)) { ++ result = CKYISO_UNAUTHORIZED; ++ } ++ + if (status != CKYSUCCESS) { + if ( status == CKYSCARDERR ) { + handleConnectionError(); +@@ -3434,11 +4255,15 @@ retry: + // version0 keys could be logged out in the middle by someone else, + // reauthenticate... This code can go away when we depricate. + // version0 applets. +- if (!isVersion1Key && !loginAttempted && ++ if (!isVersion1Key && !loginAttempted && pinCache.isValid() && + (result == CKYISO_UNAUTHORIZED)) { + // try to reauthenticate + try { +- oldAttemptLogin(); ++ if (state & GOV_CARD) { ++ attemptCACLogin(); ++ } else { ++ oldAttemptLogin(); ++ } + } catch(PKCS11Exception& ) { + // attemptLogin can throw things like CKR_PIN_INCORRECT + // that don't make sense from a crypto operation. This is +@@ -3458,7 +4283,7 @@ void + Slot::seedRandom(SessionHandleSuffix suffix, CK_BYTE_PTR pData, + CK_ULONG ulDataLen) + { +- if (state & CAC_CARD) { ++ if (state & GOV_CARD) { + /* should throw unsupported */ + throw PKCS11Exception(CKR_DEVICE_ERROR); + } +@@ -3510,7 +4335,7 @@ void + Slot::generateRandom(SessionHandleSuffix suffix, const CK_BYTE_PTR pData, + CK_ULONG ulDataLen) + { +- if (state & CAC_CARD) { ++ if (state & GOV_CARD) { + /* should throw unsupported */ + throw PKCS11Exception(CKR_DEVICE_ERROR); + } +@@ -3544,7 +4369,7 @@ Slot::generateRandom(SessionHandleSuffix + + #define MAX_NUM_KEYS 8 + unsigned int +-Slot::getKeySize(CKYByte keyNum) ++Slot::getRSAKeySize(CKYByte keyNum) + { + unsigned int keySize = CryptParams::DEFAULT_KEY_SIZE; + int modSize = 0; +@@ -3574,3 +4399,238 @@ Slot::getKeySize(CKYByte keyNum) + + return keySize; + } ++ ++unsigned int ++Slot::getECCKeySize(CKYByte keyNum) ++{ ++ return calcECCKeySize(keyNum); ++} ++ ++unsigned int ++Slot::calcECCKeySize(CKYByte keyNum) ++{ ++ unsigned int keySize = CryptParams::ECC_DEFAULT_KEY_SIZE; ++ ++ if(keyNum >= MAX_NUM_KEYS) { ++ return keySize; ++ } ++ ++ ObjectConstIter iter; ++ iter = find_if(tokenObjects.begin(), tokenObjects.end(), ++ KeyNumMatch(keyNum,*this)); ++ ++ if( iter == tokenObjects.end() ) { ++ return keySize; ++ } ++ ++ CKYBuffer const *eccParams = iter->getAttribute(CKA_EC_PARAMS); ++ ++ if (eccParams == NULL) { ++ return keySize; ++ } ++ ++ /* Extract the oid from the params */ ++ ++ CKYByte ecParamsLen = CKYBuffer_GetChar(eccParams, 1); ++ ++ if ( ecParamsLen == 0 ) { ++ return keySize; ++ } ++ ++/* Now compare against the limited known list of oid byte info */ ++ ++ unsigned int oidByteLen = 0; ++ ++ CKYByte curByte = 0; ++ ++ for (int i = 0 ; i < numECCurves ; i++ ) { ++ ++ oidByteLen = curveBytesNamePair[i].bytes[0]; ++ ++ if ( oidByteLen != (unsigned int ) ecParamsLen ) { ++ continue; ++ } ++ ++ int match = 1; ++ for ( int j = 0 ; j < ecParamsLen ; j++ ) { ++ curByte = CKYBuffer_GetChar(eccParams, 2 + j ); ++ if ( curveBytesNamePair[i].bytes[ j + 1 ] != curByte ) { ++ match = 0; ++ break; ++ } ++ } ++ ++ if ( match == 1 ) { ++ keySize = curveBytesNamePair[i].length; ++ return keySize; ++ } ++ ++ } ++ ++ return keySize; ++} ++ ++void ++Slot::derive(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) ++{ ++ ++ log->log("Inside of Slot::Derive! \n"); ++ ++ ECCKeyAgreementParams params(CryptParams::ECC_DEFAULT_KEY_SIZE); ++ SessionIter session = findSession(suffix); ++ ++ PKCS11Object::KeyType keyType = getKeyTypeFromHandle(hBaseKey); ++ ++ session->keyAgreementState.initialize(objectHandleToKeyNum(hBaseKey), keyType); ++ deriveECC(suffix, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey, params); ++ ++} ++ ++void Slot::deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params) ++{ ++ if (pMechanism == NULL ) { ++ throw PKCS11Exception(CKR_ARGUMENTS_BAD); ++ } ++ ++ CK_ECDH1_DERIVE_PARAMS *mechParams = NULL; ++ ++ mechParams = (CK_ECDH1_DERIVE_PARAMS*) pMechanism->pParameter; ++ ++ if (mechParams == NULL || mechParams->kdf != CKD_NULL ) { ++ throw PKCS11Exception(CKR_ARGUMENTS_BAD); ++ } ++ ++ refreshTokenState(); ++ SessionIter session = findSession(suffix); ++ if( session == sessions.end() ) { ++ throw PKCS11Exception(CKR_SESSION_HANDLE_INVALID); ++ } ++ ++ /* version 1 keys may not need login. We catch the error ++ on the operation. The token will not allow us to sign with ++ a protected key unless we are logged in. ++ can be removed when version 0 support is depricated. */ ++ ++ if (!isVersion1Key && ! isLoggedIn() ) { ++ throw PKCS11Exception(CKR_USER_NOT_LOGGED_IN); ++ } ++ ++ CryptOpState& opState = params.getOpState(*session); ++ CKYBuffer *result = &opState.result; ++ CKYByte keyNum = opState.keyNum; ++ ++ unsigned int keySize = getECCKeySize(keyNum); ++ ++ if(keySize != CryptParams::ECC_DEFAULT_KEY_SIZE) ++ params.setKeySize(keySize); ++ ++ CK_MECHANISM_TYPE deriveMech = pMechanism->mechanism; ++ ++ CK_ULONG otherPublicLen = mechParams->ulPublicDataLen; ++ CK_BYTE_PTR otherPublicData = mechParams->pPublicData; ++ ++ CKYBuffer secretKeyBuffer; ++ CKYBuffer_InitEmpty(&secretKeyBuffer); ++ CKYBuffer publicDataBuffer; ++ CKYStatus status = CKYBuffer_InitFromData(&publicDataBuffer,otherPublicData, otherPublicLen); ++ ++ if (status != CKYSUCCESS) { ++ CKYBuffer_FreeData(&secretKeyBuffer); ++ throw PKCS11Exception(CKR_HOST_MEMORY); ++ } ++ ++ PKCS11Object *secret = NULL; ++ *phKey = 0; ++ ++ if( CKYBuffer_Size(result) == 0 ) { ++ try { ++ performECCKeyAgreement(deriveMech, &publicDataBuffer, &secretKeyBuffer, ++ keyNum, params.getKeySize()); ++ CK_OBJECT_HANDLE keyObjectHandle = generateUnusedObjectHandle(); ++ secret = createSecretKeyObject(keyObjectHandle, &secretKeyBuffer, pTemplate, ulAttributeCount); ++ } catch(PKCS11Exception& e) { ++ CKYBuffer_FreeData(&secretKeyBuffer); ++ CKYBuffer_FreeData(&publicDataBuffer); ++ throw(e); ++ } ++ } ++ ++ CKYBuffer_FreeData(&secretKeyBuffer); ++ CKYBuffer_FreeData(&publicDataBuffer); ++ ++ if ( secret ) { ++ ++ *phKey = secret->getHandle(); ++ delete secret; ++ } ++} ++ ++void ++Slot::performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, CKYBuffer *publicDataBuffer, ++ CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize) ++{ ++ if (!mECC) { ++ throw PKCS11Exception(CKR_FUNCTION_NOT_SUPPORTED); ++ } ++ ++ Transaction trans; ++ CKYStatus status = trans.begin(conn); ++ if( status != CKYSUCCESS ) handleConnectionError(); ++ ++ if (state & GOV_CARD) { ++ selectCACApplet(keyNum, true); ++ } else { ++ selectApplet(); ++ } ++ ++ CKYISOStatus result; ++ int loginAttempted = 0; ++ ++retry: ++ ++ if (state & PIV_CARD) { ++ status = PIVApplet_SignDecrypt(conn, pivKey, keySize/8, 1, publicDataBuffer, ++ secretKeyBuffer, &result); ++ } else if (state & CAC_CARD) { ++ status = CACApplet_SignDecrypt(conn, publicDataBuffer, secretKeyBuffer, &result); ++ } else { ++ status = CKYApplet_ComputeECCKeyAgreement(conn, keyNum, ++ publicDataBuffer , NULL, secretKeyBuffer, getNonce(), &result); ++ } ++ /* map the ISO not logged in code to the coolkey one */ ++ if ((result == CKYISO_CONDITION_NOT_SATISFIED) || ++ (result == CKYISO_SECURITY_NOT_SATISFIED)) { ++ result = (CKYStatus) CKYISO_UNAUTHORIZED; ++ } ++ ++ if (status != CKYSUCCESS) { ++ if ( status == CKYSCARDERR ) { ++ handleConnectionError(); ++ } ++ ++ if (result == CKYISO_DATA_INVALID) { ++ throw PKCS11Exception(CKR_DATA_INVALID); ++ } ++ if (!isVersion1Key && !loginAttempted && ++ (result == CKYISO_UNAUTHORIZED)) { ++ try { ++ if (state & GOV_CARD) { ++ attemptCACLogin(); ++ } else { ++ oldAttemptLogin(); ++ } ++ } catch(PKCS11Exception& ) { ++ throw PKCS11Exception(CKR_DEVICE_ERROR); ++ } ++ loginAttempted = true; ++ goto retry; ++ } ++ ++ throw PKCS11Exception( result == CKYISO_UNAUTHORIZED ? ++ CKR_USER_NOT_LOGGED_IN : CKR_DEVICE_ERROR); ++ ++ } ++} +diff -up ./src/coolkey/slot.h.piv-ecc ./src/coolkey/slot.h +--- ./src/coolkey/slot.h.piv-ecc 2013-09-08 15:50:33.098428320 -0700 ++++ ./src/coolkey/slot.h 2013-09-08 15:50:33.125428774 -0700 +@@ -211,24 +211,27 @@ class CryptOpState { + State state; + CKYByte keyNum; + CKYBuffer result; ++ PKCS11Object::KeyType keyType; + +- CryptOpState() : state(NOT_INITIALIZED), keyNum(0) ++ CryptOpState() : state(NOT_INITIALIZED), keyNum(0), keyType(PKCS11Object::unknown) + { CKYBuffer_InitEmpty(&result); } + CryptOpState(const CryptOpState &cpy) : +- state(cpy.state), keyNum(cpy.keyNum) { ++ state(cpy.state), keyNum(cpy.keyNum), keyType(cpy.keyType) { + CKYBuffer_InitFromCopy(&result, &cpy.result); + } + CryptOpState &operator=(const CryptOpState &cpy) { + state = cpy.state, + keyNum = cpy.keyNum; ++ keyType = cpy.keyType; + CKYBuffer_Replace(&result, 0, CKYBuffer_Data(&cpy.result), + CKYBuffer_Size(&cpy.result)); + return *this; + } + ~CryptOpState() { CKYBuffer_FreeData(&result); } +- void initialize(CKYByte keyNum) { ++ void initialize(CKYByte keyNum, PKCS11Object::KeyType theKeyType) { + state = IN_PROCESS; + this->keyNum = keyNum; ++ this->keyType = theKeyType; + CKYBuffer_Resize(&result, 0); + } + }; +@@ -258,6 +261,7 @@ class Session { + + CryptOpState signatureState; + CryptOpState decryptionState; ++ CryptOpState keyAgreementState; + }; + + typedef list SessionList; +@@ -267,12 +271,11 @@ typedef SessionList::const_iterator Sess + class CryptParams { + private: + unsigned int keySize; // in bits +- protected: +- unsigned int getKeySize() const { return keySize; } + public: + // set the actual key size obtained from the card + void setKeySize(unsigned int newKeySize) { keySize = newKeySize; } +- enum { DEFAULT_KEY_SIZE = 1024 }; ++ unsigned int getKeySize() const { return keySize; } ++ enum { DEFAULT_KEY_SIZE = 1024, ECC_DEFAULT_KEY_SIZE=256 }; + + + CryptParams(unsigned int keySize_) : keySize(keySize_) { } +@@ -304,12 +307,15 @@ class Slot { + ATR_MATCH = 0x04, + APPLET_SELECTABLE = 0x08, + APPLET_PERSONALIZED = 0x10, +- CAC_CARD = 0x20 ++ CAC_CARD = 0x20, ++ PIV_CARD = 0x40 + }; + enum { + NONCE_SIZE = 8 + }; + ++ static const SlotState GOV_CARD = (SlotState)(CAC_CARD|PIV_CARD); ++ + private: + Log *log; + char *readerName; +@@ -339,7 +345,10 @@ class Slot { + bool fullTokenName; + bool mCoolkey; + bool mOldCAC; +- ++ bool mCACLocalLogin; ++ int pivContainer; ++ int pivKey; ++ bool mECC; + //enum { RW_SESSION_HANDLE = 1, RO_SESSION_HANDLE = 2 }; + + #ifdef USE_SHMEM +@@ -386,6 +395,7 @@ class Slot { + const CKYBuffer *getATR(); + bool isLoggedIn(); + bool needLoggedIn(); ++ bool getPIVLoginType(); + void testNonce(); + + void addKeyObject(list& objectList, +@@ -395,6 +405,7 @@ class Slot { + const CKYBuffer *derCert, CK_OBJECT_HANDLE handle); + void addObject(list& objectList, + const ListObjectInfo& info, CK_OBJECT_HANDLE handle); ++ PKCS11Object *createSecretKeyObject(CK_OBJECT_HANDLE handle, CKYBuffer *secretKeyBuffer,CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount); + + void ensureValidSession(SessionHandleSuffix suffix); + +@@ -408,7 +419,7 @@ class Slot { + CKYStatus readCACCertificateAppend(CKYBuffer *cert, CKYSize nextSize); + + void selectApplet(); +- void selectCACApplet(CKYByte instance); ++ void selectCACApplet(CKYByte instance,bool do_disconnect); + void unloadObjects(); + void loadCACObjects(); + void loadCACCert(CKYByte instance); +@@ -432,12 +443,23 @@ class Slot { + CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, + CK_ULONG_PTR pulOutputLen, CryptParams& params); + +- void performRSAOp(CKYBuffer *out, const CKYBuffer *input, CKYByte keyNum, +- CKYByte direction); ++ void performRSAOp(CKYBuffer *out, const CKYBuffer *input, unsigned int keySize, ++ CKYByte keyNum, CKYByte direction); ++ ++ void signECC(SessionHandleSuffix suffix, CK_BYTE_PTR pInput, ++ CK_ULONG ulInputLen, CK_BYTE_PTR pOutput, ++ CK_ULONG_PTR pulOutputLen, CryptParams& params); ++ ++ void performECCSignature(CKYBuffer *out, const CKYBuffer *input, ++ unsigned int keySize, CKYByte keyNum); ++ void performECCKeyAgreement(CK_MECHANISM_TYPE deriveMech, ++ CKYBuffer *publicDataBuffer, ++ CKYBuffer *secretKeyBuffer, CKYByte keyNum, unsigned int keySize); + + void processComputeCrypt(CKYBuffer *result, const CKYAPDU *apdu); + + CKYByte objectHandleToKeyNum(CK_OBJECT_HANDLE hKey); ++ unsigned int calcECCKeySize(CKYByte keyNum); + Slot(const Slot &cpy) + #ifdef USE_SHMEM + : shmem(readerName) +@@ -469,7 +491,10 @@ class Slot { + } + + // actually get the size of a key in bits from the card +- unsigned int getKeySize(CKYByte keyNum); ++ unsigned int getRSAKeySize(CKYByte keyNum); ++ unsigned int getECCKeySize(CKYByte keyNum); ++ ++ PKCS11Object::KeyType getKeyTypeFromHandle(CK_OBJECT_HANDLE hKey); + + SessionHandleSuffix openSession(Session::Type type); + void closeSession(SessionHandleSuffix handleSuffix); +@@ -511,6 +536,16 @@ class Slot { + CK_ULONG len); + void generateRandom(SessionHandleSuffix suffix, CK_BYTE_PTR data, + CK_ULONG len); ++ ++ void derive(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey); ++ ++ void deriveECC(SessionHandleSuffix suffix, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey, CryptParams& params); ++ ++ bool getIsECC() { return mECC; } + }; + + class SlotList { +@@ -604,6 +639,10 @@ class SlotList { + void seedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen); + ++ void derive(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey); ++ + + }; + #endif +diff -up ./src/install/pk11install.c.piv-ecc ./src/install/pk11install.c +--- ./src/install/pk11install.c.piv-ecc 2007-02-06 11:40:36.000000000 -0800 ++++ ./src/install/pk11install.c 2013-09-08 15:50:33.126428790 -0700 +@@ -171,7 +171,7 @@ int dirListCount = sizeof(dirList)/sizeo + static void + usage(char *prog) + { +- fprintf(stderr,"usage: %s [-u][-v] [-p path] module\n", prog); ++ fprintf(stderr,"usage: %s [-u][-v][-s][-l] [-p path] module\n", prog); + return; + } + +@@ -181,9 +181,9 @@ usage(char *prog) + + #define CONFIG_TAG "configDir=" + int +-installPKCS11(char *dirPath, InstType type, char *module) ++installPKCS11(char *dirPath, char *dbType, InstType type, char *module) + { +- char *paramString = (char *)malloc(strlen(dirPath)+sizeof(CONFIG_TAG)+3); ++ char *paramString = (char *)malloc(strlen(dbType)+strlen(dirPath)+sizeof(CONFIG_TAG)+3); + char *cp; + char **rc; + +@@ -191,7 +191,7 @@ installPKCS11(char *dirPath, InstType ty + PINST_SET_ERROR(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } +- sprintf(paramString,CONFIG_TAG"\"%s\" ",dirPath); ++ sprintf(paramString,CONFIG_TAG"\"%s%s\" ",dbType,dirPath); + + /* translate all the \'s to /'s */ + for (cp=paramString; *cp; cp++) { +@@ -214,7 +214,7 @@ installPKCS11(char *dirPath, InstType ty + + + int +-installAllPKCS11(char *dirPath, char *search, char *tail, ++installAllPKCS11(char *dirPath, char *dbType, char *search, char *tail, + InstType type, char *module) + { + char *searchString; +@@ -282,9 +282,9 @@ installAllPKCS11(char *dirPath, char *se + + myPath=PINST_FULLPATH(tempPath,path); + if (tail) { +- installAllPKCS11(myPath, tail, NULL, type, module); ++ installAllPKCS11(myPath, dbType, tail, NULL, type, module); + } else { +- installPKCS11(myPath, type, module); ++ installPKCS11(myPath, dbType, type, module); + } + } while (PINST_NEXT(iter, fileData)); + free(tempPath); +@@ -309,6 +309,7 @@ int main(int argc, char **argv) + int i; + InstType type = Install; + char * path = NULL; ++ char *dbType = ""; + #ifdef WIN32 + BOOL brc; + HKEY regKey; +@@ -333,6 +334,12 @@ int main(int argc, char **argv) + case 'v': + verbose = 1; + break; ++ case 'l': ++ dbType = "dbm:"; ++ break; ++ case 's': ++ dbType = "sql:"; ++ break; + case 'p': + path = *argv++; + if (path == NULL) { +@@ -359,7 +366,7 @@ int main(int argc, char **argv) + } + + if (path) { +- installAllPKCS11(path, "", NULL, type, module); ++ installAllPKCS11(path, dbType, "", NULL, type, module); + return 0; + } + +@@ -444,7 +451,7 @@ int main(int argc, char **argv) + if (!dirPath) { + continue; + } +- installAllPKCS11(dirPath, dirList[i].search, dirList[i].tail, ++ installAllPKCS11(dirPath, dbType, dirList[i].search, dirList[i].tail, + type, module); + } + +diff -up ./src/libckyapplet/cky_applet.c.piv-ecc ./src/libckyapplet/cky_applet.c +--- ./src/libckyapplet/cky_applet.c.piv-ecc 2013-09-08 15:50:33.099428337 -0700 ++++ ./src/libckyapplet/cky_applet.c 2013-09-08 15:50:33.126428790 -0700 +@@ -103,6 +103,22 @@ CKYAppletFactory_ComputeCryptOneStep(CKY + } + + CKYStatus ++CKYAppletFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, const void *param) ++{ ++ const CKYAppletArgComputeECCSignature *ccs=(const CKYAppletArgComputeECCSignature *)param; ++ return CKYAPDUFactory_ComputeECCSignatureOneStep(apdu, ccs->keyNumber, ++ ccs->location, ccs->data, ccs->sig); ++} ++ ++CKYStatus ++CKYAppletFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, const void *param) ++{ ++ ++ const CKYAppletArgComputeECCKeyAgreement *ccs=(const CKYAppletArgComputeECCKeyAgreement *)param; ++ return CKYAPDUFactory_ComputeECCKeyAgreementOneStep(apdu, ccs->keyNumber, ccs->location, ccs->publicValue, ccs->secretKey); ++} ++ ++CKYStatus + CKYAppletFactory_CreatePIN(CKYAPDU *apdu, const void *param) + { + const CKYAppletArgCreatePIN *cps = (const CKYAppletArgCreatePIN *)param; +@@ -245,10 +261,25 @@ CACAppletFactory_SignDecryptFinal(CKYAPD + } + + CKYStatus ++PIVAppletFactory_SignDecrypt(CKYAPDU *apdu, const void *param) ++{ ++ const PIVAppletArgSignDecrypt *psd = (const PIVAppletArgSignDecrypt *)param; ++ return PIVAPDUFactory_SignDecrypt(apdu, psd->chain, psd->alg, psd->key, ++ psd->len, psd->buf); ++} ++ ++CKYStatus + CACAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) + { + const char *pin=(const char *)param; +- return CACAPDUFactory_VerifyPIN(apdu, pin); ++ return CACAPDUFactory_VerifyPIN(apdu, CAC_LOGIN_GLOBAL, pin); ++} ++ ++CKYStatus ++PIVAppletFactory_VerifyPIN(CKYAPDU *apdu, const void *param) ++{ ++ const char *pin=(const char *)param; ++ return CACAPDUFactory_VerifyPIN(apdu, PIV_LOGIN_LOCAL, pin); + } + + CKYStatus +@@ -259,6 +290,13 @@ CACAppletFactory_GetCertificate(CKYAPDU + } + + CKYStatus ++PIVAppletFactory_GetCertificate(CKYAPDU *apdu, const void *param) ++{ ++ CKYBuffer *tag =(CKYBuffer*)param; ++ return PIVAPDUFactory_GetData(apdu, tag, 0); ++} ++ ++CKYStatus + CACAppletFactory_ReadFile(CKYAPDU *apdu, const void *param) + { + const CACAppletArgReadFile *rfs = (const CACAppletArgReadFile *)param; +@@ -325,6 +363,7 @@ CKYAppletFill_AppendBuffer(const CKYBuff + CKYBuffer_Size(response) -2); + } + ++ + CKYStatus + CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param) + { +@@ -725,6 +764,32 @@ CKYApplet_ComputeCryptProcess(CKYCardCon + &ccd, nonce, 0, CKYAppletFill_Null, NULL, apduRC); + } + ++/* computeECCValue returns data in the form : ++ * len: short ++ * data: byte[len] ++ * This fill routine returns A buffer with a copy of data and a length of len */ ++static CKYStatus ++ckyAppletFill_ComputeECCValueFinal(const CKYBuffer *response, ++ CKYSize size, void *param) ++{ ++ CKYBuffer *cbuf = (CKYBuffer *)param; ++ CKYSize respSize = CKYBuffer_Size(response); ++ CKYSize dataLen; ++ ++ if (cbuf == 0) { ++ return CKYSUCCESS; /* app didn't want the result */ ++ } ++ /* data response code + length code */ ++ if (respSize < 4) { ++ return CKYAPDUFAIL; ++ } ++ dataLen = CKYBuffer_GetShort(response, 0); ++ if (dataLen > (respSize-4)) { ++ return CKYAPDUFAIL; ++ } ++ return CKYBuffer_Replace(cbuf, 0, CKYBuffer_Data(response)+2, dataLen); ++} ++ + /* computeCrypt returns data in the form : + * len: short + * data: byte[len] +@@ -872,6 +937,77 @@ fail: + return ret; + } + ++CKYStatus ++CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber, ++ const CKYBuffer *publicValue, CKYBuffer *sharedSecret, ++ CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret = CKYAPDUFAIL; ++ CKYAppletArgComputeECCKeyAgreement ccd; ++ CKYBuffer empty; ++ CKYISOStatus status; ++ /* Routine creates a sym key, should easily fit in one apdu */ ++ ++ CKYBuffer_InitEmpty(&empty); ++ ccd.keyNumber = keyNumber; ++ ccd.location = CKY_DL_APDU; ++ ++ if (!apduRC) ++ apduRC = &status; ++ ++ if (ccd.location == CKY_DL_APDU) { ++ ccd.publicValue = publicValue; ++ ccd.secretKey = sharedSecret; ++ ret = CKYApplet_HandleAPDU(conn, ++ CKYAppletFactory_ComputeECCKeyAgreementOneStep, &ccd, nonce, ++ CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal, ++ result, apduRC); ++ if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++ ++CKYStatus ++CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber, ++ const CKYBuffer *data, CKYBuffer *sig, ++ CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC) ++{ ++ int use2APDUs = 0; ++ int use_dl_object = 0; ++ short dataSize = 0; ++ CKYStatus ret = CKYAPDUFAIL; ++ CKYAppletArgComputeECCSignature ccd; ++ CKYBuffer empty; ++ CKYISOStatus status; ++ ++ CKYBuffer_InitEmpty(&empty); ++ ccd.keyNumber = keyNumber; ++ ++ /* Assume APDU, the signature can only get so big with our key sizes, ~ 130 for 521 bit key. */ ++ ccd.location = CKY_DL_APDU; ++ ++ if (!apduRC) ++ apduRC = &status; ++ ++ if (ccd.location == CKY_DL_APDU) { ++ ccd.data = data; ++ ccd.sig = sig; ++ ret = CKYApplet_HandleAPDU(conn, ++ CKYAppletFactory_ComputeECCSignatureOneStep, &ccd, nonce, ++ CKY_SIZE_UNKNOWN, ckyAppletFill_ComputeECCValueFinal, ++ result, apduRC); ++ if (ret == CKYAPDUFAIL && *apduRC == CKYISO_INCORRECT_P2) { ++ return ret; ++ } ++ ++ } ++ ++ return ret; ++} ++ + /* + * do a CAC Sign/Decrypt + */ +@@ -920,7 +1056,7 @@ done: + * do a CAC VerifyPIN + */ + CKYStatus +-CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, ++CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, int local, + CKYISOStatus *apduRC) + { + CKYStatus ret; +@@ -929,7 +1065,7 @@ CACApplet_VerifyPIN(CKYCardConnection *c + apduRC = &status; + } + +- ret = CKYApplet_HandleAPDU(conn, ++ ret = CKYApplet_HandleAPDU(conn, local ? PIVAppletFactory_VerifyPIN : + CACAppletFactory_VerifyPIN, pin, NULL, + 0, CKYAppletFill_Null, + NULL, apduRC); +@@ -942,6 +1078,7 @@ CACApplet_VerifyPIN(CKYCardConnection *c + return ret; + } + ++ + /* + * Get a CAC Certificate + */ +@@ -1078,6 +1215,278 @@ CACApplet_GetCertificateAppend(CKYCardCo + return ret; + } + ++/* Select the PIV applet */ ++static CKYByte pivAid[] = {0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, ++ 0x10, 0x00}; ++CKYStatus ++PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYBuffer PIV_Applet_AID,return_AID; ++ ++ CKYBuffer_InitEmpty(&return_AID); ++ CKYBuffer_InitFromData(&PIV_Applet_AID, pivAid, sizeof(pivAid)); ++ ret = CKYApplet_HandleAPDU(conn, CKYAppletFactory_SelectFile, ++ &PIV_Applet_AID, ++ NULL, CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, ++ &return_AID, apduRC); ++ /* Some cards return OK, but don't switch to our applet */ ++ /* PIV has a well defined return for it's select, check to see if we have ++ * a PIV card here */ ++ if (CKYBuffer_GetChar(&return_AID,0) != 0x61) { ++ /* not an application property template, so not a PIV. We could ++ * check that the aid tag (0x4f) and theallocation authority tag (0x79) ++ * are present, but what we are really avoiding is broken cards that ++ * lie about being able to switch to a particular applet, so the first ++ * tag should be sufficient */ ++ ret = CKYAPDUFAIL; /* what we should have gotten */ ++ } ++ CKYBuffer_FreeData(&PIV_Applet_AID); ++ CKYBuffer_FreeData(&return_AID); ++ return ret; ++} ++ ++/* ++ * Get a PIV Certificate ++ */ ++CKYStatus ++PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, int tag, ++ CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYISOStatus status; ++ CKYBuffer tagBuf; ++ ++ CKYBuffer_InitEmpty(&tagBuf); ++ CKYBuffer_Reserve(&tagBuf,4); /* can be up to 4 bytes */ ++ ++ CKYBuffer_Resize(cert,0); ++ if (apduRC == NULL) { ++ apduRC = &status; ++ } ++ if (tag >= 0x01000000) { ++ ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 24) & 0xff); ++ if (ret != CKYSUCCESS) { goto loser; } ++ } ++ if (tag >= 0x010000) { ++ ret = CKYBuffer_AppendChar(&tagBuf, (tag >> 16) & 0xff); ++ if (ret != CKYSUCCESS) { goto loser; } ++ } ++ if (tag >= 0x0100) { ++ ret =CKYBuffer_AppendChar(&tagBuf, (tag >> 8) & 0xff); ++ if (ret != CKYSUCCESS) { goto loser; } ++ } ++ ret = CKYBuffer_AppendChar(&tagBuf, tag & 0xff); ++ if (ret != CKYSUCCESS) { goto loser; } ++ ++ ++ ret = CKYApplet_HandleAPDU(conn, ++ PIVAppletFactory_GetCertificate, &tagBuf, NULL, ++ CKY_SIZE_UNKNOWN, CKYAppletFill_AppendBuffer, cert, ++ apduRC); ++loser: ++ CKYBuffer_FreeData(&tagBuf); ++ ++ return ret; ++} ++ ++ ++/* ++ * record the next ber tag and length. NOTE: this is a state machine. ++ * we can handle the case where we are passed the data just one byte ++ * at a time. ++ */ ++static CKYStatus ++pivUnwrap(const CKYBuffer *buf, CKYOffset *offset, ++ CKYSize *dataSize, PIVUnwrapState *unwrap) ++{ ++ if (unwrap->tag == 0) { ++ unwrap->tag = CKYBuffer_GetChar(buf, *offset); ++ if (unwrap->tag == 0) unwrap->tag = 0xff; ++ (*offset)++; ++ (*dataSize)--; ++ } ++ if (*dataSize == 0) { ++ return CKYSUCCESS; ++ } ++ if (unwrap->length_bytes != 0) { ++ int len; ++ if (unwrap->length_bytes == -1) { ++ len = CKYBuffer_GetChar(buf, *offset); ++ unwrap->length_bytes = 0; ++ unwrap->length = len; ++ (*offset)++; ++ (*dataSize)--; ++ if (len & 0x80) { ++ unwrap->length = 0; ++ unwrap->length_bytes = len & 0x7f; ++ } ++ } ++ while ((*dataSize != 0) && (unwrap->length_bytes != 0)) { ++ len = CKYBuffer_GetChar(buf, *offset); ++ (*offset) ++; ++ (*dataSize) --; ++ unwrap->length = ((unwrap->length) << 8 | len); ++ unwrap->length_bytes--; ++ } ++ } ++ return CKYSUCCESS; ++} ++ ++/* ++ * Remove the BER wrapping first... ++ */ ++static CKYStatus ++pivAppletFill_AppendUnwrapBuffer(const CKYBuffer *response, ++ CKYSize size, void *param) ++{ ++ PIVAppletRespSignDecrypt *prsd = (PIVAppletRespSignDecrypt *)param; ++ CKYBuffer *buf = prsd->buf; ++ CKYSize dataSize = CKYBuffer_Size(response); ++ CKYOffset offset = 0; ++ ++ if (dataSize <= 2) { ++ return CKYSUCCESS; ++ } ++ dataSize -= 2; ++ /* remove the first tag */ ++ (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_1); ++ if (dataSize == 0) { ++ return CKYSUCCESS; ++ } ++ /* remove the second tag */ ++ (void) pivUnwrap(response, &offset, &dataSize, &prsd->tag_2); ++ if (dataSize == 0) { ++ return CKYSUCCESS; ++ } ++ /* the rest is real data */ ++ return CKYBuffer_AppendData(buf, CKYBuffer_Data(response) + offset, ++ dataSize); ++} ++ ++static CKYStatus ++piv_wrapEncodeLength(CKYBuffer *buf, int length, int ber_len) ++{ ++ if (ber_len== 1) { ++ CKYBuffer_AppendChar(buf,length); ++ } else { ++ ber_len--; ++ CKYBuffer_AppendChar(buf,0x80+ber_len); ++ while(ber_len--) { ++ CKYBuffer_AppendChar(buf,(length >> (8*ber_len)) & 0xff); ++ } ++ } ++ return CKYSUCCESS; ++} ++/* ++ * do a PIV Sign/Decrypt ++ */ ++CKYStatus ++PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, unsigned int keySize, int derive, ++ const CKYBuffer *data, CKYBuffer *result, CKYISOStatus *apduRC) ++{ ++ CKYStatus ret; ++ CKYSize dataSize = CKYBuffer_Size(data); ++ CKYSize outputSize = keySize; ++ CKYOffset offset = 0; ++ CKYBuffer tmp; ++ CKYByte alg; ++ int ber_len_1; ++ int ber_len_2; ++ int length; ++ PIVAppletArgSignDecrypt pasd; ++ PIVAppletRespSignDecrypt prsd; ++ ++ /* PIV only defines RSA 1024 and 2048, ECC 256 and ECC 384!!! */ ++ if (keySize == 128) { /* 1024 bit == 128 bytes */ ++ ber_len_2 = 2; ++ ber_len_1 = 2; ++ alg = 0x6; ++ } else if (keySize == 256) { /* 2048 bits == 256 bytes */ ++ ber_len_2 = 3; ++ ber_len_1 = 3; ++ alg = 0x7; ++ } else if (keySize == 32) { /* 256 bits = 32 bytes */ ++ ber_len_2 = 1; ++ ber_len_1 = 1; ++ alg = 0x11; ++ if (!derive) outputSize = keySize*2; ++ } else if (keySize == 48) { /* 384 bits = 48 bytes */ ++ ber_len_2 = 1; ++ ber_len_1 = 1; ++ alg = 0x14; ++ if (!derive) outputSize = keySize*2; ++ } else { ++ return CKYINVALIDARGS; ++ } ++ ++ CKYBuffer_InitEmpty(&tmp); ++ ret = CKYBuffer_Reserve(&tmp, CKY_MAX_WRITE_CHUNK_SIZE); ++ if (ret != CKYSUCCESS) { ++ goto done; ++ } ++ CKYBuffer_AppendChar(&tmp,0x7c); ++ piv_wrapEncodeLength(&tmp,dataSize + ber_len_2 + 3,ber_len_1); ++ CKYBuffer_AppendChar(&tmp,0x82); ++ CKYBuffer_AppendChar(&tmp,0x0); ++ CKYBuffer_AppendChar(&tmp, derive ? 0x85 : 0x81); ++ piv_wrapEncodeLength(&tmp,dataSize,ber_len_2); ++ ++ /* now length == header length from here to the end*/ ++ length = CKYBuffer_Size(&tmp); ++ ++ if (length + dataSize > CKY_MAX_WRITE_CHUNK_SIZE) { ++ CKYBuffer_AppendBuffer(&tmp, data, 0, CKY_MAX_WRITE_CHUNK_SIZE-length); ++ } else { ++ CKYBuffer_AppendBuffer(&tmp, data, 0, dataSize); ++ } ++ ++ prsd.tag_1.tag = 0; ++ prsd.tag_1.length_bytes = -1; ++ prsd.tag_1.length = 0; ++ prsd.tag_2.tag = 0; ++ prsd.tag_2.length_bytes = -1; ++ prsd.tag_2.length = 0; ++ prsd.buf = result; ++ pasd.alg = alg; ++ pasd.key = key; ++ pasd.buf = &tmp; ++ ++ CKYBuffer_Resize(result,0); ++ for(offset = -length; (dataSize-offset) > CKY_MAX_WRITE_CHUNK_SIZE; ) { ++ pasd.chain = 1; ++ pasd.len = 0; ++ ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, ++ &pasd, NULL, CKY_SIZE_UNKNOWN, ++ pivAppletFill_AppendUnwrapBuffer, ++ &prsd, apduRC); ++ if (ret != CKYSUCCESS) { ++ goto done; ++ } ++ CKYBuffer_Resize(&tmp,0); ++ /* increment before we append the next tmp buffer */ ++ offset += CKY_MAX_WRITE_CHUNK_SIZE; ++ CKYBuffer_AppendBuffer(&tmp, data, offset, ++ MIN(dataSize-offset, CKY_MAX_WRITE_CHUNK_SIZE)); ++ } ++ ++ pasd.chain = 0; ++ pasd.len = outputSize; ++ ++ ret = CKYApplet_HandleAPDU(conn, PIVAppletFactory_SignDecrypt, ++ &pasd, NULL, CKY_SIZE_UNKNOWN, ++ pivAppletFill_AppendUnwrapBuffer, ++ &prsd, apduRC); ++ ++ if ((ret == CKYSUCCESS) && (CKYBuffer_Size(result) != outputSize)) { ++ /* RSA returns the same data size as input, didn't happen, so ++ * something is wrong. */ ++ } ++ ++done: ++ CKYBuffer_FreeData(&tmp); ++ return ret; ++} + + /* + * PIN cluster +diff -up ./src/libckyapplet/cky_applet.h.piv-ecc ./src/libckyapplet/cky_applet.h +--- ./src/libckyapplet/cky_applet.h.piv-ecc 2013-09-08 15:50:33.099428337 -0700 ++++ ./src/libckyapplet/cky_applet.h 2013-09-08 15:50:33.127428807 -0700 +@@ -43,6 +43,8 @@ typedef unsigned short CKYISOStatus; /* + #define CKYISO_MORE_MASK 0xff00 /* More data mask */ + #define CKYISO_MORE 0x6300 /* More data available */ + #define CKYISO_DATA_INVALID 0x6984 ++#define CKYISO_CONDITION_NOT_SATISFIED 0x6985 /* AKA not logged in (CAC)*/ ++#define CKYISO_SECURITY_NOT_SATISFIED 0x6982 /* AKA not logged in (PIV)*/ + /* Applet Defined Return codes */ + #define CKYISO_NO_MEMORY_LEFT 0x9c01 /* There have been memory + * problems on the card */ +@@ -78,6 +80,7 @@ typedef unsigned short CKYISOStatus; /* + + #define CAC_TAG_CARDURL 0xf3 + #define CAC_TAG_CERTIFICATE 0x70 ++#define CAC_TAG_CERTINFO 0x71 + #define CAC_TLV_APP_PKI 0x04 + + /* +@@ -218,12 +221,47 @@ typedef struct _CKYAppletArgComputeCrypt + const CKYBuffer *sig; + } CKYAppletArgComputeCrypt; + ++typedef struct _CKYAppletArgComputeECCSignature { ++ CKYByte keyNumber; ++ CKYByte location; ++ const CKYBuffer *data; ++ const CKYBuffer *sig; ++} CKYAppletArgComputeECCSignature; ++ ++typedef struct _CKYAppletArgComputeECCKeyAgreement { ++ CKYByte keyNumber; ++ CKYByte location; ++ const CKYBuffer *publicValue; ++ const CKYBuffer *secretKey; ++} CKYAppletArgComputeECCKeyAgreement; ++ ++ + typedef struct _CACAppletArgReadFile { + CKYByte type; + CKYByte count; + unsigned short offset; + } CACAppletArgReadFile; + ++typedef struct _PIVAppletArgSignDecrypt { ++ CKYByte alg; ++ CKYByte key; ++ CKYByte chain; ++ CKYSize len; ++ CKYBuffer *buf; ++} PIVAppletArgSignDecrypt; ++ ++typedef struct _pivUnwrapState { ++ CKYByte tag; ++ CKYByte length; ++ int length_bytes; ++} PIVUnwrapState; ++ ++typedef struct _PIVAppletRespSignDecrypt { ++ PIVUnwrapState tag_1; ++ PIVUnwrapState tag_2; ++ CKYBuffer *buf; ++} PIVAppletRespSignDecrypt; ++ + /* fills in an APDU from a structure -- form of all the generic factories*/ + typedef CKYStatus (*CKYAppletFactory)(CKYAPDU *apdu, const void *param); + /* fills in an a structure from a response -- form of all the fill structures*/ +@@ -335,7 +373,6 @@ CKYStatus CKYAppletFill_AppendBuffer(con + /* Single value fills: Byte, Short, & Long */ + /* param == CKYByte * */ + CKYStatus CKYAppletFill_Byte(const CKYBuffer *response, CKYSize size, void *param); +-/* param == CKYByte * */ + CKYStatus CKYAppletFill_Short(const CKYBuffer *response, CKYSize size, void *param); + CKYStatus CKYAppletFill_Long(const CKYBuffer *response, CKYSize size, void *param); + +@@ -361,7 +398,7 @@ CKYBool CKYApplet_VerifyResponse(const C + * Sends the ADPU to the card through the connection conn. + * Checks that the response was valid (returning the responce code in apduRC. + * Formats the response data into fillArg with fillFunc +- * nonce and apduRC can be NULL (no nonce is added, not status returned ++ * nonce and apduRC can be NULL (no nonce is added, no status returned + * legal values for afArg are depened on afFunc. + * legal values for fillArg are depened on fillFunc. + */ +@@ -377,7 +414,7 @@ CKYStatus CKYApplet_HandleAPDU(CKYCardCo + * into function calls, with input and output parameters. + * The application is still responsible for + * 1) creating a connection to the card, +- * 2) Getting a tranaction long, then ++ * 2) Getting a transaction lock, then + * 3) selecting the appropriate applet (or Card manager). + * Except for those calls that have been noted, the appropriate applet + * is the CoolKey applet. +@@ -490,9 +527,18 @@ CKYStatus CACApplet_GetCertificateAppend + CKYISOStatus *apduRC); + + /*CKYStatus CACApplet_GetProperties(); */ +-CKYStatus CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, +- CKYISOStatus *apduRC); ++CKYStatus CACApplet_VerifyPIN(CKYCardConnection *conn, const char *pin, ++ int local, CKYISOStatus *apduRC); + ++/* Select a PIV applet */ ++CKYStatus PIVApplet_Select(CKYCardConnection *conn, CKYISOStatus *apduRC); ++ ++CKYStatus PIVApplet_GetCertificate(CKYCardConnection *conn, CKYBuffer *cert, ++ int tag, CKYISOStatus *apduRC); ++CKYStatus PIVApplet_SignDecrypt(CKYCardConnection *conn, CKYByte key, ++ unsigned int keySize, int derive, ++ const CKYBuffer *data, CKYBuffer *result, ++ CKYISOStatus *apduRC); + /* + * There are 3 read commands: + * +@@ -553,6 +599,18 @@ CKYStatus CKYApplet_GetIssuerInfo(CKYCar + CKYStatus CKYApplet_GetBuiltinACL(CKYCardConnection *conn, + CKYAppletRespGetBuiltinACL *gba, CKYISOStatus *apduRC); + ++/** ECC commands ++ * * */ ++ ++CKYStatus CKYApplet_ComputeECCSignature(CKYCardConnection *conn, CKYByte keyNumber, ++ const CKYBuffer *data, CKYBuffer *sig, ++ CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC); ++ ++CKYStatus ++CKYApplet_ComputeECCKeyAgreement(CKYCardConnection *conn, CKYByte keyNumber, ++ const CKYBuffer *publicValue, CKYBuffer *sharedSecret, ++ CKYBuffer *result, const CKYBuffer *nonce, CKYISOStatus *apduRC); ++ + + /* + * deprecates 0.x functions +diff -up ./src/libckyapplet/cky_base.c.piv-ecc ./src/libckyapplet/cky_base.c +--- ./src/libckyapplet/cky_base.c.piv-ecc 2013-09-08 15:50:33.100428354 -0700 ++++ ./src/libckyapplet/cky_base.c 2013-09-08 15:50:33.128428824 -0700 +@@ -41,6 +41,7 @@ ckyBuffer_initBuffer(CKYBuffer *buf) + buf->data = NULL; + buf->size = 0; + buf->len = 0; ++ buf->reserved = NULL; /* make coverity happy */ + } + + /* +@@ -573,6 +574,7 @@ CKYAPDU_Init(CKYAPDU *apdu) + assert(sizeof(CKYAPDU) == sizeof(CKYAPDUPublic)); + #endif + ckyBuffer_initBuffer(&apdu->apduBuf); ++ apdu->reserved = NULL; + return CKYBuffer_Resize(&apdu->apduBuf, CKYAPDU_MIN_LEN); + } + +@@ -583,6 +585,7 @@ CKYAPDU_InitFromData(CKYAPDU *apdu, cons + assert(sizeof(CKYAPDU) == sizeof(CKYAPDUPublic)); + #endif + ckyBuffer_initBuffer(&apdu->apduBuf); ++ apdu->reserved = NULL; + if (len > CKYAPDU_MAX_DATA_LEN) { + return CKYDATATOOLONG; + } +@@ -710,8 +713,15 @@ CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKY + return CKYBuffer_SetChar(&apdu->apduBuf, CKY_LE_OFFSET, recvlen); + } + ++CKYStatus ++CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen) ++{ ++ return CKYBuffer_AppendChar(&apdu->apduBuf, recvlen); ++} ++ ++ + void +-CKY_SetName(char *p) ++CKY_SetName(const char *p) + { + } + +diff -up ./src/libckyapplet/cky_base.h.piv-ecc ./src/libckyapplet/cky_base.h +--- ./src/libckyapplet/cky_base.h.piv-ecc 2013-09-08 15:50:33.100428354 -0700 ++++ ./src/libckyapplet/cky_base.h 2013-09-08 15:50:33.128428824 -0700 +@@ -278,9 +278,10 @@ CKYStatus CKYAPDU_AppendSendDataBuffer(C + /* set Le in the APDU header to the amount of bytes expected to be + * returned. */ + CKYStatus CKYAPDU_SetReceiveLen(CKYAPDU *apdu, CKYByte recvlen); ++CKYStatus CKYAPDU_AppendReceiveLen(CKYAPDU *apdu, CKYByte recvlen); + + /* set the parent loadmodule name */ +-void CKY_SetName(char *name); ++void CKY_SetName(const char *name); + + CKY_END_PROTOS + +diff -up ./src/libckyapplet/cky_card.c.piv-ecc ./src/libckyapplet/cky_card.c +--- ./src/libckyapplet/cky_card.c.piv-ecc 2013-09-08 15:50:33.109428505 -0700 ++++ ./src/libckyapplet/cky_card.c 2013-09-08 15:50:33.128428824 -0700 +@@ -27,6 +27,7 @@ + + #ifndef WINAPI + #define WINAPI ++typedef SCARD_READERSTATE *LPSCARD_READERSTATE; + #endif + + #ifndef SCARD_E_NO_READERS_AVAILABLE +@@ -843,6 +844,11 @@ CKYCardContext_WaitForStatusChange(CKYCa + rv = ctx->scard->SCardGetStatusChange(ctx->context, timeout, + readers, readerCount); + if (rv != SCARD_S_SUCCESS) { ++ if ((rv == SCARD_E_NO_SERVICE) || (rv == SCARD_E_SERVICE_STOPPED)) { ++ /* if we were stopped, don't reuse the old context, ++ * pcsc-lite hangs */ ++ ckyCardContext_release(ctx); ++ } + ctx->lastError = rv; + return CKYSCARDERR; + } +@@ -1071,25 +1077,39 @@ CKYCardConnection_ExchangeAPDU(CKYCardCo + CKYBuffer *response) + { + CKYStatus ret; ++ CKYBuffer getResponse; ++ CKYSize size = 0; + + ret = CKYCardConnection_TransmitAPDU(conn, apdu, response); + if (ret != CKYSUCCESS) { + return ret; + } ++ CKYBuffer_InitEmpty(&getResponse); + +- if (CKYBuffer_Size(response) == 2 && CKYBuffer_GetChar(response,0) == 0x61) { ++ /* automatically handle the response data protocol */ ++ while ((ret == CKYSUCCESS) && ++ (size = CKYBuffer_Size(response)) >= 2 && ++ (CKYBuffer_GetChar(response,size-2) == 0x61)) { + /* get the response */ + CKYAPDU getResponseAPDU; + ++ CKYBuffer_Zero(&getResponse); + CKYAPDU_Init(&getResponseAPDU); + CKYAPDU_SetCLA(&getResponseAPDU, 0x00); + CKYAPDU_SetINS(&getResponseAPDU, 0xc0); + CKYAPDU_SetP1(&getResponseAPDU, 0x00); + CKYAPDU_SetP2(&getResponseAPDU, 0x00); +- CKYAPDU_SetReceiveLen(&getResponseAPDU, CKYBuffer_GetChar(response,1)); +- ret = CKYCardConnection_TransmitAPDU(conn, &getResponseAPDU, response); ++ CKYAPDU_SetReceiveLen(&getResponseAPDU, ++ CKYBuffer_GetChar(response,size-1)); ++ ret = CKYCardConnection_TransmitAPDU(conn, &getResponseAPDU, ++ &getResponse); + CKYAPDU_FreeData(&getResponseAPDU); ++ if ((ret == CKYSUCCESS) && (CKYBuffer_Size(&getResponse) >= 2)) { ++ CKYBuffer_Resize(response, size-2); ++ CKYBuffer_AppendCopy(response,&getResponse); ++ } + } ++ CKYBuffer_FreeData(&getResponse); + return ret; + } + +diff -up ./src/libckyapplet/cky_card.h.piv-ecc ./src/libckyapplet/cky_card.h +diff -up ./src/libckyapplet/cky_factory.c.piv-ecc ./src/libckyapplet/cky_factory.c +--- ./src/libckyapplet/cky_factory.c.piv-ecc 2013-09-08 15:50:33.101428370 -0700 ++++ ./src/libckyapplet/cky_factory.c 2013-09-08 15:50:33.129428841 -0700 +@@ -62,7 +62,6 @@ CKYAPDUFactory_GetCPLCData(CKYAPDU *apdu + CKYAPDU_SetP2(apdu, 0x7f); + return CKYAPDU_SetReceiveLen(apdu, CKY_SIZE_GET_CPLCDATA); + } +- + /* + * applet commands must be issued with the appplet selected. + */ +@@ -184,6 +183,49 @@ fail: + } + + CKYStatus ++CKYAPDUFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, CKYByte keyNumber, ++ CKYByte location, ++ const CKYBuffer *publicData, const CKYBuffer *secretKey) ++{ ++ CKYStatus ret = CKYINVALIDARGS; ++ CKYSize len; ++ CKYBuffer buf; ++ ++ if (!publicData) ++ return ret; ++ ++ if (!(len = CKYBuffer_Size(publicData))) ++ return ret; ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); ++ CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_ECC_KEY_AGREEMENT); ++ CKYAPDU_SetP1(apdu, keyNumber); ++ CKYAPDU_SetP2(apdu, CKY_CIPHER_ONE_STEP); ++ ++ CKYBuffer_InitEmpty(&buf); ++ ++ ret = CKYBuffer_Reserve(&buf, 3); ++ ++ if (ret == CKYSUCCESS) ++ ret = CKYBuffer_AppendChar(&buf, location); ++ if (ret == CKYSUCCESS) ++ ret = CKYBuffer_AppendShort(&buf, (unsigned short)len); ++ if (ret == CKYSUCCESS) ++ ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); ++ if (ret == CKYSUCCESS) ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, publicData); ++ if (ret == CKYSUCCESS && secretKey && 0 < (len = CKYBuffer_Size(secretKey))) { ++ CKYBuffer_Resize(&buf,2); ++ CKYBuffer_SetShort(&buf, 0, (unsigned short)len); ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, &buf); ++ if (ret == CKYSUCCESS) ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, secretKey); ++ } ++ CKYBuffer_FreeData(&buf); ++ return ret; ++} ++ ++CKYStatus + CKYAPDUFactory_ComputeCryptOneStep(CKYAPDU *apdu, CKYByte keyNumber, CKYByte mode, + CKYByte direction, CKYByte location, + const CKYBuffer *idata, const CKYBuffer *sig) +@@ -386,6 +428,49 @@ fail: + } + + CKYStatus ++CKYAPDUFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, CKYByte keyNumber, ++ CKYByte location, ++ const CKYBuffer *idata, const CKYBuffer *sig) ++{ ++ CKYStatus ret = CKYINVALIDARGS; ++ CKYSize len; ++ CKYBuffer buf; ++ ++ if (!idata) ++ return ret; ++ ++ if (!(len = CKYBuffer_Size(idata)) && location != CKY_DL_OBJECT) ++ return ret; ++ ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_COOLKEY); ++ CKYAPDU_SetINS(apdu, CKY_INS_COMPUTE_ECC_SIGNATURE); ++ CKYAPDU_SetP1(apdu, keyNumber); ++ CKYAPDU_SetP2(apdu, CKY_CIPHER_ONE_STEP); ++ ++ CKYBuffer_InitEmpty(&buf); ++ ++ ret = CKYBuffer_Reserve(&buf, 3); ++ ++ if (ret == CKYSUCCESS) ++ ret = CKYBuffer_AppendChar(&buf, location); ++ if (ret == CKYSUCCESS) ++ ret = CKYBuffer_AppendShort(&buf, (unsigned short)len); ++ if (ret == CKYSUCCESS) ++ ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); ++ if (ret == CKYSUCCESS) ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, idata); ++ if (ret == CKYSUCCESS && sig && 0 < (len = CKYBuffer_Size(sig))) { ++ CKYBuffer_Resize(&buf,2); ++ CKYBuffer_SetShort(&buf, 0, (unsigned short)len); ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, &buf); ++ if (ret == CKYSUCCESS) ++ ret = CKYAPDU_AppendSendDataBuffer(apdu, sig); ++ } ++ CKYBuffer_FreeData(&buf); ++ return ret; ++} ++ ++CKYStatus + CKYAPDUFactory_ReadObject(CKYAPDU *apdu, unsigned long objectID, + CKYOffset offset, CKYByte size) + { +@@ -622,7 +707,6 @@ fail: + CKYBuffer_FreeData(&buf); + return ret; + } +- + CKYStatus + CACAPDUFactory_GetProperties(CKYAPDU *apdu) + { +@@ -634,7 +718,7 @@ CACAPDUFactory_GetProperties(CKYAPDU *ap + } + + CKYStatus +-CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin) ++CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, const char *pin) + { + CKYStatus ret; + CKYSize size; +@@ -642,7 +726,7 @@ CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, + CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); + CKYAPDU_SetINS(apdu, CAC_INS_VERIFY_PIN); + CKYAPDU_SetP1(apdu, 0x00); +- CKYAPDU_SetP2(apdu, 0x00); ++ CKYAPDU_SetP2(apdu, keyRef); + /* no pin, send an empty buffer */ + if (!pin) { + return CKYAPDU_SetReceiveLen(apdu, 0); +@@ -663,3 +747,63 @@ CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, + return ret; + + } ++ ++CKYStatus ++PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, ++ CKYByte key, int len, const CKYBuffer *data) ++{ ++ CKYStatus ret; ++ CKYAPDU_SetCLA(apdu, chain ? CKY_CLASS_ISO7816_CHAIN : ++ CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, PIV_INS_GEN_AUTHENTICATE); ++ CKYAPDU_SetP1(apdu, alg); ++ CKYAPDU_SetP2(apdu, key); ++ ret = CKYAPDU_SetSendDataBuffer(apdu, data); ++ if (ret == CKYSUCCESS && chain == 0 && len != 0) { ++ if (len >= 256) len = 0; ++ ret = CKYAPDU_AppendReceiveLen(apdu, len); ++ } ++ return ret; ++} ++ ++CKYStatus ++PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, CKYByte count) ++{ ++ CKYStatus ret; ++ CKYBuffer buf; ++ CKYByte objectSize; ++ ++ CKYBuffer_InitEmpty(&buf); ++ CKYAPDU_SetCLA(apdu, CKY_CLASS_ISO7816); ++ CKYAPDU_SetINS(apdu, 0xcb); ++ CKYAPDU_SetP1(apdu, 0x3f); ++ CKYAPDU_SetP2(apdu, 0xff); ++ ++ objectSize = CKYBuffer_Size(object); ++ ++ ret = CKYBuffer_Reserve(&buf, 2+objectSize); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendChar(&buf, 0x5c); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendChar(&buf, objectSize); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYBuffer_AppendCopy(&buf, object); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYAPDU_SetSendDataBuffer(apdu, &buf); ++ if (ret != CKYSUCCESS) { ++ goto fail; ++ } ++ ret = CKYAPDU_AppendReceiveLen(apdu, count); ++fail: ++ CKYBuffer_FreeData(&buf); ++ return ret; ++} ++ +diff -up ./src/libckyapplet/cky_factory.h.piv-ecc ./src/libckyapplet/cky_factory.h +--- ./src/libckyapplet/cky_factory.h.piv-ecc 2013-09-08 15:50:33.101428370 -0700 ++++ ./src/libckyapplet/cky_factory.h 2013-09-08 15:50:33.130428858 -0700 +@@ -25,10 +25,11 @@ + /* + * Various Class bytes + */ +-#define CKY_CLASS_ISO7816 0x00 ++#define CKY_CLASS_ISO7816 0x00 ++#define CKY_CLASS_ISO7816_CHAIN 0x10 + #define CKY_CLASS_GLOBAL_PLATFORM 0x80 +-#define CKY_CLASS_SECURE 0x84 +-#define CKY_CLASS_COOLKEY 0xb0 ++#define CKY_CLASS_SECURE 0x84 ++#define CKY_CLASS_COOLKEY 0xb0 + + /* + * Applet Instruction Bytes +@@ -66,6 +67,8 @@ + /* nonce validated & Secure Channel */ + #define CKY_INS_IMPORT_KEY 0x32 + #define CKY_INS_COMPUTE_CRYPT 0x36 ++#define CKY_INS_COMPUTE_ECC_SIGNATURE 0x37 ++#define CKY_INS_COMPUTE_ECC_KEY_AGREEMENT 0x38 + #define CKY_INS_CREATE_PIN 0x40 + #define CKY_INS_CHANGE_PIN 0x44 + #define CKY_INS_CREATE_OBJ 0x5A +@@ -91,6 +94,12 @@ + #define CAC_SIZE_GET_PROPERTIES 48 + #define CAC_P1_STEP 0x80 + #define CAC_P1_FINAL 0x00 ++#define CAC_LOGIN_GLOBAL 0x00 ++ ++/* PIV */ ++#define PIV_LOGIN_LOCAL 0x80 ++#define PIV_LOGIN_GLOBAL CAC_LOGIN_GLOBAL ++#define PIV_INS_GEN_AUTHENTICATE 0x87 + + /* + * Fixed return sized from various commands +@@ -123,6 +132,7 @@ + #define CKY_DES_ECB_NOPAD 0x21 + + /* operations (Cipher Direction) */ ++#define CKY_DIR_NONE 0x00 + #define CKY_DIR_SIGN 0x01 + #define CKY_DIR_VERIFY 0x02 + #define CKY_DIR_ENCRYPT 0x03 +@@ -187,6 +197,12 @@ CKYStatus CKYAPDUFactory_ComputeCryptFin + CKYStatus CKYAPDUFactory_ComputeCryptOneStep(CKYAPDU *apdu, CKYByte keyNumber, + CKYByte mode, CKYByte direction, CKYByte location, + const CKYBuffer *data, const CKYBuffer *sig); ++CKYStatus CKYAPDUFactory_ComputeECCSignatureOneStep(CKYAPDU *apdu, CKYByte keyNumber, ++ CKYByte location, ++ const CKYBuffer *data, const CKYBuffer *sig); ++CKYStatus CKYAPDUFactory_ComputeECCKeyAgreementOneStep(CKYAPDU *apdu, CKYByte keyNumber, ++ CKYByte location, ++ const CKYBuffer *publicData, const CKYBuffer *secretKey); + CKYStatus CKYAPDUFactory_CreatePIN(CKYAPDU *apdu, CKYByte pinNumber, + CKYByte maxAttempts, const char *pinValue); + CKYStatus CKYAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte pinNumber, +@@ -218,11 +234,16 @@ CKYStatus CKYAPDUFactory_GetBuiltinACL(C + + CKYStatus CACAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte type, + const CKYBuffer *data); +-CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, const char *pin); ++CKYStatus CACAPDUFactory_VerifyPIN(CKYAPDU *apdu, CKYByte keyRef, ++ const char *pin); + CKYStatus CACAPDUFactory_GetCertificate(CKYAPDU *apdu, CKYSize size); + CKYStatus CACAPDUFactory_ReadFile(CKYAPDU *apdu, unsigned short offset, + CKYByte type, CKYByte count); + CKYStatus CACAPDUFactory_GetProperties(CKYAPDU *apdu); ++CKYStatus PIVAPDUFactory_GetData(CKYAPDU *apdu, const CKYBuffer *object, ++ CKYByte count); ++CKYStatus PIVAPDUFactory_SignDecrypt(CKYAPDU *apdu, CKYByte chain, CKYByte alg, ++ CKYByte key, int len, const CKYBuffer *data); + + CKY_END_PROTOS + diff --git a/SOURCES/coolkey-simple-bugs.patch b/SOURCES/coolkey-simple-bugs.patch new file mode 100644 index 0000000..f8f3ab7 --- /dev/null +++ b/SOURCES/coolkey-simple-bugs.patch @@ -0,0 +1,71 @@ +diff -up ./configure.in.coolkey-simple-bugs ./configure.in +--- ./configure.in.coolkey-simple-bugs 2009-09-16 11:21:55.621493844 -0700 ++++ ./configure.in 2009-09-16 11:22:23.354492383 -0700 +@@ -124,9 +124,9 @@ then + if test $WINDOWS -ne 1; then + PKG_CHECK_MODULES(NSS, nss, true, [ AC_MSG_ERROR(could not find NSS Crypto libraries) ]) + fi +- enable_pk11install = "yes" ++ enable_pk11install="yes" + else +- enable_pk11install = "no" ++ enable_pk11install="no" + AC_MSG_WARN([skipping pk11install]) + fi + +diff -up ./Makefile.am.coolkey-simple-bugs ./Makefile.am +--- ./Makefile.am.coolkey-simple-bugs 2009-09-16 11:23:18.715515063 -0700 ++++ ./Makefile.am 2009-09-16 13:15:29.570492412 -0700 +@@ -25,7 +25,6 @@ if BUILD_PK11INSTALL + SUBDIRS += src/install + endif + +-ACLOCAL_AMFLAGS = -I m4 + + EXTRA_DIST = cookey.spec LICENSE + +diff -up ./src/coolkey/object.cpp.coolkey-simple-bugs ./src/coolkey/object.cpp +--- ./src/coolkey/object.cpp.coolkey-simple-bugs 2009-09-16 10:36:29.300516245 -0700 ++++ ./src/coolkey/object.cpp 2009-09-16 10:37:17.747492199 -0700 +@@ -397,7 +397,7 @@ PKCS11Object::getLabel() + { + // clean up old one + if (label) { +- delete label; ++ delete [] label; + label = NULL; + } + // find matching attribute +diff -up ./src/coolkey/object.h.coolkey-simple-bugs ./src/coolkey/object.h +--- ./src/coolkey/object.h.coolkey-simple-bugs 2009-09-16 16:05:27.233488140 -0700 ++++ ./src/coolkey/object.h 2009-09-16 16:05:54.161492421 -0700 +@@ -82,7 +82,7 @@ class PKCS11Object { + PKCS11Object(unsigned long muscleObjID, CK_OBJECT_HANDLE handle); + PKCS11Object(unsigned long muscleObjID, const CKYBuffer *data, + CK_OBJECT_HANDLE handle); +- ~PKCS11Object() { delete label; delete name; CKYBuffer_FreeData(&pubKey); } ++ ~PKCS11Object() { delete [] label; delete [] name; CKYBuffer_FreeData(&pubKey); } + + PKCS11Object(const PKCS11Object& cpy) : + attributes(cpy.attributes), muscleObjID(cpy.muscleObjID), +diff -up ./src/coolkey/slot.cpp.coolkey-simple-bugs ./src/coolkey/slot.cpp +--- ./src/coolkey/slot.cpp.coolkey-simple-bugs 2009-09-16 10:28:15.412492201 -0700 ++++ ./src/coolkey/slot.cpp 2009-09-16 10:57:27.692492487 -0700 +@@ -979,7 +979,7 @@ Slot::makeLabelString(char *label, int m + // + #define COOLKEY "CoolKey" + #define POSSESSION " for " +- if (!personName || personName == "") { ++ if (!personName || personName[0] == '\0' ) { + const int coolKeySize = sizeof(COOLKEY) ; + memcpy(label, COOLKEY, coolKeySize-1); + makeSerialString(&label[coolKeySize], maxSize-coolKeySize, cuid); +@@ -1528,7 +1528,7 @@ SlotMemSegment::SlotMemSegment(const cha + } + sprintf(segName,SEGMENT_PREFIX"%s",readerName); + segment = SHMem::initSegment(segName, MAX_OBJECT_STORE_SIZE, needInit); +- delete segName; ++ delete [] segName; + if (!segment) { + // just run without shared memory + return; diff --git a/SOURCES/coolkey-thread-fix.patch b/SOURCES/coolkey-thread-fix.patch new file mode 100644 index 0000000..e3b552a --- /dev/null +++ b/SOURCES/coolkey-thread-fix.patch @@ -0,0 +1,158 @@ +Index: src/coolkey/coolkey.cpp +=================================================================== +RCS file: /cvs/dirsec/coolkey/src/coolkey/coolkey.cpp,v +retrieving revision 1.2 +diff -u -r1.2 coolkey.cpp +--- src/coolkey/coolkey.cpp 14 Feb 2007 19:54:01 -0000 1.2 ++++ src/coolkey/coolkey.cpp 18 Dec 2009 23:22:58 -0000 +@@ -42,7 +42,9 @@ + + static SlotList *slotList = NULL; + +-static OSLock finalizeLock(false); ++static OSLock *finalizeLock = NULL; ++#define FINALIZE_GETLOCK() if (finalizeLock) finalizeLock->getLock(); ++#define FINALIZE_RELEASELOCK() if (finalizeLock) finalizeLock->releaseLock(); + + static CK_BBOOL initialized = FALSE; + static CK_BBOOL finalizing = FALSE; +@@ -208,11 +210,13 @@ + if( initialized ) { + return CKR_CRYPTOKI_ALREADY_INITIALIZED; + } +- if (!finalizeLock.isValid()) { ++ if (finalizeLock && !finalizeLock->isValid()) { + return CKR_CANT_LOCK; + } + CK_C_INITIALIZE_ARGS* initArgs = (CK_C_INITIALIZE_ARGS*) pInitArgs; ++ OSLock::setThreadSafe(0); + if( initArgs != NULL ) { ++ bool needThreads; + /* work around a bug in NSS where the library parameters are only + * send if locking is requested */ + if (initArgs->LibraryParameters) { +@@ -220,7 +224,17 @@ + } else { + Params::ClearParams(); + } +- if( (initArgs->flags & CKF_OS_LOCKING_OK) || initArgs->LockMutex ){ ++ needThreads = ((initArgs->flags & CKF_OS_LOCKING_OK) != 0); ++ OSLock::setThreadSafe(needThreads); ++ /* don't get a finalize lock unless someone initializes us asking ++ * us to use threads */ ++ if (needThreads && !finalizeLock) { ++ finalizeLock = new OSLock(true); ++ if (finalizeLock == NULL) return CKR_HOST_MEMORY; ++ } ++ /* only support OS LOCKING threads */ ++ if( ((initArgs->flags & CKF_OS_LOCKING_OK) == 0) ++ && initArgs->LockMutex ){ + throw PKCS11Exception(CKR_CANT_LOCK); + } + } +@@ -259,9 +273,9 @@ + // the finalizing call first, we know it will set waitEvent before + // we can get the lock, so we only need to protect setting finalizing + // to true. +- finalizeLock.getLock(); ++ FINALIZE_GETLOCK(); + finalizing = TRUE; +- finalizeLock.releaseLock(); ++ FINALIZE_RELEASELOCK(); + if (waitEvent) { + /* we're waiting on a slot event, shutdown first to allow + * the wait function to complete before we pull the rug out. +@@ -273,10 +287,10 @@ + } + delete slotList; + delete log; +- finalizeLock.getLock(); ++ FINALIZE_GETLOCK(); + finalizing = FALSE; + initialized = FALSE; +- finalizeLock.releaseLock(); ++ FINALIZE_RELEASELOCK(); + return CKR_OK; + } + +@@ -595,17 +609,17 @@ + CK_RV + C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) + { +- finalizeLock.getLock(); ++ FINALIZE_GETLOCK(); + if( ! initialized ) { +- finalizeLock.releaseLock(); ++ FINALIZE_RELEASELOCK(); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + if (finalizing) { +- finalizeLock.releaseLock(); ++ FINALIZE_RELEASELOCK(); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + waitEvent = TRUE; +- finalizeLock.releaseLock(); ++ FINALIZE_RELEASELOCK(); + try { + log->log("C_WaitForSlotEvent called\n"); + slotList->waitForSlotEvent(flags, pSlot, pReserved); +Index: src/coolkey/machdep.cpp +=================================================================== +RCS file: /cvs/dirsec/coolkey/src/coolkey/machdep.cpp,v +retrieving revision 1.7 +diff -u -r1.7 machdep.cpp +--- src/coolkey/machdep.cpp 14 Feb 2008 23:48:19 -0000 1.7 ++++ src/coolkey/machdep.cpp 18 Dec 2009 23:22:58 -0000 +@@ -37,6 +37,8 @@ + #include + #endif + ++bool OSLock::needThread = 0; ++ + #ifdef _WIN32 + // + // Windows functions to grab a named shared memory segment of a specific size, +@@ -123,6 +125,10 @@ + + OSLock::OSLock(bool exceptionAllowed) + { ++ if (!needThread) { ++ lockData = NULL; ++ return; ++ } + lockData = new OSLockData; + if (lockData) { + InitializeCriticalSection(&lockData->mutex); +@@ -360,6 +366,9 @@ + int rc; + + lockData = NULL; ++ if (!needThread) { ++ return; ++ } + #ifdef MAC + if (!OSLock_attr_init) { + rc = pthread_mutexattr_init(&OSLock_attr); +Index: src/coolkey/machdep.h +=================================================================== +RCS file: /cvs/dirsec/coolkey/src/coolkey/machdep.h,v +retrieving revision 1.1 +diff -u -r1.1 machdep.h +--- src/coolkey/machdep.h 9 Jun 2006 18:39:11 -0000 1.1 ++++ src/coolkey/machdep.h 18 Dec 2009 23:22:58 -0000 +@@ -40,12 +40,14 @@ + class OSLock { + private: + OSLockData *lockData; ++ static bool needThread; + public: + OSLock(bool exceptionAllowed = true); + ~OSLock(); + bool isValid(); + void getLock(); + void releaseLock(); ++ static void setThreadSafe(bool thread) { needThread = thread; } + }; + + typedef unsigned long OSTime; diff --git a/SOURCES/coolkey.module b/SOURCES/coolkey.module new file mode 100644 index 0000000..521b87b --- /dev/null +++ b/SOURCES/coolkey.module @@ -0,0 +1,8 @@ +# This file describes how to load the coolkey module +# See: http://p11-glue.freedesktop.org/doc/p11-kit/config.html + +# This is a relative path, which means it will be loaded from +# the p11-kit default path which is usually $(libdir)/pkcs11. +# Doing it this way allows for packagers to package coolkey for +# 32-bit and 64-bit and make them parallel installable +module: libcoolkeypk11.so diff --git a/SPECS/coolkey.spec b/SPECS/coolkey.spec new file mode 100644 index 0000000..f0a48b5 --- /dev/null +++ b/SPECS/coolkey.spec @@ -0,0 +1,334 @@ +# BEGIN COPYRIGHT BLOCK +# Copyright (C) 2005 Red Hat, Inc. +# All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation version +# 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# END COPYRIGHT BLOCK + +%define coolkey_module "CoolKey PKCS #11 Module" +%define nssdb %{_sysconfdir}/pki/nssdb + +Name: coolkey +Version: 1.1.0 +Release: 37.51%{?dist} +Summary: CoolKey PKCS #11 module +License: LGPLv2 +URL: http://directory.fedora.redhat.com/wiki/CoolKey +Source: coolkey-%{version}.tar.gz +Source1: coolkey.module +Patch1: coolkey-cache-dir-move.patch +Patch2: coolkey-gcc43.patch +Patch3: coolkey-latest.patch +Patch4: coolkey-simple-bugs.patch +Patch5: coolkey-thread-fix.patch +Patch6: coolkey-cac.patch +Patch7: coolkey-cac-1.patch +Patch8: coolkey-pcsc-lite-fix.patch +Patch9: coolkey-fix-token-removal-failure.patch +Patch10: coolkey-piv-ecc-el7.patch +Patch20: coolkey-1.1.0-noapplet.patch +Patch21: coolkey-1.1.0-fix-spurious-event.patch +Patch22: coolkey-1.1.0-p15.patch +Patch23: coolkey-1.1.0-p15-coverity.patch +Patch24: coolkey-1.1.0-more-keys.patch +Patch25: coolkey-1.1.0-fail-on-bad-mechanisms.patch +Patch26: coolkey-1.1.0-max-cpu-bug.patch +Patch27: coolkey-1.1.0-rhel7-alt-cac.patch +Patch28: coolkey-1.1.0-cardos-5-3.patch +Patch29: coolkey-1.1.0-alt-tokens-2.patch + + +Group: System Environment/Libraries +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +BuildRequires: autoconf +BuildRequires: pcsc-lite-devel +BuildRequires: zlib-devel +BuildRequires: nss-devel +Requires: nss-tools +Requires: pcsc-lite +Requires: pcsc-lite-libs +Requires: ccid +Provides: CoolKey Openkey +Obsoletes: CoolKey Openkey +# 390 does not have libusb or smartCards +ExcludeArch: s390 s390x + +%description +Linux Driver support for the CoolKey and CAC products. + +%package devel +Summary: CoolKey Applet libraries +Group: System Environment/Libraries +Requires: coolkey = %{version}-%{release} + +%description devel +Linux Driver support to access the CoolKey applet. + + +%prep +%setup -q +%patch1 -b .cache.dir.move +%patch2 -b .coolkey-gcc43 +%patch3 -b .coolkey-latest +%patch4 -b .coolkey-simple-bugs +%patch5 -b .coolkey-thread-fix +%patch6 -b .cac +%patch7 -b .cac-1 +%patch8 -b .reader-state-fix +%patch9 -p1 -b .fix-token-removal-failure +%patch10 -b .piv-ecc +%patch20 -b .noapplet +%patch21 -b .fix-spurious +%patch22 -b .p15 +%patch23 -b .p15-coverity +%patch24 -b .more-keys +%patch25 -b .fail-on-bad-mechanisms +%patch26 -b .max-cpu-bug +%patch27 -b .alt-cac +%patch28 -b .cardos-5-3 +%patch29 -b .alt-tokens-2 + +%build +autoconf +%configure --with-debug --disable-dependency-tracking --enable-pk11install +make %{?_smp_mflags} CFLAGS="$CFLAGS -g -O2 -fno-strict-aliasing $CFLAGS " CXXFLAGS="$CXXFLAGS -g -O2 -fno-strict-aliasing $CFLAGS" + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT +ln -s pkcs11/libcoolkeypk11.so $RPM_BUILD_ROOT/%{_libdir} +mkdir -p $RPM_BUILD_ROOT/var/cache/coolkey +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/p11-kit/modules/ +cp %{SOURCE1} $RPM_BUILD_ROOT/%{_datadir}/p11-kit/modules/ + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +/sbin/ldconfig +if [ -x %{_bindir}/pk11install ]; then + isThere=`modutil -rawlist -dbdir dbm:%{nssdb} | grep %{coolkey_module} || echo NO` + if [ "$isThere" == "NO" ]; then + pk11install -l -p %{nssdb} 'name=%{coolkey_module} library=libcoolkeypk11.so' ||: + fi + isThere=`modutil -rawlist -dbdir sql:%{nssdb} | grep %{coolkey_module} || echo NO` + if [ "$isThere" == "NO" ]; then + pk11install -s -p %{nssdb} 'name=%{coolkey_module} library=libcoolkeypk11.so' ||: + fi +fi + + +%postun +/sbin/ldconfig +if [ $1 -eq 0 ]; then + modutil -delete %{coolkey_module} -dbdir dbm:%{nssdb} -force || : + modutil -delete %{coolkey_module} -dbdir sql:%{nssdb} -force || : +fi + + +%files +%defattr(-,root,root,-) +%doc ChangeLog LICENSE +%{_bindir}/pk11install +%{_libdir}/libcoolkeypk11.so +%{_libdir}/pkcs11 +%{_libdir}/libckyapplet.so.1 +%{_libdir}/libckyapplet.so.1.0.0 +%{_datadir}/p11-kit/modules/coolkey.module +%attr(1777,root,root) /var/cache/coolkey + + +%files devel +%{_libdir}/libckyapplet.so +%{_libdir}/pkgconfig/libckyapplet.pc +%{_includedir}/*.h + + +%changelog +* Mon Jun 25 2018 Robert Relyea - 1.1.0-37.51 +- Fix regression in alt token patch that prevented blank cards from working + in ESC. + +* Mon Apr 23 2018 Robert Relyea - 1.1.0-37.50 +- support cac alt tokens which don't have a cert is slot 0, don't have + a CCC, and uses a ACA. + +* Tue Mar 14 2017 Robert Relyea - 1.1.0-37 +- get Cardos 5.3 cards working properly + +* Thu Dec 1 2016 Robert Relyea - 1.1.0-36 +- recognize up to 10 cac and PIV certs rather than 3 + +* Thu Jun 30 2016 Robert Relyea - 1.1.0-35 +- include sleep on unknown reader errors. + +* Mon Jun 13 2016 Robert Relyea - 1.1.0-34 +- recognize up to 32 keys and certs in coolkeys. (can go up to 62 with just #define changes) +- own our cache file. +- verify we are using the correct mechanisms + +* Mon Jul 6 2015 Robert Relyea - 1.1.0-33 +- fix more coverity issues in p15 patch + +* Mon Jul 6 2015 Robert Relyea - 1.1.0-32 +- fix coverity issues in p15 patch + +* Mon Jul 6 2015 Robert Relyea - 1.1.0-31 +- fix requires as identified in rpmdiff + +* Mon Jul 6 2015 Robert Relyea - 1.1.0-30 +- Add pk11-kit modules file + +* Mon Jul 6 2015 Robert Relyea - 1.1.0-29 +- Add partical support for pkcs15 tokens + +* Fri Sep 26 2014 Robert Relyea - 1.1.0-28 +- update coolkey to handle the new error ccid/pcsc-lite returns on reader + removal. + +* Fri Dec 27 2013 Daniel Mach - 1.1.0-27 +- Mass rebuild 2013-12-27 + +* Tue Nov 26 2013 Robert Relyea - 1.1.0-26 +- delete coolkey from sql db as well as the old one. + +* Wed Nov 6 2013 Robert Relyea - 1.1.0-25 +- delete coolkey from sql db as well as the old one. + +* Sun Sep 8 2013 Robert Relyea - 1.1.0-24 +- Add ECC and PIV support + +* Wed May 22 2013 Ray Strode 1.1.0-23 +- Fix token removal issue + +* Wed Feb 13 2013 Fedora Release Engineering - 1.1.0-22 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Jul 18 2012 Fedora Release Engineering - 1.1.0-21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Thu Jan 12 2012 Fedora Release Engineering - 1.1.0-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Feb 08 2011 Fedora Release Engineering - 1.1.0-19 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Oct 19 2010 Ville Skyttä - 1.1.0-18 +- Own the %%{_libdir}/pkcs11 dir. + +* Wed Sep 8 2010 Robert Relyea - 1.1.0-17 +- pscs-lite removed SCARD_READERSTATE_A definition. revert to the prefered + SCARD_READERSTATE + +* Wed Jun 23 2010 Jack Magne - 1.1.0-16 +- fix possible crash when loading cac certs. + +* Wed Jun 16 2010 Robert Relyea - 1.1.0-15 +- better cac support. + +* Mon Feb 22 2010 Robert Relyea - 1.1.0-14 +- remove dependency on ifd-egate + +* Tue Jan 5 2010 Robert Relyea - 1.1.0-13 +- bump the release number to rebuild + +* Fri Dec 18 2009 Robert Relyea - 1.1.0-12 +- Fix threading issue. Coolkey will now work with non-threaded applications +- that don't link with libpthread. + +* Wed Sep 16 2009 Jack magne - 1.1.0-11 +- Misc bug fixes. Resolves: 485032, #250738, #497758. + +* Fri Sep 11 2009 Jack Magne - 1.1.0-10 +- Include latest changes for Gemalto 64K and Safenet 330J. + +* Fri Jul 24 2009 Fedora Release Engineering - 1.1.0-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Tue Feb 24 2009 Fedora Release Engineering - 1.1.0-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sun Sep 14 2008 Matt Domsch - 1.1.0-7 +- BR: nss-devel not mozilla-nss-devel (FTBFS BZ#440753) + +* Wed Feb 13 2008 Jack magne - 1.1.0-6 +- Clean up building with gcc 4.3. +* Thu Sep 27 2007 Jack Magne - 1.1.0-5 +- Include patch for moving the cache directory to a safe location. +- Bug #299481. +* Mon Aug 20 2007 Bob Relyea - 1.1.0-4 +- Update License description to the new Fedora standard + +* Thu Jun 21 2007 Kai Engert - 1.1.0-3.1 +- rebuild + +* Tue Jun 5 2007 Bob Relyea - 1.1.0-3 +- add build requires, bump version number for make tag. + +* Thu May 31 2007 Bob Relyea - 1.1.0-2 +- Back out RHEL-4 version of spec from CVS, add pcsc-lite-lib requires. + +* Tue Feb 20 2007 Bob Relyea - 1.1.0-1 +- Pick up lates release. + +* Wed Nov 1 2006 Bob Relyea - 1.0.1-15 +- Don't grab the CUID on cac's. Resting the card causes it to +- logout of other applications. + +* Wed Nov 1 2006 Bob Relyea - 1.0.1-14 +- Shared memory directory needs to be writeable by all so +- coolkey can create caches for any user. (lack of caches +- show up in screen savers reactly slowly). + +* Fri Oct 20 2006 Bob Relyea - 1.0.1-13 +- fix login hw race failures + +* Fri Oct 20 2006 Bob Relyea - 1.0.1-12 +- add the dist flag + +* Wed Oct 18 2006 Bob Relyea - 1.0.1-11 +- CAC cards sometimes need to reset before they can get their +- initial transaction (problem is noticed on insertion an removal) + +* Tue Oct 17 2006 Jesse Keating - 1.0.1-10 +- Only run pk11install if the binary is there (multilib fun) + +* Mon Oct 09 2006 Bob Relyea - 1.0.1-9 +- use pk11install which does not require loading the module to install it. + +* Mon Oct 09 2006 Bob Relyea - 1.0.1-8 +- pcscd must be running in order to add coolkey. + +* Wed Oct 4 2006 Bob Relyea - 1.0.1-7 +- silence modutil warnings + +* Fri Sep 29 2006 Bob Relyea - 1.0.1-5 +- install and uninstall coolkey in the system secmod.db + +* Thu Sep 7 2006 Bob Relyea - 1.0.1-4 +- make the coolkey token caches persist over application calls. +- make a separate cache for each user. + +* Sun Jul 16 2006 Florian La Roche - 1.0.1-2 +- fix excludearch line + +* Mon Jul 10 2006 Bob Relyea - 1.0.1-1 +- Don't require pthread library in coolkey + +* Mon Jul 10 2006 Bob Relyea - 1.0.0-2 +- remove s390 from the build + +* Mon Jun 5 2006 Bob Relyea - 1.0.0-1 +- Initial revision for fedora