Extended APDU status per reader

Extended APDU are more and more used by applications and cards. But an application has no way to know if the reader is capable of supporting extended APDU. So an application + card may work with one reader and fail with another reader.

Extended APDU

An extended APDU is an command to the card with a data or response of more than 256 bytes. It is not easy to detect an extended APDU unless you talk ISO 7816-3 fluently. I will not go into details here.

PC/SC limitation

PC/SC (also called WinSCard) is the standard API to use smart card reader and a smart card. Unfortunately PC/SC can't tell the application if the reader will, or not, support extended APDU.

I proposed an evolution to the standard to add such a feature (adding a new tag to FEATURE_GET_TLV_PROPERTIES) but the PC/SC workgroup was not really enthusiastic about the idea. An application could have displayed an error message to the user indicating that the reader does not support extended APDU and that another reader should be used instead.

What we get instead is a PC/SC error to the application and bug reports to me and others.

Reader support

Not all readers will support extended APDU. I created a web page to explain the situation when using my CCID driver. But the page may be do hard to find.

So, after yet another bug report about a failing application because of extended APDU, I modified the reader lists (supported, should work, unsupported, disabled) and the reader matrix to contain the information: does this reader support extended APDU?

Examples

The user in the latest bug report was using a Xiring MyLeo reader. If you go to the description of this reader at https://ccid.apdu.fr/ccid/supported.html#0x0F140x0037 you will now see:

 XIRING MyLeo
XIRING MyLeo
USB descriptor: readers/Xiring_MyLeo.txt
Features: PIN Verification, PIN Modification
Limitations: No extended APDU
In CCID release: 1.4.2
The new field is "Limitations" (in red).

The reader matrix contains a new column 'limitations'. As with any column it is possible to sort the matrix using this 'limitations' column. You then get 2 groups of readers: those supporting extended APDU, and this NOT supporting extended APDU.

It will not help user determine why the application failed to work correctly. But at least it will indicate that the reader they are using is "limited".

Conclusion

If you plan to buy a new reader you should take care of the reader ability to supported extended APDU.

If you have an application failure you can check if the reader you use does support extended APDU.

If you have other ideas to solve the problem just contact me or the MUSCLE mailing list.

"new" version of libusb, part 2

I an previous blog article I talked about the situation of libusb:

  • a stable libusb has not been released since May 2010 (exactly one year ago)
  • bugs are still open even if they have patches attached in the bug report

Impacts on libccid


The bug [PATCH] Fix a race condition in io.c libusb_handle_events_timeout() (also known as ticket #56) has a great impact on users of my CCID driver by blocking the USB transfer for 60 seconds in some cases.

It looks like that bug #56 happens more often on recent Linux kernels, like the Linux kernel provided with Ubuntu 11.04. So it is now more-or-less urgent to fix the problem in libusb.

The bug has been reported since 8 months. A fix is available since April 2010 but is not yet applied in upstream libusb git repository. So I fixed it in my libusb repository on github to not forget it and because I needed a fixed version for myself.

libusb git snapshot x2


I already provide a git snapshot of the upstream (official) repository in the file libusb-1.0.8-git.tar.bz2 from http://ludovic.rousseau.free.fr/softwares/pcsc-lite/.

I now also provide a git snapshot of my own github version with ticket #56 fixed in the file libusb-1.0.8-github.tar.bz2.

Conclusion


So before reporting a bug in libccid first try a newer version of libusb. If the bug is at the communication level it may already be solved in the upstream libusb snapshot version or my patched version.

libccid and USB selective suspend

I committed in revision 5706 a patch to activate the USB automatic power suspend. The idea is to power off a reader when it is not used.

What is it?

From the LessWatts page about Device and Bus Power Management:

USB selective suspend

This is a project in progress for the USB subsystem. USB selective suspend allows you to suspend a USB device on demand. If one device doesn't support selective suspend, then the entire bus must remain active. This not only consumes USB bus power unnecessarily, but also prevents the CPU from entering lower power states.
It should be a good idea to use the USB selective suspend for USB smart card readers.

How does it work?

The very good news is that USB selective suspend is managed by the kernel itself when set in automatic mode. The libccid driver source code do not have to be changed in any way.

The only thing to do is to tell the kernel to use the automatic mode (instead of always on mode). This can be done by a udev rule. Since libccid version 1.4.1 a udev rule file is provided to set the access rights for the pcscd auto start feature.

I just needed to a new line to the udev rule file to enable the automatic USB selective suspend:
ENV{ID_USB_INTERFACES}=="*:0b0000:*", RUN+="/bin/sh -c 'echo auto > /sys/$devpath/power/level ; echo auto > /sys/$devpath/power/control'"


Details

For every device with ID_USB_INTERFACES set to 0xB or 11 (CCID devices) do:
  • echo auto in /sys/$devpath/power/level
  • echo auto in /sys/$devpath/power/control

$devpath will be expanded by udev to something like bus/usb/devices/8-1.3. So the full filename is something like /sys/bus/usb/devices/8-1.3/power/level.

I use two echo lines because .../power/level is used in "old" kernels like the 2.6.32 used by Debian squeeze (stable) but has been deprecated in Linux kernel 2.6.35 and replaced by .../power/control. So to be able to work on a majority of kernel versions I use both filenames. The old filename should be removed at some point in the future.

Automatic USB selective suspend does work if no process if using the USB device. So pcscd must not be running so no driver is using the USB device. Since pcscd auto start feature, pcscd also does an auto stop so if no application is using the PC/SC API no pcscd should be running and then the kernel will be able to suspend unused USB smart card readers.

The default kernel timeout is 2 seconds. So 2 seconds after pcscd exits the USB smart card readers should be suspended. For example with my Gemalto PC Twin reader the LED will stop blinking and will be off.

Possible issues

I have not used many different readers with this change. So it may be possible that some readers does not support well the USB selective suspend.

Conclusion

I have not measured the power saving of this new feature. The MaxPower (returned by lsusb) of the Gemalto PC Twin reader is 200mA. But I guess the power really consumed if less than that. And on a desktop computer the difference will be quiet small.

But if you have a laptop and you rarely use your smart card reader the saving will be more interesting.

Monosim: SIM card phone book listing (part 3)

In my article SIM card phone book listing of November 2010 a reader left a comment about the existence of the Monosim project.

" what is monosim ?
14-06-2007 18.20
...is a simple application that can be used to read, write, update, delete and backup your sim card contacts. It open and save also some format files to manage your contacts also in a text files. To connect monosim to your smartcard you need use a standard PCSC smartcard reader as towitoko, acs, athena, blutronics, etc. "

The monosim project is from Armando Basile and uses the GNU GPL v2 license. The project started in 2007. The latest version 1.5.2 is from July 2009.

Installation

The installation is quiet easy on a Debian system. The documentation is enough.

Just use the clasical:
$ ./configure
$ make

If a dependency is missing install it and go to the previous step.

Use

$ mono monosim/bin/Release/monosim.exe 





Monosim is the most user friendly and usable application I found:
  • The application is using a GUI (and not just text like PSSI).
  • The application allows to edit the SIM card, not just dump it content. It is possible to same the phone book in a text file and copy-paste between the SIM and the file.

Technical side

Monosim uses the PC/SC API using its own C# wrapper also written by Armando Basile. Maybe because many PCSC C# wrapper where did not exists in 2007 when the monosim project was started.

After writing a phone book to my SIM the program now always crashes when reading the SIM. I filed a bug. Maybe you will have more luck with your own SIM :-)

Conclusion

Monosim is a nice project. It may help you manage your phone books.

PyKCS11 provided samples: dumpit.py

After getinfo.py in a previous article the next sample from the PyKCS11 Python wrapper is dumpit.py.

Setup


The setup is the same as in the previous article.

Example codes provided with PyKCS11: dumpit.py


dumpit.py is a short sample code to... dump (nearly) all the information from the token.

Output


$ ./dumpit.py
Library manufacturerID: Gemalto                         
Available Slots: 5
Slot no: 0
  slotDescription: Gemalto GemPC Twin 00 00
  manufacturerID: Unknown
TokenInfo
  label: .NET #AFC4963915BE14DB
  manufacturerID: Gemalto
  model: .NET Card
Opened session 0x00000001

Found 2 objects: [257, 514]

==================== Object: 257 ====================
Dumping attributes:
  CKA_CLASS: CKO_CERTIFICATE (1)
  CKA_PRIVATE: False
  CKA_LABEL: eb155854-5522-60ea-16dc-016a5b43cd32
  CKA_TOKEN: True
  CKA_ID: 20 bytes
0000   EB 15 58 54 55 22 60 EA 16 DC 01 6A 5B 43 CD 32    ..XTU"`....j[C.2
0010   69 3D BE 1E                                        i=..
  CKA_VALUE: 1114 bytes
0000   30 82 04 56 30 82 03 BF A0 03 02 01 02 02 01 17    0..V0...........
0010   30 0D 06 09 2A 86 48 86 F7 0D 01 01 05 05 00 30    0...*.H........0
0020   7D 31 0B 30 09 06 03 55 04 06 13 02 46 52 31 13    }1.0...U....FR1.
0030   30 11 06 03 55 04 08 13 0A 53 6F 6D 65 2D 53 74    0...U....Some-St
0040   61 74 65 31 10 30 0E 06 03 55 04 07 13 07 41 75    ate1.0...U....Au
0050   62 61 67 6E 65 31 15 30 13 06 03 55 04 0A 13 0C    bagne1.0...U....
0060   54 65 73 74 20 43 41 20 73 61 72 6C 31 10 30 0E    Test CA sarl1.0.
0070   06 03 55 04 03 13 07 54 65 73 74 20 43 41 31 1E    ..U....Test CA1.
0080   30 1C 06 09 2A 86 48 86 F7 0D 01 09 01 16 0F 72    0...*.H........r
0090   6F 6F 74 40 74 65 73 74 63 61 2E 63 6F 6D 30 1E    oot@testca.com0.
00A0   17 0D 31 31 30 34 32 32 30 37 35 31 34 38 5A 17    ..110422075148Z.
00B0   0D 31 32 30 34 32 31 30 37 35 31 34 38 5A 30 7B    .120421075148Z0{
00C0   31 0B 30 09 06 03 55 04 06 13 02 46 52 31 11 30    1.0...U....FR1.0
00D0   0F 06 03 55 04 07 13 08 4D 61 6E 6F 73 71 75 65    ...U....Manosque
00E0   31 15 30 13 06 03 55 04 0A 13 0C 54 65 73 74 20    1.0...U....Test 
00F0   43 41 20 73 61 72 6C 31 19 30 17 06 03 55 04 03    CA sarl1.0...U..
0100   13 10 4C 75 64 6F 76 69 63 20 52 6F 75 73 73 65    ..Ludovic Rousse
0110   61 75 31 27 30 25 06 09 2A 86 48 86 F7 0D 01 09    au1'0%..*.H.....
0120   01 16 18 6C 75 64 6F 76 69 63 2E 72 6F 75 73 73    ...ludovic.rouss
0130   65 61 75 40 66 72 65 65 2E 66 72 30 82 01 22 30    eau@free.fr0.."0
0140   0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 82    ...*.H..........
0150   01 0F 00 30 82 01 0A 02 82 01 01 00 9F 9F 1E A4    ...0............
0160   4F 9B FD 76 23 D9 DD 43 46 EF DF 86 BC 80 D3 76    O..v#..CF......v
0170   30 40 22 C5 DB C6 E8 65 1F 37 7C 13 03 37 3A E2    0@"....e.7|..7:.
0180   C5 E8 5E 0B ED 51 5A 41 8F EF 2A 74 CE 10 10 2F    ..^..QZA..*t.../
0190   0C 63 FE 1A 0F 25 86 20 EF 90 B8 EA 4E A6 1F 3B    .c...%. ....N..;
01A0   B5 A2 76 37 B9 A4 E1 D2 0B 00 9E 6E 85 E3 AB 6B    ..v7.......n...k
01B0   A1 4C 78 3C 5F B6 10 F6 0A 94 78 19 B2 B5 49 38    .Lx<_.....x...I8
01C0   FF B4 4F 69 41 90 ED 78 81 85 61 1C B6 75 70 CD    ..OiA..x..a..up.
01D0   1D 16 7A 56 E7 FE C5 E8 F9 9B E9 CC F5 5E 3D BC    ..zV.........^=.
01E0   10 94 FF 77 08 6D 51 2F 28 76 81 86 3C 4B 84 7D    ...w.mQ/(v..<K.}
01F0   FE B3 4B FD 78 DA 8B 26 50 58 0E 80 3F 6F C0 F4    ..K.x..&PX..?o..
0200   2C 89 05 20 AF B9 1C 44 81 DA 51 5C DA BB 77 45    ,.. ...D..Q...wE
0210   85 E5 58 6C 14 A3 B6 24 D3 FB 00 A8 30 66 98 2A    ..Xl...$....0f.*
0220   E2 C0 42 F6 E9 8D 3F 05 EF 77 0C 36 D1 DE D9 2F    ..B...?..w.6.../
0230   EA DC 40 E9 A9 01 5B 88 E4 B3 46 4C AB CC 95 5C    ..@...[...FL....
0240   AA C6 0C 3E 21 E0 42 1A 74 AD 7C D4 08 5B 07 14    ...>!.B.t.|..[..
0250   67 4F D1 A0 4C 8C 6C 41 A0 27 05 5D 02 03 01 00    gO..L.lA.'.]....
0260   01 A3 82 01 62 30 82 01 5E 30 09 06 03 55 1D 13    ....b0..^0...U..
0270   04 02 30 00 30 11 06 09 60 86 48 01 86 F8 42 01    ..0.0...`.H...B.
0280   01 04 04 03 02 04 B0 30 2B 06 09 60 86 48 01 86    .......0+..`.H..
0290   F8 42 01 0D 04 1E 16 1C 54 69 6E 79 43 41 20 47    .B......TinyCA G
02A0   65 6E 65 72 61 74 65 64 20 43 65 72 74 69 66 69    enerated Certifi
02B0   63 61 74 65 30 1D 06 03 55 1D 0E 04 16 04 14 87    cate0...U.......
02C0   7D A3 23 06 BF AE A8 D0 0C CD 6A 1F 91 DB 39 CB    }.#.......j...9.
02D0   7E 95 2D 30 81 B0 06 03 55 1D 23 04 81 A8 30 81    ~.-0....U.#...0.
02E0   A5 80 14 40 3A CC 78 B2 19 6F 4B 15 2B A0 50 45    ...@:.x..oK.+.PE
02F0   83 85 B7 84 32 B8 A1 A1 81 81 A4 7F 30 7D 31 0B    ....2.......0}1.
0300   30 09 06 03 55 04 06 13 02 46 52 31 13 30 11 06    0...U....FR1.0..
0310   03 55 04 08 13 0A 53 6F 6D 65 2D 53 74 61 74 65    .U....Some-State
0320   31 10 30 0E 06 03 55 04 07 13 07 41 75 62 61 67    1.0...U....Aubag
0330   6E 65 31 15 30 13 06 03 55 04 0A 13 0C 54 65 73    ne1.0...U....Tes
0340   74 20 43 41 20 73 61 72 6C 31 10 30 0E 06 03 55    t CA sarl1.0...U
0350   04 03 13 07 54 65 73 74 20 43 41 31 1E 30 1C 06    ....Test CA1.0..
0360   09 2A 86 48 86 F7 0D 01 09 01 16 0F 72 6F 6F 74    .*.H........root
0370   40 74 65 73 74 63 61 2E 63 6F 6D 82 09 00 88 E6    @testca.com.....
0380   B9 B6 6A BE 9B 09 30 1A 06 03 55 1D 12 04 13 30    ..j...0...U....0
0390   11 81 0F 72 6F 6F 74 40 74 65 73 74 63 61 2E 63    ...root@testca.c
03A0   6F 6D 30 23 06 03 55 1D 11 04 1C 30 1A 81 18 6C    om0#..U....0...l
03B0   75 64 6F 76 69 63 2E 72 6F 75 73 73 65 61 75 40    udovic.rousseau@
03C0   66 72 65 65 2E 66 72 30 0D 06 09 2A 86 48 86 F7    free.fr0...*.H..
03D0   0D 01 01 05 05 00 03 81 81 00 8C 06 99 70 61 45    .............paE
03E0   7E 3E A5 7F D9 11 41 78 5C 8D C6 B5 65 28 2B 3D    ~>....Ax....e(+=
03F0   03 D0 E7 B8 6E CC B4 5D C3 FE 73 27 57 0B D6 62    ....n..]..s'W..b
0400   FF 0B 8B AE 55 16 E1 42 2B E4 52 7E 3F F7 F7 DA    ....U..B+.R~?...
0410   52 18 E9 60 F7 9E 03 86 84 07 3D 83 AB DC F9 85    R..`......=.....
0420   32 F5 75 54 6E 5B 9E B8 E3 30 4D 40 CA 54 25 DC    2.uTn[...0M@.T%.
0430   75 24 DB 55 E5 9E A4 70 EF D0 A6 97 03 12 22 DE    u$.U...p......".
0440   64 51 6C 3B 11 4A 0C 58 E1 F8 66 E2 AB 96 09 FA    dQl;.J.X..f.....
0450   81 F9 18 A4 52 7E FC 52 93 96                      ....R~.R..
  CKA_CERTIFICATE_TYPE: CKC_X_509 (0)
  CKA_ISSUER: 127 bytes
0000   30 7D 31 0B 30 09 06 03 55 04 06 13 02 46 52 31    0}1.0...U....FR1
0010   13 30 11 06 03 55 04 08 13 0A 53 6F 6D 65 2D 53    .0...U....Some-S
0020   74 61 74 65 31 10 30 0E 06 03 55 04 07 13 07 41    tate1.0...U....A
0030   75 62 61 67 6E 65 31 15 30 13 06 03 55 04 0A 13    ubagne1.0...U...
0040   0C 54 65 73 74 20 43 41 20 73 61 72 6C 31 10 30    .Test CA sarl1.0
0050   0E 06 03 55 04 03 13 07 54 65 73 74 20 43 41 31    ...U....Test CA1
0060   1E 30 1C 06 09 2A 86 48 86 F7 0D 01 09 01 16 0F    .0...*.H........
0070   72 6F 6F 74 40 74 65 73 74 63 61 2E 63 6F 6D       root@testca.com
  CKA_SERIAL_NUMBER: 3 bytes
0000   02 01 17                                           ...
  CKA_TRUSTED: True
  CKA_SUBJECT: 125 bytes
0000   30 7B 31 0B 30 09 06 03 55 04 06 13 02 46 52 31    0{1.0...U....FR1
0010   11 30 0F 06 03 55 04 07 13 08 4D 61 6E 6F 73 71    .0...U....Manosq
0020   75 65 31 15 30 13 06 03 55 04 0A 13 0C 54 65 73    ue1.0...U....Tes
0030   74 20 43 41 20 73 61 72 6C 31 19 30 17 06 03 55    t CA sarl1.0...U
0040   04 03 13 10 4C 75 64 6F 76 69 63 20 52 6F 75 73    ....Ludovic Rous
0050   73 65 61 75 31 27 30 25 06 09 2A 86 48 86 F7 0D    seau1'0%..*.H...
0060   01 09 01 16 18 6C 75 64 6F 76 69 63 2E 72 6F 75    .....ludovic.rou
0070   73 73 65 61 75 40 66 72 65 65 2E 66 72             sseau@free.fr
  CKA_START_DATE: 0 bytes
  CKA_END_DATE: 0 bytes
  CKA_MODIFIABLE: True

==================== Object: 514 ====================
Dumping attributes:
  CKA_CLASS: CKO_PUBLIC_KEY (2)
  CKA_PRIVATE: False
  CKA_LABEL: eb155854-5522-60ea-16dc-016a5b43cd32
  CKA_TOKEN: True
  CKA_ID: 20 bytes
0000   EB 15 58 54 55 22 60 EA 16 DC 01 6A 5B 43 CD 32    ..XTU"`....j[C.2
0010   69 3D BE 1E                                        i=..
  CKA_DERIVE: 1 bytes
0000   00                                                 .
  CKA_KEY_TYPE: CKK_RSA (0)
  CKA_SUBJECT: 0 bytes
  CKA_ENCRYPT: True
  CKA_WRAP: True
  CKA_VERIFY: True
  CKA_VERIFY_RECOVER: True
  CKA_START_DATE: 0 bytes
  CKA_END_DATE: 0 bytes
  CKA_MODULUS: 256 bytes
0000   9F 9F 1E A4 4F 9B FD 76 23 D9 DD 43 46 EF DF 86    ....O..v#..CF...
0010   BC 80 D3 76 30 40 22 C5 DB C6 E8 65 1F 37 7C 13    ...v0@"....e.7|.
0020   03 37 3A E2 C5 E8 5E 0B ED 51 5A 41 8F EF 2A 74    .7:...^..QZA..*t
0030   CE 10 10 2F 0C 63 FE 1A 0F 25 86 20 EF 90 B8 EA    .../.c...%. ....
0040   4E A6 1F 3B B5 A2 76 37 B9 A4 E1 D2 0B 00 9E 6E    N..;..v7.......n
0050   85 E3 AB 6B A1 4C 78 3C 5F B6 10 F6 0A 94 78 19    ...k.Lx<_.....x.
0060   B2 B5 49 38 FF B4 4F 69 41 90 ED 78 81 85 61 1C    ..I8..OiA..x..a.
0070   B6 75 70 CD 1D 16 7A 56 E7 FE C5 E8 F9 9B E9 CC    .up...zV........
0080   F5 5E 3D BC 10 94 FF 77 08 6D 51 2F 28 76 81 86    .^=....w.mQ/(v..
0090   3C 4B 84 7D FE B3 4B FD 78 DA 8B 26 50 58 0E 80    <K.}..K.x..&PX..
00A0   3F 6F C0 F4 2C 89 05 20 AF B9 1C 44 81 DA 51 5C    ?o..,.. ...D..Q.
00B0   DA BB 77 45 85 E5 58 6C 14 A3 B6 24 D3 FB 00 A8    ..wE..Xl...$....
00C0   30 66 98 2A E2 C0 42 F6 E9 8D 3F 05 EF 77 0C 36    0f.*..B...?..w.6
00D0   D1 DE D9 2F EA DC 40 E9 A9 01 5B 88 E4 B3 46 4C    .../..@...[...FL
00E0   AB CC 95 5C AA C6 0C 3E 21 E0 42 1A 74 AD 7C D4    .......>!.B.t.|.
00F0   08 5B 07 14 67 4F D1 A0 4C 8C 6C 41 A0 27 05 5D    .[..gO..L.lA.'.]
  CKA_MODULUS_BITS: 2048
  CKA_PUBLIC_EXPONENT: 3 bytes
0000   01 00 01                                           ...
  CKA_MODIFIABLE: True
  CKA_LOCAL: False

Slot no: 1
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot no: 2
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot no: 3
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot no: 4
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)


Comments

The card only contains one certificate and a pair of RSA keys. Without the PIN code only the public key is visible, the private key is hidden.

Output after authentication


If I enter my PIN the PKCS#11 token provides a 3rd object corresponding to the private key.

[...]
Found 3 objects: [257, 514, 4881]
[...]
==================== Object: 4881 ====================
Dumping attributes:
  CKA_CLASS: CKO_PRIVATE_KEY (3)
  CKA_PRIVATE: True
  CKA_LABEL: gemalto
  CKA_TOKEN: True
  CKA_ID: 20 bytes
0000   EB 15 58 54 55 22 60 EA 16 DC 01 6A 5B 43 CD 32    ..XTU"`....j[C.2
0010   69 3D BE 1E                                        i=..
  CKA_DERIVE: 1 bytes
0000   00                                                 .
  CKA_KEY_TYPE: CKK_RSA (0)
  CKA_SUBJECT: 125 bytes
0000   30 7B 31 0B 30 09 06 03 55 04 06 13 02 46 52 31    0{1.0...U....FR1
0010   11 30 0F 06 03 55 04 07 13 08 4D 61 6E 6F 73 71    .0...U....Manosq
0020   75 65 31 15 30 13 06 03 55 04 0A 13 0C 54 65 73    ue1.0...U....Tes
0030   74 20 43 41 20 73 61 72 6C 31 19 30 17 06 03 55    t CA sarl1.0...U
0040   04 03 13 10 4C 75 64 6F 76 69 63 20 52 6F 75 73    ....Ludovic Rous
0050   73 65 61 75 31 27 30 25 06 09 2A 86 48 86 F7 0D    seau1'0%..*.H...
0060   01 09 01 16 18 6C 75 64 6F 76 69 63 2E 72 6F 75    .....ludovic.rou
0070   73 73 65 61 75 40 66 72 65 65 2E 66 72             sseau@free.fr
  CKA_SENSITIVE: True
  CKA_DECRYPT: True
  CKA_UNWRAP: True
  CKA_SIGN: True
  CKA_SIGN_RECOVER: True
  CKA_START_DATE: 0 bytes
  CKA_END_DATE: 0 bytes
  CKA_MODULUS: 256 bytes
0000   9F 9F 1E A4 4F 9B FD 76 23 D9 DD 43 46 EF DF 86    ....O..v#..CF...
0010   BC 80 D3 76 30 40 22 C5 DB C6 E8 65 1F 37 7C 13    ...v0@"....e.7|.
0020   03 37 3A E2 C5 E8 5E 0B ED 51 5A 41 8F EF 2A 74    .7:...^..QZA..*t
0030   CE 10 10 2F 0C 63 FE 1A 0F 25 86 20 EF 90 B8 EA    .../.c...%. ....
0040   4E A6 1F 3B B5 A2 76 37 B9 A4 E1 D2 0B 00 9E 6E    N..;..v7.......n
0050   85 E3 AB 6B A1 4C 78 3C 5F B6 10 F6 0A 94 78 19    ...k.Lx<_.....x.
0060   B2 B5 49 38 FF B4 4F 69 41 90 ED 78 81 85 61 1C    ..I8..OiA..x..a.
0070   B6 75 70 CD 1D 16 7A 56 E7 FE C5 E8 F9 9B E9 CC    .up...zV........
0080   F5 5E 3D BC 10 94 FF 77 08 6D 51 2F 28 76 81 86    .^=....w.mQ/(v..
0090   3C 4B 84 7D FE B3 4B FD 78 DA 8B 26 50 58 0E 80    <K.}..K.x..&PX..
00A0   3F 6F C0 F4 2C 89 05 20 AF B9 1C 44 81 DA 51 5C    ?o..,.. ...D..Q.
00B0   DA BB 77 45 85 E5 58 6C 14 A3 B6 24 D3 FB 00 A8    ..wE..Xl...$....
00C0   30 66 98 2A E2 C0 42 F6 E9 8D 3F 05 EF 77 0C 36    0f.*..B...?..w.6
00D0   D1 DE D9 2F EA DC 40 E9 A9 01 5B 88 E4 B3 46 4C    .../..@...[...FL
00E0   AB CC 95 5C AA C6 0C 3E 21 E0 42 1A 74 AD 7C D4    .......>!.B.t.|.
00F0   08 5B 07 14 67 4F D1 A0 4C 8C 6C 41 A0 27 05 5D    .[..gO..L.lA.'.]
  CKA_PUBLIC_EXPONENT: 3 bytes
0000   01 00 01                                           ...
  CKA_EXTRACTABLE: 1 bytes
0000   00                                                 .
  CKA_NEVER_EXTRACTABLE: True
  CKA_ALWAYS_SENSITIVE: True
  CKA_MODIFIABLE: True
  CKA_LOCAL: False

Slot no: 1
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot no: 2
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot no: 3
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot no: 4
  slotDescription: empty
  manufacturerID: Unknown
Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)

Comments


Of course only part of the private key is available: the public exponent and the modulus. The private exponent (of the private key) is NOT available.

Source code


The program does not try to fetch and display sensitive attributes and so remove them from the list of attributes to fetch:

# remove the CKR_ATTRIBUTE_SENSITIVE attributes since we can't get
# their values and will get an exception instead
all_attributes.remove(PyKCS11.CKA_PRIVATE_EXPONENT)
all_attributes.remove(PyKCS11.CKA_PRIME_1)
all_attributes.remove(PyKCS11.CKA_PRIME_2)
all_attributes.remove(PyKCS11.CKA_EXPONENT_1)
all_attributes.remove(PyKCS11.CKA_EXPONENT_2)
all_attributes.remove(PyKCS11.CKA_COEFFICIENT)


#!/usr/bin/env python

#   Copyright (C) 2006-2008 Ludovic Rousseau (ludovic.rousseau@free.fr)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

import PyKCS11
import getopt
import sys
import platform

# from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/142812
# Title: Hex dumper
# Submitter: Sebastien Keim (other recipes)
# Last Updated: 2002/08/05
# Version no: 1.0


def hexx(intval):
    x = hex(intval)[2:]
    if (x[-1:].upper() == 'L'):
        x = x[:-1]
    if len(x) % 2 != 0:
        return "0%s" % x
    return x


def dump(src, length=8):
    FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
    N = 0
    result = ''
    while src:
        s, src = src[:length], src[length:]
        hexa = ' '.join(["%02X" % ord(x) for x in s])
        s = s.translate(FILTER)
        result += "%04X   %-*s   %s\n" % (N, length * 3, hexa, s)
        N += length
    return result


def usage():
    print "Usage:", sys.argv[0],
    print "[-p pin][--pin=pin]",
    print "[-c lib][--lib=lib]",
    print "[-S][--sign]",
    print "[-d][--decrypt]",
    print "[-h][--help]",

try:
    opts, args = getopt.getopt(sys.argv[1:], "p:c:Sd:h", ["pin=", "lib=", "sign", "decrypt", "help"])
except getopt.GetoptError:
    # print help information and exit:
    usage()
    sys.exit(2)

pin_available = False
decrypt = sign = False
lib = None
for o, a in opts:
    if o in ("-h", "--help"):
        usage()
        sys.exit()
    elif o in ("-p", "--pin"):
        pin = a
        pin_available = True
    elif o in ("-c", "--lib"):
        lib = a
        print "using PKCS11 lib:", lib
    elif o in ("-S", "--sign"):
        sign = True
    elif o in ("-d", "--decrypt"):
        decrypt = True

red = blue = magenta = normal = ""
if sys.stdout.isatty() and platform.system().lower() != 'windows':
    red = "\x1b[01;31m"
    blue = "\x1b[34m"
    magenta = "\x1b[35m"
    normal = "\x1b[0m"

format_long = magenta + "  %s:" + blue + " %s (%s)" + normal
format_binary = magenta + "  %s:" + blue + " %d bytes" + normal
format_normal = magenta + "  %s:" + blue + " %s" + normal

pkcs11 = PyKCS11.PyKCS11Lib()
pkcs11.load(lib)
info = pkcs11.getInfo()
print "Library manufacturerID: " + info.manufacturerID

slots = pkcs11.getSlotList()
print "Available Slots:", len(slots)

for s in slots:
    try:
        i = pkcs11.getSlotInfo(s)
        print "Slot no:", s
        print format_normal % ("slotDescription", i.slotDescription.strip())
        print format_normal % ("manufacturerID", i.manufacturerID.strip())

        t = pkcs11.getTokenInfo(s)
        print "TokenInfo"
        print format_normal % ("label", t.label.strip())
        print format_normal % ("manufacturerID", t.manufacturerID.strip())
        print format_normal % ("model", t.model.strip())

        session = pkcs11.openSession(s)
        print "Opened session 0x%08X" % session.session.value()
        if pin_available:
            try:
                session.login(pin=pin)
            except:
                print "login failed, exception:", str(sys.exc_info()[1])

        objects = session.findObjects()
        print
        print "Found %d objects: %s" % (len(objects), [x.value() for x in objects])

        all_attributes = PyKCS11.CKA.keys()
        # remove the CKR_ATTRIBUTE_SENSITIVE attributes since we can't get
        # their values and will get an exception instead
        all_attributes.remove(PyKCS11.CKA_PRIVATE_EXPONENT)
        all_attributes.remove(PyKCS11.CKA_PRIME_1)
        all_attributes.remove(PyKCS11.CKA_PRIME_2)
        all_attributes.remove(PyKCS11.CKA_EXPONENT_1)
        all_attributes.remove(PyKCS11.CKA_EXPONENT_2)
        all_attributes.remove(PyKCS11.CKA_COEFFICIENT)
        # only use the integer values and not the strings like 'CKM_RSA_PKCS'
        all_attributes = [e for e in all_attributes if isinstance(e, int)]

        for o in objects:
            print
            print (red + "==================== Object: %d ====================" + normal) % o.value()
            attributes = session.getAttributeValue(o, all_attributes)
            attrDict = dict(zip(all_attributes, attributes))
            if attrDict[PyKCS11.CKA_CLASS] == PyKCS11.CKO_PRIVATE_KEY \
               and attrDict[PyKCS11.CKA_KEY_TYPE] == PyKCS11.CKK_RSA:
                m = attrDict[PyKCS11.CKA_MODULUS]
                e = attrDict[PyKCS11.CKA_PUBLIC_EXPONENT]
                if m and e:
                    mx = eval('0x%s' % ''.join(chr(c) for c in m).encode('hex'))
                    ex = eval('0x%s' % ''.join(chr(c) for c in e).encode('hex'))
                if sign:
                    try:
                        toSign = "12345678901234567890"  # 20 bytes, SHA1 digest
                        print "* Signing with object 0x%08X following data: %s" % (o.value(), toSign)
                        signature = session.sign(o, toSign)
                        s = ''.join(chr(c) for c in signature).encode('hex')
                        sx = eval('0x%s' % s)
                        print "Signature:"
                        print dump(''.join(map(chr, signature)), 16)
                        if m and e:
                            print "Verifying using following public key:"
                            print "Modulus:"
                            print dump(''.join(map(chr, m)), 16)
                            print "Exponent:"
                            print dump(''.join(map(chr, e)), 16)
                            decrypted = pow(sx, ex, mx)  # RSA
                            print "Decrypted:"
                            d = hexx(decrypted).decode('hex')
                            print dump(d, 16)
                            if toSign == d[-20:]:
                                print "*** signature VERIFIED!\n"
                            else:
                                print "*** signature NOT VERIFIED; decrypted value:"
                                print hex(decrypted), "\n"
                        else:
                            print "Unable to verify signature: MODULUS/PUBLIC_EXP not found"
                    except:
                        print "Sign failed, exception:", str(sys.exc_info()[1])
                if decrypt:
                    if m and e:
                        try:
                            toEncrypt = "12345678901234567890"
                            # note: PKCS1 BT2 padding should be random data,
                            # but this is just a test and we use 0xFF...
                            padded = "\x00\x02%s\x00%s" % ("\xFF" * (128 - (len(toEncrypt)) - 3), toEncrypt)
                            print "* Decrypting with 0x%08X following data: %s" % (o.value(), toEncrypt)
                            print "padded:\n", dump(padded, 16)
                            encrypted = pow(eval('0x%sL' % padded.encode('hex')), ex, mx)  # RSA
                            encrypted1 = hexx(encrypted).decode('hex')
                            print "encrypted:\n", dump(encrypted1, 16)
                            decrypted = session.decrypt(o, encrypted1)
                            decrypted1 = ''.join(chr(i) for i in decrypted)
                            print "decrypted:\n", dump(decrypted1, 16)
                            if decrypted1 == toEncrypt:
                                print "decryption SUCCESSFULL!\n"
                            else:
                                print "decryption FAILED!\n"
                        except:
                            print "Decrypt failed, exception:", str(sys.exc_info()[1])
                    else:
                        print "ERROR: Private key don't have MODULUS/PUBLIC_EXP"

            print "Dumping attributes:"
            for q, a in zip(all_attributes, attributes):
                if a == None:
                    # undefined (CKR_ATTRIBUTE_TYPE_INVALID) attribute
                    continue
                if q == PyKCS11.CKA_CLASS:
                    print format_long % (PyKCS11.CKA[q], PyKCS11.CKO[a], a)
                elif q == PyKCS11.CKA_CERTIFICATE_TYPE:
                    print format_long % (PyKCS11.CKA[q], PyKCS11.CKC[a], a)
                elif q == PyKCS11.CKA_KEY_TYPE:
                    print format_long % (PyKCS11.CKA[q], PyKCS11.CKK[a], a)
                elif session.isBin(q):
                    print format_binary % (PyKCS11.CKA[q], len(a))
                    if a:
                        print dump(''.join(map(chr, a)), 16),
                elif q == PyKCS11.CKA_SERIAL_NUMBER:
                    print format_binary % (PyKCS11.CKA[q], len(a))
                    if a:
                        print dump(a, 16),
                else:
                    print format_normal % (PyKCS11.CKA[q], a)
        print

        if pin_available:
            try:
                session.logout()
            except:
                print "logout failed, exception:", str(sys.exc_info()[1])

        session.closeSession()

    except PyKCS11.PyKCS11Error, e:
        print "Error:", e

Signing and verifying


The sample code also allows to sign a fixed message/pattern (a SHA1 digest) using the private key and verify the signature using the corresponding public key. Of course this is possible only if the private key is usable, so only after presenting the PIN code.

$ ./dumpit.py --pin=0000 --sign
[...]
==================== Object: 4881 ====================
* Signing with object 0x00001311 following data: 12345678901234567890
Signature:
0000   61 A8 4D EC A2 56 CA E8 23 03 1F 64 D9 C9 ED DD    a.M..V..#..d....
0010   AA 4D DD 08 E9 21 1E 90 26 9B EB C0 29 AA 8E 3E    .M...!..&...)..>
0020   AF 8D A6 70 1A 24 69 62 5A 70 F5 DD 7A 00 04 15    ...p.$ibZp..z...
0030   14 4A BC 45 24 5B 36 48 FC 2D C8 A1 81 E9 46 D3    .J.E$[6H.-....F.
0040   11 E4 C9 47 7A 63 26 67 9D 64 88 2F E1 DA 8D D7    ...Gzc&g.d./....
0050   A3 93 0E 58 7A 52 BE DC 55 09 95 88 56 8D 02 81    ...XzR..U...V...
0060   75 37 E8 A3 82 DC 91 D3 95 67 D4 26 91 6B 39 6A    u7.......g.&.k9j
0070   0D B3 70 EB 99 EC 52 94 2C D1 33 14 78 B8 8F C0    ..p...R.,.3.x...
0080   B5 DA 3E 60 45 6F 11 AF 5C 56 57 85 05 45 B3 E6    ..>`Eo...VW..E..
0090   0D 05 17 8A 2A 20 D2 59 EC F9 97 60 0B E2 2C D5    ....* .Y...`..,.
00A0   62 3C D4 4B 31 5A 6A D6 B5 25 98 9A 2F 92 99 50    b<.K1Zj..%../..P
00B0   35 89 2F 13 2A C1 55 DF B9 52 BB 51 ED B7 98 90    5./.*.U..R.Q....
00C0   1C 46 F3 82 03 A5 CC 72 E9 C2 47 ED 0E A2 24 89    .F.....r..G...$.
00D0   0C 5B D0 A0 79 CF BD 5D 61 50 51 16 C9 62 6C B0    .[..y..]aPQ..bl.
00E0   4E 0D 6D 48 86 8F 94 E7 0F E8 56 50 36 18 AF E3    N.mH......VP6...
00F0   C5 73 4F B9 DD 0B 2C B0 A6 68 57 D5 A4 8A C2 5C    .sO...,..hW.....

Verifying using following public key:
Modulus:
0000   9F 9F 1E A4 4F 9B FD 76 23 D9 DD 43 46 EF DF 86    ....O..v#..CF...
0010   BC 80 D3 76 30 40 22 C5 DB C6 E8 65 1F 37 7C 13    ...v0@"....e.7|.
0020   03 37 3A E2 C5 E8 5E 0B ED 51 5A 41 8F EF 2A 74    .7:...^..QZA..*t
0030   CE 10 10 2F 0C 63 FE 1A 0F 25 86 20 EF 90 B8 EA    .../.c...%. ....
0040   4E A6 1F 3B B5 A2 76 37 B9 A4 E1 D2 0B 00 9E 6E    N..;..v7.......n
0050   85 E3 AB 6B A1 4C 78 3C 5F B6 10 F6 0A 94 78 19    ...k.Lx<_.....x.
0060   B2 B5 49 38 FF B4 4F 69 41 90 ED 78 81 85 61 1C    ..I8..OiA..x..a.
0070   B6 75 70 CD 1D 16 7A 56 E7 FE C5 E8 F9 9B E9 CC    .up...zV........
0080   F5 5E 3D BC 10 94 FF 77 08 6D 51 2F 28 76 81 86    .^=....w.mQ/(v..
0090   3C 4B 84 7D FE B3 4B FD 78 DA 8B 26 50 58 0E 80    <K.}..K.x..&PX..
00A0   3F 6F C0 F4 2C 89 05 20 AF B9 1C 44 81 DA 51 5C    ?o..,.. ...D..Q.
00B0   DA BB 77 45 85 E5 58 6C 14 A3 B6 24 D3 FB 00 A8    ..wE..Xl...$....
00C0   30 66 98 2A E2 C0 42 F6 E9 8D 3F 05 EF 77 0C 36    0f.*..B...?..w.6
00D0   D1 DE D9 2F EA DC 40 E9 A9 01 5B 88 E4 B3 46 4C    .../..@...[...FL
00E0   AB CC 95 5C AA C6 0C 3E 21 E0 42 1A 74 AD 7C D4    .......>!.B.t.|.
00F0   08 5B 07 14 67 4F D1 A0 4C 8C 6C 41 A0 27 05 5D    .[..gO..L.lA.'.]

Exponent:
0000   01 00 01                                           ...

Decrypted:
0000   01 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0010   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0020   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0030   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0040   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0050   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0060   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0070   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0080   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
0090   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
00A0   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
00B0   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
00C0   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
00D0   FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF    ................
00E0   FF FF FF FF FF FF FF FF FF FF 00 31 32 33 34 35    ...........12345
00F0   36 37 38 39 30 31 32 33 34 35 36 37 38 39 30       678901234567890

*** signature VERIFIED!

Conclusion


As with getinfo.py the sample code dumpit.py is more complex than needed to parse the command line arguments and pretty display the results (in color).

If you just want to display the attribute of an object you can do that using a much simpler code. But that is for a future article.

PyKCS11 provided samples: getinfo.py

I will continue the serie about PyKCS11 I started last year in PyKCS11 introduction. Just to remind you, PyKCS11 is a Python wrapper above the PKCS#11 API.

Setup


In the following I will use a .Net card and the PKCS#11 for .Net cards I blogged about in April 2010.

I generated a X509 certificate using tinyca2 and loaded it on the card using Firefox.

Example codes provided with PyKCS11: getinfo.py


getinfo.py is a short sample code to... get information from the token.

Output


cryptokiVersion: 2.20 
flags:  
libraryDescription: Gemalto .NET PKCS11              
libraryVersion: 2.2 
manufacturerID: Gemalto                          
Available Slots: 5 [0, 1, 2, 3, 4]
Slot n.: 0
 firmwareVersion: 0.00 
 flags: CKF_TOKEN_PRESENT, CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: Gemalto GemPC Twin 00 00                                         
 SessionInfo
  flags: CKF_SERIAL_SESSION 
  slotID: 0 
  state: 0 
  ulDeviceError: 0 
 TokenInfo
  firmwareVersion: 0.0 
  flags: CKF_LOGIN_REQUIRED, CKF_TOKEN_INITIALIZED, CKF_USER_PIN_INITIALIZED 
  hardwareVersion: 0.0 
  label: .NET #AFC4963915BE14DB           
  manufacturerID: Gemalto                          
  model: .NET Card        
  serialNumber: AFC4963915BE14DB 
  ulFreePrivateMemory: -1 
  ulFreePublicMemory: -1 
  ulMaxPinLen: 0 
  ulMaxRwSessionCount: 0 
  ulMaxSessionCount: 0 
  ulMinPinLen: 0 
  ulRwSessionCount: 0 
  ulSessionCount: 1 
  ulTotalPrivateMemory: -1 
  ulTotalPublicMemory: -1 
  utcTime:  
  Mechanism list: 
   CKM_RSA_PKCS_KEY_PAIR_GEN 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_GENERATE_KEY_PAIR 
   CKM_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_ENCRYPT, CKF_HW, CKF_DECRYPT, CKF_SIGN, CKF_VERIFY 
   CKM_RSA_X_509 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_ENCRYPT, CKF_HW, CKF_DECRYPT, CKF_SIGN, CKF_VERIFY 
   CKM_MD5_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_SIGN, CKF_VERIFY 
   CKM_SHA1_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_SIGN, CKF_VERIFY 
   CKM_SHA256_RSA_PKCS 
    ulMinKeySize: 512 
    ulMaxKeySize: 2048 
    flags: CKF_HW, CKF_SIGN, CKF_VERIFY 
   CKM_MD5 
    flags: CKF_DIGEST 
   CKM_SHA_1 
    flags: CKF_DIGEST 
   CKM_SHA256 
    flags: CKF_DIGEST 
Slot n.: 1
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot n.: 2
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot n.: 3
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)
Slot n.: 4
 firmwareVersion: 0.00 
 flags: CKF_REMOVABLE_DEVICE, CKF_HW_SLOT 
 hardwareVersion: 0.00 
 manufacturerID: Unknown                          
 slotDescription: empty                                                            
 SessionInfo Error: CKR_TOKEN_NOT_PRESENT (0x000000E0)

Comments


The PKCS#11 token has 5 virtual slots. Only slot 0 contains information about the card.

The getinfo.py sample code uses colors to display the information.

Source code


A large part of the source code is to manage:
  • command arguments
  • display and colorization

We will see in a future blog article how to use the wrapper in an interactive Python. So a lot of the extra code in the sample will be useless in that case.

#!/usr/bin/env python

#   Copyright (C) 2006-2010 Ludovic Rousseau (ludovic.rousseau@free.fr)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

import PyKCS11
import platform
import sys


class getInfo(object):
    red = blue = magenta = normal = ""

    def colorize(self, text, arg):
        print self.magenta + text + self.blue, arg, self.normal

    def display(self, obj, indent=""):
        dico = obj.to_dict()
        for key in sorted(dico.keys()):
            type = obj.fields[key]
            left = indent + key + ":"
            if type == "flags":
                self.colorize(left, ", ".join(dico[key]))
            elif type == "pair":
                self.colorize(left, "%d.%d" % dico[key])
            else:
                self.colorize(left, dico[key])

    def __init__(self, lib=None):
        if sys.stdout.isatty() and platform.system().lower() != 'windows':
            self.red = "\x1b[01;31m"
            self.blue = "\x1b[34m"
            self.magenta = "\x1b[35m"
            self.normal = "\x1b[0m"

        self.pkcs11 = PyKCS11.PyKCS11Lib()
        self.pkcs11.load(lib)

    def getSlotInfo(self, slot):
        print "Slot n.:", slot
        self.display(self.pkcs11.getSlotInfo(slot), " ")

    def getTokenInfo(self, slot):
        print " TokenInfo"
        self.display(self.pkcs11.getTokenInfo(slot), "  ")

    def getMechanismInfo(self, slot):
        print "  Mechanism list: "
        m = self.pkcs11.getMechanismList(slot)
        for x in m:
            self.colorize("  ", x)
            i = self.pkcs11.getMechanismInfo(slot, x)
            if not i.flags & PyKCS11.CKF_DIGEST:
                if i.ulMinKeySize != PyKCS11.CK_UNAVAILABLE_INFORMATION:
                    self.colorize("    ulMinKeySize:", i.ulMinKeySize)
                if i.ulMaxKeySize != PyKCS11.CK_UNAVAILABLE_INFORMATION:
                    self.colorize("    ulMaxKeySize:", i.ulMaxKeySize)
            self.colorize("    flags:", ", ".join(i.flags2text()))

    def getInfo(self):
        self.display(self.pkcs11.getInfo())

    def getSessionInfo(self, slot, pin=None):
        print " SessionInfo",
        session = self.pkcs11.openSession(slot)

        if pin:
            print "(using pin: %s)" % pin
            session.login(pin)
        else:
            print

        self.display(session.getSessionInfo(), "  ")

        if pin:
            session.logout()


def usage():
    print "Usage:", sys.argv[0],
    print "[-p pin][--pin=pin]",
    print "[-s slot][--slot=slot]",
    print "[-c lib][--lib=lib]",
    print "[-h][--help]",
    print "[-o][--opensession]"

if __name__ == '__main__':
    import getopt

    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:s:c:ho",
            ["pin=", "slot=", "lib=", "help", "opensession"])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)

    slot = None
    lib = None
    pin = None
    open_session = False
    pin_available = False
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-p", "--pin"):
            pin = a
            pin_available = True
            open_session = True
        if o in ("-s", "--slot"):
            slot = int(a)
        if o in ("-c", "--lib"):
            lib = a
        if o in ("-o", "--opensession"):
            open_session = True

    gi = getInfo(lib)
    gi.getInfo()

    slots = gi.pkcs11.getSlotList()
    print "Available Slots:", len(slots), slots

    if len(slots) == 0:
        sys.exit(2)

    if slot:
        slots = [slots[slot]]

    for slot in slots:
        try:
            gi.getSlotInfo(slot)
            gi.getSessionInfo(slot, pin)
            gi.getTokenInfo(slot)
            gi.getMechanismInfo(slot)
        except PyKCS11.PyKCS11Error, e:
            print "Error:", e

Conclusion

If you have to use a PKCS#11 token and you can choose the programming language you want then you can use Python instead of C.

"new" version of libusb

My CCID driver uses libusb-1.0 to talk to USB readers.

libusb evolution stalled


The latest version 1.0.8 of libusb-1.0 was released in May 2010 (nearly a year ago). Since then many bugs have been reported and most have been fixed in the git repository. The problem is that now new stable or 1.0.9 version has been released since May 2010. So if you suffer from a bug in libusb it is not easy to update it.

libusb git snapshot

To ease the use of a more up-to-date version of libusb-1.0 I made a snapshot of the version in the git repository and provide it on my web page of beta versions or http://ludovic.rousseau.free.fr/softwares/pcsc-lite/

The files in the libusb git repository have not evolved in the last 6 months so I do not expect to have to make a new snapshot within the next 6 months. I sent a mail on the libusb mailing list.

Conclusion


So before reporting a bug in libccid first try a newer version of libusb. If the bug is at the communication level it may already be solved in the libusb snapshot version.

pcscd now uses libudev instead of libhal

pcsc-lite first started (in the previous century) when only serial readers were in use. The serial readers are configured using the /etc/reader.conf file. The problem is that the configuration is static and can't be used with a USB reader.

History of USB plug-n-play mechanism


To detect the insertion or removal of a USB reader different mechanisms have been used in the last 10 years.

Linux


hotplug_linux.c: introduced at least in (or before) version 1.0.2beta2 (20 Dec, 2001).

The problem is that the code is Linux specific.

libusb


hotplug_libusb.c: introduced in version 1.2.0-rc1 (26 August, 2003). From the changelog:
- src/hotplug_libusb.c: Add support of libusb. Allow to use USB readers on
*BSD or any plateform supported by libusb. Thanks to Toni Andjelkovic for
the great job.
The problem of libusb is that pcscd is constantly polling the USB bus to detect reader hotplug.

libhal


hotplug_libhal.c: introduced in version 1.4.100 (23 March 2008). From the changelog:

- add libhal support to avoid polling the USB bus.  libusb is still
  supported but libhal is now the default

The problem is that libhal has been deprecated upstream in May 2008 (2 months after pcscd started using it). Some distributions are actively migrating out of libhal. See Debian bug #587979
"pcscd: uses deprecated HAL" for example. And the wiki page "HALRemoval".

libudev


hotplug_libudev.c: introduced in version 1.6.8 (not yet released as of February 2011).

It looks like libudev is a good choice for the future. I don't know if it is supported by other systems than GNU/Linux. I removed the support of libhal but support of libusb is still present.

Mac OS X


Mac OS X has its own hotplug mechanism in hotplug_macosx.c. Apple maintains its own forked version of pcsc-lite. So I do not plan to change anything Mac OS X specific in pcsc-lite.

Conclusion


The hotplug mechanism is a fast moving target on GNU/Linux. I hope libudev will last at least a few months before I have to move to something else.

New version of libccid: 1.4.2

I just released a new version of libccid.

Changelog

1.4.2 - 22 February 2011, Ludovic Rousseau

  • Add support of Feitian SCR310 reader (also known as 301v2), ACS
    APG8201 PINhandy 1, Oberthur ID-ONE TOKEN SLIM v2, new Neowave
    Weneo token, Vasco DIGIPASS KEY 860, Vasco DIGIPASS KEY 200,
    Xiring Leo v2, Xiring MyLeo, Aktiv Rutoken lite readers
  • Add back support of "bogus" Oz776, REINER SCT and BLUDRIVE II
  • Ease detection of OpenCT by pcsc-lite
  • disable use of interrupt card events for multi slots readers (the
    algorithm is bogus and can't be used)
  • fix minor problems detected by the clang tool
  • some minor bugs removed