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
{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
It should look like:
[...]
{applications,
[kernel,
stdlib,
pcsc
]},
[...]
Sample code
Create the source code file
-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.