diff options
author | rrelyea <rrelyea@fba4d07e-fe0f-4d7f-8147-e0026e666dc0> | 2010-09-08 20:14:41 +0000 |
---|---|---|
committer | rrelyea <rrelyea@fba4d07e-fe0f-4d7f-8147-e0026e666dc0> | 2010-09-08 20:14:41 +0000 |
commit | c6876ff8464a46a87ccc27d8084b8621ab2c23cd (patch) | |
tree | 669af2f47db76ac199ac222d772ff16ca536ebd2 /src | |
parent | b855b24f093deb6fb52a004c4daebb9523f04658 (diff) |
New CAC support
git-svn-id: http://svn.fedorahosted.org/svn/coolkey/trunk@87 fba4d07e-fe0f-4d7f-8147-e0026e666dc0
Diffstat (limited to 'src')
-rw-r--r-- | src/coolkey/object.cpp | 4 | ||||
-rw-r--r-- | src/coolkey/slot.cpp | 255 | ||||
-rw-r--r-- | src/coolkey/slot.h | 9 | ||||
-rw-r--r-- | src/libckyapplet/cky_applet.c | 171 | ||||
-rw-r--r-- | src/libckyapplet/cky_applet.h | 30 | ||||
-rw-r--r-- | src/libckyapplet/cky_base.c | 94 | ||||
-rw-r--r-- | src/libckyapplet/cky_base.h | 12 | ||||
-rw-r--r-- | src/libckyapplet/cky_factory.c | 42 | ||||
-rw-r--r-- | src/libckyapplet/cky_factory.h | 12 |
9 files changed, 577 insertions, 52 deletions
diff --git a/src/coolkey/object.cpp b/src/coolkey/object.cpp index 2213dd0..1d7bb05 100644 --- a/src/coolkey/object.cpp +++ b/src/coolkey/object.cpp @@ -505,6 +505,10 @@ dataStart(const CKYByte *buf, unsigned int length, 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 --git a/src/coolkey/slot.cpp b/src/coolkey/slot.cpp index c9645bf..455b073 100644 --- a/src/coolkey/slot.cpp +++ b/src/coolkey/slot.cpp @@ -372,7 +372,7 @@ Slot::Slot(const char *readerName_, Log *log_, CKYCardContext* context_) : 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 *log_, CKYCardContext* context_) } 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 <class C> @@ -583,6 +589,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); @@ -671,12 +678,12 @@ 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); + log->log("CAC Select failed 0x%x\n", status); if (status == CKYSCARDERR) { - log->log("CAC Card Failure 0x%x\n", - CKYCardConnection_GetLastError(conn)); + log->log("CAC Card Failure 0x%x\n", + CKYCardConnection_GetLastError(conn)); disconnect(); } return; @@ -689,6 +696,15 @@ 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(); + } + return; } @@ -772,17 +788,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(); @@ -1020,7 +1130,7 @@ Slot::makeModelString(char *model, int maxSize, const unsigned char *cuid) struct _manList { unsigned short type; - char *string; + const char *string; }; static const struct _manList manList[] = { @@ -1281,13 +1391,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 @@ -2060,10 +2187,90 @@ Slot::fetchCombinedObjects(const CKYBuffer *header) return objInfoList; } +CKYStatus +Slot::readCACCertificateFirst(CKYBuffer *cert, CKYSize *nextSize, + bool throwException) +{ + CKYStatus status; + CKYISOStatus apduRC; + *nextSize = 0; + + 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; + } + + 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; @@ -2098,12 +2305,10 @@ 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); + if(CKYBuffer_Size(&rawCert) == 0) { + handleConnectionError(); + } log->log("CAC Cert %d: fetch CAC Cert: %d ms\n", instance, OSTimeNow() - time); } @@ -2144,8 +2349,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' */ @@ -2160,8 +2364,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); @@ -2177,9 +2380,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 { @@ -2190,7 +2394,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) { @@ -2527,7 +2732,7 @@ Slot::attemptCACLogin() switch( result ) { case CKYISO_SUCCESS: break; - case 6981: + case 0x6981: throw PKCS11Exception(CKR_PIN_LOCKED); default: if ((result & 0xff00) == 0x6300) { @@ -3206,6 +3411,10 @@ retry: 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 (status == CKYISO_CONDITION_NOT_SATISFIED) { + status = (CKYStatus) CKYISO_UNAUTHORIZED; + } if (status != CKYSUCCESS) { if ( status == CKYSCARDERR ) { handleConnectionError(); diff --git a/src/coolkey/slot.h b/src/coolkey/slot.h index 6a1a086..eb7cde0 100644 --- a/src/coolkey/slot.h +++ b/src/coolkey/slot.h @@ -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<ListObjectInfo> fetchCombinedObjects(const CKYBuffer *header); list<ListObjectInfo> 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 --git a/src/libckyapplet/cky_applet.c b/src/libckyapplet/cky_applet.c index 7c85b07..0ffc3aa 100644 --- a/src/libckyapplet/cky_applet.c +++ b/src/libckyapplet/cky_applet.c @@ -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 *apdu, const void *param) } 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 *apdu, const void *param) } 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 *conn, const CKYBuffer *AID, 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(CKYCardConnection *conn, CKYISOStatus *apduRC) 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(CKYCardConnection *conn, CKYISOStatus *apduRC) 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(CKYCardConnection *conn, CKYByte keyNumber, 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 *conn, const CKYBuffer *data, 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(CKYCardConnection *conn, CKYBuffer *cert, } 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 --git a/src/libckyapplet/cky_applet.h b/src/libckyapplet/cky_applet.h index e369113..3f0dc80 100644 --- a/src/libckyapplet/cky_applet.h +++ b/src/libckyapplet/cky_applet.h @@ -43,6 +43,7 @@ typedef unsigned short CKYISOStatus; /* applet return status */ #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 */ /* Applet Defined Return codes */ #define CKYISO_NO_MEMORY_LEFT 0x9c01 /* There have been memory * problems on the card */ @@ -71,6 +72,15 @@ typedef unsigned short CKYISOStatus; /* applet return status */ #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 +219,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 +467,17 @@ CKYStatus CKYApplet_DeleteObject(CKYCardConnection *conn, unsigned long objectID /* 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 --git a/src/libckyapplet/cky_base.c b/src/libckyapplet/cky_base.c index b12ac96..811684d 100644 --- a/src/libckyapplet/cky_base.c +++ b/src/libckyapplet/cky_base.c @@ -220,6 +220,22 @@ CKYBuffer_AppendShort(CKYBuffer *buf, unsigned short val) 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, unsigned long val) 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, CKYOffset offset, unsigned short val) } 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, CKYOffset offset, unsigned long val) 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, CKYOffset offset) 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, CKYOffset offset) 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 --git a/src/libckyapplet/cky_base.h b/src/libckyapplet/cky_base.h index c042359..0ce252e 100644 --- a/src/libckyapplet/cky_base.h +++ b/src/libckyapplet/cky_base.h @@ -170,9 +170,15 @@ CKYStatus CKYBuffer_AppendChar(CKYBuffer *buf, CKYByte b); /* 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 *buf, CKYOffset offset, 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 --git a/src/libckyapplet/cky_factory.c b/src/libckyapplet/cky_factory.c index 1a99059..ed321f4 100644 --- a/src/libckyapplet/cky_factory.c +++ b/src/libckyapplet/cky_factory.c @@ -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 *apdu) } 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 *apdu, CKYSize size) } 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 --git a/src/libckyapplet/cky_factory.h b/src/libckyapplet/cky_factory.h index 9b6fe83..bad8ebb 100644 --- a/src/libckyapplet/cky_factory.h +++ b/src/libckyapplet/cky_factory.h @@ -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(CKYAPDU *apdu, const CKYBuffer *data); 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 |