Tuesday, November 17, 2015

OS X El Capitan bug: SCARD_W_RESET_CARD not returned by SCardTransmit()

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

SCARD_W_RESET_CARD not returned by SCardTransmit()

SCardTransmit() do not work correctly on El Capitan (and maybe also on Yosemite but that is untested).

When an application calls SCardTransmit() after the card has been reseted the returned value should be SCARD_W_RESET_CARD to indicate that the card has been reseted.
What happens on El Capitan is that the SCardTransmit() returns SCARD_S_SUCCESS but the card answer will (in general) be incorrect since the card has been reseted.

The error SCARD_W_RESET_CARD is returned from the next SCardTransmit() call.

This behaviour can confuse applications since the SCardTransmit() that should fail just works but with a (possibly) wrong answer from the card.

See also

Apple bug report #23574394 "SCARD_W_RESET_CARD not returned by SCardTransmit()"

Sample code

For the example I used a smart card with a test JavaCard applet loaded. The idea is to send commands to the applet. If the command is send before the applet is selected (after a reset) then the command will fail.

I used my test applet installed in the card.

The 2 programs are written in Python and use the PySCard wrapper so the code is much shorter than if written in C-language.

Looping program


#! /usr/bin/env python

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

# define the APDUs used in this script
SELECT = toBytes("00 A4 04 00 06 A0 00 00 00 18 FF")
COMMAND = toBytes("80 34 00 0E 0E")

# use the 1st available reader
reader = readers()[0]
print "Using:", reader

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

data, sw1, sw2 = connection.transmit(SELECT)

while True:
    data, sw1, sw2 = connection.transmit(COMMAND)
    print data
    print "Command: %02X %02X" % (sw1, sw2)

Reset program


#! /usr/bin/env python

from smartcard.System import readers
from smartcard.scard import SCARD_RESET_CARD

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

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

Expected result (on Debian)

$ ./loop.py
Using: Gemalto PC Twin Reader 00 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
Traceback (most recent call last):
  File "./loop2.py", line 20, in 
    data, sw1, sw2 = connection.transmit(COMMAND)
  File "/usr/lib/python2.7/dist-packages/smartcard/CardConnectionDecorator.py", line 82, in transmit
    return self.component.transmit(bytes, protocol)
  File "/usr/lib/python2.7/dist-packages/smartcard/CardConnection.py", line 144, in transmit
    data, sw1, sw2 = self.doTransmit(bytes, protocol)
  File "/usr/lib/python2.7/dist-packages/smartcard/pcsc/PCSCCardConnection.py", line 198, in doTransmit
    SCardGetErrorMessage(hresult))
smartcard.Exceptions.CardConnectionException: Failed to transmit with protocol T0. Card was reset.

In another terminal I run the reset program:
$ ./reset.py
Using: Gemalto PC Twin Reader

Result (on El Capitan)

$ ./loop.py
Using: Gemalto PC Twin Reader
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Command: 90 00
[]
Command: 69 85
Traceback (most recent call last):
  File "./loop2.py", line 20, in 
    data, sw1, sw2 = connection.transmit(COMMAND)
  File "/Library/Python/2.7/site-packages/pyscard-1.9.0-py2.7-macosx-10.10-intel.egg/smartcard/CardConnectionDecorator.py", line 82, in transmit
    return self.component.transmit(bytes, protocol)
  File "/Library/Python/2.7/site-packages/pyscard-1.9.0-py2.7-macosx-10.10-intel.egg/smartcard/CardConnection.py", line 142, in transmit
    data, sw1, sw2 = self.doTransmit(bytes, protocol)
  File "/Library/Python/2.7/site-packages/pyscard-1.9.0-py2.7-macosx-10.10-intel.egg/smartcard/pcsc/PCSCCardConnection.py", line 205, in doTransmit
    SCardGetErrorMessage(hresult))
smartcard.Exceptions.CardConnectionException: Failed to transmit with protocol T0. Card was reset.

Results interpretation

In the two executions you see mostly the same results. The loop program runs and at one point it fails and returns with the error "Card was reset."

The difference is that on El Capitan, just before returning with the error code, the program returned an error from the card "69 85". This is because the applet is not selected so the command is no more understood by the card. And the applet is no more selected because the card has been reseted.

The last command before the error should never have been sent to the card. That is the problem on El Capitan.

Known workaround

None known.