Smart card Usage in Debian: pcscd and drivers

Debian uses a system to evaluate the popularity of packages called: popularity-contest

Each package has a popcon page with the number of installations of the package and its evolution in time.

popularity-contest

Not every Debian user has the package popularity-contest installed (and enabled). Example with the graph of popularity-contest itself.
The package is installed on 198 548 Debian systems. But we can imagine that Debian is installed in much more than 200 000 systems.

The popularity-contest package is also available in Ubuntu. The Ubuntu results are also available. On Ubuntu the popularity-contest package is installed in 2 796 046 systems. We have a factor of x14 compared to Debian.

So what is important is not the absolute value of installations but the evolution in time and the percentages of installed systems.
I was not able to find the graphs for Ubuntu. So I will only use the results on Debian.

pcsc-lite


pcsc-lite software provides 3 packages: the daemon, the library, the development files. Here we see the 3 packages on the same graph.

libpcsclite1 is, by far, the most installed package. This is because (nearly) all smart card application uses PC/SC and so depends on libpcsclite1.

Package # of installation % of Debian systems
libpcsclite1 124551 62.63%
pcscd 23796 11.96%
libpcsclite-dev 1001 0.50%

On Ubuntu we have:
Package # of installation % of Ubuntu systems
libpcsclite1 2019980 72.2%
pcscd 32834 1.17%
libpcsclite-dev 9494 0.34%

libpcsclite1


The number of installations of libpcsclite1 is much higher than the number of installation of pcscd. But libpcsclite1 is useless without pcscd.

This is because libpcsclite1 is a dependency of some packages that provides a smart card support but not every user is using it. For example wpasupplicant depends on libpcsclite1 but not every users of wpasupplicant are using a smart card.

pcscd

The two spikes occurred when I changed the dependency between libpcsclite1 and pcscd. When libpcsclite1 depends on pcscd then each installation of libpcsclite1 will also install pcscd. See Debian bugs #476483 and #612971.
After I solved the 2 bugs the level went down.

The rise that starts in 2017 should indicate a real use of smart card since that date. That is a good sign.

From the 2 tables above we can notice that pcscd is installed in x10 more Debian systems (11.96%) than Ubuntu systems (1.17%).
My interpretation is that Debian users are more security conscious and use more smart cards than Ubuntu users.

libpcsclite-dev

This package is needed only if you want to build applications using PC/SC. It is expect to have a number of installation far lower than the number of pcscd installations.

Smart card drivers

On Debian, smart card drivers should each provide the virtual package: pcsc-ifd-handler

Package # installations "market share"
libccid 23791 96.06%
libacsccid1 483 1.95%
libifd-cyberjack6 282 1.14%
libasedrive-usb 68 0.27%
libgempc430 54 0.22%
libgcr410
29 0.12%
libasedrive-serial 23 0.09%
libtowitoko2 22 0.09%
libgempc410 15 0.06%



libccid


CCID is the USB standard for USB smart card readers.
It is also the default driver installed with pcscd. It is then expected that the pcscd and libccid graphs are quiet similar.

libacsccid1


The libacsccid1 driver is a (friendly) fork of my libccid driver adapted to some ACS readers.

libifd-cyberjack6


This driver is for REINER SCT cyberJack USB chipcard readers.

libasedrive


This driver is for Athena ASEDrive IIIe serial and USB devices.

GemPC 410 & 430


I am also the upstream maintainer of 2 other drivers:
  • libgempc410 is a driver for the Gemplus GemPC 41x serial readers.
  • libgempc430 is a driver for the Gemplus GemPC 43x USB readers.
These readers are older than the CCID specification document.
    I am surprised to see systems with these drivers installed. I have not used them myself since ages.

    libgcr410


    This driver is for Gemplus GCR410 serial device.

    libtowitoko2


    This driver is for Towitoko smartcard reader PCSC and CT-API driver.

    Conclusion

    Around 12% of Debian users are using smart cards. I would not have expected so much.
    The percentage on Ubuntu, 1%, is (I imagine) more representative of the general population.

    New version of libccid: 1.4.33

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

    Changes:

    1.4.33 - 25 June 2020, Ludovic Rousseau
    • Add support of
      • Genesys Logic CCID Card Reader (idProduct: 0x0771)
      • Swissbit Secure USB PU-50n SE/PE
      • TOPPAN FORMS CO.,LTD TC63CUT021
    • add --enable-oslog argument for macOS
      • use os_log(3) for macOS >= 10.12 (Sierra)
    • Update PCSC submodule to get Unicode support
    • Some minor improvements

    New version of pcsc-lite: 1.9.0

    I just released a new version of pcsc-lite 1.9.0.
    pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.


    This version includes 2 changes I already documented on this blog:
    Because of the major speed improvement I decided to name this version 1.9.0.

    Changes

    1.9.0: Ludovic Rousseau
    14 June 2020
    • SCardEndTransaction(): greatly improve performances (x300)
    • tokenparser: accept any Unicode character in a reader name
    • Use /run instead of /var/run by default
    • Fix a memory leak from a polkit call
    • Some other minor improvements

    Unicode characters in a reader name

    It is now possible to use Unicode characters in a reader name.

    History

    Since the beginning of pcsc-lite (at least since the first version of pcsc-lite in 2002 that is in a Version Control System) only a subset of ASCII was considered as legal characters for a PC/SC reader name.

    In 2011 I added the character ";" in the list so that it is possible to use the "&" sign (encoded as "&" since the reader list is encoded as XML in the Info.plist file). This was to support a reader name like "Giesecke & Devrient".

    In 2012 I added the characters "[" and "]".

    In 2020 I add support of any Unicode character.

    This request came from the use of the reader name "SoloKeys Solo 🐝". See the Salsa ticket "Unicode in USB Product string not supported." for more details.

    Demo

    First example

    Β± pcsc_scan 
    Using reader plug'n play mechanism
    Scanning present readers...
    0: Ω…Ψ±Ψ­Ψ¨Ψ§ Ψ¨Ψ§Ω„ΨΉΨ§Ω„Ω… πŸ˜€ πŸŽ‚ 00 00
    1: Χ©ΧœΧ•Χ Χ’Χ•ΧœΧ 😎 😼 01 00

    Sat May 16 10:52:58 2020
    Reader 0: Ω…Ψ±Ψ­Ψ¨Ψ§ Ψ¨Ψ§Ω„ΨΉΨ§Ω„Ω… πŸ˜€ πŸŽ‚ 00 00
    Event number: 1
    Card state: Card removed,
    Reader 1: Χ©ΧœΧ•Χ Χ’Χ•ΧœΧ 😎 😼 01 00
    Event number: 0
    Card state: Card inserted,
    ATR: 3B BE 96 00 00 41 03 00 00 00 00 00 00 00 00 00 02 90 00
    In case you do not have the correct font installed in your web browser here is a picture version of the same output.

    You can note that the reader names are reversed between the text version and the image version. I let you find what is the "problem" here.

    Second example

    Β± pcsc_scan 
    Using reader plug'n play mechanism
    Scanning present readers...
    0: 😺 😸 😹 😻 😼 😽 πŸ™€ 😿 😾 00 00
    1: πŸ’‹πŸ’˜πŸ’πŸ’–πŸ’—πŸ’“πŸ’žπŸ’•πŸ’ŸπŸ’”πŸ§‘πŸ’›πŸ’šπŸ’™πŸ’œπŸ–€ 01 00
     
    Sat May 16 11:17:00 2020
     Reader 0: 😺 😸 😹 😻 😼 😽 πŸ™€ 😿 😾 00 00
      Event number: 0
      Card state: Card removed, 
     Reader 1: πŸ’‹πŸ’˜πŸ’πŸ’–πŸ’—πŸ’“πŸ’žπŸ’•πŸ’ŸπŸ’”πŸ§‘πŸ’›πŸ’šπŸ’™πŸ’œπŸ–€ 01 00
      Event number: 0
      Card state: Card inserted, 
      ATR: 3B BE 96 00 00 41 03 00 00 00 00 00 00 00 00 00 02 90 00
    Again with the screen capture:

    Of course you have no obligation to use some many funny Unicode characters in your reader name. It was just an example.

    Availability

    You need to use CCID version 1.4.33 or more and pcsc-lite version 1.9.0 or more.

    These versions are not yet available (when I write this article) so I prepared snapshot of both software at http://ludovic.rousseau.free.fr/softwares/pcsc-lite/. They are pcsc-lite-1.8.26-047789c.tar.bz2 and ccid-1.4.32-e782d48.tar.bz2.

    You can also use the current git version of pcsc-lite and ccid if you know what you do.

    Linux

    I made the development and tests on a Debian GNU/Linux system.

    macOS

    I also tested the new CCID driver on macOS Mojave and it works fine with Unicode characters. I found no issue.

    Conclusion

    I do not expect to see many smart card readers with emoticons, but maybe names with characters from non-Latin alphabets.

    10 years of blogging

    I started this blog 10 years ago, in April 7th 2010.
    Since then I wrote about many aspects of the smart card use in GNU/Linux and macOS.

    Statistics

    Some statistics about the number of articles per year.
    Years 2010 and 2020 represent only half a year of activity.


    Conclusion

    I do plan to be present and continue in the next 10 years.

    GitHub Sponsors: first payment

    Since January 2020 I am part of the Github sponsors program. See my previous article: GitHub Sponsors.

    Payment

    I just got my first payment in May 2020 for the amount of €66.25. Yeah!

    The next payment should occur in June 2022, in 2 years. Unless new sponsors arrive in the meantime.

    Sponsors

    For now I have 3 sponsors:Martin Paljak, Jaroslav Imrich and CrazyMarvin. A big thank you to you!
    The sponsor list is public. You can see it at https://github.com/sponsors/LudovicRousseau/.
    Β 
    They sponsor me for a total of $9/month. This number is NOT public but I want to be transparent with you. What you can see on my sponsor page is that I am "90% towards $10 per month goal". So after some mathematical calculation it is easy to get the $9/month.

    Github also has the GitHub Sponsors Matching Fund. So half of that money comes from github/Microsoft. That is is first time I receive something from Microsoft πŸ˜€.

    Use of the money

    That is not a huge amount of money but that will help pay for the VPS I rent at OVH to host my projects at https://muscle.apdu.fr/. They are mostly pcsc-lite and libccid.

    Conclusion

    I have 2 active options to send me money:
    Feel free to use whatever to prefer.

    New PyKCS11 1.5.8 available

    I just released a new version of PyKCS11, a Python wrapper above the PKCS#11 API.
    See "PyKCS11 introduction" or "PyKCS11’s documentation".

    The project is registered at Pypi: https://pypi.org/project/PyKCS11/

    Changes:

    1.5.8 - May 2020, Ludovic Rousseau
    • CKA_ALWAYS_AUTHENTICATE is boolean
    • CKM_VENDOR_DEFINED_...
      • Fix name: use CKM_ instead of CKR_ prefix
      • Use an explicit hex prefix: CKM_VENDOR_DEFINED_0x45
    • Add missing CKM_*, CKA_*, CKF_*, CKD_*, CKK_*, CKN_*, CKO_*, CKR_* from PKCS#11 v3.0
    • fix test_asymetric.py for RSA_PSS_Mechanism

    Your PC/SC application 200% faster

    Dana Keeler reported an issue on pcsc-lite at "SYS_USleep in SCardEndTransaction in winscard_clnt.c causing slowness in Firefox".

    The problem was initially reported on Firefox "firefox is very slow and crashes because of p11-kit-proxy.so" and also on p11-kit "firefox is very slow and crashes because of p11-kit-proxy.so".

    The issue

    The pcsc-lite performance issue comes from this piece of code in SCardEndTransaction:

     /*
      * This helps prevent starvation
      */
     randnum = SYS_RandomInt(1000, 10000);
     (void)SYS_USleep(randnum);
    

    For each call to SCardEndTransaction() you will get a delay between 1 ms and 11 ms. So on average you get 6 ms of delay each time.

    Performance measures

    I wrote a Python program to get the duration of 100 calls to SCardEndTransaction():

    #! /usr/bin/env python3
    
    from smartcard.scard import *
    from smartcard.pcsc.PCSCExceptions import *
    from time import time
    
    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)
    print('PC/SC Readers:', readers)
    reader = readers[0]
    print("Using reader:", reader)
    
    hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, reader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_ANY)
    if hresult != SCARD_S_SUCCESS:
        raise BaseSCardException(hresult)
    
    nb = 100
    total = 0
    for i in range(nb):
        hresult = SCardBeginTransaction(hcard)
        if hresult != SCARD_S_SUCCESS:
            raise BaseSCardException(hresult)
    
        before = time()
        hresult = SCardEndTransaction(hcard, SCARD_LEAVE_CARD)
        if hresult != SCARD_S_SUCCESS:
            raise BaseSCardException(hresult)
        after = time()
        delta = after - before
        print(delta)
        total += delta
    print("total: {} ms".format(total * 1000))
    print("average: {} ms".format(total * 1000 / nb))
    
    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)
    

    Results


    As expected the random delay is of 6 ms (or 0.006 second) on average.

    The history

    I don't know why this delay is needed. I went into the previous versions of the source code file PCSC/src/winscard_clnt.c but the oldest version (from March 2002, 18 years ago) already has this delay.

    The code was written David Corcoran, the initial author of pcsc-lite. It was my very early days in the pcsc-lite project at that time.

    The solution

    I don't see any good reasons to have a delay here. I guess it was to solve a problem with the communication between pcscd and libpcsclite at that time. The communication mechanism has been redesigned in version 1.6.0 (May 2010) and the delay should now be useless and even problematic as we saw.

    My solution is then to remove the problematic code. The was done in this commit.

    The results

    I used again my Python program to measure the performances of SCardEndTransaction(). I now have:

    The mean delay is 9.5x10-6 s so 9 Β΅s or 0.009 ms or 0.000009 second. The speedup factor is huge: x647.
    The function is now 600 times faster than before.

    You can note a high decrease on the 4 first values. My guess is that it is an effect of CPU memory caches in action. I have not investigated this point. This is left as an exercise for my readers.

    Macro benchmark

    It is nice to have a huge improvement in one PC/SC function but does that help real applications?

    For a real smart card application I used OpenSC with a standard command: list all the objects of a smart card.
    I used a very simple shell script:

    #!/bin/bash
    
    for i in {1..100}
    do
     pkcs11-tool --list-objects
    done
    

    Slow smart card

    First I used a very old smart card I have in my collection: a Gemplus GPK 8000 card.
    Since the card is very old and slow the shell script will do 10 rounds instead of 100.

    what time (s)
    Before 23.34
    After 22.84

    Speedup: x1.02 or 2%

    The gain is very limited. This is because the card is so slow that the benefit from the new SCardEndTransaction() is negligible.

    Fast smart card

    I then used a much faster smart card: a Yubikey 5 from Yubico. It is not a real smart card but a token with a CCID interface and a chip that understand APDU commands.
    Since the device is fast I used 100 rounds of pkcs11-tool.

    what time (s)
    Before 9.85
    After 3.02

    Speedup: x3.26 or 226%

    This time the gain is highly visible. The pkcs11-tool command is now 3 times faster.

    Results

    You will get a high acceleration with fast smart card.
    I was able to get a full execution of pkcs11-tool 3 (three) times faster than before the change.

    I was really impressed by this result. For almost 10 years pcsc-lite was slow and could be improved by just removing 2 lines of code.

    Potential regression?

    I don't think this change will create a regression and will break existing code.

    I made a beta version of pcsc-lite including the change available at http://ludovic.rousseau.free.fr/softwares/pcsc-lite/pcsc-lite-1.8.26-05d48e5.tar.bz2.
    Please test this version with your PC/SC application. In case of a problem with PC/SC transactions then open an issue at Salsa https://salsa.debian.org/rousseau/PCSC/-/issues or github https://github.com/LudovicRousseau/PCSC/issues.

    I plan to wait a few weeks to get potential feedback before I make a new release of pcsc-lite.

    Conclusion

    pcsc-lite is now much faster.

    This happened because someone complained that Firefox was slow and someone at Mozilla investigated the issue to find a problem in pcsc-lite.

    I am very happy to use Free Software programs where it is possible and easy to find problems in software you use.

    PCSC sample in Free Pascal (Lazarus)

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

    Lazarus

    From Lazarus web site:
    What is Lazarus?
    Lazarus is a Delphi compatible cross-platform IDE for Rapid Application Development. It has variety of components ready for use and a graphical form designer to easily create complex graphical user interfaces.

    Infintuary Pascal PC/SC Sample

    I found a Pascal PC/SC Sample code for PCSC with Lazarus at http://infintuary.org/stpcsc.php.

    This program is also available at https://github.com/ccy/pcsc in a more recent version. Unfortunately the more recent version has Windows specific code and can't be used on GNU/Linux. I reported the issue at Fix build on GNU/Linux: CheckOSError() is for Windows.

    License

    The license is custom but could be enough. From http://infintuary.org/stpcsc.php
    The Pascal PC/SC Sample is freeware and can be used for any purpose.
    By downloading the Pascal PC/SC Sample you agree to the terms of use.
    The terms of use is different from the license available on the github project. For example the terms of use does not explicitly allow modification of the source code, and the use is allowed only for legal purpose. In general it is a bad idea to re-invent a new license text.

    Installation

    You copy the files MD_Events.pas, MD_PCSC.pas, MD_PCSCDef.pas, MD_PCSCRaw.pas and MD_Tools.pas.

    Source code


    unit main;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
      MD_PCSCRaw, MD_PCSCDef, MD_Tools;
    
    type
    
      { TForm1 }
    
      TForm1 = class(TForm)
        Button1: TButton;
        Memo1: TMemo;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure AddLogMemo(Msg: string);
      private
        FPCSCRaw: TPCSCRaw;
    
      public
    
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.lfm}
    
    { TForm1 }
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      PCSCResult: Dword;
      hContext: THandle;
      SizeReaders: LongWord;
      pReaders: PChar;
      hCard: LongInt;
      dwActiveProtocol: Cardinal;
      pioSendPCI, pioRecvPCI: pSCardIORequest;
      inBuffer : TBytes;
      outBuffer: TBytes;
      outSize: Cardinal;
      i: Integer;
      outString: String;
    begin
        FPCSCRaw := TPCSCRaw.Create;
        FPCSCRaw.Initialize;
    
        // Establish context
        PCSCResult := FPCSCRaw.SCardEstablishContext(SCARD_SCOPE_SYSTEM, nil, nil, hContext);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardEstablishContext succeeded.')
          else AddLogMemo('SCardEstablishContext failed: ' + PCSCErrorToString(PCSCResult));
    
        // List readers
        PCSCResult := FPCSCRaw.SCardListReaders(hContext, nil, nil, SizeReaders);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardListReaders succeeded.')
          else AddLogMemo('SCardListReaders failed: ' + PCSCErrorToString(PCSCResult));
    
        GetMem(pReaders, SizeReaders);
        PCSCResult := FPCSCRaw.SCardListReaders(hContext, nil, pReaders, SizeReaders);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardListReaders succeeded.')
          else AddLogMemo('SCardListReaders failed: ' + PCSCErrorToString(PCSCResult));
    
        // Use the first reader
        AddLogMemo('Using: ' + pReaders);
    
        // Connect to the card
        hCard := -1;
        dwActiveProtocol := 0;
        PCSCResult := FPCSCRaw.SCardConnect(hContext, pReaders, SCARD_SHARE_SHARED,
          SCARD_PROTOCOL_Tx, hCard, dwActiveProtocol);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardConnect succeeded.')
          else AddLogMemo('SCardConnect failed: ' + PCSCErrorToString(PCSCResult));
    
        // Send Select Applet command
        pioRecvPCI := nil;
        if dwActiveProtocol = SCARD_PROTOCOL_T0
          then pioSendPCI := @SCARDPCIT0
          else pioSendPCI := @SCARDPCIT1;
    
        inBuffer := HexStringToBuffer('00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01');
        outSize := 258;
        SetLength(outBuffer, outSize);
        PCSCResult := FPCSCRaw.SCardTransmit(hCard, pioSendPCI, Pointer(inBuffer),
          length(inBuffer), pioRecvPCI, Pointer(outBuffer), outSize);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardTransmit succeeded.')
          else AddLogMemo('SCardTransmit failed: ' + PCSCErrorToString(PCSCResult));
    
        SetLength(outBuffer, outSize);
        AddLogMemo('Received (' + IntToStr(outSize) + ' bytes): ' + BufferToHexString(outBuffer));
    
        // Send test command
        inBuffer := HexStringToBuffer('00 00 00 00');
        outSize := 258;
        SetLength(outBuffer, outSize);
        PCSCResult := FPCSCRaw.SCardTransmit(hCard, pioSendPCI, Pointer(inBuffer),
          length(inBuffer), pioRecvPCI, Pointer(outBuffer), outSize);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardTransmit succeeded.')
          else AddLogMemo('SCardTransmit failed: ' + PCSCErrorToString(PCSCResult));
    
        SetLength(outBuffer, outSize);
        AddLogMemo('Received (' + IntToStr(outSize) + ' bytes): ' + BufferToHexString(outBuffer));
    
        outString := '';
        for i := 0 to outSize -3 do
          outString := outString + chr(outBuffer[i]);
        AddLogMemo(outString);
    
        // Disconnect
        PCSCResult := FPCSCRaw.SCardDisconnect(hCard, SCARD_LEAVE_CARD);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardDisconnect succeeded.')
          else AddLogMemo('SCardDisconnect failed: ' + PCSCErrorToString(PCSCResult));
    
        // Release context
        PCSCResult := FPCSCRaw.SCardReleaseContext(hContext);
        if PCSCResult = SCARD_S_SUCCESS
          then AddLogMemo('SCardReleaseContext succeeded.')
          else AddLogMemo('SCardReleaseContext failed: ' + PCSCErrorToString(PCSCResult));
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Memo1.Clear;
    end;
    
    procedure TForm1.AddLogMemo(Msg: string);
    begin
      Memo1.Lines.Add(Msg);
    end;
    
    end.
    
    

    Remarks

    You will have to create a Form with a TMemo widget and a TButton widget. This part of the solution is not described here.

    You click on the button and the program executes.

    Output



    Comments

    High level API

    I used the low level API provided in file MD_PCSCRaw.pas. These are direct equivalent of the WinSCard API. But a higher level API is also available and is provided in the file MD_PCSC.pas.

    Unfortunately I was not able to find a way to get the list of connected readers with the high level API. Maybe I missed something (I am very new to Free Pascal). I reported the issue at make TPCSC.GetPCSCReaderList() a public method.

    64-bits and GNU/Linux issue

    A much more important issue is that this wrapper does not work on a 64-bits GNU/Linux system. I had to install a Debian system on a i386 CPU to write and test my code.

    The problem is that the wrapper uses a Free Pascal type THandle to store the SCARDCONTEXT and SCARDHANDLE types. The problem is that a THandle in 32-bits on a 64-bits CPU but SCARDCONTEXT and SCARDHANDLE are 64-bits types on a 64-bits CPU using GNU/Linux.

    The problem comes from the definition of long (DWORD) on a 64-bits CPU. On GNU/Linux a long is 64-bits. But on Windows a long is 32-bits only. If you want to have a 64-bits variable on Windows you need to use the long long type. This is because Linux is LP64 and Windows is LLP64. See 64-bit data models for more details.

    The wrapper should work on macOS even on a 64-bits CPU. This is because Apple does not use any DWORD in the WinSCard API but int32_t instead.

    I reported the problem at Using THandle for SCARDCONTEXT and SCARDHANDLE is wrong on GNU/Linux.

    Windows only?

    As I already wrote in the introduction the, more recent, code available at https://github.com/ccy/pcsc is for Windows only. Some work is needed to make it available (again) for macOS and GNU/Linux.

    Conclusion

    If you use the Free Pascal language and you use Windows then this wrapper can help you.
    If your system is not Windows or you want portability on diffrent systems then this wrapper may not be a good choice.

    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?