WinSCard SCARD_E_PROTO_MISMATCH when connecting to Estonian ID card

2k Views Asked by At

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!
    }
}
2

There are 2 best solutions below

0
On BEST ANSWER

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.

1
On

You are asking SCardConnect() for exclusive access to the T0 protocol only, so if the card is in use then SCARD_E_SHARING_VIOLATION is returned, and if the card is not in use but T1 is active then SCARD_E_PROTO_MISMATCH is returned instead.

When SCardGetStatusChange() reports a notification, you are only checking for the SCARD_STATE_PRESENT flag, but other flags can be present as well that are you ignoring, such as SCARD_STATE_INUSE.

If you need exclusive access to the reader, you will have to wait until SCARD_STATE_INUSE is cleared, and/or SCARD_E_SHARING_VIOLATION is no longer begin reported. Nothing you can do about that, unless you change your logic to allow connecting in sharing mode instead of exclusive mode.

If you want to connect to the reader regardless of its current protocol (and thus gain exclusive access sooner), you can ask SCardConnect() to accept both T0 and T1 protocols at the same time, by OR'ing them together in the dwPreferredProtocol parameter (see the example in the documentation). Then, if the pdwActiveProtocol parameter outputs that T1 is the active protocol, you can wait for a status change to the T0 protocol before reading the card.