Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Tuesday, January 6, 2015

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.