I'm trying to send an APDU that contains some command data, and then expect some data in response from the card. I'm using this example code by Ludovic Rousseau as a starting to point (modified code below).
The APDU I'm sending is the following:
0x80 0x02 0x00 0x00 0x08 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x08
I.e. I've picked CLA 0x80
, INS 0x02
, not using P1 and P2, Lc and Le both 0x08
.
The data buffer I get back only contains 0x90 0x00
.
I've checked which protocol gets negotiated - that's T=1, as expected. If it were T=0, I would expect to get a 61XX
-series answer (see this related question).
Every other APDU format works just fine (i.e. empty, only sending or only receiving data). Is there something I'm overlooking here?
// source: https://ludovicrousseau.blogspot.nl/2010/04/pcsc-sample-in-c.html
// This is based on code by Ludovic Rousseau, modified to match our example
#ifdef WIN32
#undef UNICODE
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif
#ifdef WIN32
static char *pcsc_stringify_error(LONG rv)
{
static char out[20];
sprintf_s(out, sizeof(out), "0x%08X", rv);
return out;
}
#endif
#define CHECK(f, rv) \
if (SCARD_S_SUCCESS != rv) \
{ \
printf(f ": %s\n", pcsc_stringify_error(rv)); \
return -1; \
}
int main(void)
{
LONG rv;
SCARDCONTEXT hContext;
LPTSTR mszReaders;
SCARDHANDLE hCard;
DWORD dwReaders, dwActiveProtocol, dwRecvLength;
SCARD_IO_REQUEST pioSendPci;
BYTE pbRecvBuffer[258];
BYTE selectapdu[] = { 0x00, 0xA4, 0x04, 0x00, 0x0A,
0x01, 0x02, 0x03, 0x04, 0x05,
0x48, 0x45, 0x4C, 0x4C, 0x4F };
BYTE echoapdu[] = { 0x80, 0x02, 0x00, 0x00, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x08 };
unsigned int i;
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
CHECK("SCardEstablishContext", rv)
#ifdef SCARD_AUTOALLOCATE
dwReaders = SCARD_AUTOALLOCATE;
rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders);
CHECK("SCardListReaders", rv)
#else
rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
CHECK("SCardListReaders", rv)
mszReaders = calloc(dwReaders, sizeof(char));
rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
CHECK("SCardListReaders", rv)
#endif
printf("reader name: %s\n", mszReaders);
rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
CHECK("SCardConnect", rv)
switch(dwActiveProtocol)
{
case SCARD_PROTOCOL_T0:
printf("T0\n");
pioSendPci = *SCARD_PCI_T0;
break;
case SCARD_PROTOCOL_T1:
printf("T1\n");
pioSendPci = *SCARD_PCI_T1;
break;
}
// selecting the application
dwRecvLength = sizeof(pbRecvBuffer);
rv = SCardTransmit(hCard, &pioSendPci, selectapdu, sizeof(selectapdu),
NULL, pbRecvBuffer, &dwRecvLength);
CHECK("SCardTransmit", rv)
printf("response (%d): ", dwRecvLength);
for(i=0; i<dwRecvLength; i++)
printf("%02X ", pbRecvBuffer[i]);
printf("\n");
// sending a non-empty APDU that expects a reply
dwRecvLength = sizeof(pbRecvBuffer);
printf("sent (%d): ", sizeof(echoapdu));
for(i=0; i<sizeof(echoapdu); i++)
printf("%02X ", echoapdu[i]);
printf("\n");
rv = SCardTransmit(hCard, &pioSendPci, echoapdu, sizeof(echoapdu),
NULL, pbRecvBuffer, &dwRecvLength);
CHECK("SCardTransmit", rv)
printf("response (%d): ", dwRecvLength);
for(i=0; i<dwRecvLength; i++)
printf("%02X ", pbRecvBuffer[i]);
printf("\n");
// disconnecting
rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
CHECK("SCardDisconnect", rv)
#ifdef SCARD_AUTOALLOCATE
rv = SCardFreeMemory(hContext, mszReaders);
CHECK("SCardFreeMemory", rv)
#else
free(mszReaders);
#endif
rv = SCardReleaseContext(hContext);
CHECK("SCardReleaseContext", rv)
return 0;
}
This gives as output:
reader name: OMNIKEY AG CardMan 3121 00 00
T1
response (2): 90 00
sent (14): 80 02 00 00 08 01 02 03 04 05 06 07 08 08
response (2): 90 00
When I try to do the same thing in Python using pyscard
, everything works as expected, i.e. calling data, sw1, sw2 = connection.transmit(...)
with the same APDU bytes as input makes data
contain the expected data.
This makes me believe that the relevant code on the card is fine (but also posted below for completeness).
private void getEcho(APDU apdu) {
byte[] buffer = apdu.getBuffer();
short numBytes = (short) (buffer[ISO7816.OFFSET_LC] & 0x00FF);
short bytesRead = apdu.setIncomingAndReceive();
short pos = 0;
while (pos < numBytes) {
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, transientMemory, pos, bytesRead);
pos += bytesRead;
bytesRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA);
}
apdu.setOutgoing();
apdu.setOutgoingLength(numBytes);
apdu.sendBytesLong(transientMemory, (short)0, bytesRead);
}
I'm not sure why you would receive the correct data through pyscard. However, using bytesRead
as the length in apdu.sendBytesLong()
is obviously a mistake:
setIncomingAndReceive()
. In that case, apdu.receiveBytes(ISO7816.OFFSET_CDATA)
will return zero and bytesRead
would be zero upon invoking sendBytesLong()
.bytesRead
would be set to the number (N) of bytes that were received with the last call of apdu.receiveBytes(ISO7816.OFFSET_CDATA)
. sendBytesLong()
would return exactly this amount (N) of bytes from the beginning of the command data.Consequently, the count should probably be numBytes
:
apdu.sendBytesLong(transientMemory, (short)0, numBytes);