summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrrelyea <rrelyea@fba4d07e-fe0f-4d7f-8147-e0026e666dc0>2010-09-08 20:14:41 +0000
committerrrelyea <rrelyea@fba4d07e-fe0f-4d7f-8147-e0026e666dc0>2010-09-08 20:14:41 +0000
commitc6876ff8464a46a87ccc27d8084b8621ab2c23cd (patch)
tree669af2f47db76ac199ac222d772ff16ca536ebd2
parentb855b24f093deb6fb52a004c4daebb9523f04658 (diff)
New CAC support
git-svn-id: http://svn.fedorahosted.org/svn/coolkey/trunk@87 fba4d07e-fe0f-4d7f-8147-e0026e666dc0
-rw-r--r--src/coolkey/object.cpp4
-rw-r--r--src/coolkey/slot.cpp255
-rw-r--r--src/coolkey/slot.h9
-rw-r--r--src/libckyapplet/cky_applet.c171
-rw-r--r--src/libckyapplet/cky_applet.h30
-rw-r--r--src/libckyapplet/cky_base.c94
-rw-r--r--src/libckyapplet/cky_base.h12
-rw-r--r--src/libckyapplet/cky_factory.c42
-rw-r--r--src/libckyapplet/cky_factory.h12
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