Wednesday, April 22, 2020

SCARD_ATTR_CHANNEL_ID and USB devices

Sometimes you need a way to identify which USB device is the smart reader reported by PC/SC.
That was the request of Stephan Guilloux in issue 68 and Pull Request 69.

SCARD_ATTR_CHANNEL_ID

The WinSCard API already provides something like that with the SCARD_ATTR_CHANNEL_ID attribute.

The Microsoft documentation says:
SCARD_ATTR_CHANNEL_ID
DWORD encoded as 0xDDDDCCCC, where DDDD = data channel type and CCCC = channel number:
  • The following encodings are defined for DDDD:
  • 0x01 serial I/O; CCCC is a port number.
  • 0x02 parallel I/O; CCCC is a port number.
  • 0x04 PS/2 keyboard port; CCCC is zero.
  • 0x08 SCSI; CCCC is SCSI ID number.
  • 0x10 IDE; CCCC is device number.
  • 0x20 USB; CCCC is device number.
  • 0xFy vendor-defined interface with y in the range zero through 15; CCCC is vendor defined.
So for a USB device the DDDD value is 0x0020 and we have 2 bytes for the CCCC value.
We decided to use the 2 bytes to encode the bus number in the most significant byte (MSB) and the device address in the least significant byte (LSB).

Implementation

I added the support of this new attribute in the CCID driver version 1.4.32.

I also added support in the PCSC Unitary Test SCardGetAttrib.py to check the code is working correctly.

Example (Low Level Python API)


#! /usr/bin/env python

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

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)

for reader in readers:
    hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, reader,
        SCARD_SHARE_DIRECT, SCARD_PROTOCOL_ANY)
    if hresult != SCARD_S_SUCCESS:
        raise BaseSCardException(hresult)

    print("reader:", reader)
    hresult, attrib = SCardGetAttrib(hcard, SCARD_ATTR_CHANNEL_ID)
    if hresult != SCARD_S_SUCCESS:
        print(SCardGetErrorMessage(hresult))
    else:
        print(attrib)
        # get the DWORD value
        DDDDCCCC = unpack("i", bytearray(attrib))[0]
        DDDD = DDDDCCCC >> 16
        if DDDD == 0x0020:
            bus = (DDDDCCCC & 0xFF00) >> 8
            addr = DDDDCCCC & 0xFF
            print(" USB: bus: {}, addr: {}".format(bus, addr))

    hresult = SCardDisconnect(hcard, SCARD_LEAVE_CARD)
    if hresult != SCARD_S_SUCCESS:
        raise BaseSCardException(hresult)

hresult = SCardReleaseContext(hcontext)
if hresult != SCARD_S_SUCCESS:
    raise ReleaseContextException(hresult)

The important part of the code is the line:
        DDDDCCCC = unpack("i", bytearray(attrib))[0]
As documented by Microsoft, the SCARD_ATTR_CHANNEL_ID attribute does not return a buffer of bytes but a DWORD.
The difference is that the order of the 4 bytes of the DWORD is important and depends on the CPU architecture. The code must take into account the endianess of the CPU. Since the Python program does not know if the CPU is little or big endian we must use unpack() to let the CPU decode the 4 bytes as an integer using its internal endianess.

If you have a better solution please share it.

Output

$ python3 SCardGetAttrib.py 
reader: Gemalto PC Twin Reader 00 00
[8, 1, 32, 0]
 USB: bus: 1, addr: 8
reader: Cherry KC 1000 SC Z [KC 1000 SC Z] 01 00
[5, 1, 32, 0]
 USB: bus: 1, addr: 5

Example (high level Python API)

We can also use the higher level Python API.
The code is much shorter but may be more complex to understand and translate into a PC/SC wrapper in another language.

#! /usr/bin/env python

from smartcard.System import readers
from smartcard.scard import (SCARD_SHARE_DIRECT, SCARD_ATTR_CHANNEL_ID)
from struct import unpack

for reader in readers():
    print("reader:", reader)
    card_connection = reader.createConnection()
    card_connection.connect(mode=SCARD_SHARE_DIRECT)

    attrib = card_connection.getAttrib(SCARD_ATTR_CHANNEL_ID)
    print(attrib)
    DDDDCCCC = unpack("i", bytearray(attrib))[0]
    DDDD = DDDDCCCC >> 16
    if DDDD == 0x0020:
        bus = (DDDDCCCC & 0xFF00) >> 8
        addr = DDDDCCCC & 0xFF
        print(" USB: bus: {}, addr: {}".format(bus, addr))

Output

Of course the output is the same.
$ python3 getAttrib.py 
reader: Gemalto PC Twin Reader 00 00
[8, 1, 32, 0]
 USB: bus: 1, addr: 8
reader: Cherry KC 1000 SC Z [KC 1000 SC Z] 01 00
[5, 1, 32, 0]
 USB: bus: 1, addr: 5

Use case

The use case of this new feature is be able to make a bijective relation between a PC/SC smart card reader and a USB device connected to the host.

The information returned by SCARD_ATTR_CHANNEL_ID is also returned by the lsusb command (on GNU/Linux).
$ lsusb 
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 006: ID 1bcf:0005 Sunplus Innovation Technology Inc. Optical Mouse
Bus 001 Device 005: ID 046a:00a4 Cherry GmbH 
Bus 001 Device 008: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

You can see that the PC/SC reader "Gemalto PC Twin Reader 00 00" is at "USB: bus: 1, addr: 8" for PC/SC and at "Bus 001 Device 008:" for lsusb.

You can also ask lsusb to list only this specific device using:
$ lsusb -s 1:8
Bus 001 Device 008: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader

You may have noticed that the device "Bus 001 Device 005" is the PC/SC reader "Cherry KC 1000 SC Z [KC 1000 SC Z] 01 00" and it is also my keyboard. It is a Cherry KC 1000 SC Z.

More details

You can also get more information about the USB reader at the PC/SC level using CM_IOCTL_GET_FEATURE_REQUEST to get PCSCv2_PART10_PROPERTY_wIdVendor and PCSCv2_PART10_PROPERTY_wIdProduct values.
See "Identifying a reader model (part 2)".

Conclusion

Not everybody will need to use this new feature.
But it was easy to implement and it has no side effect. So why not?

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

Saturday, April 18, 2020

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.

Tuesday, April 7, 2020

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.

Sunday, April 5, 2020

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.