Thursday, December 17, 2015

OS X El Capitan missing feature: SCardGetStatusChange() and number of card events

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

SCardGetStatusChange() and number of card events

SCardGetStatusChange() does not return the number of card events in the upper word of dwEventState.

This is not a bug since I don't think this feature was every present in the PC/SC from Apple. It is a feature request and not a bug report.

From the Doxygen documentation of pcsc-lite:
dwEventState also contains a number of events in the upper 16 bits (dwEventState & 0xFFFF0000). This number of events is incremented for each card insertion or removal in the specified reader. This can be used to detect a card removal/insertion between two calls to SCardGetStatusChange().
This behaviour is also present in PC/SC on Windows.

It is the only way to know that the card has been removed from a reader and inserted again while no call to SCardGetStatusChange() was running. Potentially the inserted card is not the same as the removed card so the PC/SC application may have to build a new applicative context for the new card.

See also

Apple bug report #23937633 "PC/SC SCardGetStatusChange() and number of card events"

Sample code

Using the Python PC/SC wrapper PySCard to have a shorter code.

#! /usr/bin/env python

from smartcard.scard import *
from smartcard.pcsc.PCSCExceptions import *

def parseEventState(eventstate):
    stateList = list()
    states = {SCARD_STATE_IGNORE: "Ignore",
              SCARD_STATE_CHANGED: "Changed",
              SCARD_STATE_UNKNOWN: "Unknown",
              SCARD_STATE_UNAVAILABLE: "Unavailable",
              SCARD_STATE_EMPTY: "Empty",
              SCARD_STATE_PRESENT: "Present",
              SCARD_STATE_ATRMATCH: "ATR match",
              SCARD_STATE_EXCLUSIVE: "Exclusive",
              SCARD_STATE_INUSE: "In use",
              SCARD_STATE_MUTE: "Mute"}
    for state in states:
        if eventstate & state:
    return stateList

hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
    raise EstablishContextException(hresult)

hresult, readers = SCardListReaders(hcontext, [])
if hresult != SCARD_S_SUCCESS:
    raise ListReadersException(hresult)
print 'PC/SC Readers:', readers

readerstates = {}
for reader in readers:
    readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
hresult, newstates = SCardGetStatusChange(hcontext, 0, readerstates.values())
if hresult != SCARD_S_SUCCESS:
    raise BaseSCardException(hresult)
print newstates
for readerState in newstates:
    readername, eventstate, atr = readerState
    readerstates[readername] = (readername, eventstate)

print "Move the smart card"

ok = True
while ok:
        hresult, newstates = SCardGetStatusChange(hcontext, INFINITE, newstates)
        if hresult != SCARD_S_SUCCESS and hresult != SCARD_E_TIMEOUT:
            raise BaseSCardException(hresult)
    except KeyboardInterrupt:
        ok = False
    for readerState in newstates:
        readername, eventstate, atr = readerState
        states = parseEventState(eventstate)
        print states, hex(eventstate)

hresult = SCardReleaseContext(hcontext)
print "SCardReleaseContext()", SCardGetErrorMessage(hresult)
if hresult != SCARD_S_SUCCESS:
    raise ReleaseContextException(hresult)

Result (on El Capitan)

$ ./
PC/SC Readers: ['Gemalto PC Twin Reader']
[('Gemalto PC Twin Reader', 18, [])]
Move the smart card
['Changed', 'Present'] 0x22
['Empty', 'Changed'] 0x12
['Changed', 'Present'] 0x22
['Empty', 'Changed'] 0x12
^C['Empty', 'Changed'] 0x12
SCardReleaseContext() Command successful.

For the tests I insert and remove the card 2 times.

Then press Control-C and insert the card to quit the program.

Expected result (on Debian)

$ ./ 
PC/SC Readers: ['Gemalto PC Twin Reader 00 00']
[('Gemalto PC Twin Reader 00 00', 18, [])]
Move the smart card
['Changed', 'Present'] 0x10022
['Empty', 'Changed'] 0x20012
['Changed', 'Present'] 0x30022
['Empty', 'Changed'] 0x40012
^C['Empty', 'Changed'] 0x40012
SCardReleaseContext() Command successful.

The change is the value eventstate displayed (in hexadecimal).

On Debian the high word (upper 16 bits) takes the values 1, 2, 3 and 4.

If the value change from 0x10022 to 0x30022, the reader state is the same (SCARD_STATE_CHANGED and SCARD_STATE_PRESENT) but you know that the card has been removed and inserted.

Known workaround

None known.

[Update 2022-08-31, after 7 years]

Apple just answered to my bug report, now called feedback.  The bug report #23937633 is now known as FB7531166. Apple answer is (I have it in French in my Feedback Assistant application. Surprising!):

"Thank you for your review and patience. We apologize for the late response. We do not intend to treat this report as an enhancement.

Indeed, adding new features to the existing PC/SC interface is not on the agenda. However, we will consider this suggestion in case our plans change."

Apple is reviewing (old) PC/SC issues. Nice to know.
But do not plan to add new features.