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.