OS X Yosemite bug: T=0 is used instead of T=1 on dual protocol cards

This is part of the series: "OS X Yosemite and smart cards: known bugs".

SCardConnect(..., SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, ...)

SCardConnect() has changed its preferred protocol on Yosemite.

If a smart card can do both protocols T=0 and T=1 it is possible to let SCardConnect() select the active protocol. T=1 has some advantages over T=0 (transparent support of extended APDU for example) so it is a good idea for a PC/SC layer to prefer T=1 over T=0.

On Mavericks (Mac OS X 10.9) the T=1 protocol was preferred over T=0. On Yosemite this has changed and T=0 is preferred now.

It is not really a bug since by using SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 the application explicitly let the PC/SC layer to select the protocol. But it is a change compared to the previous OS X version and can be considered a regression.

I guess the behaviour change is due to the major rewrite of the PC/SC layer in Yosemite. See "OS X Yosemite and smart cards status".

ATR parsing

You can use my online ATR (Answer To Reset) parsing tool at Smart card ATR parsing. For my tests I use a smart card with the ATR: 3B D6 18 00 80 B1 80 6D 1F 03 80 51 00 61 10 30 9E. You can see the ATR parsing results at http://smartcard-atr.appspot.com/parse?ATR=3BD6180080B1806D1F038051006110309E.

In the case of my smart card you can see from the parsing results that both protocols are supported:
[...]
TD(1) = 0x80 Y(i+1) = b1000, Protocol T=0
----
TD(2) = 0xB1 Y(i+1) = b1011, Protocol T=1
[...]


See also

Apple bug report #19384330 "PC/SC SCardConnect(): T=0 is used instead of T=1 on dual protocol cards". Closed by Apple, 9th January 2015, as a duplicated of #18567029.

Sample code

The sample code does:
  1. connect using SCardConnect(..., SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, ...)
  2. display the protocol selected by SCardConnect()
  3. display the ATR so you can feed it to Smart card ATR parsing

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(int argc, const char * argv[]) {
SCARDCONTEXT hContext;
LPSTR mszReaders;
DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
if (err != SCARD_S_SUCCESS) {
printf("ScardEstablishedContext : %08x\n",err);
return -1;
}

DWORD cchReaders = 0;
err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
if (err != 0) {
printf("ScardListReaders : %08x\n",err);
return -1;
}
mszReaders = calloc(cchReaders, sizeof(char));
if (!mszReaders) {
printf("calloc\n");
return -1;
}
err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
if (err != SCARD_S_SUCCESS) {
printf("ScardListReaders : %08x\n",err);
return -1;
}

printf("Reader : %s\n", mszReaders);

SCARDHANDLE hCard;
DWORD dwActiveProtocol;
err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
if (err != SCARD_S_SUCCESS) {
printf("SCardConnect: 0x%08x\n",err);
return -1;
}

printf("Prococol: ");
switch(dwActiveProtocol)
{
case SCARD_PROTOCOL_T0:
printf("SCARD_PROTOCOL_T0\n");
break;
case SCARD_PROTOCOL_T1:
printf("SCARD_PROTOCOL_T1\n");
break;
default:
printf("unknown\n");
}

char name[100];
DWORD len = sizeof name;
DWORD dwState, dwProtocol;
unsigned char atr[MAX_ATR_SIZE];
DWORD atr_len = sizeof atr;
err = SCardStatus(hCard, name, &len, &dwState, &dwProtocol, atr, &atr_len);
if (err != SCARD_S_SUCCESS) {
printf("SCardStatus: 0x%08x\n",err);
return -1;
}
printf("ATR: ");
for (int i=0; i<atr_len; i++)
printf("%02X ", atr[i]);
printf("\n");

SCardDisconnect(hCard, SCARD_LEAVE_CARD);
SCardReleaseContext(hContext);

return 0;
}

Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC main.c -o main

$ ./main
Reader : Gemalto PC Twin Reader
Prococol: SCARD_PROTOCOL_T0
ATR: 3B D6 18 00 80 B1 80 6D 1F 03 80 51 00 61 10 30 9E

T=0 (SCARD_PROTOCOL_T0) has been selected.

Expected result (on Mavericks)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC main.c -o main

$ ./main
Reader : Gemplus GemPC Twin 00 00
Prococol: SCARD_PROTOCOL_T1
ATR: 3B D6 18 00 80 B1 80 6D 1F 03 80 51 00 61 10 30 9E

T=1 (SCARD_PROTOCOL_T1) has been selected.

Known workaround

Force the use of T=1 only on dual protocol cards it you really want to use T=1 instead of T=0.

You have to parse the ATR to know if the card supports T=0 and/or T=1. This is not an easy task. Maybe it is simpler to first call SCardConnect(..., SCARD_PROTOCOL_T1, ...) and if that fails call SCardConnect(..., SCARD_PROTOCOL_T0, ...).

Note that in case of protocol not supported the error returned is SCARD_E_NOT_TRANSACTED on Yosemite but SCARD_E_PROTO_MISMATCH on Mavericks. See OS X Yosemite bug: SCARD_E_PROTO_MISMATCH not returned.

Update

This bug is now fixed in Mac OS X Yosemite 10.10.2.
Previous
Next Post »