Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Friday, March 17, 2023

FAQ: wintypes.h or winscard.h not found

One of the most popular search requests that bring people on my website https://pcsclite.apdu.fr/ is about wintypes.h not found.

Problem

For example you try to compile something and get the error:

smartcard/scard/helpers.c:28:10: fatal error: winscard.h: No such file or directory
 #include <winscard.h>
          ^~~~~~~~~~~~ 

Solution

The PC/SC header files (winscard.h, wintypes.h and some others) are provided by the development pcsclite package.

  • for Debian, Ubuntu or derivatives the package is pcsclite-dev
  • for RedHat, Fedora and derivatives the package is pcsc-lite-devel
  • for other Unixes, use your favourite search engine 😜

You install the correct package and you try again to build your software.

Conclusion

I hope this blog article will be correctly indexed by search engines to help people find the solution.

Tuesday, November 29, 2022

PC/SC sample in TypeScript (Deno)

To continue the list of PC/SC wrappers initiated in 2010 with "PC/SC sample in different languages" I now present a new sample code in Deno a modern runtime for JavaScript and TypeScript.

pcsc-deno

The wrapper is available at https://github.com/cryptographix/pcsc-deno and https://deno.land/x/pcsc

The author is Sean Michael Wykes.

The license is MIT.

I used version 0.4.
This version includes the fixes I proposed for GNU/Linux.

Deno

From Wikipedia Deno article:

Deno is a runtime for JavaScript, TypeScript, and WebAssembly that is based on the V8 JavaScript engine and the Rust programming language. Deno was co-created by Ryan Dahl, who also created Node.js.

Deno explicitly takes on the role of both runtime and package manager within a single executable, rather than requiring a separate package-management program.


Installation

Installation is very easy. First install Deno as documented in https://deno.land/#installation

The PC/SC wrapper will be downloaded and installed automatically at run time.

Source code

import {
  CommandAPDU,
  ContextProvider,
  ISO7816,
  PCSC,
} from 'https://deno.land/x/pcsc/mod.ts';

try {
  // establish a PC/SC context
  const context = ContextProvider.establishContext();

  // get all available readers
  const readers = context.listReaders();

  for (const reader of readers) {
    console.log(`Using reader: ${reader.name}`);
    if (reader.isMute) {
      console.log(`Reader ${reader.name}: MUTE`);
    } else if (reader.isPresent) {
      // connect
      const card = await reader.connect();

      // send Select Applet APDU
      const selectApplet = CommandAPDU
        .from([ISO7816.CLA.ISO, ISO7816.INS.SelectFile, 0x04, 0x00]) // ISO SELECT
        .setData([0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01]);

      const resp = await card.transmitAPDU(selectApplet);

      // check for 0x90 0x00
      if (resp.SW == ISO7816.SW.SUCCESS) {
        // success ..
        console.log(`Reader ${reader.name}: applet successfully selected`);

        // send Test APDU
        const command = CommandAPDU
          .from([ISO7816.CLA.ISO, 0, 0, 0]);

        const resp = await card.transmitAPDU(command);
        if (resp.SW == ISO7816.SW.SUCCESS) {
          // success ..
          console.log(`Reader ${reader.name}: Test command successful`);

          // convert from bytes to string and display
          console.log(String.fromCharCode.apply(null, resp.data));
        } else {
          // something went wrong ..
          console.error(
            `Reader ${reader.name}: error SW=${resp.SW.toString(16)}`,
          );
        }
      } else {
        // something went wrong ..
        console.error(
          `Reader ${reader.name}: error SW=${resp.SW.toString(16)}`,
        );
      }

      // unpower and disconnect
      await card.disconnect(PCSC.Disposition.UnpowerCard);
    } else {
      console.log(`Reader ${reader.name}: NO CARD`);
    }
  }
  // release the PC/SC context
  context.shutdown();
} catch (e: PCSCException) {
  console.log(e, "error");
}


Output

$ deno run --unstable --allow-ffi blog.ts
Using reader: Gemalto PC Twin Reader (F8345B4A) 00 00
Reader Gemalto PC Twin Reader (F8345B4A) 00 00: applet successfully selected
Reader Gemalto PC Twin Reader (F8345B4A) 00 00: command successful
Hello world!


Conclusion

Nothing special to say. Thanks Sean for the wrapper.

If you work on a Free Software PC/SC wrapper that is not yet in my list please let me know.

Wednesday, September 28, 2022

PySCard 2.0.5 released

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

The PySCard project is available at:

This version is a not even a bug fix release. No code has changed.

The problem is that for the previous version, 2.0.4, I uploaded a incorrect source archive to Pypi. I inadvertently included some generated files in the .tar.gz archive. I discovered the problem while creating the Debian package. I removed the incorrect file from Pypi. But then it is not possible to upload a new file with a name that was already present on Pypi.

The source .tar.gz is already present in the pyscard project on sourceforge.net so I thought it was OK.

But then I received bug reports like Missing source release for 2.0.4? or pyscard 2.0.4 not available on linux from pypi so I had to do something.


Changes:

Sunday, August 28, 2022

PySCard 2.0.4 released

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

The PySCard project is available at:

This version is a bug fix release.

Changes:

2.0.4 (August 2022)
  • Fix a problem on PCSCCardConnection.disconnect
  • Add support of BaseSCardException(hresult) format
  • Do not use deprecated distutils anymore


Saturday, May 14, 2022

SCardListReaders() and non-initialized pcchReaders parameter

Sample C sample

We start with the following sample.c program:

01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <winscard.h>
04: 
05: #define CHECK(f, rv) \
06:  if (SCARD_S_SUCCESS != rv) \
07:  { \
08:   printf(f ": %s\n", pcsc_stringify_error(rv)); \
09:   return -1; \
10:  }
11: 
12: int main(void)
13: {
14:     LONG rv;
15: 
16:     SCARDCONTEXT hContext;
17:     LPTSTR mszReaders;
18:     DWORD dwReaders;
19: 
20:     rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
21:     CHECK("SCardEstablishContext", rv)
22: 
23:     rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
24:     CHECK("SCardListReaders", rv)
25: 
26:     mszReaders = calloc(dwReaders, sizeof(char));
27:     rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
28:     CHECK("SCardListReaders", rv)
29:     printf("1st reader name: %s\n", mszReaders);
30: 
31:     free(mszReaders);
32: 
33:     rv = SCardReleaseContext(hContext);
34:     CHECK("SCardReleaseContext", rv)
35: 
36:     return 0;
37: }
38: 

Makefile

I used this Makefile

# Linux
PCSC_CFLAGS := $(shell pkg-config --cflags libpcsclite)
LDLIBS := $(shell pkg-config --libs libpcsclite)

CFLAGS = $(PCSC_CFLAGS) -g

sample: sample.c

clean:
	rm -f sample

Output

Compilation and execution give:

$ make
cc -pthread -I/usr/include/PCSC -g   sample.c  -lpcsclite -o sample
$ ./sample 
1st reader name: Alcor Micro AU9540 00 00

It looks like everything is correct.

But can you find the problem?

 

Valgrind is your friend

valgrind is a very nice tool to debug issues.

Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. You can also use Valgrind to build new tools.

valgrind sees a problem:

$ valgrind --track-origins=yes ./sample 
==99878== Memcheck, a memory error detector
==99878== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==99878== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==99878== Command: ./sample
==99878== 
==99878== Conditional jump or move depends on uninitialised value(s)
==99878==    at 0x486E02E: SCardListReaders (in /usr/lib/x86_64-linux-gnu/libpcsclite.so.1.0.0)
==99878==    by 0x109210: main (sample.c:23)
==99878==  Uninitialised value was created by a stack allocation
==99878==    at 0x109199: main (sample.c:13)
==99878== 
1st reader name: Alcor Micro AU9540 00 00
==99878== 
==99878== HEAP SUMMARY:
==99878==     in use at exit: 112 bytes in 4 blocks
==99878==   total heap usage: 10 allocs, 6 frees, 1,434 bytes allocated
==99878== 
==99878== LEAK SUMMARY:
==99878==    definitely lost: 0 bytes in 0 blocks
==99878==    indirectly lost: 0 bytes in 0 blocks
==99878==      possibly lost: 0 bytes in 0 blocks
==99878==    still reachable: 112 bytes in 4 blocks
==99878==         suppressed: 0 bytes in 0 blocks
==99878== Rerun with --leak-check=full to see details of leaked memory
==99878== 
==99878== For lists of detected and suppressed errors, rerun with: -s
==99878== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Using uninitialized variables is bad. It creates bugs that declare at random time and are not easy to find.


SCardListReaders

The problem is at line 23 which is:

23:     rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);

If we read the documentation for SCardListReaders() we have:

Returns a list of currently available readers on the system.

mszReaders is a pointer to a character string that is allocated by the application. If the application sends mszGroups and mszReaders as NULL then this function will return the size of the buffer needed to allocate in pcchReaders.

If *pcchReaders is equal to SCARD_AUTOALLOCATE then the function will allocate itself the needed memory. Use SCardFreeMemory() to release it.

Parameters
[in] hContext Connection context to the PC/SC Resource Manager.
[in] mszGroups List of groups to list readers (not used).
[out] mszReaders Multi-string with list of readers.
[in,out] pcchReaders Size of multi-string buffer including NULL's.

You can note that the parameter pcchReaders is in and out. This is because the value is compared to SCARD_AUTOALLOCATE. So the value of pcchReaders shall be set before calling SCardListReaders().

The fix is simple. Just update line 18 like this:

18: DWORD dwReaders = 0;

 

Looking for problems

What happens if we initialize dwReaders to the special value SCARD_AUTOALLOCATE instead of 0?

18: DWORD dwReaders = SCARD_AUTOALLOCATE;

We build and run the sample:

$ ./sample 
SCardListReaders: Invalid parameter given.

The execution of SCardListReaders() fails.

23:     rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);

This is because we are asking SCardListReaders() to allocate and store the result in the parameter mszReaders. But in our sample this parameter is NULL. pcsc-lite has a check for that and returns an error code.

Instead of using NULL we could use something else like 0x1234.

23:     rv = SCardListReaders(hContext, NULL, 0x1234, &dwReaders);

This time we have a nice crash:

$ ./sample 
Segmentation fault

This is because we asked SCardListReaders() to write at the address 0x1234. This address is, in general, not valid.

This problem could happen if you use something like:

23:     rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);

And both variables mszReaders and dwReaders are uninitialized, and by a total lack of luck dwReaders has the value SCARD_AUTOALLOCATE (i.e. -1).

 

GnuPG

The problem was found in by oddlama and reported to PCSC at "pcscd fails to read future yubikeys after removing a yubikey, until restarted #125".

The problem is not in pcsc-lite but in GnugPG. So I reported the problem at "SCardListReaders: Conditional jump or move depends on uninitialised value(s)".


WinSCard API

macOS does not support (yet) the value SCARD_AUTOALLOCATE. But pcsc-lite and Windows WinSCard do.

I agree the API to use SCARD_AUTOALLOCATE is ugly. We pass the address of a buffer pointer in a parameter that is a buffer pointer. We have to cast the variable like I did in the C sample like:

  dwReaders = SCARD_AUTOALLOCATE;
  rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders);

This feature is a Microsoft extension that is not present in the PCSC workgroup specification.


Conclusion

I think the problem is not very known and should be better documented. That was my motivation for writting this blog article.

Morality: in C language, always initialize your variables to a known/safe value (like 0).

Thursday, April 14, 2022

PC/SC sample in Erlang

To continue the list of PC/SC wrappers initiated in 2010 with "PC/SC sample in different languages" I now present a new sample code in Erlang.

I use the PC/SC wrapper for Erlang: erlang-pcsc from Alex Wilson. The project description is "libpcsc NIF binding for erlang". The license is BSD 2 clause.

This is the same wrapper I used in the previous blog article "PC/SC sample in Elixir".

The wrapper is available on Hex.pm (The package manager for the Erlang ecosystem) at https://hex.pm/packages/pcsc.

API documentation is available at https://arekinath.github.io/erlang-pcsc/index.html

 

Create a new application

$ rebar3 new app blog
===> Writing blog/src/blog_app.erl
===> Writing blog/src/blog_sup.erl
===> Writing blog/src/blog.app.src
===> Writing blog/rebar.config
===> Writing blog/.gitignore
===> Writing blog/LICENSE
===> Writing blog/README.md

Install pcsc module

Edit the file rebar.config to use:

{deps, [pcsc]}.
{erl_opts, [{i, "_build/default/lib/pcsc/include/"}]}. 

The erl_opts option is needed because we will include one header file from the pcsc module. Maybe it is possible to have a less ugly/hardcoded path.

Upgrade the dependencies:

$ rebar3 upgrade --all
===> Verifying dependencies...
===> Fetching pcsc v1.3.1
===> Fetching rebar3_hex v7.0.1
===> Fetching hex_core v0.8.4
===> Fetching verl v1.1.1
===> Analyzing applications...
===> Compiling verl
===> Compiling hex_core
===> Compiling rebar3_hex
_build/default/plugins/rebar3_hex/src/rebar3_hex_organization.erl:391:5: Warning: public_key:ssh_encode/2 is deprecated and will be removed in OTP 26; use ssh_file:encode/2 instead

===> Fetching lager v3.9.2
===> Fetching goldrush v0.1.9
===> No upgrade needed for pcsc
===> No upgrade needed for lager
===> No upgrade needed for goldrush

Build the dependencies:

$ rebar3 compile
===> Verifying dependencies...
make : on entre dans le répertoire « /home/rousseau/Documents/blog/erlang/_build/default/lib/pcsc/c_src »
cc  -Wall -g -O2 -Wextra -pipe -funsigned-char -fstrict-aliasing -Wchar-subscripts -Wundef -Wshadow -Wcast-align -Wwrite-strings -Wunused -Wuninitialized -Wpointer-arith -Wredundant-decls -Winline -Wformat -Wformat-security -Wswitch-enum -Winit-self -Wmissing-include-dirs -Wempty-body -fdiagnostics-color=auto -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wbad-function-cast -Wnested-externs  -Wmissing-declarations -fPIC -I /usr/lib/erlang/erts-12.3.1/include/ -I /usr/lib/erlang/lib/erl_interface-5.2.2/include -pthread -I/usr/include/PCSC  -c -o /home/rousseau/Documents/blog/erlang/_build/default/lib/pcsc/c_src/pcsc_nif.o /home/rousseau/Documents/blog/erlang/_build/default/lib/pcsc/c_src/pcsc_nif.c
[...]
cc /home/rousseau/Documents/blog/erlang/_build/default/lib/pcsc/c_src/pcsc_nif.o -shared -L /usr/lib/erlang/lib/erl_interface-5.2.2/lib -lei -lpcsclite -o /home/rousseau/Documents/blog/erlang/_build/default/lib/pcsc/c_src/../priv/pcsc_nif.so
make : on quitte le répertoire « /home/rousseau/Documents/blog/erlang/_build/default/lib/pcsc/c_src »
===> Analyzing applications...
===> Compiling goldrush
===> Compiling lager
===> Compiling pcsc
===> Analyzing applications... ===> Compiling blog

Edit the file src/blog.app.src to declare the pcsc application.

It should look like:

[...]
  {applications,
   [kernel,
    stdlib,
    pcsc
   ]},
[...]

Sample code

Create the source code file src/blog.erl with the content:

-module(blog).
-export([main/0]).

-include("iso7816.hrl").

main() ->
  % get all the available readers
  {ok, Readers} = pcsc_card_db:list_readers(),

  % use the first reader
  [Reader | _] = Readers,
  io:fwrite("Using: ~s~n", [Reader]),

  % connect
  {ok, Card} = pcsc_card:start_link(Reader, shared, [t1, t0]),

  % select applet
  Aid = << 160, 0, 0, 0, 98, 3, 1, 12, 6, 1 >>,
  Select_apdu = #apdu_cmd{cla = iso, ins = select, p1 = 4, p2 = 0, data = Aid},
  {ok, Select_replies} = pcsc_card:command(Card, Select_apdu),
  io:write(Select_replies),
  io:fwrite("~n"),

  % command
  Command_apdu = #apdu_cmd{ cla = iso, ins = 0, p1 = 0, p2 = 0 },
  {ok, Command_replies} = pcsc_card:command(Card, Command_apdu),
  io:write(Command_replies),
  io:fwrite("~n"),

  % extract the answer
  [Reply | _] = Command_replies,
  case Reply of
    {apdu_reply, _, ok, Msg} ->
      io:write(binary_to_atom(Msg)),
      io:fwrite("~n");
    {apdu_reply, _, error, _} ->
      io:fwrite("Error~n")
  end.

Output

Generating a standalone Erlang application looks complex. But you can test your code using the interactive shell.

$ rebar3 shell
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling blog
Erlang/OTP 24 [erts-12.3.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Eshell V12.3.1  (abort with ^G)
===> Booted syntax_tools
===> Booted compiler
===> Booted goldrush
===> Booted lager
===> Booted pcsc
===> Booted blog
 
1> blog:main().
Using: Gemalto PC Twin Reader 00 00
[{apdu_reply,t1,ok,none}]
[{apdu_reply,t1,ok,<<72,101,108,108,111,32,119,111,114,108,100,33>>}]
'Hello world!'
ok
2> 

Comments

ehecatl

An older github project also exist at https://github.com/zgbjgg/ehecatl
Its description is "An erlang library for read/write nfc smart cards".

I filed 2 issues in 2020: Missing <PCSC/winscard.h>? and More complete PC/SC wrapper? but got no answer.

This project has not seen any commit since 2014.

So I guess this project is dead.

erlang-pcsc 

The only problem I have with erlang-pcsc is that, if no smart card reader is connected, I get:

$ rebar3 shell
===> Verifying dependencies...
===> Analyzing applications...
===> Compiling blog
Erlang/OTP 24 [erts-12.3.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Eshell V12.3.1  (abort with ^G)
1> ===> Booted syntax_tools
===> Booted compiler
===> Booted goldrush
===> Booted lager
===> Booted pcsc
===> Booted blog
23:31:49.563 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.622 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.677 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.729 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.782 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.836 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.889 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.941 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:49.992 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
23:31:50.044 [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok
etc...

The error -2146435026 is SCARD_E_NO_READERS_AVAILABLE and is not really an error for SCardListReaders().

I filed a github issue [warning] got pcsc_reader_error: {error,{pcsc_error,-2146435026,no_readers,"Cannot find a smart card reader."}}; restarted ok #4 but with no answer so far.

Conclusion

Thanks Alex for the Erlang PC/SC wrapper. And thanks tofferoma, from the elixir forum, for your help in writing the Erlang sample code.

If you work on a Free Software PC/SC wrapper that is not yet in my list please let me know.

Wednesday, April 13, 2022

PC/SC sample in Elixir

To continue the list of PC/SC wrappers initiated in 2010 with "PC/SC sample in different languages" I now present a new sample code in Elixir.

Elixir uses the Erlang virtual machine: BEAM. I wanted to start with an example in Erlang but Erlang is more complex (for me) so I have not yet a working sample code in Erlang.

I use the PC/SC wrapper for Erlang: erlang-pcsc from Alex Wilson. The project description is "libpcsc NIF binding for erlang". The license is BSD 2 clause.

The wrapper is available on Hex.pm (The package manager for the Erlang ecosystem) at https://hex.pm/packages/pcsc.

API documentation is available at https://arekinath.github.io/erlang-pcsc/index.html

Elixir sample project

Create a new Elixir project using mix new ...

$ mix new blog
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/blog.ex
* creating test
* creating test/test_helper.exs
* creating test/blog_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd blog
    mix test

Run "mix help" for more commands.

Edit the file mix.exs and add the pcsc dependency. You should have something like:

[...]
  defp deps do
    [
	{:pcsc, "~> 1.3"},
    ]
  end
[...]

Install the dependency using mix deps.get

$ mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
New:
  goldrush 0.1.9
  lager 3.9.2
  pcsc 1.3.1
* Getting pcsc (Hex package)
* Getting lager (Hex package)
* Getting goldrush (Hex package)

Source code

Now we create a file blog.exs that contains (sorry, source-highlight does not provide syntax colorization for Elixir):

# list card readers
{:ok, readers} = :pcsc_card_db.list_readers()

# use the fist reader
[reader | _] = readers
IO.puts("Using reader: " <> reader)

# connect to the card
{:ok, card} = :pcsc_card.start_link(reader, :shared, [:t1, :t0])

aid = << 160, 0, 0, 0, 98, 3, 1, 12, 6, 1 >>
select_apdu = {:apdu_cmd, :default, :iso, :select, 4, 0, aid, :none}

# send select APDU
{:ok, replies} = :pcsc_card.command(card, select_apdu)
IO.inspect replies

# send command APDU
command_apdu = {:apdu_cmd, :default, :iso, 0, 0, 0, :none, :none}
{:ok, replies} = :pcsc_card.command(card, command_apdu)
IO.inspect replies

# get the first reply only
[reply | _] = replies
case reply do
	{:apdu_reply, _, :ok, msg} -> IO.puts(msg)
	{:apdu_reply, _, :error, _} -> IO.puts("Failed")
end

Output

You can now build and run the code using mix run ...

The first time you run mix run the pcsc wrapper will be built automatically. After that you only get the build & execution of the sample code.

$ mix run blog.exs

17:40:50.239 [error] calling logger:remove_handler(default) failed: :error {:badmatch, {:error, {:not_found, :default}}}
17:40:50.273 [info] Application lager started on node nonode@nohost
17:40:50.280 [info] Application pcsc started on node nonode@nohost
17:40:50.281 [info] Application blog started on node nonode@nohost
Using reader: Gemalto PC Twin Reader 00 00
[{:apdu_reply, :t1, :ok, :none}]
[{:apdu_reply, :t1, :ok, "Hello world!"}]
Hello world!

Remarks

I do not have any complaints for the Erlang PC/SC wrapper. It built fine on the first try. Nice work Alex.

As always my sample code is very minimal with no error handling. It is just a short sample.

Thanks to Stéphane Bortzmeyer for his Elixir training. That gave me the idea to try Elixir.

A big thank to tofferoma for his help on the Elixir forum on "How to access PCSC card readers via erlang/elixir?".

Conclusion

If you work on a Free Software PC/SC wrapper that is not yet in my list please let me know.

Friday, March 18, 2022

PySCard 2.0.3 released

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

The PySCard project is available at:

This version is a bug fix release.

Changes:

  • PCSCExceptions: include error code in the message
  • getReaderNames(): fix Windows 10 issue when the last reader is disconnected

Monday, February 14, 2022

Fedora, flatpak and pcsc-lite

The bug "Unable to list readers inside flatpak, when pcscd runs on host." was very strange. It is not like the potential problem I described in "Accessing smart cards from inside a flatpak sandbox" where we have 2 sides (host & flatpak container) using different versions of the pcsc-lite internal protocol.

In the present case both sides are using the same protocol but SCardGetStatusChange() fails with a very strange error: Unknown error: 0x53204253. 0x53204253 is not a (valid) error code returned by any of the PC/SC functions.

Fedora custom build

After some debug I discovered that Fedora provides pcsc-lite packages with a modification.

From https://fedora.pkgs.org/35/fedora-updates-x86_64/pcsc-lite-libs-1.9.5-1.fc35.x86_64.rpm.html your can download the source package which contains the patch file pcsc-lite-1.9.1-maxreaders.patch.

diff -up ./src/PCSC/pcsclite.h.in.readers_32 ./src/PCSC/pcsclite.h.in
--- ./src/PCSC/pcsclite.h.in.readers_32	2018-08-20 16:02:17.708302410 -0700
+++ ./src/PCSC/pcsclite.h.in	2018-08-20 16:02:49.462500967 -0700
@@ -281,7 +281,7 @@ extern const SCARD_IO_REQUEST g_rgSCardT
 
 #define PCSCLITE_VERSION_NUMBER		"@VERSION@"	/**< Current version */
 /** Maximum readers context (a slot is count as a reader) */
-#define PCSCLITE_MAX_READERS_CONTEXTS			16
+#define PCSCLITE_MAX_READERS_CONTEXTS			48
 
 #define MAX_READERNAME			128
 
diff -up ./src/PCSC/pcsclite.h.readers_32 ./src/PCSC/pcsclite.h
--- ./src/PCSC/pcsclite.h.readers_32	2018-08-20 16:02:30.993385481 -0700
+++ ./src/PCSC/pcsclite.h	2018-08-20 16:03:00.061567242 -0700
@@ -281,7 +281,7 @@ extern const SCARD_IO_REQUEST g_rgSCardT
 
 #define PCSCLITE_VERSION_NUMBER		"1.9.5"	/**< Current version */
 /** Maximum readers context (a slot is count as a reader) */
-#define PCSCLITE_MAX_READERS_CONTEXTS			16
+#define PCSCLITE_MAX_READERS_CONTEXTS			48
 
 #define MAX_READERNAME			128
 

This patch redefines PCSCLITE_MAX_READERS_CONTEXTS which is the maximum number of readers supported by pcsc-lite and update the value from 16 to 48.

pcsc-lite internal protocol

To implement the function SCardGetStatusChange() pcsc-lite exchanges the list of readers between the daemon (pcscd) and the client (libpcsclite.so.1).

pcscd will sent a list of PCSCLITE_MAX_READERS_CONTEXTS readers and libpcsclite.so.1 is expecting a list of PCSCLITE_MAX_READERS_CONTEXTS readers.

Flatpak issue

In the case of flatpak the server is running on the host and is provided by Fedora, and the client is running inside the flatpak container and is provided by whoever provides the flatpak so possibly with a pcsc-lite client not from Fedora.

And now we have a problem: the daemon is sending a list of 48 readers while the client is expecting a list of only 16 readers. After that, many bad things can happen, like incorrect return values. 

Flatpak solution

The solution is simple: include in the flatpak container a client library that is configured like the server on your host i.e. patch the pcsc-lite included in the container.

The bad news is that the flatpak contained application will not be universal any-more. You will need 2 different containers for Fedora and for Debian for example.

pcsc-lite-ccid patch

The pcsc-lite-ccid Fedora package (CCID reader driver) also uses a patch to increase the number of supported readers.

The package source code is available at https://fedora.pkgs.org/35/fedora-updates-x86_64/pcsc-lite-ccid-1.4.36-2.fc35.x86_64.rpm.html and contains the file ccid-1.4.34-maxreaders.patch

diff -up ./src/ccid_ifdhandler.h.readers_32 ./src/ccid_ifdhandler.h
--- ./src/ccid_ifdhandler.h.readers_32	2018-08-20 16:06:34.080905748 -0700
+++ ./src/ccid_ifdhandler.h	2018-08-20 16:07:04.638097096 -0700
@@ -47,7 +47,7 @@ extern int DriverOptions;
  * The maximum number of readers is also limited in pcsc-lite (16 by default)
  * see the definition of PCSCLITE_MAX_READERS_CONTEXTS in src/PCSC/pcsclite.h
  */
-#define CCID_DRIVER_MAX_READERS 16
+#define CCID_DRIVER_MAX_READERS 48
 
 /*
  * CCID driver specific functions

According to the spec file pcsc-lite-ccid.spec this changes dates from June 2021:

* Fri Jun 25 2021 Jakub Jelen <jjelen@redhat.com> - 1.4.34-2
- Add support for more readers

So less than a year.

Increase the number of readers?

I guess Fedora/RedHat has one (or more) customer(s) with a need to support more than 16 readers. Maybe that is the case of 0.01% of pcsc-lite users (rough estimation out of my mind). And for some users 48 readers will not be enough (See "A reader for 96 smart cards? sysmoSIMBANK").

A long term solution is to move from a hard coded limit of CCID_DRIVER_MAX_READERS readers to a dynamic (unlimited) list. This is already planed for pcsc-lite in "use a list instead of a fixed size array for 16 reader states" and for ccid in "use a list instead of a fixed size array for 16 reader states". I had no motivation to implement that so far. If you need this change please contact me.

Revert the Fedora patches?

One option to solve the issue with Flatpak would be for Fedora/RedHat to revert the patches.

I don't know if they have more customers using more than 16 smart card readers, or more customers using Flatpak applications.

Conclusion

Flatpak applications with a need to access smart cards or tokens (Chrome, Teams) may be difficult to use on Fedora/RedHat.

Friday, February 11, 2022

Accessing smart cards from inside a flatpak sandbox

I received a bug report about "Unable to list readers inside flatpak, when pcscd runs on host." complaining that a smart card reader is not accessible from inside a flatpak sandbox. 

I then discovered many other flatpak issues regarding smart cards access in https://github.com/flathub:

I never used flatpak so I start by trying to reproduce the problem.

Environment

  • GNU/Linux Debian testing
  • flatpak 1.12.4-1
  • pcscd 1.9.5-1

Simple test

The idea is to run pcsc_scan from a flatpak sandbox using the pcscd daemon on the host. This is possible since the flatpak patch "Add support for PCSC socket".

Since the daemon (pcscd) and the client library (libpcsclite.so.1) use a socket (/run/pcscd/pcscd.comm by default) to communicate we just have to export the socket in the sandbox to allow the communication.

The important part for that is:

finish-args:
  - --socket=pcsc

Sources

File fr.apdu.pcsc_scan.yml

app-id: fr.apdu.pcsc_scan
runtime: org.freedesktop.Platform
runtime-version: '21.08'
sdk: org.freedesktop.Sdk
command: pcsc_scan.sh
finish-args:
  - --socket=pcsc
modules:
  - name: pcsc-lite
    config-opts:
      - --disable-libudev
      - --disable-libsystemd
      - --without-systemdsystemunitdir
      - --disable-serial
      - --disable-usb
      - --disable-documentation
    cleanup:
      - '/include'
      - '/bin/pcsc-spy'
      - '/lib/libpcscspy*'
      - '/lib/pkg-config'
      - '/share/doc'
      - '/share/man'
    post-install:
      - rm /app/sbin/pcscd
      - rmdir /app/sbin || true
    sources:
      - type: archive
        url: https://pcsclite.apdu.fr/files/pcsc-lite-1.9.5.tar.bz2
        sha256: 9ee3f9b333537562177893559ad4f7b8d5c23ebe828eef53056c02db14049d08
  - name: pcsc-tools
    cleanup:
      - '/bin/gscriptor'
      - '/bin/scriptor'
      - '/bin/ATR_analysis'
      - '/share/pcsc'
      - '/share/man'
    sources:
      - type: archive
        url: http://ludovic.rousseau.free.fr/softwares/pcsc-tools/pcsc-tools-1.6.0.tar.bz2
        sha256: 651c8dd74bcb33db4c16935ce5d80dd1aa6eb20ba9d5c4b9631a098326ef8b9f
  - name: pcsc_scan.sh
    buildsystem: simple
    build-commands:
      - install -D pcsc_scan.sh /app/bin/pcsc_scan.sh
    sources:
      - type: file
        path: pcsc_scan.sh

 

File pcsc_scan.sh

By default pscs_scan calls ATR_analysis which is a Perl script. I do not want to include Perl in my sandbox so I create a wrapper to call pcsc_scan with the -n argument to disable the use of ATR_analysis.

#!/bin/sh
pcsc_scan -n

Generation

$ flatpak-builder --user --install build-dir fr.apdu.pcsc_scan.yml
[...]

Execution

$ flatpak run fr.apdu.pcsc_scan 
Using reader plug'n play mechanism
Scanning present readers...
0: Gemalto PC Twin Reader 00 00
 
Fri Feb 11 17:55:17 2022
 Reader 0: Gemalto PC Twin Reader 00 00
  Event number: 6
  Card state: Card inserted, Shared Mode, 
  ATR: 3B A7 00 40 18 80 65 A2 08 01 01 52

It works fine. Youpi!

Limitations

You need to use the same protocol version for the pcsc-lite daemon and the pcsc-lite client.

If not then pcscd will complain with something like:

Journalctl :
Jan 16 16:24:42 nuc01.lan pcscd[2066]: 99999999 winscard_svc.c:383:ContextThread() Client protocol is 4:4
Jan 16 16:24:42 nuc01.lan pcscd[2066]: 00000040 winscard_svc.c:385:ContextThread() Server protocol is 4:3

The protocol version major & minor is defined in winscard_msg.h.

Protocol history

  • 4.4 since pcsc-lite 1.8.24, Oct 2018
  • 4.3 since pcsc-lite 1.8.9, Oct 2013
  • 4.2 since pcsc-lite 1.6.5, Dec 2010
  • 4.1 since pcsc-lite 1.6.5, Dec 2010
  • 4.0 since pcsc-lite 1.6.0, May 2010

So if the server side is older than 1.8.24 (Oct 2018) and the client side is newer you have a problem.

For example Ubuntu 18.04 LTS provides pcscd 1.8.23-1. This can be problematic.

Reproducing the problem

To check that I modified the file fr.apdu.pcsc_scan.yml to build pcsc-lite 1.8.23 instead of the latest version 1.9.5.

The build is successful but the execution fails with:

$ flatpak run fr.apdu.pcsc_scan
SCardEstablishContext: Service was stopped.

And in the logs on the host I see:

$ journalctl --unit=pcscd
[...]
févr. 11 18:55:07 debian pcscd[3715]: 00000006 winscard_svc.c:361:ContextThread() Received command: CMD_VERSION from client 8
févr. 11 18:55:07 debian pcscd[3715]: 00000004 winscard_svc.c:373:ContextThread() Client is protocol version 4:3
févr. 11 18:55:07 debian pcscd[3715]: 00000001 winscard_svc.c:382:ContextThread() Communication protocol mismatch!
févr. 11 18:55:07 debian pcscd[3715]: 00000002 winscard_svc.c:384:ContextThread() Client protocol is 4:3
févr. 11 18:55:07 debian pcscd[3715]: 00000001 winscard_svc.c:386:ContextThread() Server protocol is 4:4
févr. 11 18:55:07 debian pcscd[3715]: 00000002 winscard_svc.c:396:ContextThread() CMD_VERSION rv=0x8010001E for client 8

Here the server is using version 4.4 but the client is using version 4.3.
The error code 0x8010001E is SCARD_E_SERVICE_STOPPED.

Solution

The solution is simple: use the same protocol version on both sides.

If your host is using an old pcsc-lite using the internal protocol 4.3 then include an old (between 1.8.9 and 1.8.23) pcsc-lite in your flatpak image.

I know this is not perfect. It is then not possible to provide an application in one flatpak and deploy it everywhere and anywhere.

Conclusion

The protocol between the daemon and the client is internal to pcsc-lite. It has not been designed to be interoperable with something else, including older versions of pcsc-lite itself.

pcsc-lite is free software. So you are free to install whatever version you want.

Friday, January 21, 2022

Multi-thread and Atomic

Multi-thread programming seams easy but it is difficult to write correct multi-threading code.

For example pcsc-lite and my CCID driver use threads and are not (yet) perfect. One problem in particular is the access to the same variable from different threads.

C11 standard defines the Atomic types to make multi-thread programming easier.

Source code

This source code exhibits the problem.

#include <pthread.h>
#include <stdio.h>

enum CONSTANTS {
    NUM_THREADS = 1000,
    NUM_ITERS = 1000
};

_Atomic int global_a = 0;
int global = 0;

static void* main_thread(void *arg)
{
    (void)arg;

    int i;
    for (i = 0; i < NUM_ITERS; ++i)
    {
        global_a++;
        global++;
    }
    return NULL;
}

int main(void)
{
    int i;
    pthread_t threads[NUM_THREADS];

    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, main_thread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);

    printf("global_a %d %s\n", global_a,
        global_a == NUM_THREADS * NUM_ITERS ? "OK" : "FAIL");

    printf("global   %d %s\n", global,
        global == NUM_THREADS * NUM_ITERS ? "OK" : "FAIL");

    return 0;
}

Result

If I compile and run the sample code I get:

global_a 1000000 OK
global   660409 FAIL
or, with another execution:
global_a 1000000 OK
global   691552 FAIL

You can see that the variable global that is NOT declared with _Atomic does not have the expected value. Some updates of the variable value failed (were skipped).

Another option if you do not want or can't use _Atomic is to use pthread_mutex_lock() and pthread_mutex_unlock() to protect the accesses to the variable. But the code is then harder to read.

Impact on pcsc-lite and libccid

The problem was reported by andrei-datcu in the pull request No data races in EHStatusHandlerThread.

I then fixed different problems in these changes (non-exhautive list):

And simplified the code by removing a mutex in Remove mutex and use _Atomic instead.

Conclusion

The next versions of pcsc-lite and libccid will be safer and more correct.

Saturday, October 23, 2021

PCSC sample in Flutter/Dart

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

Fabien Rousseau wrote a PC/SC wrapper for Dart (the language) and Flutter (the UI toolkit).

The library is at https://pub.dev/packages/flutter_pcsc. It is available for GNU/Linux, macOS and Windows.

The source code is at https://github.com/fabienrousseau/flutter_pcsc and uses the MIT license. The current version is 0.0.4.

Fabien also provides an sample code at https://github.com/fabienrousseau/flutter_pcsc/blob/main/flutter_pcsc/example/lib/main.dart. I reused this code for my example bellow.

 

Installation

I just cloned the flutter_pcsc git repository.

You also need to install the flutter SDK to build and run the application. But if you use Flutter I imagine your already have the SDK installed.


Source code

You can use flutter create test to create a sample application with the correct structure and configuration files.

Then edit the pubspec.yaml file to add in the dependencies: section something like:

dependencies:
  flutter_pcsc:
    path: /home/rousseau/Documents/github/flutter_pcsc/flutter_pcsc

Of course you adapt the path to the location of your flutter_pcsc directory.

The source code of the sample application lib/main.dart is:

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';

import 'package:flutter_pcsc/flutter_pcsc.dart';

void main() {
  MyApp? myApp;

  runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    FlutterError.onError = (FlutterErrorDetails details) {
      FlutterError.dumpErrorToConsole(details);
      myApp?.addError(details.toString());
    };

    runApp(myApp = MyApp());
  }, (Object error, StackTrace stack) {
    myApp?.addError(error.toString());
  });
}

class MyApp extends StatelessWidget {
  final GlobalKey<_MyAppBodyState> _myAppKey = GlobalKey();

  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('PCSC example app'),
          ),
          body: MyAppBody(key: _myAppKey)),
    );
  }

  void addError(String msg) {
    _myAppKey.currentState?.messages.add(Message.error(msg));
  }
}

class MyAppBody extends StatefulWidget {
  const MyAppBody({required Key key}) : super(key: key);

  @override
  _MyAppBodyState createState() {
    return _MyAppBodyState();
  }
}

enum MessageType { info, error }

class Message {
  final String content;
  final MessageType type;
  Message(this.type, this.content);

  static info(String content) {
    return Message(MessageType.info, content);
  }

  static error(String content) {
    return Message(MessageType.error, content);
  }
}

class _MyAppBodyState extends State<MyAppBody> {
  static const List<int> selectAppletCommand = [
    0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01
  ];
  static const List<int> appCommand = [
    0x00, 0x00, 0x00, 0x00
  ];
  final ScrollController _scrollController = ScrollController();

  final List<Message> messages = [];

  @override
  void initState() {
    super.initState();
    helloWorld();
  }

  Future<void> helloWorld() async {
    /* establish PCSC context */
    int ctx = await Pcsc.establishContext(PcscSCope.user);
    CardStruct? card;
    try {
      /* get the reader list */
      List<String> readers = await Pcsc.listReaders(ctx);

      if (readers.isEmpty) {
        messages.add(Message.error('Could not detect any reader'));
      } else {
        /* use the first reader */
        String reader = readers[0];
        setState(() {
          messages.add(Message.info('Using reader: $reader'));
        });

        /* connect to the card */
        card = await Pcsc.cardConnect(
            ctx, reader, PcscShare.shared, PcscProtocol.any);

        /* send select applet APDU */
        var response = await Pcsc.transmit(card, selectAppletCommand);
            messages.add(Message.info('Card returned: ${hexDump(response)}'));

        /* send applet test command */
        response = await Pcsc.transmit(card, appCommand);
        var sw = response.sublist(response.length - 2);
        var bytes = response.sublist(0, response.length - 2);
        var text = utf8.decode(bytes);

        messages.add(Message.info('Card returned: ${hexDump(response)}'));

        if (sw[0] != 0x90 || sw[1] != 0x00) {
          setState(() {
            messages
                .add(Message.error('Card returned an error: ${hexDump(sw)}'));
          });
        } else {
          setState(() {
            messages.add(Message.info('text is: ${text}'));
          });
        }
      }
    } finally {
      if (card != null) {
        try {
          /* disconnect from the card */
          await Pcsc.cardDisconnect(card.hCard, PcscDisposition.resetCard);
        } on Exception catch (e) {
          messages.add(Message.error(e.toString()));
        }
      }
      try {
        /* release PCSC context */
        await Pcsc.releaseContext(ctx);
      } on Exception catch (e) {
        messages.add(Message.error(e.toString()));
      }
    }
  }

  static String hexDump(List<int> csn) {
    return csn
        .map((i) => i.toRadixString(16).padLeft(2, '0').toUpperCase())
        .join(' ');
  }

  _scrollToBottom() {
    _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
  }

  @override
  Widget build(BuildContext context) {
    TextStyle errorStyle = const TextStyle(color: Colors.red);
    WidgetsBinding.instance?.addPostFrameCallback((_) => _scrollToBottom());
    return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
      Expanded(
          child: Column(children: [
        Expanded(
            child: ListView(
                controller: _scrollController,
                children: messages
                    .map((e) => Text(e.content,
                        style: e.type == MessageType.error ? errorStyle : null))
                    .toList())),
        Container(
            margin: const EdgeInsets.all(10),
            child: ElevatedButton(
                onPressed: () async {
                  await tryAgain();
                },
                child: const Text("Try again")))
      ]))
    ]);
  }

  tryAgain() async {
    messages.clear();
    await helloWorld();
  }
}

Output

$ flutter run
hanging current working directory to: /home/rousseau/Documents/flutter/blog
Launching lib/main.dart on Linux in debug mode...
Building Linux application...                                           
Syncing files to device Linux...                                    55ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

💪 Running with sound null safety 💪

An Observatory debugger and profiler on Linux is available at:
http://127.0.0.1:34019/ORqpNzBQLEQ=/
The Flutter DevTools debugger and profiler on Linux is available at:
http://127.0.0.1:9100?uri=http://127.0.0.1:34019/ORqpNzBQLEQ=/

Flutter is a framework to create graphical applications. So when you run the sample code you get a new window.

Remarks

The code is long because it is a complete Flutter application so you have to handle the graphical interface.

I asked Fabien for a Dart only interface but flutter_pcsc depends on some Flutter mechanisms to handle asynchronism. So it is not possible to write a PC/SC application for the console just using Dart. But if you use Dart I guess you also use flutter.

Despite of his (nice) name Fabien Rousseau is not part of my direct family. So I do not have a potential conflict of interest here 😜.


Conclusion

Thanks to Fabien for telling about his Dart/Flutter project.

If you work on a Free Software PC/SC wrapper that is not yet in my list please let me know.