Friday, April 22, 2016

OS X El Capitan and CCID driver upgrades

Silent upgrade of the CCID driver

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

The first version of El Capitan 10.11 in September, 2015 was provided with the CCID driver version 1.4.14. See "OS X El Capitan and smart cards status" for more details.

I recently discovered that Apple upgraded the CCID driver in the minor upgrades (also thanks to Martin P. for the notice):
  • Mac OS X 10.11.3 provides the CCID driver version 1.4.20 (driver released 5 August 2015)
  • Mac OS X 10.11.4 provides the CCID driver version 1.4.21 (driver released 21 October 2015)

OS X 10.11

On Mac OS X 10.11 (or 10.11.0) I have:
$ ls -lR /usr/libexec/SmartCardServices/drivers
total 0
drwxr-xr-x  3 root  wheel  102 23 aoû  2015 ifd-ccid.bundle

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle:
total 0
drwxr-xr-x  5 root  wheel  170  5 oct  2015 Contents

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents:
total 56
-rw-r--r--  1 root  wheel  27616  5 oct  2015 Info.plist
drwxr-xr-x  4 root  wheel    136  2 oct  2015 MacOS
-rw-r--r--  1 root  wheel    471 23 aoû  2015 version.plist

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/MacOS:
total 176
lrwxr-xr-x  1 root  wheel      20  2 oct  2015 libccid.dylib -> libccid.dylib.1.4.14
-rwxr-xr-x  1 root  wheel  165888 17 sep  2015 libccid.dylib.1.4.14

OS X 10.11.3

On Mac OS X 10.11.3 I have:

$ ls -lR /usr/libexec/SmartCardServices/drivers
total 0
drwxr-xr-x  3 root  wheel  102 Aug 23  2015 ifd-ccid.bundle

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle:
total 0
drwxr-xr-x  5 root  wheel  170 Jan 28 10:05 Contents

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents:
total 24
-rw-r--r--  1 root  wheel  33873 Sep 23  2015 Info.plist
drwxr-xr-x  5 root  wheel    170 Jan 28 10:05 MacOS
-rw-r--r--  1 root  wheel    469 Sep 23  2015 version.plist

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/MacOS:
total 344
lrwxr-xr-x  1 root  wheel      20 Dec  9 15:06 libccid.dylib -> libccid.dylib.1.4.14
-rwxr-xr-x  1 root  wheel  165888 Oct 18  2015 libccid.dylib.1.4.14
-rwxr-xr-x  1 root  wheel  166096 Jan 14 03:06 libccid.dylib.1.4.20


OS X 10.11.4

On Mac OS X 10.11.4 I have:

$ pwd
/Volumes/ElCapitan/usr/libexec/SmartCardServices/drivers

$ ls -lR ifd-ccid.bundle/
total 0
drwxr-xr-x  5 root  wheel  170 Mar 22 13:23 Contents

ifd-ccid.bundle//Contents:
total 24
-rw-r--r--  1 root  wheel  36860 Dec 21 06:05 Info.plist
drwxr-xr-x  5 root  wheel    170 Mar 22 13:23 MacOS
-rw-r--r--  1 root  wheel    470 Dec 21 06:05 version.plist

ifd-ccid.bundle//Contents/MacOS:
total 344
lrwxr-xr-x  1 root  wheel      20 Sep 16  2015 libccid.dylib -> libccid.dylib.1.4.14
-rwxr-xr-x  1 root  wheel  165888 Sep  3  2015 libccid.dylib.1.4.14
-rwxr-xr-x  1 root  wheel  166096 Mar 12 09:30 libccid.dylib.1.4.21

Half upgrade

What is strange is that only the driver Info.plist file has been updated. A new binary driver is installed (libccid.dylib.1.4.20 or libccid.dylib.1.4.21) but not used.

The driver Info.plist file contains:

<key>CFBundleExecutable</key>
 <string>libccid.dylib</string>

So the driver binary is always the file libccid.dylib. And, as you can see from the previous commands libccid.dylib is just a symbolic link to the same libccid.dylib.1.4.14 file.

It looks like the upgrade is not complete:
  • New readers present in the driver Info.plist file will be recognized
  • Bugs fixes and new code from versions 1.4.15 to 1.4.21 are not used since the driver binary used is still at version 1.4.14.

Broken upgrade

Some/many people reported that the CCID driver was not working any more after the 10.11.4 upgrade. I have not (yet) reproduce this problem myself.

The driver configuration looks like this:

/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/MacOS:
lrwxr-xr-x 1 root wheel         20    3 déc 08:37   libccid.dylib -> libccid.dylib.1.4.20
-rwxr-xr-x 1 root wheel     166096   12 mar 09:30   libccid.dylib.1.4.21

The symbolic link points to a non-existent libccid.dylib.1.4.20 file. So, of course, the driver is not loaded and the support of CCID readers is broken.

Error message in the system log file:
com.apple.ifdreader[219]: Failed to load IFD bundle executable:
'file:///usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/' with error: Error Domain=NSCocoaErrorDomain Code=4 "The bundle “CCIDCLASSDRIVER” couldn’t be loaded because its executable couldn’t be located." UserInfo={NSLocalizedFailureReason=The bundle’s executable couldn’t be located., NSLocalizedRecoverySuggestion=Try reinstalling the bundle., NSBundlePath=/usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle, NSLocalizedDescription=The bundle “CCIDCLASSDRIVER” couldn’t be loaded because its executable couldn’t be located.}

Proposed solution

Because of System Integrity Protection [or wikipedia], it is forbidden to change/remove/add files in the /usr/ directory.

My proposal to fix the issue, until Apple provides a fix, is to:
  1. disable SIP
  2. fix the symbolic link using some think like
    cd /usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/MacOS
    ln -sf libccid.dylib.1.4.21 libccid.dylib
  3. enable SIP

Another solution

In the Apple developer forums "gtall" reported the same problem in "el capitan 10.11.4 unable to see smartcard" and "Metsma" posted a different workaround.

Maybe Apple will publish a Technical Note to give a better solution or, better, provide a version 10.11.5 with a fix for the CCID driver.

Conclusion

I reported the problem to Apple as bug #25873806 "CCID smart card reader driver update failed in 10.11.4".

That is the first time that Apple updates a smart card component (instead of just fixing bugs) with minor revisions of Mac OS X. Maybe it is a sign that Apple cares about smart cards?

My list of El Capitan known smart card bugs contains 7 unfixed bugs and 3 wanted features. There is still Apple work to do for the next (minor or major) release of Mac OS X.

Update

On 26th April 2016, Apple closed my bug report as a duplicate:
"Engineering has determined that your bug report (25873806) is a duplicate of another issue (25416818) and will be closed."

Wednesday, April 20, 2016

New version of libccid: 1.4.23

I just released a version 1.4.23 of libccid the Free Software CCID class smart card reader driver.

Changes:
1.4.23 - 20 April 2016, Ludovic Rousseau
  • Add support of
    • ACS ACR3901U ICC Reader
    • Alcor Micro AU9560
    • Cherry SmartTerminal XX44
    • HID Global OMNIKEY 3x21 Smart Card Reader
    • HID Global OMNIKEY 5022 Smart Card Reader
    • HID Global OMNIKEY 6121 Smart Card Reader
    • IonIDe Smartcard Reader reader
    • KACST HSID Reader
    • KACST HSID Reader Dual Storage
    • KACST HSID Reader Single Storage
  • Remove support of
    • VMware Virtual USB CCID
  • Do NOT add support of
    • DUALi DE-ABCM6
  • Fix a busy loop consuming 100% of CPU for some composite USB devices
    impacted readers: Yubico Yubikey NEO U2F+CCID and Broadcom BCM5880
  • Remove support of (unused) option DRIVER_OPTION_RESET_ON_CLOSE
  • log libusb error name instead of decimal value
  • Some minor improvements

Tuesday, April 19, 2016

OS X El Capitan bug: SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED when it should not

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

SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED when it should not

SCardGetAttrib() does not work correctly any more on El Capitan. SCardGetAttrib() is used to get an attribute value from the IFD Handler (the smart card reader driver).

One idea of SCardGetAttrib() is to use a double call:
  • 1st call to get the correct buffer size to store the attribute
  • 2nd call to fill the allocated buffer with the requested attribute value

The program then looks like:
  rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &attrLen);
  attr = malloc(attrLen);
  rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);

  1. On the first call the pbAttr pointer is NULL because no buffer is used. This call will just but the correct buffer size in the pcbAttrLen parameter (named attrLen here).
  2. A big enough buffer is allocated to store the attribute value.
  3. On the second call the buffer is passed as argument and is filled by the SCardGetAttrib() call.

I discovered that the first call to SCardGetAttrib() fails and returns SCARD_E_NOT_TRANSACTED if the value in attrLen (given to the function) is lower than the needed buffer size.

This input value (buffer size) should just be ignored by PC/SC since the buffer pointer is NULL (no buffer is used). The parameter is used to get a value from the function, not to give a value to the function.

See also

Apple bug report #25802143 "PCSC SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED when it should not"

Sample code


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

#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag)))
#define SCARD_CLASS_ICC_STATE       9   /**< ICC State specific definitions */
#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) /**< Answer to reset (ATR) string. */

#define RED "\33[01;31m"
#define NORMAL "\33[0m"
#define pcsc_error(fct) printf(RED fct ": %s 0x%08X\n" NORMAL, pcsc_stringify_error(err), err)

int main(void)
{
    SCARDCONTEXT hContext;
    LPSTR mszReaders;
    DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardEstablishedContext");
        return -1;
    }
    DWORD cchReaders = 0;
    err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
    if (err != 0) {
        pcsc_error("ScardListReaders");
        return -1;
    }
    mszReaders = calloc(cchReaders, sizeof(char));
    if (!mszReaders) {
        pcsc_error("calloc\n");
        return -1;
    }
    err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardListReaders");
        return -1;
    }

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

    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_RAW, &hCard, &dwActiveProtocol);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardConnect");
    } else {
        unsigned char attr[33] = { 0 };
        unsigned int i;
        DWORD attrLen;

        attrLen = 22;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        }

        attrLen = 123456;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        }

        attrLen = sizeof attr;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        } else {
            for (i=0; i<attrLen; i++)
                printf("%02X ", attr[i]);
            printf("\n");
        }
    }
    SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    SCardReleaseContext(hContext);
    return 0;
}


Result (on El Capitan)

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

Reader: Gemalto PC Twin Reader
attrLen: 22
SCardGetAttrib: Transaction failed. 0x80100016
attrLen: 23
attrLen: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 

Here the requested attribute is the card ATR (dwAttrId = SCARD_ATTR_ATR_STRING) with a size of 23 bytes. If the first call to SCardGetAttrib() uses a value of 22 (or less) then the function returns with the error SCARD_E_NOT_TRANSACTED (0x80100016)

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC    -lpcsclite    main.c   -o main

Reader: Gemalto PC Twin Reader (70D7E2EE) 00 00
attrLen: 23
attrLen: 23
attrLen: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 

Known workaround

Do not use a random value for attrLen on the first call (or you will get random results). But initialize the variable to a big enough value. A value of 65535 should be good enough in most cases.

Saturday, April 2, 2016

ATR statistics: TA1 - Global, encodes Fi and Di

Article from the series "ATR statistics"

TA1 - Global, encodes Fi and Di

The ISO 7816-3 specification is not public. So I can't copy/paste part of the text. I will use Wikipedia instead.

From Wikipedia https://en.wikipedia.org/wiki/Answer_to_reset#Interface_byte_TA1 (with some edition to remove extra details):
Interface byte TA1, if present, is global, and encodes the maximum clock frequency fmax supported by the card, and the number of clock periods per ETU that it suggests to use after the ATR, expressed as the ratio Fi/Di of two integers. When TA1 is absent, it's assumed default value is ‘11’, corresponding to fmax = 5 MHz, Fi = 372, Di = 1.

The 4 low-order bits of TA1 (4th MSbit to 1st LSbit) encode Di as:
4th to 1st bits 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
Di RFU 1 2 4 8 16 32 64 12 20 RFU RFU RFU RFU RFU RFU

The 4 high-order bits of TA1 (8th MSbit to 5th LSbit) encode fmax and Fi as:
8th to 5th bits 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
Fi 372 372 558 744 1 116 1 488 1 860 RFU RFU 512 768 1 024 1 536 2 048 RFU RFU
fmax (MHz) 4 5 6 8 12 16 20 5 7.5 10 15 20

TA1#%
93244.98 %
0x1824111.63 %
0x962029.75 %
0x941678.06 %
0x951587.63 %
0x131366.56 %
0x111245.98 %
0x00180.87 %
0x91160.77 %
0x12150.72 %
0x98150.72 %
0x14140.68 %
0x2180.39 %
0x1570.34 %
0x9760.29 %
0x3840.19 %
0x0110.05 %
0x0410.05 %
0x1610.05 %
0x3210.05 %
0x3610.05 %
0x3F10.05 %
0xA810.05 %
0xD610.05 %
0xFF10.05 %


The TA1 value indicates the maximal communication speed between the card and the reader supported by the card. The reader may not support such a high speed. A lower speed will then be used (negotiated by the reader itself or by the reader driver).

Data rate (communication speed)

To know the speed value we need to convert the TA1 value in Fi/Di into a speed value in bit/s.

TA1FiDicycles/ETUbits/s at 4 MhzFmax Mhzbits/s at Fmax
0x01372137210752410752
0x0437284686956486956
0x1637232113636365454545
0x32744237210752821505
0x3674432231739138347826
0x3F744RFURFURFURFURFU
0xA87681264625007.5117187.5
0xD6204832646250020312500
0xFFRFURFURFURFURFURFU
0x387441262645168129032
0x975126485000005625000
0x1537216231739135217391
0x2155815587168610752
0x14372846869565108695
0x12372218621505526881
0x985121242952385119047
0x915121512781259765
0x00372RFURFURFURFURFU
0x11372137210752513440
0x1337249343010553763
0x9551216321250005156250
0x9451286462500578125
0x9651232162500005312500
0x1837212311290325161290
0x11372137210752513440

The table provides the data rate value for a clock of 4 Mhz. We have seen in "CCID descriptor statistics: dwDefaultClock" that 4 Mhz is the default clock of 48% of the CCID readers in my list.

You can note that there is different values of Fi and Di that give the same data rate. For example TA1=0xA8 and TA1=0xD6 both give a data rate of 62500 bits/s when using a clock at 4 MHz.

Population

We can count the number of ATR for each value of TA1. We get the table bellow:
TA1bits/s at 4 Mhzbits/s at Fmax#
0x0110752107521
0x0486956869561
0x163636364545451
0x3210752215051
0x361739133478261
0x3FRFURFU1
0xA862500117187.51
0xD6625003125001
0xFFRFURFU1
0x38645161290324
0x975000006250006
0x151739132173917
0x217168107528
0x148695610869514
0x12215052688115
0x989523811904715
0x917812976516
0x00RFURFU18
0x111075213440124
0x134301053763136
0x95125000156250158
0x946250078125167
0x96250000312500202
0x18129032161290241
1075213440932

We can draw a graph of the number of ATR for a given data rates to get an idea of the repartition:

In the graph I merged the results for a same data rate. So for the value 10 752 bits/s we get the sum of TA1=0x11 and no TA1 so the default value of 0x11. The total is then 932+124=1056.

We can also compute and display the median value:

Half of the cards have a data rate below 86 956 bits/s and the other half has a data rate above 86 956 bits/s.

Using Fmax

The Fi value also gives the maximal clock speed supported by the smart card. The value goes from 4 Mhz to 20 MHz. If we reuse the same examples the maximal clock speed for TA1=0xA8 is 7.5 Mhz and for TA1=0xD6 it is 20 Mhz. The data rates using the maximal clock speed are then quiet different: we have 11 7187.5 bits/s for TA1=0xA8 and 312 500 bits/s for TA1=0xD6.

So we get another distribution graph:

And another median:

This time the median value is 113 871 bits/s.

Reader max clock speed

Note that only 4 readers have a clock that can go up to 20 Mhz (or more). But I guess they are bogus readers.
The highest "common" clock speed is more likely 16 Mhz with 16 readers (3.95%).

Thursday, March 31, 2016

OS X El Capitan bug: SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED instead of SCARD_E_INSUFFICIENT_BUFFER

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

SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED instead of SCARD_E_INSUFFICIENT_BUFFER

SCardGetAttrib() does not work correctly on El Capitan 10.11.4 (on Yosemite the function was not usable at all. See "OS X Yosemite bug: SCardGetAttrib").

When the buffer is too small to store the result the function may return SCARD_E_NOT_TRANSACTED instead of the expected SCARD_E_INSUFFICIENT_BUFFER.

The problem is not present for all the attributes. For example it is the case for SCARD_ATTR_ATR_STRING (Answer to reset (ATR) string) but not for SCARD_ATTR_VENDOR_IFD_SERIAL_NO (Vendor-supplied interface device serial number). This is because for SCARD_ATTR_ATR_STRING the CCID diver checks the buffer size and returns IFD_ERROR_INSUFFICIENT_BUFFER to the PC/SC middleware. In the case of SCARD_ATTR_VENDOR_IFD_SERIAL_NO the buffer size is not checked by the CCID driver and the CCID returns IFD_SUCCESS (the buffer between pcscd and the driver is 264 bytes long and is enough to store a serial number).

It should be the job of the PC/SC middleware to check the user provided buffer is large enough and return SCARD_E_INSUFFICIENT_BUFFER to the application when needed.

See also

Apple bug report #25463286 "PCSC SCardGetAttrib() returns SCARD_E_NOT_TRANSACTED instead of SCARD_E_INSUFFICIENT_BUFFER"

Sample code


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

#define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag)))
#define SCARD_CLASS_ICC_STATE       9   /**< ICC State specific definitions */
#define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) /**< Answer to reset (ATR) string. */

#define RED "\33[01;31m"
#define NORMAL "\33[0m"
#define pcsc_error(fct) printf(RED fct ": %s 0x%08X\n" NORMAL, pcsc_stringify_error(err), err)

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) {
        pcsc_error("ScardEstablishedContext");
        return -1;
    }
    DWORD cchReaders = 0;
    err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
    if (err != 0) {
        pcsc_error("ScardListReaders");
        return -1;
    }
    mszReaders = calloc(cchReaders, sizeof(char));
    if (!mszReaders) {
        pcsc_error("calloc\n");
        return -1;
    }
    err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardListReaders");
        return -1;
    }

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

    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_RAW, &hCard, &dwActiveProtocol);
    if (err != SCARD_S_SUCCESS) {
        pcsc_error("ScardConnect");
    } else {
        DWORD attrLen = 33;
        unsigned char attr[attrLen];
        unsigned int i;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        } else {
            printf("SCARD_ATTR_ATR_STRING: %d\n", attrLen);
            for (i=0; i<attrLen; i++)
                printf("%02X ", attr[i]);
            printf("\n");
        }

        attrLen = 2;
        err = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, attr, &attrLen);
        printf("attrLen: %d\n", attrLen);
        if (err != SCARD_S_SUCCESS) {
            pcsc_error("SCardGetAttrib");
        } else {
            printf("SCARD_ATTR_ATR_STRING: %d\n", attrLen);
            for (i=0; i<attrLen; i++)
                printf("%02X ", attr[i]);
            printf("\n");
        }
    }
    SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    SCardReleaseContext(hContext);
    return 0;
}

The sample code performs 2 calls to SCardGetAttrib() with 2 different buffer sizes.
  • The first time the size is 33 bytes and is big enough to contain the ATR.
  • The second time the size is 2 bytes and is obviously too short.

Result (on El Capitan 10.11.4)

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

$ ./main
Reader: Gemalto PC Twin Reader
attrLen: 23
SCARD_ATTR_ATR_STRING: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 
attrLen: 2
SCardGetAttrib: Transaction failed. 0x80100016

As expected the first SCardGetAttrib() call succeeds and returns the card ATR value.

The second call with pcbAttrLen set to 2 fails and returns the unexpected error SCARD_E_NOT_TRANSACTED.

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC -lpcsclite main.c -o main

$ ./main 
Reader: Gemalto PC Twin Reader (70D7E2EE) 00 00
attrLen: 23
SCARD_ATTR_ATR_STRING: 23
3B FD 94 00 00 81 31 20 43 80 31 80 65 B0 83 02 04 7E 83 00 90 00 B6 
attrLen: 2
SCardGetAttrib: Insufficient buffer. 0x80100008

On GNU/Linux we have the expected behavior: SCARD_E_INSUFFICIENT_BUFFER is returned on the second call.

Known workaround

None known.
Be sure to always use big enough buffers.

Sunday, March 20, 2016

New version of pcsc-lite: 1.8.16

I just released a new version of pcsc-lite 1.8.16.
pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.

Changes:
1.8.16: Ludovic Rousseau
20 March 2016
  • SCardCancel() was not correctly handled
    When a SCardGetStatusChange() was cancelled then a next PC/SC call after the SCardGetStatusChange() may fail with a strange error code if the event waited in SCardGetStatusChange() occurs.
  • Doxygen: fix different documentation issues
  • SCARD_SCOPE_GLOBAL is now defined in a public header (even if never used)
  • Enable Trace and Profile features using compiler flags and without modifying the source code
  • Some other minor improvements and bug corrections

New version of pcsc-tools: 1.4.26

I just released a new version of pcsc-tools, a suite of tools for PC/SC.

Changes:
1.4.26 - 19 March 2016, Ludovic ROUSSEAU
  • 77 new ATRs
  • ATR_analysis: fix display the submission message
  • fix typos in pcsc_scan.1 and scriptor.1p man pages