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:- connect using
SCardConnect(..., SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, ...)
- display the protocol selected by
SCardConnect()
- 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.