Pre-built parse binaries

Since October 2023, I've been offering a simple way to check if a reader whether or not a reader complies with the CCID standard. See Check reader's compliance to CCID specification.

Parse tool

As early as the first version of my CCID driver, I provided a tool called parse to analyze the USB descriptor and see if a connected device is declared as a CCID devide (USB interface class = 11).

The parse tool is written in C and you have to build it from source code. This is not an easy task for non-developers.

Non-developers

For non developers, it wasn't easy. Hence the idea of providing pre-built versions of the parse tool.

I can't provide a binary for all possible systems (Unix, not just GNU/Linux).

I provide a binary version for:

  • Linux x86_64 (GNU/Linux on Intel/AMD 64 bits CPU)

  • Linux aarch64 (GNU/Linux on ARM 64 bits CPU)

  • Linux armv7l (GNU/Linux on for example on a Raspberry)

  • Darwin x86_64 (macOS on Intel 64 bits CPU)

  • Darwin arm64 (macOS on Apple M1, M2, etc. CPU)

You do not have to know what system or processor you have. Just use, in a terminal:

curl https://ccid.apdu.fr/files/parse.sh | bash -

Statistics

From last month's HTTP server logs, I get:

Architecture

#

%

Linux-x86_64

52

86.67 %

Darwin-x86_64

4

6.67 %

Linux-aarch64

4

6.67 %

Darwin-arm64

0

0 %

Linux-armv7l

0

0 %

/images/2025/06/parse.png

Unsurprisingly, the majority of users are on a GNU/Linux system with an Intel/AMD 64-bits processor.

I am pleasantly surprised to see GNU/Linux systems on ARM processors.

Security

I understand that some people prefer to build a program from source code rather than run a binary retrieved from the Internet.

I also do not like using:

curl https://random.web.site/install_script.sh | bash -

This is why is is still possible to rebuild the parse binary from the source code.

Conclusion

It should (now) be easy to check if a USB smart card reader complies with hthe CCID standard.

If you are the product owner of a new smart card reader and don't have a GNU/Linux developer available to help you, this tool will allow you to send me the details as documented at Check reader's compliance to CCID specification.

I can then add your reader in the "Should work but untested by me" list. If you want to have your reader listed in the "Supported CCID readers/ICCD tokens" list, contact me.

In case of smart card issues on macOS

If you have a problem with your smart card or smart card reader on macOS, the first step is to disable the CCID provided by Apple and enable my CCID driver.

Enable a good CCID driver

It is very simple. Just do, in a Terminal:

sudo defaults write /Library/Preferences/com.apple.security.smartcard useIFDCCID -bool yes

If the problem is solved then there is no need to report it to me.

You will find more information in Apple's own CCID driver in Sonoma.

Update of my CCID driver

In macOS Sonoma 14.0 Apple provided version 1.5.1 of my CCID driver (macOS Sonoma and smart cards status) whereas the current version was 1.5.2.

In macOS Sequoia 15.0 my CCID driver was still version 1.5.1 whereas the current version was 1.6.1 (macOS Sequoia and smart cards status).

If you want/need a version of my CCID driver newer than 1.5.1, or a custom installer for macOS, you can contact me.

Conclusion

An issue was reported recently (MacOS NFC reader issue for large APDUs) and activating my CCID driver solved the problem.

First step is to use a good CCID driver.

Card state synchronisation on SEC1210 reader 2 interfaces

The next CCID driver (version 1.6.3 not yet released) will contain a custom code to change the behavior of the Microchip SEC1210 reader.

SEC1210 reader

The Microchip SEC1210 smart card reader is a chip that you can integrate in your hardware projects.

A development board is available.

https://ccid.apdu.fr/ccid/img/Microchip-SEC1210.gif

The board has 2 CCID interfaces that are seen as 2 CCID readers at the USB level (it is a composite device). See the USB descriptor:

idVendor: 0x0424
 iManufacturer: SMSC
idProduct: 0x1202
 iProduct: SMSC USX101x Reader
bcdDevice: 2.22 (firmware release?)
bLength: 9
bDescriptorType: 4
bInterfaceNumber: 0
bAlternateSetting: 0
bNumEndpoints: 3
 bulk-IN, bulk-OUT and Interrupt-IN
bInterfaceClass: 0x0B [Chip Card Interface Device Class (CCID)]
[...]

idVendor: 0x0424
 iManufacturer: SMSC
idProduct: 0x1202
 iProduct: SMSC USX101x Reader
bcdDevice: 2.22 (firmware release?)
bLength: 9
bDescriptorType: 4
bInterfaceNumber: 1
bAlternateSetting: 0
bNumEndpoints: 2
 bulk-IN and bulk-OUT
bInterfaceClass: 0x0B [Chip Card Interface Device Class (CCID)]

The 2 interfaces have the same descriptor, except for:

  • bInterfaceNumber: 0 or 1

  • bNumEndpoints: 2 or 3

One interface provides the Interrupt-IN endpoint while the other interface does not. This means one interface will provide card movements notification on the interrupt pipe while the other interface will not.

In fact the 2nd interface does not support RDR_to_PC_NotifySlotChange but also does not support card presence using the CCID command PC_to_RDR_GetSlotStatus. The reader will always report a card is inserted even if no card is inserted.

This second interface is, in general, used with a SAM (Secure access module) that is rarely removed from the reader.

New use case

A user of this reader asked me to add the option of duplicating the card presence status of the 1st interface on the 2nd interface. The idea is to be able to insert or remove 2 cards from both interfaces at the same time.

The 2nd interface can then be used as a almost normal reader.

Custom code

The patch is visible at https://salsa.debian.org/rousseau/CCID/-/commit/4842c97561794acb7269dfcba21c877d42895a4e

The code is not active by default. You need to uncomment a line and rebuild the driver:

// Uncomment if you want to synchronize the card movements on the 2
// interfaces of the Microchip SEC 1210 reader
// #define SEC1210_SYNC

The code is a bit complex, as each interface is managed by a different pcsc-lite thread. The 2 threads are synchronized with a pthread_cond and share some states.

Conclusion

The 2 interfaces of the Microchip SEC1210 smart card reader can be used mostly as normal interfaces. The (industrial) user is happy.

If you need custom development of the CCID driver and/or pcsc-lite, please don't hesitate to contact me. See About me.

Resources release in PySCard

With PySCard version 2.2.2 (New version of PySCard: 2.2.2) it is now possible to release/free the PC/SC context used by some objects.

Sample

As see in PCSC sample in Python a typical PySCard code looks like:

#! /usr/bin/env python

from smartcard.System import readers

# define the APDUs used in this script
SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01]
COMMAND = [0x00, 0x00, 0x00, 0x00]

# get all the available readers
r = readers()
print "Available readers:", r

reader = r[0]
print "Using:", reader

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

data, sw1, sw2 = connection.transmit(SELECT)
print data
print "Select Applet: %02X %02X" % (sw1, sw2)

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

You can see that a connection is created with connection = reader.createConnection() and then the card is connected with connection.connect(), but the card is never explicitly disconnected and the connection is never released.

It was already possible to use:

connection.disconnect()

It is now also possible to use:

connection.release()

The code becomes (after convertion to Python 3):

#! /usr/bin/env python

from smartcard.System import readers
from smartcard.util import toASCIIString

# define the APDUs used in this script
SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01]
COMMAND = [0x00, 0x00, 0x00, 0x00]

# get all the available readers
r = readers()
print("Available readers:", r)

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

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

data, sw1, sw2 = connection.transmit(SELECT)
print(data)
print(f"Select Applet: {sw1:02X} {sw2:02X}")

data, sw1, sw2 = connection.transmit(COMMAND)
print(data)
print(toASCIIString(data))
print(f"Command: {sw1:02X} {sw2:02X}")

connection.disconnect()
connection.release()

Output

Available readers: ['Gemalto PC Twin Reader']
Using: Gemalto PC Twin Reader
[]
Select Applet: 90 00
[72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]
Hello world!
Command: 90 00

If you do not want to explicitly call .disconnect() and .release() you can use a context manager and the Python with statement.

[...]

with reader.createConnection() as connection:
    connection.connect()

    data, sw1, sw2 = connection.transmit(SELECT)
    print(data)
    print(f"Select Applet: {sw1:02X} {sw2:02X}")

    data, sw1, sw2 = connection.transmit(COMMAND)
    print(data)
    print(toASCIIString(data))
    print(f"Command: {sw1:02X} {sw2:02X}")

Or you can also use the del instruction to call the object finalizer.

del connection

CardRequest

You can also use with with a CardRequest object.

#! /usr/bin/env python

from smartcard.CardRequest import CardRequest

print("Insert a card within 10 seconds")
with CardRequest(timeout=10) as req:
    svc = req.waitforcard()
    svc.connection.connect()
    r = svc.connection.transmit([0,0,0,0])
    print(r)
    svc.connection.disconnect()
    svc.connection.release()

Ouput

Insert a card within 10 seconds
([], 109, 0)

CardService

And also with a CardService object.

#! /usr/bin/env python

from smartcard.CardRequest import CardRequest

print("Insert a card within 10 seconds")
with CardRequest(timeout=10) as req:
    with req.waitforcard() as svc:
        svc.connection.connect()
        r = svc.connection.transmit([0,0,0,0])
        print(r)

Traces

It is possible and easy to trace the actions performed by PySCard using an smartcard.Observer object.

#! /usr/bin/env python

from time import sleep
from smartcard.CardRequest import CardRequest
from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver

print("Insert a card within 10 seconds")
with CardRequest(timeout=10) as req:
    with req.waitforcard() as svc:
        observer = ConsoleCardConnectionObserver()
        svc.connection.addObserver(observer)

        svc.connection.connect()
        r = svc.connection.transmit([0,0,0,0])
        print(r)

sleep(10)
print("end")

Ouput

Insert a card within 10 seconds
connecting to Gemalto PC Twin Reader
> 00 00 00 00
<  [] 6D 00
([], 109, 0)
disconnecting from Gemalto PC Twin Reader
release from Gemalto PC Twin Reader
end

You can see that .disconnect() and .release() methods are called and traced.

The traces are displayed 10 seconds before the "end", i.e. when the two with exit, not when the program ends (and the garbage collector is used to clean each object).

Why

The CardConnection().release() method has been added to release the PC/SC context and allow pcscd (the daemon used in pcsc-lite) to exit automatically before the end of the Python program. See the issue PCSCCardConnection.__del__ method raises exceptions that cannot be easily handled in python code. #223 and in particular this comment https://github.com/LudovicRousseau/pyscard/issues/223#issuecomment-2773120282:

pcscd should not auto exit before all the contexts have been released.

Btw this was indeed the other (only loosely related) issue that I mentioned at the end of initial post.

I.e. that using pyscard in a main process or a thread always holds up pcsc-lite running (unless I missed something, iirc with connection management in C code), and there seem to be no way to say "please close the damn context, time to read cards was gone 10 hours ago, and this silly NFC reader eats up 300 milliamps" :)

So would be nice to have a way to cleanly close such context/connection manually (allowing pcsc-lite to exit too, and without purging pyscard from memory with subprocess) for other reasons as well, but again, probably a separate issue.

And I think context != connection, as latter are managed somewhere in C, not in python, but might be wrong or misremembering.

The problem is that the user has a smart card reader that consumes 300 milliamps even when it's doing "nothing".

So it's a good idea to let pcscd exit so that the USB device will be put in low power thanks to the autosuspend Linux feature.

I haven't checked the behavior on macOS or Windows.

Conclusion

Not all users will need or use the .release() method. If your Python program runs for a long time and you only use the smart card at certain times, it may be a good idea to take a look at your code and update it.

New version of PySCard: 2.2.2

I just released a new version of pyscard. PySCard is a Python module adding smart cards support (PC/SC) to Python.

The PySCard project is available at:

Changes:

2.2.2 (April 2025)

  • add .release() method to CardConnection & PCSCCardRequest

  • add context manager protocol to CardRequest & CardService

  • fix some parallel execution issues

  • PCSCCardRequest: correctly initialize waitforcardevent()

  • user-guide: Update documentation

  • Minor changes

New version of pcsc-lite: 2.3.3

I just released a new version of pcsc-lite 2.3.3.

pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.

Polkit rules was broken. So it was no more possible to give access to remote users for example. See pcsc-lite and polkit.

Thanks to Mike Kazantsev for the very nice bug report in PrivateUsers=yes in systemd sandboxing options prevents polkit rules from getting user/group info.

Changes:

2.3.3: Ludovic Rousseau

2 April 2025

  • Make polkit rules work again (bug introduced in 2.3.2)

Ignore readers using PCSCLITE_IGNORE udev property

Version 2.3.2 of pcsc-lite (New version of pcsc-lite: 2.3.2) introduced a new way to ignore a smart card reader.

The patch included in pcsc-lite is from Valtteri Vuorikoski and I will quote his git commit message in this article.

Use case

"This is similar to the existing filtering support provided by PCSCLITE_FILTER_IGNORE_READER_NAMES (Remove and/or customize PC/SC reader names), but allows using the full range of udev rules to match specific devices. For example, if you have several Yubikeys and one is attached to a specific USB port that is being forwarded to a virtual machine (viz. it should not be touched by programs on the host machine), this facility allows setting a rule to keep pcscd away from that Yubikey. Yubikeys attached to other ports keep working normally."

udev

By default pcsc-lite uses libudev to detect readers events (plug or unplug of a USB reader). The idea is to use a flag set by udev to tell pcsc-lite to ignore a reader.

Example rule that targets a Yubikey attached to a specific USB port and causes pcscd to ignore that device:

ACTION!="remove|unbind", SUBSYSTEM=="usb", ATTR{idVendor}=="1050",
ATTR{idProduct}=="0406", KERNEL=="1-2.3", ENV{PCSCLITE_IGNORE}="1"

The KERNEL match targets a USB device attached to port 3 of a hub on port 2 of USB bus 1, and rule sets property PCSCLITE_IGNORE to cause the device to be ignored.

The reader with idVendor=0x1050 and idProduct=0x0406 is the YubiKey FIDO+CCID from Yubico.

Or if you want to ignore any reader connected to USB port 1-3:

ACTION!="remove|unbind", SUBSYSTEM=="usb", KERNEL=="1-3", ENV{PCSCLITE_IGNORE}="1"

You put the above rule is a file /etc/udev/rules.d/foobar.rules and voila.

Config

To know what value for KERNEL= to use you can run udevadm monitor and connect a USB reader. You will get something like:

$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[2022.067560] add      /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
KERNEL[2022.069016] change   /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
KERNEL[2022.069332] add      /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0 (usb)
KERNEL[2022.069416] bind     /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
UDEV  [2022.690370] add      /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
UDEV  [2022.695881] change   /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
UDEV  [2022.700310] add      /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0 (usb)
UDEV  [2022.728691] bind     /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)

You can see the "1-3" information to use in the .rules file.

Debug

To debug your configuration and check the new field PCSCLITE_IGNORE is correctly set you can use udevadm monitor --property and you get something like:

$ udevadm monitor --property
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[2265.405075] add      /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-3
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/015
DEVTYPE=usb_device
PRODUCT=8e6/3437/100
TYPE=0/0/0
BUSNUM=001
DEVNUM=015
SEQNUM=4692
MAJOR=189
MINOR=14

[...]

UDEV  [2266.066186] bind     /devices/pci0000:00/0000:00:14.0/usb1/1-3 (usb)
ACTION=bind
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-3
SUBSYSTEM=usb
DEVNAME=/dev/bus/usb/001/015
DEVTYPE=usb_device
DRIVER=usb
PRODUCT=8e6/3437/100
TYPE=0/0/0
BUSNUM=001
DEVNUM=015
SEQNUM=4695
USEC_INITIALIZED=2265405122
ID_BUS=usb
ID_MODEL=USB_SmartCard_Reader
ID_MODEL_ENC=USB\x20SmartCard\x20Reader
ID_MODEL_ID=3437
ID_SERIAL=Gemplus_USB_SmartCard_Reader
ID_VENDOR=Gemplus
ID_VENDOR_ENC=Gemplus
ID_VENDOR_ID=08e6
ID_REVISION=0100
ID_USB_MODEL=USB_SmartCard_Reader
ID_USB_MODEL_ENC=USB\x20SmartCard\x20Reader
ID_USB_MODEL_ID=3437
ID_USB_SERIAL=Gemplus_USB_SmartCard_Reader
ID_USB_VENDOR=Gemplus
ID_USB_VENDOR_ENC=Gemplus
ID_USB_VENDOR_ID=08e6
ID_USB_REVISION=0100
ID_USB_INTERFACES=:0b0000:
ID_VENDOR_FROM_DATABASE=Gemalto (was Gemplus)
ID_MODEL_FROM_DATABASE=GemPC Twin SmartCard Reader
ID_PATH_WITH_USB_REVISION=pci-0000:00:14.0-usbv2-0:3
ID_PATH=pci-0000:00:14.0-usb-0:3
ID_PATH_TAG=pci-0000_00_14_0-usb-0_3
ID_SMARTCARD_READER=1
ID_FOR_SEAT=usb-pci-0000_00_14_0-usb-0_3
SYSTEMD_WANTS=smartcard.target
SYSTEMD_USER_WANTS=smartcard.target
PCSCLITE_IGNORE=1
MAJOR=189
MINOR=14
TAGS=:uaccess:security-device:seat:systemd:
CURRENT_TAGS=:uaccess:security-device:seat:systemd:

In pcscd logs I get:

04337722 [140613033731776] ../src/hotplug_libudev.c:656:HPEstablishUSBNotifications() USB Device add
00000411 [140613033731776] ../src/hotplug_libudev.c:299:get_driver() Looking for a driver for VID: 0x08E6, PID: 0x3437, path: /dev/bus/usb/001/016
00000303 [140613033731776] ../src/hotplug_libudev.c:417:HPAddDevice() Device Gemalto PC Twin Reader at /dev/bus/usb/001/016 (1-3:1.0) has PCSCLITE_IGNORE set: ignored

And I do not see the PC/SC reader with pcsc_scan command:

$ pcsc_scan -r
No reader found.

Conclusion

I am sure you will find other use cases for this new feature.

Thanks to Valtteri Vuorikoski for the patch. If you have ideas of changes or improvements for pcsc-lite do not hesitate to submit them.

New version of pcsc-lite: 2.3.2

I just released a new version of pcsc-lite 2.3.2.

pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.

Changes:

2.3.2: Ludovic Rousseau

26 March 2025

  • Hardening systemd pcscd.service file

  • pcscd.service: add missing Requires=polkit.service

  • pcsc-spy: add missing PCSCv2_PART10_PROPERTY_* definitions

  • Support udev PCSCLITE_IGNORE property to filter readers

  • debuglog: force use of colors when --color is used

  • Some other minor improvements

New version of pcsc-tools: 1.7.3

I just released a new version of pcsc-tools, a suite of tools for PC/SC.

Changes:

1.7.3 - 22 March 2025, Ludovic ROUSSEAU

  • 199 new ATRs

  • meson:
    • add Windows support

    • add macOS support

    • add option to set ATRparser

  • minor fixes and improvements

New version of libccid: 1.6.2

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

Changes:

1.6.2 - 19 March 2025, Ludovic Rousseau

  • Add support of

    • Arculus AuthentiKey

    • BHDC Reader-HHD02

    • CHERRY Smart Terminal 1150

    • HSIC CCID-Reader

    • Ledger Flex

    • SYC USB CCID Reader

    • Thales RF CR2000

    • TOKEN2 FIDO2 Security Key(0026)

  • Give more time to initialize the ACS ACR122U

  • Do not build examples and contrib by default

  • meson: add missing check for pthread_condattr_setclock

  • Don't assume that all notifications are NotifySlotChange

  • Hide unexported functions and variables

  • Some other minor improvements