macOS HighSierra bug: SCardTransmit() returns different error codes than on GNU/Linux and Windows

This is part of the series: "macOS High Sierra and smart cards: known bugs"

SCardTransmit() returns different error codes than on GNU/Linux and Windows

SCardTransmit() on High Sierra returns error codes that are different than on GNU/Linux and Windows.

SCardTransmit() returns SCARD_W_RESET_CARD after a card change

On High Sierra SCardTransmit() returns SCARD_W_RESET_CARD if the card has been removed and inserted again in the reader.

On GNU/Linux and Windows SCardTransmit() returns SCARD_W_REMOVED_CARD instead.

See also

Apple bug report #44638067 "PC/SC SCardTransmit() returns different error codes than on GNU/Linux and Windows". Marked as duplicate of #23900844.

This bug is very similar to a previous bug I found in El Capitan in 2015 "OS X El Capitan bug: SCardBeginTransaction() returns different error codes than on GNU/Linux and Windows". It is the same effects for the same behaviour but now with SCardTransmit() instead of SCardBeginTransaction().

The previous bug has been reported to Apple as bug report #23900844 and is still not closed (or fixed or discussed).

Sample code

#! /usr/bin/env python3

from smartcard.System import readers
from smartcard.util import toBytes

import sys
try:
    r = int(sys.argv[1])
except IndexError as e:
    r = 0

reader = readers()[r]
print("Using:", reader)

connection = reader.createConnection()
connection.connect()

input("Remove and insert the card. Then press Enter")

# Any APDU will work
apdu = toBytes("00 00 00 00 00")
response = connection.transmit(apdu)
print(response)

Result (on High Sierra)

$ ./transmit_remove_card.py 
Using: Gemalto PC Twin Reader
Remove and insert the card. Then press Enter
Traceback (most recent call last):
  File "./transmit_remove_card.py", line 22, in 
    response = connection.transmit(apdu)
  File "/usr/local/lib/python3.7/site-packages/pyscard-1.9.7-py3.7-macosx-10.13-x86_64.egg/smartcard/CardConnectionDecorator.py", line 82, in transmit
    return self.component.transmit(bytes, protocol)
  File "/usr/local/lib/python3.7/site-packages/pyscard-1.9.7-py3.7-macosx-10.13-x86_64.egg/smartcard/CardConnection.py", line 146, in transmit
    data, sw1, sw2 = self.doTransmit(bytes, protocol)
  File "/usr/local/lib/python3.7/site-packages/pyscard-1.9.7-py3.7-macosx-10.13-x86_64.egg/smartcard/pcsc/PCSCCardConnection.py", line 203, in doTransmit
    SCardGetErrorMessage(hresult))
smartcard.Exceptions.CardConnectionException: Failed to transmit with protocol T0. Card was reset.

Expected result (on Debian)


$ ./transmit_remove_card.py
Using: Gemalto PC Twin Reader (70D7E2EE) 00 00
Remove and insert the card. Then press Enter
Traceback (most recent call last):
  File "./transmit_remove_card.py", line 22, in 
    response = connection.transmit(apdu)
  File "/usr/lib/python3/dist-packages/smartcard/CardConnectionDecorator.py", line 82, in transmit
    return self.component.transmit(bytes, protocol)
  File "/usr/lib/python3/dist-packages/smartcard/CardConnection.py", line 146, in transmit
    data, sw1, sw2 = self.doTransmit(bytes, protocol)
  File "/usr/lib/python3/dist-packages/smartcard/pcsc/PCSCCardConnection.py", line 205, in doTransmit
    SCardGetErrorMessage(hresult))
smartcard.Exceptions.CardConnectionException: Failed to transmit with protocol T0. Card was removed.

Why is it important

A card reset error can happen while the card is still in the reader. This is a normal PC/SC behaviour in case of multiple PC/SC applications running at the same time. One application may reset the card after using it.
When an application receive the error SCARD_W_RESET_CARD it can assume that the same card is still present in the reader.

A card removed error SCARD_W_REMOVED_CARD indicates that the card was removed. The same card or a different card may now be present in the reader. The application must assume that a different card has been inserted and that any cached information or application internal state should be refreshed by reading the newly inserted card.

Known workaround

None known.

An idea would be to use the dwEventState field of SCardGetStatusChange(). From the pcsc-lite documentation:
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()

Unfortunately Apple PC/SC does not support this feature. I reported the missing feature to Apple in bug report #23937633 and "OS X El Capitan missing feature: SCardGetStatusChange() and number of card events" in 2015. But nothing moved since then.