c++winapismartcardwinscardid-card

WinSCard SCARD_E_PROTO_MISMATCH when connecting to Estonian ID card


I am trying to read the personal file of the Estonian ID card.

I need to send the following data to the card (from here) in order to read records from the personal file (ie ID Number, name, etc):

00 A4 00 0C          # We choose root folder
00 A4 01 0C 02 EE EE # We choose folder EEEE
00 A4 02 04 02 50 44 # We choose file 5044, which contains personal data
00 B2 XX 04          # We read record XX (see table) from the personal data file
                     # The card responds 61 YY, where YY denotes the number of bytes waiting to be read
00 C0 00 00 YY       # We read YY bytes from the card
                     # Card responds ... 90 00, where ... is the requested data and 90 00 means status OK 

However, the raw bytes are in the T=0 protocol, and the card is stuck in T=1 for an unreasonably long time before accepting T=0. The sequence of events is as follows:

  1. Card is attached to reader
  2. Program returns from SCardStatusChange and starts handling card
  3. On trying to connect to the card (SCardConnect or SCardReconnect) the error SCARD_E_SHARING_VIOLATION is received over and over, for around 5s
  4. Then, on trying to connect, the error SCARD_E_PROTO_MISMATCH is received for anywhere between 3 and 30 seconds, maybe more.
  5. After that, the card connects successfully and reads the data.

Can I somehow connect to it quicker in the T=0 protocol?

A simplified version of my source code is as follows:

// NOTE: this is approximately what I do.
// I haven't tested this code yet - it's almost 1 AM here.
#include <winscard.h>
void readSmartCard() {
    LONG sCardErrorCode;
    SCARDCONTEXT sCardContext;
    DWORD sCardReaderStrLen = 1024;
    wchar_t sCardReaderStr[1024];
    SCARDHANDLE sCardHandle;
    DWORD sCardActiveProtocol;
    SCARD_READERSTATE readerState;

    sCardErrorCode = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &sCardContext);

    // error handling macro

    ZeroMemory(&sCardReaderState, sizeof(sCardReaderState));
    sCardReaderState.szReader = L"\\\\?PnP?\\Notification";
    sCardReaderState.pvUserData = nullptr;
    sCardReaderState.dwEventState = SCARD_STATE_PRESENT;

    sCardErrorCode = SCardGetStatusChange(sCardContext, INFINITE, &readerState, 1);

    // e.h.m

    if (readerState.dwCurrentState == 65538) {
        sCardErrorCode = SCardListReaders(sCardContext, NULL, sCardReaderStr, &sCardReaderStrLen);
        // e.h.m
        readerState.szReader = sCardReaderStr;
    }

    sCardErrorCode = SCardGetStatusChange(sCardContext, INFINITE, &readerState, 1);
    // e.h.m

    if (sCardReaderState.dwEventState & SCARD_STATE_PRESENT) {

        while (true) {
            sCardErrorCode = SCardConnect(sCardContext, readerState.szReader, SCARD_SHARE_EXCLUSIVE,
                SCARD_PROTOCOL_T0, &sCardHandle, &sCardActiveProtocol);

            // e.h.m
            printf("%x", sCardErrorCode); 
            // this will print:
            // 8010000b (for around 5s)
            // 8010000f (for around 20s)
            if (sCardErrorCode == SCARD_S_SUCCESS) {
                break;
            }
            Sleep(1000);
        }

        // open personal file and read data, yay!
    }
}

Solution

  • After a long scouring of the internet, I discovered that no changes to the commands were necessary.

    I simply needed to add an extra zero byte to the end of transmitted read/open commands, and receive the data as the response of the read command, not use a separate command to receive the bytes as well. (T=0 uses a "request data/read data" model, while T=1 just responds with the data, it seems)

    I also needed to change all mentions of SCARD_PCI_T0 to conditionally use SCARD_PCI_T1 and make the SCardConnect() function accept T1 too.

    I will post a decent code sample here later.