PCSC sample in Ruby

Here is the PCSC sample in Ruby language I promised in PC/SC sample in different languages.

A PC/SC Wrapper for Ruby also exists at Smart-card communication, straight from ruby.. The latest version is 0.4.11 from 2009. I used an older version 0.3.1 when I worked with this wrapper.

Installation


You first need to install ruby and the other components needed for the compilation of smartcard for Ruby.

apt-get install ruby rake rubygems libopenssl-ruby ruby1.8-dev

Then fetch the archive smartcard-0.4.11.gem


$ sudo gem install echoe
[...]
$ unzip smartcard.zip
$ cd smartcard
$ rake manifest
[...]
$ rake package
(in /home/rousseau/HSLM/smartcard)
  Successfully built RubyGem
  Name: smartcard
  Version: 0.3.1
  File: smartcard-0.3.1.gem
Private key not found; gem will not be signed.
Targeting "ruby" platform.
$ rake test
(in /home/rousseau/HSLM/smartcard)
/usr/bin/ruby1.8 extconf.rb
checking for main() in -lpcsclite... yes
checking for wintypes.h... yes
checking for reader.h... yes
checking for winscard.h... yes
checking for pcsclite.h... yes
creating Makefile
[...]
/usr/bin/ruby1.8 -Ilib:ext:bin:test "/var/lib/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/test_containers.rb" "test/test_smoke.rb" 
Loaded suite /var/lib/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
Started
...
Finished in 0.046223 seconds.

3 tests, 14 assertions, 0 failures, 0 errors
$ rake docs
[...]

Source code


require 'smartcard'

context = Smartcard::PCSC::Context.new(Smartcard::PCSC::SCOPE_SYSTEM)
readers = context.list_readers nil
context.cancel

# Use the first reader
reader = readers.first

# Connect to the card
card = Smartcard::PCSC::Card.new(context, reader, Smartcard::PCSC::SHARE_SHARED, Smartcard::PCSC::PROTOCOL_ANY)

# Get the protocol to use
card_status = card.status

# Select applet
aid = [0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01]
select_apdu = [0x00, 0xA4, 0x04, 0x00, aid.length, aid].flatten
send_ioreq = {Smartcard::PCSC::PROTOCOL_T0 => Smartcard::PCSC::IOREQUEST_T0,
              Smartcard::PCSC::PROTOCOL_T1 => Smartcard::PCSC::IOREQUEST_T1}[card_status[:protocol]]
recv_ioreq = Smartcard::PCSC::IoRequest.new
response = card.transmit(select_apdu.map {|byte| byte.chr}.join(''), send_ioreq, recv_ioreq)
response_str = (0...response.length).map { |i| ' %02x' % response[i].to_i }.join('')
puts "Answer: #{response_str}\n"

# test APDU
test_apdu = [0, 0, 0, 0]
response = card.transmit(test_apdu.map {|byte| byte.chr}.join(''), send_ioreq, recv_ioreq)
response_str = (0...response.length).map { |i| ' %02x' % response[i].to_i }.join('')
puts "Answer: #{response_str}\n"
response_str = (0...response.length-2).map { |i| '%c' % response[i].to_i }.join('')
puts "Answer: #{response_str}\n"

# Deconnect
card.disconnect Smartcard::PCSC::DISPOSITION_LEAVE
context.release

Output


Answer: 90 00
Answer: 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 90 00
Answer: Hello world!

Conclusion


Nothing more to add. If you are a Ruby user you may be interested by this wrapper.

PCSC sample in Prolog

Here is the PCSC sample in Prolog language I promised in PC/SC sample in different languages.

Renaud Mariana wrote a PC/SC wrapper for GNU Prolog. The latest version is 1.01 dated 2003 and is available in GNU Prolog contributions PC/SC Lite interface for GNU Prolog.

Installation


You first need to install GNU Prolog:

apt-get install gprolog

Then apply some patches to the wrapper project, see my mail in the users-prolog mailing list. Then the compilation succeeds without any problem.

gplpcsc/contribs/gplpcsc$ make
gplc -c -C "-Wall -g `pkg-config --cflags libpcsclite`"  pcsc/muscle_c.c
gplc -o testapdu pcsc/muscle_c.o  -L "`pkg-config --libs libpcsclite` -lcrypto -lpthread"  testapdu.pl util_pcsc/util.pl

Source code


:- include('pcsc/muscle').

start :-
    scard_establish_context,
    scard_connect,

    % print ATR data
    scard_status(_, Atr),
    print('ATR: '), print_hex_list(Atr), nl, 

    % enable trace
    scard_enable_verbose(true),

    Commande = [0,0xA4,4,0,0x0A,0xA0,0,0,0,0x62,3,1,0x0C,6,1],
    scard_transmit(Commande, _ApduOut1, _Sw1, _Time1),

    print_hex_list(_ApduOut1), 
    print_hex_list(_Sw1), nl,

    scard_transmit([0,0,0,0,0], _ApduOut2, _Sw2, _Time2),

    print_hex_list(_ApduOut2),
    print_hex_list(_Sw2), nl,
    print_ASCII_list(_ApduOut2), nl.

print_ASCII_list(L) :-
    ( list(L) ->
        print('['), '$print_ASCII_list'(L,[]), print(']')
    ;
        print('print_ASCII_list error, argument is not a list')
    ).

'$print_ASCII_list' --> [], !.
'$print_ASCII_list' --> [A], { format('%c',[A]) },
    '$print_ASCII_list'.

:- initialization(start).

Output


ATR: [3B FA 94 00 00 81 31 20 43 80 65 A2 01 01 01 3D 72 D6 43 21 ]
tr: 00a404000aa00000006203010c0601 - 9000, t= 15ms.
[][90 00 ]
tr: 0000000000 - 48656c6c6f20776f726c64219000, t= 23ms.
[48 65 6C 6C 6F 20 77 6F 72 6C 64 21 ][90 00 ]
[Hello world!]
GNU Prolog 1.3.0
By Daniel Diaz
Copyright (C) 1999-2007 Daniel Diaz
| ?- 

Conclusion


Nothing more to add. If you are a Prolog user you may be interested by this wrapper.

Comments on my blog

Some of you have tried to add comments to some posts on my blog. The comments are moderated so do not appear immediately.

The problem is that I did not know where to look for comments to moderate :-). So some very old comments (more than a month) were still not moderated and approved. Sorry for that.

On 6 comments 1 was a spam. All the 5 other have been accepted.

I also changed the configuration to be notified by mail of new comments. I should then moderate your comments within a few hours.

Lachez vos comm's.

PCSC sample in OCaml

Here is the PCSC sample in OCaml language I promised in PC/SC sample in different languages.

Manuel Preliteiro wrote a OCaml PC/SC OCaml PC/SC
Binding to the PCSC standard
started in 2003 and still active in 2007. The latest version of the project is 0.6 and is still in beta but works fine according to my tests.

Installation


You first need to install OCaml. A simple apt-get install ocaml is enough on a Debian (or derivative like Ubuntu) system. Then get the ocamlpcsc-0.6.tar.gz archive from OCaml PC/SC
Binding to the PCSC standard
.

ocamlpcsc-0.6$ make
cc `pkg-config libpcsclite --cflags` -c convert.c
ocamlopt -c pcscmacros.ml
ocamlc -c pcscmacros.ml
cc -I /usr/lib/ocaml/3.10.2 `pkg-config libpcsclite --cflags` -c pcscC.c
ocamlopt -pp camlp4o -c pcscML.ml
ocamlc -custom -pp camlp4o -c pcscML.ml

Source code


I am not a Caml or OCaml expert. The example is just a modification of the example from Manuel to use my applet.

open Pcscmacros;;
open Printf;;
open PcscML;;
open Str;;

let func () =
  let (rvEstablishContext, hContext) = sCardEstablishContext scard_scope_system () () in

    if (rvEstablishContext != scard_s_success) then (
      printf "SCardEstablishContext: Cannot Connect to Resource Manager %d\n" rvEstablishContext;
      flush(stdout);
      ()
    );

    let (rvListReaders, readers) = sCardListReaders () in

      printf "Readers List: \n";
      Array.iter (fun z -> print_string (z ^ " : ")) readers;
      printf "\n";
      flush(stdout);

      let reader1 = readers.(0) in

      let (rvSCardConnect, hCard, dwActiveProtocol) = sCardConnect reader1 scard_share_shared scard_protocol_t1 in

        if (rvSCardConnect != scard_s_success) then (
          printf "SCardConnect: Cannot connect to card %d\n" rvSCardConnect;
          flush(stdout);
          ()
        );

        let pioSendPci = (scard_protocol_t1, 0) in
        let commande1 = [|0x00; 0xA4; 0x04; 0x00; 0x0A; 0xA0; 0x00; 0x00; 0x00; 0x62; 0x03; 0x01; 0x0C; 0x06; 0x01|] in
        let dwSendLength = Array.length commande1 in

        let (rvSCardTransmit, pioRecvPci, pcRecvBuffer) = sCardTransmit hCard pioSendPci commande1 dwSendLength in

          if (rvSCardTransmit != scard_s_success) then (
            printf "SCardTransmit: Cannot transmit to card %d\n" rvSCardTransmit;
            flush(stdout);
            ()
          )
          else (
            let (_, _) =  pioRecvPci in
              printf "Data transmited with success: ";
              Array.iter (fun z -> print_string ((string_of_int z) ^ " : ")) pcRecvBuffer;
              print_string "\n";
              flush(stdout)
          );

          let commande2 = [|0x00; 0x00; 0x00; 0x00|] in
          let dwSendLength = Array.length commande2 in

          let (rvSCardTransmit, pioRecvPci, pcRecvBuffer) = sCardTransmit hCard pioSendPci commande2 dwSendLength in
  
            if (rvSCardTransmit != scard_s_success) then (
              printf "SCardTransmit: Cannot transmit to card %d\n" rvSCardTransmit;
              flush(stdout);
              ()
            )
            else (
              let (_, _) =  pioRecvPci in
                printf "Data transmited with success: ";
                Array.iter (fun z -> print_string ((string_of_int z) ^ " : ")) pcRecvBuffer;
                print_string "\n";
                Array.iter (fun z -> print_char (char_of_int z)) pcRecvBuffer;
                print_string "\n";
                flush(stdout)
            );
  
            let rvSCardDisconnect = sCardDisconnect hCard scard_leave_card in
  
              if (rvSCardDisconnect != scard_s_success) then (
                printf "SCardDisconnect: Cannot disconect card %d\n" rvSCardDisconnect;
                flush(stdout);
                ()
              );

;;

let _ = func ();;

(** ocamlopt -c demo.ml **)
(** ocamlopt -pp camlp4o str.cmxa -cclib '-lpcsclite convert.o pcscC.o' -o demo pcscML.cmx pcscmacros.cmx demo.cmx **)


The last two line are the commands to execute to compile the program.

Output


Readers List: 
Gemplus GemPC Twin 00 00 : 
Data transmited with success: 144 : 0 : 
Data transmited with success: 72 : 101 : 108 : 108 : 111 : 32 : 119 : 111 : 114 : 108 : 100 : 33 : 144 : 0 : 
Hello world!?

You can see a ? character at the end. It is the status word 0x90 0x90 badly converted in ASCII. An improvement would be to only display n-2 first characters. This is left as an exercise to the reader.

Conclusion


Nothing much to say. If you are a OCaml user you may be interested by this wrapper.

This blog entry is dedicated to Alexandre.

What is in a PC/SC reader name?

USB serial number


In a recent discussion [Muscle] pcsc-lite and reader serial number? it was asked if it is possible to get the USB serial number of a USB reader using PC/SC. The PC/SC API does not provide such a service but pcsc-lite includes the USB serial number in the PC/SC name of the reader.

Example with a SCM SCR331 reader:
  • On GNU/Linux
    SCM SCR 331 (80000F3A) 00 00
  • On Mac OS X (Snow Leopard)
    SCM SCR 331 00 00
  • On Windows XP
    SCM Microsystems Inc. SCR33x USB Smart Card Reader 0
You can see that the reader name is not standardised.

You can also see that on GNU/Linux you have a "(80000F3A)". This is the serial number of the USB device. You can also see the device serial number in the file /proc/bus/usb/devices
T:  Bus=02 Lev=02 Prnt=41 Port=03 Cnt=02 Dev#= 47 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=16 #Cfgs=  1
P:  Vendor=04e6 ProdID=e001 Rev= 5.25
S:  Manufacturer=SCM Microsystems Inc.
S:  Product=SCRx31 USB Smart Card Reader
S:  SerialNumber=80000F3A
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=usbfs
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=16ms

Interface name


The Gemalto Prox-DU reader is a composite USB device with two CCID readers inside (and a HID interface but we don't use it). One reader is for contact cards and the other reader is for contactless cards.

  • On GNU/Linux
    Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 00
    Gemalto Prox-DU [Prox-DU Contactless_09A00795] (09A00795) 01 00
  • On Mac OS X (Snow Leopard)
    Gemalto Prox-DU 00 00
    Gemalto Prox-DU 00 01
    Gemalto Prox-DU 00 01
    We have two identical lines because of a bug in pcsc-lite on Mac OS X. This bug is already fixed on the smartcardservices.macosforge.org project but not yet deployed in a Mac OS X upgrade.
  • On Windows XP
    Gemalto Prox-DU Contact_09A00795 0
    Gemalto Prox-DU Contactless_09A00795 1

File /proc/bus/usb/devices:
T:  Bus=02 Lev=02 Prnt=41 Port=03 Cnt=02 Dev#= 49 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=32 #Cfgs=  1
P:  Vendor=08e6 ProdID=5503 Rev= 1.00
S:  Manufacturer=Gemalto
S:  Product=Prox Dual USB PC LinkReader
S:  SerialNumber=09A00795
C:* #Ifs= 3 Cfg#= 1 Atr=80 MxPwr=200mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid
E:  Ad=83(I) Atr=03(Int.) MxPS=   8 Ivl=254ms
I:* If#= 1 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=(none)
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=86(I) Atr=03(Int.) MxPS=   8 Ivl=24ms
I:* If#= 2 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=(none)
E:  Ad=04(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=85(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=87(I) Atr=03(Int.) MxPS=   8 Ivl=24ms

The interface name can be found in the /sys/bus/usb/devices subsystem. But it is not easy to identify the correct USB device (the 2-1.4:1.* in the example).

$ ls /sys/bus/usb/devices/2-1.4:1.*
/sys/bus/usb/devices/2-1.4:1.0:
bAlternateSetting   bInterfaceSubClass  ep_83      subsystem
bInterfaceClass     bNumEndpoints       interface  uevent
bInterfaceNumber    bus                 modalias   usb_endpoint:usbdev2.49_ep83
bInterfaceProtocol  driver              power      usb:hiddev0

/sys/bus/usb/devices/2-1.4:1.1:
bAlternateSetting   bNumEndpoints  interface  usb_endpoint:usbdev2.49_ep01
bInterfaceClass     bus            modalias   usb_endpoint:usbdev2.49_ep82
bInterfaceNumber    ep_01          power      usb_endpoint:usbdev2.49_ep86
bInterfaceProtocol  ep_82          subsystem
bInterfaceSubClass  ep_86          uevent

/sys/bus/usb/devices/2-1.4:1.2:
bAlternateSetting   bNumEndpoints  interface  usb_endpoint:usbdev2.49_ep04
bInterfaceClass     bus            modalias   usb_endpoint:usbdev2.49_ep85
bInterfaceNumber    ep_04          power      usb_endpoint:usbdev2.49_ep87
bInterfaceProtocol  ep_85          subsystem
bInterfaceSubClass  ep_87          uevent

$ cat /sys/bus/usb/devices/2-1.4:1.*/interface
Prox-DU HID_09A00795
Prox-DU Contactless_09A00795
Prox-DU Contact_09A00795

It is easier to use the hal-device command:
0: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_usbraw'
  linux.sysfs_path = '/sys/class/usb_device/usbdev2.51'  (string)
  info.subsystem = 'usb_device'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  info.product = 'USB Raw Device Access'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_usbraw'  (string)
  usbraw.device = '/dev/bus/usb/002/051'  (string)
  info.category = 'usbraw'  (string)
  info.capabilities = { 'usbraw' } (string list)
  linux.hotplug_type = 2  (0x2)  (int)
  linux.subsystem = 'usb_device'  (string)
  linux.device_file = '/dev/bus/usb/002/051'  (string)

1: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if2'
  usb.speed = 12  (double)
  usb.device_protocol = 0  (0x0)  (int)
  usb.version = 2  (double)
  usb.vendor_id = 2278  (0x8e6)  (int)
  usb.is_self_powered = false  (bool)
  usb.product_id = 21763  (0x5503)  (int)
  info.subsystem = 'usb'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  info.product = 'USB Chip/Smartcard Interface'  (string)
  usb.can_wake_up = false  (bool)
  linux.subsystem = 'usb'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if2'  (string)
  usb.vendor = 'Gemplus'  (string)
  usb.bus_number = 2  (0x2)  (int)
  usb.product = 'USB Chip/Smartcard Interface'  (string)
  usb.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.2'  (string)
  usb.device_revision_bcd = 256  (0x100)  (int)
  usb.configuration_value = 1  (0x1)  (int)
  usb.max_power = 200  (0xc8)  (int)
  usb.interface.number = 2  (0x2)  (int)
  usb.interface.class = 11  (0xb)  (int)
  usb.interface.subclass = 0  (0x0)  (int)
  usb.interface.protocol = 0  (0x0)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.2'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  usb.num_configurations = 1  (0x1)  (int)
  usb.interface.description = 'Prox-DU Contact_09A00795'  (string)
  usb.num_ports = 0  (0x0)  (int)
  usb.num_interfaces = 3  (0x3)  (int)
  usb.linux.device_number = 51  (0x33)  (int)
  usb.device_class = 0  (0x0)  (int)
  usb.serial = '09A00795'  (string)
  usb.device_subclass = 0  (0x0)  (int)

2: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if1'
  usb.speed = 12  (double)
  usb.device_protocol = 0  (0x0)  (int)
  usb.version = 2  (double)
  usb.vendor_id = 2278  (0x8e6)  (int)
  usb.is_self_powered = false  (bool)
  usb.product_id = 21763  (0x5503)  (int)
  info.subsystem = 'usb'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  info.product = 'USB Chip/Smartcard Interface'  (string)
  usb.can_wake_up = false  (bool)
  linux.subsystem = 'usb'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if1'  (string)
  usb.vendor = 'Gemplus'  (string)
  usb.bus_number = 2  (0x2)  (int)
  usb.product = 'USB Chip/Smartcard Interface'  (string)
  usb.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.1'  (string)
  usb.device_revision_bcd = 256  (0x100)  (int)
  usb.configuration_value = 1  (0x1)  (int)
  usb.max_power = 200  (0xc8)  (int)
  usb.interface.number = 1  (0x1)  (int)
  usb.interface.class = 11  (0xb)  (int)
  usb.interface.subclass = 0  (0x0)  (int)
  usb.interface.protocol = 0  (0x0)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.1'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  usb.num_configurations = 1  (0x1)  (int)
  usb.interface.description = 'Prox-DU Contactless_09A00795'  (string)
  usb.num_ports = 0  (0x0)  (int)
  usb.num_interfaces = 3  (0x3)  (int)
  usb.linux.device_number = 51  (0x33)  (int)
  usb.device_class = 0  (0x0)  (int)
  usb.serial = '09A00795'  (string)
  usb.device_subclass = 0  (0x0)  (int)

3: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if0'
  usb.speed = 12  (double)
  usb.device_protocol = 0  (0x0)  (int)
  usb.version = 2  (double)  usb.vendor_id = 2278  (0x8e6)  (int)
  usb.is_self_powered = false  (bool)
  usb.product_id = 21763  (0x5503)  (int)  info.subsystem = 'usb'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  info.product = 'USB HID Interface'  (string)
  usb.can_wake_up = false  (bool)
  linux.subsystem = 'usb'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if0'  (s
tring)  usb.vendor = 'Gemplus'  (string)
  usb.bus_number = 2  (0x2)  (int)
  usb.product = 'USB HID Interface'  (string)
  usb.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-
1.3:1.0'  (string)
  usb.device_revision_bcd = 256  (0x100)  (int)
  usb.configuration_value = 1  (0x1)  (int)  usb.max_power = 200  (0xc8)  (int)
  usb.interface.number = 0  (0x0)  (int)
  usb.interface.class = 3  (0x3)  (int)  usb.interface.subclass = 0  (0x0)  (int)
  usb.interface.protocol = 0  (0x0)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:
1.0'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (st
ring)
  usb.num_configurations = 1  (0x1)  (int)
  info.linux.driver = 'usbhid'  (string)
  usb.num_ports = 0  (0x0)  (int)
  usb.num_interfaces = 3  (0x3)  (int)
  usb.linux.device_number = 51  (0x33)  (int)
  usb.device_class = 0  (0x0)  (int)
  usb.serial = '09A00795'  (string)
  usb.device_subclass = 0  (0x0)  (int)

4: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'
  usb_device.num_configurations = 1  (0x1)  (int)
  usb_device.num_ports = 0  (0x0)  (int)
  usb_device.num_interfaces = 3  (0x3)  (int)
  usb_device.linux.device_number = 51  (0x33)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3'  (string)
  info.subsystem = 'usb_device'  (string)
  usb_device.device_class = 0  (0x0)  (int)
  usb_device.serial = '09A00795'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_4cc_1122_noserial'  (string)
  usb_device.device_subclass = 0  (0x0)  (int)
  info.vendor = 'Gemplus'  (string)
  info.product = 'Prox Dual USB PC LinkReader'  (string)
  usb_device.speed = 12  (double)
  usb_device.device_protocol = 0  (0x0)  (int)
  usb_device.version = 2  (double)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  info.linux.driver = 'usb'  (string)
  usb_device.vendor_id = 2278  (0x8e6)  (int)
  usb_device.is_self_powered = false  (bool)
  usb_device.product_id = 21763  (0x5503)  (int)
  usb_device.can_wake_up = false  (bool)
  usb_device.vendor = 'Gemplus'  (string)
  usb_device.bus_number = 2  (0x2)  (int)
  linux.hotplug_type = 2  (0x2)  (int)
  usb_device.product = 'Prox Dual USB PC LinkReader'  (string)
  linux.subsystem = 'usb'  (string)
  linux.device_file = '/dev/bus/usb/002/051'  (string)
  usb_device.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3'  (string)
  usb_device.device_revision_bcd = 256  (0x100)  (int)
  usb_device.configuration_value = 1  (0x1)  (int)
  usb_device.max_power = 200  (0xc8)  (int)

Remarks


Gemalto decided to include the serial number in the interface name. It is to be able to differentiate (on Windows) two identical readers connected. The serial number is also available on a sticker on the back of the reader so it is easy to tell which PC/SC reader is which physical reader.

The SCM SCR331 reader also has a serial number on a sticker on the back but the written serial number has nothing to do with the USB serial number.

Mac OS X does not include the serial number nor the interface name in the PC/SC name. Maybe one day I will implement these features in pcsc-lite on Mac OS X.

pcsc-lite reader name format

name [interface] (serial) index slot
This format is used by pcsc-lite on Linux if you use libhal (default with pcsc-lite 1.6.0). If you still use libusb the interface name is not included.

The name part comes from the driver Info.plist file for USB readers or the reader.conf file for serial readers.

The interface part comes from the USB layer (usb.interface.description HAL entry)

The serial part comes from the USB layer (usb.serial HAL entry)

The index part is an internal index in PC/SC. It is used to differentiate two identical readers at the PC/SC layer for readers without serial numbers (the vast majority).

The slot part is specific to pcsc-lite and is the slot index in multi-slot readers. Multi-slot readers are not common at all and this information is not really useful for the PC/SC application. I think we still have this number for historical reasons.

Remarks


On Linux the GemProx DU is seen as two CCID readers. So the PC/SC reader names differentiate on the index part.

On Mac OS X pcsc-lite does not supports composite devices. So the CCID driver emulates a single reader with 2 slots. That is why the PC/SC reader names differentiate on the slot part and have the same index part.

Conclusion


PC/SC reader names are very different from an OS to another. It is very dangerous to hard code a reader name in an application and just pass it to SCardConnect(). You should always use SCardListReaders() first.

How to know the PIN sizes supported by a pinpad reader?

The problem


Some pinpad readers (like the Gemalto PC pinpad v1) does not support PIN of less than 4 digits or more than 8 digits. The problem is that the Gemalto PC pinpad v1 reader is very strict and instead of ignoring unsupported options the reader reject the CCID command.

For example:
00000026 ifdhandler.c:1291:IFDHControl() ControlCode: 0x42330006, usb:08e6/3478:libusb:002:034 (lun: 0)
00000021 Control TxBuffer: 1E 1E 02 10 00 10 04 02 00 00 00 00 00 00 00 15 00 00 00 00 20 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00000009 commands.c:335:SecurePINVerify() Correct bNumberMessage for GemPC Pinpad (was 0)
00000009 openct/proto-t1.c:571:t1_build() more bit: 0
00000022 -> 000000 69 24 00 00 00 00 41 00 00 00 00 1E 02 10 00 10 04 02 01 00 00 00 00 40 15 00 20 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00006255 <- 000000 80 00 00 00 00 00 41 40 0F 00 
00000028 commands.c:1319:CCID_Receive error on byte 15
00000011 Control RxBuffer: 
00000012 ifdwrapper.c:644:IFDControl() Card not transacted: 612
00000011 ifdwrapper.c:646:IFDControl() ControlCode: 0x42330006 BytesReturned: 0
00000031 TxBuffer 1E 1E 02 10 00 10 04 02 01 00 00 00 00 40 15 15 00 00 00 00 20 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00000010 RxBuffer 
00000009 winscard_svc.c:692:ContextThread() CONTROL rv=0x80100016 for client 5

Here the application (OpenSC in this case) uses PC/SC v2 Part 10 to request a secure verify PIN (using SCardControl() with the ControlCode: 0x42330006.

Then the CCID driver creates a CCID Secure frame (PC_to_RDR_Secure = 0x69) and sends it to the reader. The reader returns an error on byte 15 which has the value 0x10 (or 16 in decimal) and is the first byte of the wPINMaxExtraDigit field (the second byte is 0x04). The reader does not support the value 16 for Maximum PIN size in digit.

The driver returns the generic error 612 (IFD_COMMUNICATION_ERROR) and PC/SC returns the error 0x80100016 (SCARD_E_NOT_TRANSACTED) to the application. The application has no way to know why the command failed. Someone has to look at the very low level logs to discover that the probelm is with wPINMaxExtraDigit.

The PC/SC solution: FEATURE_GET_TLV_PROPERTIES


Since PCSCv2 part 10 v2.02.07 from March 2010 a new feature is available: FEATURE_GET_TLV_PROPERTIES. It is very important to have a solution portable and documented in an "official" document. This feature is used to return a TLV formatted list of properties of the reader (or driver). Two properties are bMinPINSize and bMaxPINSize.

The only driver I know that supports FEATURE_GET_TLV_PROPERTIES is my CCID driver in version 1.3.12 release 8 May 2010. Maybe other drivers will support FEATURE_GET_TLV_PROPERTIES some time in the future.

Do not expect any thing from Microsoft. Their CCID driver and PCSC middleware are frozen in ice since some time (in years).

The sample codes

C code

If you use the sample code scardcontrol.c (772 lines of C) provided with my CCID driver you can see some results. For example with a Gemalto PC Pinpad v1 connected the output is:

$ ./scardcontrol 
SCardControl sample code
V 1.4 © 2004-2010, Ludovic Rousseau 

THIS PROGRAM IS NOT DESIGNED AS A TESTING TOOL!
Do NOT use it unless you really know what you do.

SCardListReaders: OK

Available readers (use command line argument to select)
0: Gemalto GemPC Pinpad 00 00

Using reader: Gemalto GemPC Pinpad 00 00
 Protocol: 0
SCardConnect: OK

SCardControl: OK

 TLV (24): 06 04 42 33 00 06 07 04 42 33 00 07 0A 04 42 33 00 0A 12 04 42 33 00 12 
SCardControl(CM_IOCTL_GET_FEATURE_REQUEST): OK

Reader supports: FEATURE_VERIFY_PIN_DIRECT
Reader supports: FEATURE_MODIFY_PIN_DIRECT
Reader supports: FEATURE_IFD_PIN_PROPERTIES
Reader supports: FEATURE_GET_TLV_PROPERTIES

SCardControl(GET_TLV_PROPERTIES): OK

GET_TLV_PROPERTIES (37): 01 02 00 00 03 01 00 08 13 47 65 6D 54 77 52 43 32 2D 56 32 2E 30 30 2D 47 4C 30 33 06 01 04 07 01 08 02 01 02 

Display all the properties:
 wLcdLayout: 0x0000
 bTimeOut2: 0x00
 sFirmwareID: GemTwRC2-V2.00-GL03
 bMinPINSize: 0x04
 bMaxPINSize: 0x08
 bEntryValidationCondition: 0x02

Find a specific property:
 bEntryValidationCondition: 2
 bMaxPINSize: 8

 Reader: Gemalto GemPC Pinpad 00 00 (length 27 bytes)
 State: 0x20034
 Prot: 0
 ATR (length 20 bytes): 3B FA 94 00 00 81 31 20 43 80 65 A2 01 01 01 3D 72 D6 43 21
SCardStatus: OK

 Protocol: 2
SCardReconnect: OK

Select applet:  00 A4 04 00 06 A0 00 00 00 18 FF
 card response: 90 00
SCardTransmit: OK

 Secure modify PIN
 command: 00 00 82 04 00 00 04 08 04 03 02 03 04 09 00 00 00 00 00 00 0D 00 00 00 00 24 00 00 08 30 30 30 30 30 30 30 30
Enter your PIN:  card response: 90 00
SCardControl: OK


modify PIN dump:  00 40 00 00 FF
 card response: 00 24 00 00 08 31 32 33 34 35 36 37 38 90 00
SCardTransmit: OK

SCardDisconnect: OK

This sample code is not specifically designed to just get the min and max PIN sizes. So many lines are of no value for us here.

The useful lines here are:
bMinPINSize: 0x04
 bMaxPINSize: 0x08

Python code

A much simpler source code uses the Python wrapper (described here).

Source code (13 lines)

#!/usr/bin/env python

from smartcard.pcsc.PCSCPart10 import getTlvProperties
from smartcard.pcsc.PCSCReader import readers, SCARD_SHARE_DIRECT

# get a connection to the first reader
cc = readers()[0].createConnection()
cc.connect(mode=SCARD_SHARE_DIRECT)

print "\nTlvProperties:"
properties = getTlvProperties(cc)
for k in properties.keys():
    print " %s: %s" % (k, properties[k])

Output

TlvProperties:
 PCSCv2_PART10_PROPERTY_sFirmwareID: GemTwRC2-V2.00-GL03
 PCSCv2_PART10_PROPERTY_bTimeOut2: 0
 PCSCv2_PART10_PROPERTY_bEntryValidationCondition: 2
 raw: [1, 2, 0, 0, 3, 1, 0, 8, 19, 71, 101, 109, 84, 119, 82, 67, 50, 45, 86, 50, 46, 48, 48, 45, 71, 76, 48, 51, 6, 1, 4, 7, 1, 8, 2, 1, 2]
 PCSCv2_PART10_PROPERTY_wLcdLayout: 0
 PCSCv2_PART10_PROPERTY_bMaxPINSize: 8
 PCSCv2_PART10_PROPERTY_bMinPINSize: 4

getTlvProperties() returns a dictionnay. The entry for the "raw" key is just the complete buffer without interpretation.

Another related problem: bEntryValidationCondition

Another limitation of the Gemalto PC pinpad v1 is bEntryValidationCondition. This field is used by the PC_to_RDR_Secure command (verify and modify PIN) to tell what conditions shall be used by the reader to validate an entry. According to the CCID specification it can be:
The value is a bit wise OR operation.
  • 01h Max size reached
  • 02h Validation key pressed
  • 04h Timeout occurred
But the Gemalto PC pinpad v1 only supports the 02h value (Validation key pressed). Any other value is rejected by the reader and the CCID command fails. The problem is that the PCSC application has no way to know what is supported by the reader.

Solution 1

So my CCID driver has a special treatment for this reader and bEntryValidationCondition is reset to 02h even if it is not what the applications requested.

00000032 ifdhandler.c:1291:IFDHControl() ControlCode: 0x42330007, usb:08e6/3478:libusb:002:039 (lun: 0)
00000031 Control TxBuffer: 00 00 82 04 00 00 04 08 04 03 07 03 04 09 00 00 00 00 00 00 0D 00 00 00 00 24 00 00 08 30 30 30 30 30 30 30 30 
00000014 commands.c:583:SecurePINModify() Correct bEntryValidationCondition for GemPC Pinpad (was 7)
00000012 openct/proto-t1.c:571:t1_build() more bit: 0
00000228 commands.c:679:SecurePINModify() readTimeout: 30000
00000036 -> 000000 69 21 00 00 00 00 51 00 00 00 01 00 82 04 00 00 04 08 04 03 02 03 04 09 00 00 00 00 40 0D 00 24 00 00 08 30 30 30 30 30 30 30 30

Solution 2


FEATURE_GET_TLV_PROPERTIES can also return a bEntryValidationCondition indicating what bits are supported by the reader.

Conclusion


Many variable features of a reader or driver can't be known by a PC/SC application. Or the application need to be configured specifically for one reader (or a set of readers).

It is now possible, using the FEATURE_GET_TLV_PROPERTIES mechanism to retrieve some of reader specificity and adapt the application.

Where are the old versions of libccid and pcsc-lite?

Some people request old versions of the libccid driver or the pcsc-lite middleware (for obscure reasons). These versions are no more supported and should not be used. You will not get support from me. But maybe you work on the history of famous Free Software projects :-).

But you can find the source code online thanks to the http://snapshot.debian.org/ service from Debian.

  • Versions of pcsc-lite from 1.0.2.beta5-1 (2005-03-12) are here.
  • Version of libccid from 0.9.2-2 (2005-03-12) are here.

It is also possible to use tagged version from the subversion repository. But you will not get a .tar.gz archive, just the source code files.
You may note that the versions from subversion do not go back as far as from http://snapshot.debian.org/. This is because, at the origin, the code was managed in CVS (not subversion) and not all the versions have been tagged (AFAIR).

ccid new version 1.3.12

I just released a new version of my CCID smart card reader driver.
The source code archive is available.

1.3.12 - 8 May 2010, Ludovic Rousseau

  • add support of

    • Todos AGM2 CCID
    • Cherry SmartTerminal XX7X
    • Smart SBV280
    • Ask CPL108
    • German Privacy Foundation Crypto Stick v1.2
    • id3 CL1356T5
    • Covadis Auriga
    • GoldKey PIV Token
    • Vasco DIGIPASS
    • KEY 860
    • Vasco DIGIPASS KEY 200
    • Vasco DP855
    • Vasco DP865
    • Atmel AT90SCR100
    • Atmel AT90SCR050
    • Atmel VaultIC420
    • Atmel VaultIC440
    • Atmel VaultIC460
    • KOBIL Smart Token
    • KOBIL mIDentity 4smart
    • KOBIL mIDentity 4smart AES
    • KOBIL mIDentity visual
    • KOBIL mIDentity fullsize
    • KOBIL mIDentity 4smart fullsize AES
    • SCM SCR3500
  • remove support of Smart SBV280 on manufacturer request. They use
    libusb directly.
  • remove support of SCM SDI 010 on manufacturer request since not
    supported by my driver
  • Enable the Broadcom 5880 reader. It should work after a firmware
    upgrade.
  • better support of Dell keyboard
  • better support of multislot readers (like the GemCore SIM
    Pro)
  • better support of SCM SCR3310
  • better support of ICCD version A devices
  • The Covadis Véga-Alpha reader is a GemPC pinpad inside. So we use
    the same code to:

    • load the strings for the display
    • avoid limitation of the reader
  • IFDHControl(): the (proprietary) get firmware version escape
    command is allowed with a Gemalto reader

    • the (proprietary) switch interface escape command is allowed on
      the Gemalto GemProx DU
    • return IFD_ERROR_NOT_SUPPORTED instead of
      IFD_COMMUNICATION_ERROR if the dwControlCode value is not
      supported
    • return IFD_ERROR_INSUFFICIENT_BUFFER when appropriate
  • IFDHGetCapabilities(): add support of
    SCARD_ATTR_ICC_PRESENCE and
    SCARD_ATTR_ICC_INTERFACE_STATUS
  • support FEATURE_GET_TLV_PROPERTIES
  • add support of IOCTL_FEATURE_GET_TLV_PROPERTIES
    bMinPINSize & bMaxPINSize for Gemalto Pinpad V1
    & Covadis Véga-Alpha
  • support extended APDU of up to 64kB with APDU readers.
  • get the language selected during Mac OS X installation as
    language to use for Covadis Véga-Alpha and Gemalto GemPC PinPad
    pinpad readers
  • FEATURE_MCT_READER_DIRECT is also supported by the
    Kobil mIDentity visual
  • better support of Sun Studio CC
  • some minor bugs removed

pcsc-lite 1.6.0, new major version

A new major version of pcsc-lite is now available.
This version includes a lot of new code and changes. But I do not
expect regressions.
I will describe the changes and new features later on my blog.

As usual, report any bug. Thanks

Changes:

  • redesign the client/server communication:
    • no more shared memory used (allow pcscd and libpcsclite1.so to be on different computer and talk over a network)
    • no more difference between short and extended APDU
    • no more use of a /var/run/pcscd/pcscd.events/ directory. events are sent through the socket
    • simpler command format between client and server
    The side effect is that you are not able to mix an old pcscd with a new libpcsclite1.so or the reverse. SCardEstablishContext() will fail unless you update both sides of the communication.
  • Use lists instead of fixed size arrays to store handles.
    It is now possible to have:
    • 200 simultaneous PC/SC clients instead of 16
    • 200 SCardConnect per client instead of 16
    • 200 clients per reader instead of 16
    The default value of 200 can be changed by giving an argument to pcscd --max-thread --max-card-handle-per-thread --max-card-handle-per-reader
    Thanks to Jean-Luc Giraud for the big patch
  • Make SCardReconnect(), SCardStatus() and SCardTransmit() block instead
    of returning SCARD_E_SHARING_VIOLATION immediately. These functions will then behave like on Windows.
    This can happen if these functions are called when the reader is locked by a PCSC transaction
    (SCardBeginTransaction/SCardEndTransaction).
    You can define the environment variable PCSCLITE_NO_BLOCKING to use the old behavior.
    Thanks to Jean-Luc Giraud for the patch.
    http://archives.neohapsis.com/archives/dev/muscle/2010-q1/0041.html
  • SCardEstablishContext(): try to start the pcscd daemon if not already running.
    • pcscd will suicide itself after 60 seconds of inactivity if it is started using --auto-exit. This is the default behavior when pcscd is started by libpcsclite
    • Set PCSCLITE_PCSCD_ARGS with the argument you want to pass to pcscd in autostart Only one argument is passed. The space character is not a separator. example: export PCSCLITE_PCSCD_ARGS=-dfa
  • SCardListReaders(): can use SCARD_AUTOALLOCATE
  • SCardGetAttrib(): return SCARD_E_INSUFFICIENT_BUFFER if the driver returns IFD_ERROR_INSUFFICIENT_BUFFER
    • add support of SCARD_ATTR_DEVICE_FRIENDLY_NAME as it is better implemented in pcscd (it knows the friendly name)
  • SCardGetStatusChange(): Calling with cReaders == 0 will now just return SCARD_S_SUCCESS
    • Use the special reader name "\\?PnP?\Notification" to wait for a
      reader event notification
  • SCardTransmit(): do not limit the minimum size of an APDU to 4 bytes. non ISO 7816-4 compliant cards (like Mifare DESFIRE) may use shorter commands
  • SCardStatus(): returns SCARD_E_SHARING_VIOLATION if the reader is already used More conform to Windows
  • PCSC/reader.h: update sruct PIN_PROPERTIES_STRUCTURE to be conform with Revision 2.02.06, April 2009 of PCSCv2 part 10 Fields wLcdMaxCharacters and wLcdMaxLines
    have been removed
    • rename FEATURE_MCT_READERDIRECT in FEATURE_MCT_READER_DIRECT to be
      conform with ch. 2.3 of PCSC v2 part 10
    • add FEATURE_GET_TLV_PROPERTIES and FEATURE_CCID_ESC_COMMAND from
      PC/SC part 10 v2.02.07 March 2010
    • Add PCSCv2_PART10_PROPERTY_* defines
  • SCardControl() return SCARD_E_UNSUPPORTED_FEATURE if the driver returned
    IFD_ERROR_NOT_SUPPORTED or IFD_NOT_SUPPORTED This is used to separate an unsupported value of ControlCode from a general error
  • Use the standard --sysconfdir=DIR ($prefix/etc by default) instead of --enable-confdir=DIR for defining the directory containing reader.conf
  • remove SCF support (PC/SC over Smart Card Framework). I never used this feature and SCF is now dead and replaced by JSR 268 (javax.smartcardio)
  • Better handling of PCSCLITE_STATIC_DRIVER as can be used on platforms using µClinux (without dynamic loader). This is used to statically link the reader driver to pcscd. Since the link is static you must define the IFDHandler API version at compilation time. Either define IFDHANDLERv1, IFDHANDLERv2 or IFDHANDLERv3
  • Use dynamic instead of static allocation for the driver library filename. The filename is no more limited to 100 characters.
    Closes: [#312332] MAX_LIBNAME too short?
  • force the return codes SCARD_* to be long since the SCard* functions return a LONG type
  • Add the ability to parse all the configuration files of a directory instead of just one configuration file. update-reader.conf is then now obsolete.
  • Add --enable-embedded (default is no) to build pcsc-lite for an embedded system. This will activate the NO_LOG option to disable logging and limit RAM and disk consumption.
  • If NO_LOG is defined then no log are displayed. The idea is to limit the binaries size on disk and RAM consumption at execution time. With NO_LOG defined we gain 26% (17 kB) for the .text segment of pcscd and 15% (4 kB) for the .text segment of libpcsclite.so (for i386)
  • Define a minimal pcsc_stringify_error() if NO_LOG is defined. Only the error code in hex is displayed in this case.
    Gain: 2kB of .text (10%) for libpcsclite
  • Add --disable-serial and --disable-usb options
    • --disable-serial removes support of /etc/reader.conf gain: 8.0kB of .text (12%) and 160 bytes of .bss (4%) for pcscd
    • --disable-usb removes support of USB hotplug gain: 9.7kB of .text (14%) and 960 bytes of .bss (23%) for pcscd
    • If you use both options (and use a static driver configuration) gain: 17.7kB of .text (26%) and 1152 bytes of .bss (28%) for pcscd
  • Better support of Android
  • some other minor improvements and bug corrections

PCSC sample in scriptor

Installation


scriptor and gscriptor are tools included in the pcsc-tools package (http://ludovic.rousseau.free.fr/softwares/pcsc-tools/index.html)

The licence is GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

On a Debian system just do:
apt-get install pcsc-tools

Source code for scriptor


These 2 tools allows to send APDU to a card. They are very simple by yet useful for some basic cases.

If used in interactive mode (use Ctrl-d to quit):

$ scriptor
No reader given: using Gemplus GemPC Twin 00 00
Using T=1 protocol
Reading commands from STDIN
00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01
> 00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01
< 90 00 : Normal processing.
00 00 00 00
> 00 00 00 00
< 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 : Normal processing.


If used as a script file:

$ cat essai.scriptor
#! /usr/bin/env scriptor
reset
# Select applet
00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01
# APDU de test
00 00 00 00


Empty lines or lines starting by # are comments and ignored. reset is a keyword to reset the card and start with a known state.

Output


$ ./essai.scriptor
No reader given: using Gemplus GemPC Twin 00 00
Using T=1 protocol
Using given file: ./essai.scriptor
#! /usr/bin/env scriptor
reset
> RESET
< OK: 3B EA 00 00 81 31 20 43 80 65 A2 01 01 01 3D 72 D6 43 A5
# Select applet
00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01
> 00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01
< 90 00 : Normal processing.
# APDU de test
00 00 00 00
> 00 00 00 00
< 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 : Normal processing.


Source code for gscriptor


The same script can be used by gscriptor. gscriptor is a graphical version of scriptor.

Output




gscriptor can display the results in ASCII. This is useful in our case since the test APDU just sends the string "Hello world!"



Conclusion


scriptor and gscriptor are easy to use for simple tasks. In general I use them to check the smart card stack is working correctly.