New version of libccid: 1.4.32

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

Changes:

1.4.32 - 22 April 2020, Ludovic Rousseau
  • Add support of
    • AF Care One (idProduct: 0xAFC0)
    • AF Care One (idProduct: 0xAFC1)
    • AF Care Two (idProduct: 0xAFC2)
    • AF Care Two (idProduct: 0xAFC3)
    • Access IS ATR210
    • Access IS ATR220
    • Cherry GmbH CHERRY SECURE BOARD 1.0
    • Doctolib SR with idProduct: 0xAFD0
    • Doctolib SR with idProduct: 0xAFD1
    • Doctolib SR with idProduct: 0xAFD2
    • Doctolib SR with idProduct: 0xAFD3
    • F-Secure Foundry USB Armory Mk II
    • Gemalto RF CR5400
    • Ledger Nano X support
    • Purism, SPC Librem Key
    • SPECINFOSYSTEMS DIAMOND HSM
    • SPECINFOSYSTEMS DIAMOND PLUS token
    • SPECINFOSYSTEMS DIAMOND PRO token
    • SpringCard E518 (idProduct: 0x6112)
    • SpringCard E518 (idProduct: 0x611A)
    • SpringCard H518 (idProduct: 0x6122)
    • SpringCard H518 (idProduct: 0x612A)
    • SpringCard Puck
    • SpringCard Puck (dProduct: 0x613A)
    • SpringCard SpringCore (idProduct: 0x6012)
    • SpringCard SpringCore (idProduct: 0x601A)
    • Sysking MII136C
  • Add SCardGetAttrib(.., SCARD_ATTR_CHANNEL_ID, ..) for USB devices
  • Increase the timeout used to detect the Identiv uTrust 3700/3701 F readers
  • Fix PowerOn bug for ICCD type A & B devices
  • Fix "Bus Error" on SPARC64 CPU and Solaris C compiler
  • Cherry KC 1000 SC
    • Add support of min & max PIN size
    • Fix a bNumberMessage issue
  • Add support of min & max PIN size for the Omnikey 3821
  • Disable pinpad for Chicony HP Skylab USB Smartcard Keyboard
  • Some minor improvements

PCSC sample in Scala

Here is a new PCSC sample in Scala language I promised in PC/SC sample in different languages.

JetBrains IntelliJ IDEA can convert source code from Java to Scala when you paste Java code inside a Scala project. I just used this possibility and copy/pasted the previous Java source code presented in "PCSC sample in Java using intarsys smartcard-io".

The same PC/SC wrapper can be used for Scala as for Java and Kotlin.

intarsys smartcard-io

The project intarsys smartcard-io is hosted at https://github.com/intarsys/smartcard-io.
The licence is 3-Clause BSD.

Installation

Installation is easy. Just get the provided is-smartcard-io.jar file from deploy/ directory and the 3 runtime dependencies from lib/ directory.

I have not tried to rebuild the library from source.

Source code

The API is easy to use since it is a direct mapping to the PC/SC WinScard API.

import de.intarsys.security.smartcard.pcsc.nativec._IPCSC
import de.intarsys.security.smartcard.pcsc.{IPCSCCardReader, IPCSCConnection, IPCSCContext, PCSCContextFactory}

object Hello extends App {
  try {
    /* Establish context */
    val context = PCSCContextFactory.get.establishContext

    /* Display the list of readers */
    val readers = context.listReaders
    readers.forEach (reader =>
      println("found " + reader + " named " + reader.getName)
    )

    /* Use the first reader */
    val reader = readers.get(0)

    /* Connect to the card */
    val connection = context.connect(reader.getName, _IPCSC.SCARD_SHARE_SHARED, _IPCSC.SCARD_PROTOCOL_Tx)

    /* Send Select Applet command */
    val select: Array[Byte] = Array(0x00, 0xA4.toByte, 0x04, 0x00, 10, 0xA0.toByte, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01)
    var answer: Array[Byte] = null
    answer = connection.transmit(select, 0, select.length, 256, false)
    println("answer: " + answer.length + " bytes")
    answer.foreach (byte =>
      print(String.format("%02X ",byte))
    )
    println()

    /* Send test command */
    val command: Array[Byte] = Array(0x00, 0x00, 0x00, 0x00)
    answer = connection.transmit(command, 0, command.length, 256, false)
    println("answer: " + answer.length + " bytes")
    answer.foreach (byte =>
      print(String.format("%02X ",byte))
    )
    println()
    for (i <- 0 until answer.length - 2) {
      print(answer(i).toChar)
    }
    println()

    /* Disconnect */
    connection.disconnect(_IPCSC.SCARD_LEAVE_CARD)

    /* Release context */
    context.dispose()
  } catch {
    case e: Exception =>
      println("Ouch: " + e.toString)
  }
}

Output

found pcscreader 0 named Cherry KC 1000 SC Z
answer: 2 bytes
90 00 
answer: 14 bytes
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 
Hello world!

Conclusion

I have no real merit with this code. It is an automatic translation from Java to Scala by IntelliJ IDEA. I just made some minor manual changes.

This PC/SC wrapper is easy to use and provides access to all the PC/SC functions.
You can use the same PC/SC wrapper with (at least) 3 different languages: Java, Kotlin and Scala. That is nice.

PCSC sample in Kotlin

Here is a new PCSC sample in Kotlin language I promised in PC/SC sample in different languages.

JetBrains IntelliJ IDEA provides a way to convert a source file from Java to Kotlin. I just used this possibility from the previous Java source code presented in "PCSC sample in Java using intarsys smartcard-io".

The same PC/SC wrapper can be used for Kotlin as for Java.

intarsys smartcard-io

The project intarsys smartcard-io is hosted at https://github.com/intarsys/smartcard-io.
The licence is 3-Clause BSD.

Installation

Installation is easy. Just get the provided is-smartcard-io.jar file from deploy/ directory and the 3 runtime dependencies from lib/ directory.

I have not tried to rebuild the library from source.

Source code

The API is easy to use since it is a direct mapping to the PC/SC WinScard API.


package com.company

import de.intarsys.security.smartcard.pcsc.PCSCContextFactory
import de.intarsys.security.smartcard.pcsc.nativec._IPCSC

object Blog {
    @JvmStatic
    fun main(args: Array<String>) {
        try {
            /* Establish context */
            val context = PCSCContextFactory.get().establishContext()

            /* Display the list of readers */
            val readers = context.listReaders()
            for (reader in readers) {
                println("found " + reader + " named " + reader.name)
            }

            /* Use the first reader */
            val reader = readers[0]

            /* Connect to the card */
            val connection = context.connect(
                    reader.name, _IPCSC.SCARD_SHARE_SHARED,
                    _IPCSC.SCARD_PROTOCOL_Tx)

            /* Send Select Applet command */
            val select = byteArrayOf(0x00, 0xA4.toByte(), 0x04, 0x00, 10, 0xA0.toByte(), 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01)
            var answer: ByteArray
            answer = connection.transmit(select, 0, select.size, 256, false)
            println("answer: " + answer.size + " bytes")
            for (i in answer.indices) {
                print(String.format("%02X ", answer[i]))
            }
            println()

            /* Send test command */
            val command = byteArrayOf(0x00, 0x00, 0x00, 0x00)
            answer = connection.transmit(command, 0, command.size, 256, false)
            println("answer: " + answer.size + " bytes")
            for (i in answer.indices) {
                print(String.format("%02X ", answer[i]))
            }
            println()
            for (i in 0 until answer.size - 2) {
                print(answer[i].toChar())
            }
            println()

            /* Disconnect */
            connection.disconnect(_IPCSC.SCARD_LEAVE_CARD)

            /* Release context */
            context.dispose()
        } catch (e: Exception) {
            println("Ouch: $e")
        }
    }
}

Output

found pcscreader 0 named Cherry KC 1000 SC Z
answer: 2 bytes
90 00 
answer: 14 bytes
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 
Hello world!

Conclusion

I have no real merit with this code. I did not write it myself and it is a direct translation from Java to Kotlin.

This PC/SC wrapper is easy to use and provides access to all the PC/SC functions.

PCSC sample in Java using intarsys smartcard-io

Here is a new PCSC sample in Java language I promised in PC/SC sample in different languages.

We already saw in a previous article "PCSC sample in Java" that the standard JVM includes javax.smartcardio to access smart cards. We will now see another Java wrapper.

intarsys smartcard-io

The project intarsys smartcard-io is hosted at https://github.com/intarsys/smartcard-io.
The licence is 3-Clause BSD.

Installation

Installation is easy. Just get the provided is-smartcard-io.jar files from deploy/ directory and the 3 runtime dependencies from lib/ directory.

I have not tried to rebuild the library from source.

Source code

The API is easy to use since it is a direct mapping to the PC/SC WinScard API.


package com.company;

import de.intarsys.security.smartcard.pcsc.IPCSCCardReader;
import de.intarsys.security.smartcard.pcsc.IPCSCConnection;
import de.intarsys.security.smartcard.pcsc.IPCSCContext;
import de.intarsys.security.smartcard.pcsc.PCSCContextFactory;
import de.intarsys.security.smartcard.pcsc.nativec._IPCSC;

import java.util.List;

public class Blog {
    public static void main(String[] args) {
        try {
            /* Establish context */
            IPCSCContext context = PCSCContextFactory.get().establishContext();

            /* Display the list of readers */
            List<IPCSCCardReader> readers = context.listReaders();
            for (IPCSCCardReader reader : readers) {
                System.out.println("found " + reader + " named " + reader.getName());
            }

            /* Use the first reader */
            IPCSCCardReader reader = readers.get(0);

            /* Connect to the card */
            IPCSCConnection connection = context.connect(
                    reader.getName(), _IPCSC.SCARD_SHARE_SHARED,
                    _IPCSC.SCARD_PROTOCOL_Tx);

            /* Send Select Applet command */
            byte[] select = {0x00, (byte)0xA4, 0x04, 0x00, 10, (byte)0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01};
            byte[] answer;
            answer = connection.transmit(select, 0, select.length, 256, false);
            System.out.println("answer: " + answer.length + " bytes");
            for (int i=0; i<answer.length; i++) {
                System.out.print(String.format("%02X ", answer[i]));
            }
            System.out.println();

            /* Send test command */
            byte[] command = {0x00, 0x00, 0x00, 0x00};
            answer = connection.transmit(command, 0, command.length, 256, false);
            System.out.println("answer: " + answer.length + " bytes");
            for (int i=0; i<answer.length; i++) {
                System.out.print(String.format("%02X ", answer[i]));
            }
            System.out.println();
            for (int i=0; i<answer.length - 2; i++) {
                System.out.print((char)answer[i]);
            }
            System.out.println();

            /* Disconnect */
            connection.disconnect(_IPCSC.SCARD_LEAVE_CARD);

            /* Release context */
            context.dispose();
        } catch(Exception e) {
            System.out.println("Ouch: " + e.toString());
        }
    }
}

Output

found pcscreader 0 named Gemalto PC Twin Reader
found pcscreader 1 named Cherry KC 1000 SC Z
answer: 2 bytes
90 00 
answer: 14 bytes
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 
Hello world!

High level API

Intarsys smartcard-io also provides a higher level API compatible with javax.smartcardio.
As documented in the project README:
javax.smartcardio Provider

The library comes with an alternative javax.smartcardio provider. There are a couple of things to consider:

Intended differences
  • dedicated PCSC context for terminals, terminal and card
  • waitForChange(timeout) semantics improved(?), state change is reset even in case of timeout
  • reader insertion is handled, too
  • no finalizer for card!

javax.smartcardio bug/limitation

One big problem with the SUN/Oracle implementation of javax.smartcardio is that only one PC/SC context is created.
It works fine with a mono-threaded application. But it is very problematic with a multi-threaded application if you want to access different readers at the same time from different threads.

As documented in pcsc-lite SCardEstablishContext():
Each thread of an application shall use its own SCARDCONTEXT

This is because only one thread can use a SCARDCONTEXT at the same time. So 2 SCardTransmit() calls on 2 different cards from 2 threads but using the same SCARDCONTEXT will be serialized by pcsc-lite. This is NOT what you want if you intent to execute the 2 card commands at the same time.

The problem is not present on Windows because the Microsoft WinSCard implement is different.
I don't know how javax.smartcardio behaves on macOS. The WinSCard on macOS has been rewritten using the low level API CryptoTokenKit since macOS Yosemite 10.10 in 2014 (see "OS X Yosemite and smart cards status") and is no more using pcsc-lite.

Conclusion

This wrapper is easy to use and provides access to all the PC/SC functions.

ATR statistics: ATR list growth (part 2)

In January 2016 I started a study of each byte contained in a smart card ATR. See "ATR list study".

My goal was to publish one new article every week. So with 19 bytes that was a job for 5 months. But it took me more than 4 years! I imagine it is because of some lack of time and/or motivation.

My first article was about the ATR list growth. In January 2016 I had 2098 ATRs in the list. Now I have 3316 ATRs in the list and the number is still growing.

Graph


The red line indicates the position in January 2016.

New ATRs only

In 2016 the linear regression for the graph was y = 6.308x10-6 x - 940.4.
For the last 4 years it is y = 9.381x10-6 x - 2409.7

The growth is now 48% faster than before. Woah!

I guess that is because it is very simple to submit a new ATR using the web interface at https://smartcard-atr.apdu.fr/. Before I create this service, in 2010, you had to send me a new ATR by email. Now you just (mostly) click on a button.

No, I will not re-compute all the statistics with the new, more complete, ATR list.

Thank you

to all of you who submit new ATRs.

ATR statistics: TCK - Check byte TCK (conditional)

Article from the series "ATR statistics".

TCK - Check byte TCK (conditional)

The ISO 7816-3 specification is not public. So I can't copy/paste part of the text. I will use Wikipedia instead.
The ChecK byte (if present) allows a check of the integrity of the data in the ATR. If present, TCK is the Exclusive OR of the bytes in the ATR from T0 (included) to TCK (excluded).
TCK shall be present if and only if any of the TDi present in the ATR encodes a value of T other than 0.

TCK # %
1103 53.23 %
0x00 123 5.94 %
0x12 9 0.43 %
0x28 9 0.43 %
0x05 8 0.39 %
0x89 8 0.39 %
0x99 8 0.39 %
0xC1 8 0.39 %
0x03 7 0.34 %
0x09 7 0.34 %
0x0E 7 0.34 %
0x16 7 0.34 %
0x1C 7 0.34 %
0x38 7 0.34 %
0x43 7 0.34 %
0x65 7 0.34 %
0x88 7 0.34 %
0x8C 7 0.34 %
0xA5 7 0.34 %
0xB7 7 0.34 %
0x06 6 0.29 %
0x0D 6 0.29 %
0x15 6 0.29 %
0x1B 6 0.29 %
0x4A 6 0.29 %
0x52 6 0.29 %
0x59 6 0.29 %
0x5A 6 0.29 %
0x62 6 0.29 %
0x6A 6 0.29 %
0x6D 6 0.29 %
0x76 6 0.29 %
0x77 6 0.29 %
0x7A 6 0.29 %
0x7F 6 0.29 %
0x83 6 0.29 %
0x90 6 0.29 %
0x9C 6 0.29 %
0xA1 6 0.29 %
0xA9 6 0.29 %
0xF3 6 0.29 %
0xFF 6 0.29 %
0x-1 5 0.24 %
0x0A 5 0.24 %
0x0F 5 0.24 %
0x14 5 0.24 %
0x18 5 0.24 %
0x1E 5 0.24 %
0x31 5 0.24 %
0x3A 5 0.24 %
0x3C 5 0.24 %
0x45 5 0.24 %
0x58 5 0.24 %
0x6B 5 0.24 %
0x75 5 0.24 %
0x7E 5 0.24 %
0x80 5 0.24 %
0x84 5 0.24 %
0x8F 5 0.24 %
0x98 5 0.24 %
0xB6 5 0.24 %
0xBD 5 0.24 %
0xC4 5 0.24 %
0xCC 5 0.24 %
0xD2 5 0.24 %
0xDA 5 0.24 %
0xFE 5 0.24 %
0x07 4 0.19 %
0x17 4 0.19 %
0x19 4 0.19 %
0x1A 4 0.19 %
0x1D 4 0.19 %
0x21 4 0.19 %
0x24 4 0.19 %
0x32 4 0.19 %
0x39 4 0.19 %
0x3F 4 0.19 %
0x49 4 0.19 %
0x4C 4 0.19 %
0x5C 4 0.19 %
0x5E 4 0.19 %
0x60 4 0.19 %
0x63 4 0.19 %
0x68 4 0.19 %
0x6C 4 0.19 %
0x70 4 0.19 %
0x71 4 0.19 %
0x74 4 0.19 %
0x79 4 0.19 %
0x86 4 0.19 %
0x8A 4 0.19 %
0x91 4 0.19 %
0x95 4 0.19 %
0x9B 4 0.19 %
0x9D 4 0.19 %
0x9F 4 0.19 %
0xA0 4 0.19 %
0xA2 4 0.19 %
0xA6 4 0.19 %
0xB0 4 0.19 %
0xB4 4 0.19 %
0xBE 4 0.19 %
0xC9 4 0.19 %
0xD0 4 0.19 %
0xD6 4 0.19 %
0xE0 4 0.19 %
0xE1 4 0.19 %
0xE9 4 0.19 %
0xEC 4 0.19 %
0xF8 4 0.19 %
0x01 3 0.14 %
0x02 3 0.14 %
0x08 3 0.14 %
0x0C 3 0.14 %
0x1F 3 0.14 %
0x2A 3 0.14 %
0x30 3 0.14 %
0x33 3 0.14 %
0x3D 3 0.14 %
0x47 3 0.14 %
0x4D 3 0.14 %
0x4E 3 0.14 %
0x4F 3 0.14 %
0x53 3 0.14 %
0x55 3 0.14 %
0x57 3 0.14 %
0x5B 3 0.14 %
0x6F 3 0.14 %
0x72 3 0.14 %
0x78 3 0.14 %
0x7B 3 0.14 %
0x7D 3 0.14 %
0x82 3 0.14 %
0x92 3 0.14 %
0x94 3 0.14 %
0x97 3 0.14 %
0x9A 3 0.14 %
0x9E 3 0.14 %
0xA8 3 0.14 %
0xAA 3 0.14 %
0xAB 3 0.14 %
0xAD 3 0.14 %
0xB1 3 0.14 %
0xB9 3 0.14 %
0xBC 3 0.14 %
0xC2 3 0.14 %
0xC5 3 0.14 %
0xCF 3 0.14 %
0xD3 3 0.14 %
0xD4 3 0.14 %
0xD7 3 0.14 %
0xDF 3 0.14 %
0xEF 3 0.14 %
0xF2 3 0.14 %
0xF4 3 0.14 %
0xF7 3 0.14 %
0xFA 3 0.14 %
0x10 2 0.10 %
0x11 2 0.10 %
0x13 2 0.10 %
0x22 2 0.10 %
0x25 2 0.10 %
0x26 2 0.10 %
0x27 2 0.10 %
0x29 2 0.10 %
0x2B 2 0.10 %
0x2D 2 0.10 %
0x2E 2 0.10 %
0x35 2 0.10 %
0x36 2 0.10 %
0x3B 2 0.10 %
0x40 2 0.10 %
0x41 2 0.10 %
0x48 2 0.10 %
0x50 2 0.10 %
0x51 2 0.10 %
0x54 2 0.10 %
0x5D 2 0.10 %
0x5F 2 0.10 %
0x64 2 0.10 %
0x66 2 0.10 %
0x67 2 0.10 %
0x69 2 0.10 %
0x73 2 0.10 %
0x7C 2 0.10 %
0x85 2 0.10 %
0x87 2 0.10 %
0x8B 2 0.10 %
0x8D 2 0.10 %
0x8E 2 0.10 %
0xAC 2 0.10 %
0xAF 2 0.10 %
0xBA 2 0.10 %
0xBB 2 0.10 %
0xC3 2 0.10 %
0xC7 2 0.10 %
0xC8 2 0.10 %
0xD5 2 0.10 %
0xD8 2 0.10 %
0xDC 2 0.10 %
0xDD 2 0.10 %
0xDE 2 0.10 %
0xE4 2 0.10 %
0xE5 2 0.10 %
0xE8 2 0.10 %
0xEA 2 0.10 %
0xEB 2 0.10 %
0xF0 2 0.10 %
0xF6 2 0.10 %
0xFB 2 0.10 %
0xFC 2 0.10 %
0xFD 2 0.10 %
0x04 1 0.05 %
0x0B 1 0.05 %
0x23 1 0.05 %
0x2C 1 0.05 %
0x2F 1 0.05 %
0x3E 1 0.05 %
0x42 1 0.05 %
0x46 1 0.05 %
0x56 1 0.05 %
0x61 1 0.05 %
0x81 1 0.05 %
0x93 1 0.05 %
0x96 1 0.05 %
0xA3 1 0.05 %
0xA7 1 0.05 %
0xAE 1 0.05 %
0xB2 1 0.05 %
0xB3 1 0.05 %
0xB5 1 0.05 %
0xC6 1 0.05 %
0xCA 1 0.05 %
0xCB 1 0.05 %
0xCE 1 0.05 %
0xD1 1 0.05 %
0xD9 1 0.05 %
0xDB 1 0.05 %
0xE2 1 0.05 %
0xE3 1 0.05 %
0xEE 1 0.05 %
0xF1 1 0.05 %
0xF5 1 0.05 %
0xF9 1 0.05 %


A large part (53%) or ATR do not have a TCK. These cards are T=0 only.

Some ATR have a TCK of 0 but this value is invalid. For example with 3B 88 80 01 00 00 00 00 77 83 95 00 00.
In most cases the TCK has a value of 0 but that is because a pattern is used in the ATR matching. For example with 3B 8C 80 01 04 43 FD 00 00 00 00 00 00 00 00 00 00. The pattern used to match the ATR is "3B 8C 80 01 04 43 FD .. .. .. .. .. .. .. .. .. .." to accept any value for the "." but my program replaced any "." by a "0".

Some ATR should have an ATR but do NOT contain it. For example with 3B 9E 96 80 1F C7 80 31 E0 73 FE 21 1B 66 D0 01 77 97 0D 00. My parsing program indicates TCK = 0x-1 in this case but that is only a way to indicate a missing TCK. Only 4 ATRs are in this category.

Since the TCK is a checksum no special value is expected. So it is not surprising that the values from 1 to 255 have a mostly equal distribution.

ATR statistics: Historical bytes - Historical bytes Ti (optional)

Article from the series "ATR statistics"

Historical bytes Ti (optional)

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#Historical_bytes_Ti:
Historical Characters Ti for i≥1, if present (as defined by K coded in T0), typically hold Information about the Card Builder, Type of Card (Size etc.), Version number and the State of the Card.

You can read the previous article about the T0 byte at ATR statistics: T0 - Format byte.

Regarding the T0 high nibble values we have:

hbn # %
0x0F 743 35.86 %
0x0E 212 10.23 %
0x0D 150 7.24 %
0x07 136 6.56 %
0x05 128 6.18 %
0x08 116 5.60 %
0x0B 115 5.55 %
0x0A 107 5.16 %
0x09 90 4.34 %
0x06 86 4.15 %
0x0C 72 3.47 %
0x04 46 2.22 %
0x02 40 1.93 %
0x03 15 0.72 %
0x00 13 0.63 %
0x01 3 0.14 %




A large part (38%) of the ATR have 15 (0x0F) historical bytes, i.e. the maximum size.

The bytes can contain application information like for 3B 6F 00 FF 00 56 72 75 54 6F 6B 6E 73 30 20 00 00 90 00
Historical bytes 00 56 72 75 54 6F 6B 6E 73 30 20 00 00 90 00
Category indicator byte: 0x00 (compact TLV data object)
Tag: 5, Len: 6 (card issuer's data)
Card issuer data: 72 75 54 6F 6B 6E "ruTokn"
Tag: 7, Len: 3 (card capabilities)
Selection methods: 48
- DF selection by file identifier
- DF selection by path
Data coding byte: 32
- Behaviour of write functions: proprietary
- Value 'FF' for the first byte of BER-TLV tag fields: valid
- Data unit in quartets: 0
Command chaining, length fields and logical channels: 0
- Logical channel number assignment: No logical channel
- Maximum number of logical channels: 1
Mandatory status indicator (3 last bytes)
LCS (life card cycle): 0 (No information given)
SW: 90 00 ()
or contain only the card name like for 3B 6F 00 FF 52 53 41 53 65 63 75 72 49 44 28 52 29 31 30
Historical bytes 52 53 41 53 65 63 75 72 49 44 28 52 29 31 30
Category indicator byte: 0x52 (proprietary format) "SASecurID(R)10"

I do not think it is important to detail all the possible information contained in the historical bytes.

For those who are interested you can read the ISO 7816-4 (note: not the 7816-3) chapter "8.1.1 Historical bytes".

ATR statistics: TC4

Article from the series "ATR statistics".

TC4

TC4 should have the same meaning as TC3.

For T = 1: type of error detection code used

Bit 1 of the first TC for T=1 indicates the error detection code to be used:
  • CRC if bit 1 is set to 1;
  • LRC (default value) if bit 1 is set to 0.
Bits 8 to 2 of the first TC for T=1 are reserved for future use and shall be set to 0.

TC4 # %
2072 100.00 %

In my list of ATR none of them defines an TC4.

Wait for a card state change: SCardGetStatusChange()

I am "often" asked for a way to detect a card movement. There is a simple active loop solution but the correct answer is to use SCardGetStatusChange()

Simple but inefficient approach

It is possible to get a reader status using SCardStatus(). It is then possible to imagine something like:

while(TRUE)
{
  SCardStatus(hCard, NULL, 0, &dwState, NULL, NULL, NULL);
  if (dwState & SCARD_PRESENT)
  {
    do stuff
  }
  sleep(1);
}

The problems with this code are:
  • it consumes CPU cycles even when nothing happens
  • when the code is executing the sleep(1) then no card movement is detected. So you can wait for up to 1 second before you detect a card event. You can shorted the delay to 0.1 second but then you will waste even more CPU cycles.
Of course this solution is not efficient and not recommanded.

Correct solution: SCardGetStatusChange()

The WinSCard API provides the function SCardGetStatusChange() to wait for events.

You give a list of reader names and the function returns when the state of one (or more) reader changes, or a timeout occurs. I let you read the API documentation for the details.

You can interrupt the function using SCardCancel() if needed. The function will then return before the expiration of the timeout.

Source code


#include <stdio.h>
#include <stdlib.h>

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

#define CHECK(f, rv) printf(f ":[0x%08lX] %s\n", rv, pcsc_stringify_error(rv))

int main(void)
{
    LONG rv;
    SCARDCONTEXT hContext;
    LPTSTR mszReaders = NULL;
    DWORD dwReaders;

    rv = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
    CHECK("SCardEstablishContext", rv);

#ifdef SCARD_AUTOALLOCATE
    dwReaders = SCARD_AUTOALLOCATE;

    rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders);
    CHECK("SCardListReaders", rv);
#else
    rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
    CHECK("SCardListReaders", rv);

    mszReaders = calloc(dwReaders, sizeof(char));
    rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
    CHECK("SCardListReaders", rv);
#endif

    if (dwReaders > 1)
    {
 printf("Using reader: %s\n", mszReaders);

 SCARD_READERSTATE reader_states[] = {
     {
  .szReader = mszReaders,
  .pvUserData = NULL,
  .dwCurrentState = SCARD_STATE_UNAWARE,
  .dwEventState = SCARD_STATE_UNAWARE,
     },
 };

 /* first call to get the current state */
 rv = SCardGetStatusChange(hContext, 0, reader_states, 1);
 CHECK("SCardGetStatusChange", rv);

 /* set the state we expect to change */
 reader_states[0].dwCurrentState = reader_states[0].dwEventState;

 /* wait for a state change, or a timeout after 3 seconds */
 rv = SCardGetStatusChange(hContext, 3 * 1000, reader_states, 1);
 CHECK("SCardGetStatusChange", rv);

 printf(".dwEventState: 0x%08lX\n", reader_states[0].dwEventState);
    }

#ifdef SCARD_AUTOALLOCATE
    rv = SCardFreeMemory(hContext, mszReaders);
    CHECK("SCardFreeMemory", rv);
#else
    free(mszReaders);
#endif

    rv = SCardReleaseContext(hContext);
    CHECK("SCardReleaseContext", rv);

    return 0;
}

Comments

The code uses the first reader returned by SCardListReaders(). I wanted to keep the code very simple. Adding support of multi readers is left as an execise to the reader.

pcsc-lite on GNU/Linux supports the auto allocation. But the PC/SC API on macOS does not. That is why I use the #ifdef SCARD_AUTOALLOCATE.

Outputs

The outputs on GNU/Linux and macOS are slightly different. Can you find the difference (except the reader name)?

GNU/Linux

$ ./sample 
SCardEstablishContext:[0x00000000] Command successful.
SCardListReaders:[0x00000000] Command successful.
Using reader: Gemalto PC Twin Reader 00 00
SCardGetStatusChange:[0x00000000] Command successful.
SCardGetStatusChange:[0x00000000] Command successful.
.dwEventState: 0x00050022
SCardFreeMemory:[0x00000000] Command successful.
SCardReleaseContext:[0x00000000] Command successful.

macOS


$ ./sample
SCardEstablishContext:[0x00000000] Command successful.
SCardListReaders:[0x00000000] Command successful.
SCardListReaders:[0x00000000] Command successful.
Using reader: Cherry KC 1000 SC Z
SCardGetStatusChange:[0x00000000] Command successful.
SCardGetStatusChange:[0x00000000] Command successful.
.dwEventState: 0x00000022
SCardReleaseContext:[0x00000000] Command successful.

.dwEventState

The difference is the .dwEventState value. On GNU/Linux the 16 upper bits are not 0.

From the 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()

In the GNU/Linux output we have .dwEventState: 0x00050022 so we got 5 card events for this reader. The events were card insertion, removal, insertion, removal and insertion.

The lower 16-bits value is 0x0022 in both cases and it indicates SCARD_STATE_CHANGED and SCARD_STATE_PRESENT.

Other language

SCardGetStatusChange() is for the C language API. Since it is possible to use the smart card API with many languages (at least 23) each wrapper has (or should have) its own equivalent.

For example in Python you can use the low level API with the Python method SCardGetStatusChange() or use the higher API with the CardMonitor() class.

Conclusion

It is easy to use SCardGetStatusChange(). Please use it when needed.

It is also easy to misuse it. After writting this article I (re)discovered that I already wrote about SCardGetStatusChange() in "How to use SCardGetStatusChange()?" two years ago. This articles has different details and may also help you.