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.

Update: 27th September 2016

This bug is fixed in macOS Sierra 10.12.0.

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

Monday, March 7, 2016

ATR statistics: T0 - Format byte

Article from the series "ATR statistics"

T0 - Format byte

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#Format_byte_T0:

Format byte T0

The Format byte T0 encodes in its 4 low-order bits (4th MSbit to 1st LSbit) the number K of historical bytes Ti, in range [0..15].
It also encodes in its 4 high-order bits the presence of at most 4 other interface bytes: TA1 (resp. TB1, TC1, TD1) follow, in that order, if the 5th (resp. 6th, 7th, 8th) bit of T0 is 1.

T0#%
0xFF1607.72 %
0x9F1457.00 %
0x8F1085.21 %
0x6F1014.87 %
0x7F874.20 %
0x6E823.96 %
0x65582.80 %
0x9E512.46 %
0x7D452.17 %
0x67432.08 %
0xEF401.93 %
0x88371.79 %
0x6D311.50 %
0xFD281.35 %
0x3F251.21 %
0xFA251.21 %
0x68241.16 %
0x04231.11 %
0x3B221.06 %
0x85221.06 %
0xF7221.06 %
0xDB211.01 %
0xBE200.97 %
0xBF200.97 %
0x16190.92 %
0x6B190.92 %
0x8C190.92 %
0xDF190.92 %
0xFB190.92 %
0x8A180.87 %
0xF9170.82 %
0x1F160.77 %
0x66160.77 %
0x69160.77 %
0x7E160.77 %
0x89160.77 %
0x78150.72 %
0x95150.72 %
0xFE150.72 %
0x17130.63 %
0x77130.63 %
0x8E130.63 %
0xDD130.63 %
0xF8130.63 %
0x2F120.58 %
0x6A120.58 %
0x6C120.58 %
0x86120.58 %
0x8B120.58 %
0xE9120.58 %
0x7A110.53 %
0x98110.53 %
0x9A100.48 %
0x9D100.48 %
0xBA100.48 %
0x2790.43 %
0x8290.43 %
0x8790.43 %
0xBC90.43 %
0xE690.43 %
0x0680.39 %
0x1980.39 %
0x2A80.39 %
0x7B80.39 %
0x8480.39 %
0xF280.39 %
0xFC80.39 %
0x0F70.34 %
0x2670.34 %
0x3D70.34 %
0x7570.34 %
0x7970.34 %
0xB770.34 %
0xD570.34 %
0xEA70.34 %
0xF570.34 %
0x1560.29 %
0x2360.29 %
0x3C60.29 %
0x7660.29 %
0xA760.29 %
0x0250.24 %
0x2450.24 %
0x8D50.24 %
0xB250.24 %
0xE250.24 %
0xE850.24 %
0xEE50.24 %
0x0540.19 %
0x1840.19 %
0x7C40.19 %
0x9C40.19 %
0xB340.19 %
0xD940.19 %
0xDC40.19 %
0xDE40.19 %
0xE740.19 %
0xF040.19 %
0xF640.19 %
0x0730.14 %
0x1230.14 %
0x1B30.14 %
0x1D30.14 %
0x2930.14 %
0x3730.14 %
0x3E30.14 %
0x5F30.14 %
0x6430.14 %
0x9730.14 %
0x9930.14 %
0x9B30.14 %
0xB930.14 %
0xBB30.14 %
0xBD30.14 %
0xD230.14 %
0xD630.14 %
0xEC30.14 %
0xED30.14 %
0xF430.14 %
0x0A20.10 %
0x3220.10 %
0x3420.10 %
0x5B20.10 %
0x8120.10 %
0x8320.10 %
0x9020.10 %
0x9620.10 %
0xA820.10 %
0xAA20.10 %
0xAC20.10 %
0xB020.10 %
0xB820.10 %
0xD820.10 %
0xDA20.10 %
0xE020.10 %
0xE520.10 %
0xEB20.10 %
0x0010.05 %
0x0910.05 %
0x0E10.05 %
0x1C10.05 %
0x1E10.05 %
0x2810.05 %
0x2D10.05 %
0x5710.05 %
0x5E10.05 %
0x6310.05 %
0x7410.05 %
0x8010.05 %
0x9110.05 %
0x9410.05 %
0xAB10.05 %
0xAD10.05 %
0xD010.05 %
0xE310.05 %
0xF310.05 %


Doing statistics on the complete T0 byte is not really informative.

Interface nibble

format#%
130.14 %
0130.63 %
3150.72 %
2401.93 %
4462.22 %
12723.47 %
6864.15 %
9904.34 %
101075.16 %
111155.55 %
81165.60 %
51286.18 %
71366.56 %
131507.24 %
1421210.23 %
1574335.86 %


Interpretation

In 36% of the ATRs the LSB is equal to 15 (or 1111b in binary) indicating that TA1, TB1, TC1, TD1 are present.

For 10% of ATRs the LSB is equal to 14 (or 1110b in binary) indicating that TB1, TC1, TD1 are present, and TA1 is not present.

For 0.63% of ATRs the LSB is equal to 0 (or 0000b in binary) indicate that TA1, TB1, TC1, TD1 are all absent.

A better way to display the result is to isolate TA1, TB1, TC1, TD1 and count them independently.
interface#%
TA199648.07 %
TB1135565.40 %
TC1138166.65 %
TD151925.05 %


Historic nibble

historic#%
400.00 %
570.34 %
10140.68 %
2522.51 %
0552.65 %
3703.38 %
1773.72 %
13834.01 %
11884.25 %
141004.83 %
722010.62 %
926112.60 %
829314.14 %
1533416.12 %
641820.17 %


The number of historic bytes is not equally distributed between 0 and 15 bytes. I guess that is because the historic bytes are coded using a TLV format so some sizes are more frequent than others.