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.