PCSC sample in Python

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

Installation

The wrapper project is hosted by sourceforge at http://pyscard.sourceforge.net/. The files (source code and installer for many different systems: GNU/Linux, Mac OS X, Windows) are available at http://sourceforge.net/projects/pyscard/files/.

But if you have a Debian system it is easier to use:
apt-get install python-pyscard

The licence is Lesser General Public License either version 2.1 of the License, or (at your option) any later version.

Low level API

Source code


#! /usr/bin/env python

from smartcard.scard import *
import smartcard.util

SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01]
COMMAND = [0x00, 0x00, 0x00, 0x00]

try:
    hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
    if hresult != SCARD_S_SUCCESS:
        raise Exception('Failed to establish context : ' +
            SCardGetErrorMessage(hresult))
    print 'Context established!'

    try:
        hresult, readers = SCardListReaders(hcontext, [])
        if hresult != SCARD_S_SUCCESS:
            raise Exception('Failed to list readers: ' +
                SCardGetErrorMessage(hresult))
        print 'PCSC Readers:', readers

        if len(readers) < 1:
            raise Exception('No smart card readers')

        reader = readers[0]
        print "Using reader:", reader

        try:
            hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, reader,
                SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
            if hresult != SCARD_S_SUCCESS:
                raise Exception('Unable to connect: ' +
                    SCardGetErrorMessage(hresult))
            print 'Connected with active protocol', dwActiveProtocol

            try:
                hresult, response = SCardTransmit(hcard, dwActiveProtocol,
                    SELECT)
                if hresult != SCARD_S_SUCCESS:
                    raise Exception('Failed to transmit: ' +
                        SCardGetErrorMessage(hresult))
                print 'Select: ' + smartcard.util.toHexString(response,
                    smartcard.util.HEX)
                hresult, response = SCardTransmit(hcard, dwActiveProtocol,
                    COMMAND)
                if hresult != SCARD_S_SUCCESS:
                    raise Exception('Failed to transmit: ' +
                        SCardGetErrorMessage(hresult))
                print 'Command: ' + smartcard.util.toHexString(response,
                    smartcard.util.HEX)
            finally:
                hresult = SCardDisconnect(hcard, SCARD_UNPOWER_CARD)
                if hresult != SCARD_S_SUCCESS:
                    raise Exception('Failed to disconnect: ' +
                        SCardGetErrorMessage(hresult))
                print 'Disconnected'

        except Exception, message:
            print "Exception:", message

    finally:
        hresult = SCardReleaseContext(hcontext)
        if hresult != SCARD_S_SUCCESS:
            raise Exception('Failed to release context: ' +
                    SCardGetErrorMessage(hresult))
        print 'Released context.'

except Exception, message:
    print "Exception:", message

import sys
if 'win32' == sys.platform:
    print 'press Enter to continue'
    sys.stdin.read(1)

Output

$ ./sample1.py 
Context established!
PCSC Readers: ['Gemalto GemPC Pinpad 00 00']
Using reader: Gemalto GemPC Pinpad 00 00
Connected with active protocol 2
Select: 0x90 0x00
Command: 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x77 0x6F 0x72 0x6C 0x64 0x21 0x90 0x00
Disconnected
Released context.

Comments

Using the low level API is very verbose. You have access to each PCSC function from Python.

For example I use this API to write some Unitary Tests for pcsc-lite (http://svn.debian.org/wsvn/pcsclite/trunk/PCSC/UnitaryTests/#_trunk_PCSC_UnitaryTests_)

But why use a high level language if the code is as complex as in C?

High level API

Source code


#! /usr/bin/env python

from smartcard.System import readers

# define the APDUs used in this script
SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01]
COMMAND = [0x00, 0x00, 0x00, 0x00]

# get all the available readers
r = readers()
print "Available readers:", r

reader = r[0]
print "Using:", reader

connection = reader.createConnection()
connection.connect()

data, sw1, sw2 = connection.transmit(SELECT)
print data
print "Select Applet: %02X %02X" % (sw1, sw2)

data, sw1, sw2 = connection.transmit(COMMAND)
print data
print "Command: %02X %02X" % (sw1, sw2)

Output

$ ./sample2.py 
Available readers: ['Gemalto GemPC Pinpad 00 00']
Using: Gemalto GemPC Pinpad 00 00
[]
Select Applet: 90 00
[72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]
Command: 90 00

Comments

The code is much more compact and easy to read. In particular you do not have to explicitly check for the results. In case of error at the PC/SC level the caller will receive a smartcard.Exceptions.* exception.

For example if no card is present in the first reader you get:

$ ./sample2.py 
Available readers:  ['Gemalto GemPC Pinpad 00 00']
Using: Gemalto GemPC Pinpad 00 00
Traceback (most recent call last):
  File "./sample2.py", line 18, in 
    connection.connect()
  File "/usr/lib/python2.5/site-packages/smartcard/CardConnectionDecorator.py", line 53, in connect
    self.component.connect( protocol, mode, disposition )
  File "/usr/lib/python2.5/site-packages/smartcard/pcsc/PCSCCardConnection.py", line 111, in connect
    raise CardConnectionException( 'Unable to connect with protocol: ' + dictProtocol[pcscprotocol] + '. ' + SCardGetErrorMessage(hresult) )
smartcard.Exceptions.CardConnectionException: 'Smartcard Exception: Unable to connect with protocol: T0 or T1. No smart card inserted.!'

It is easy to use a try: block to catch the exception and do whatever you need to do in such case.

Conclusion

Python is the language is use nowadays to write programs if I can choose the language. So of course pyscard is the PC/SC wrapper I use.

Free software Tokend above PKCS#11 (for Mac OS X)

Gemalto released the source code of its tokend.

Tokend

A tokend is a plugin for Mac OS X that links between the CDSA higher layer and a smart card or other cryptographic device. See Smart Card Services.

PKCS#11 Tokend

The goal of this tokend is to be able to use any PKCS#11 library in CDSA and then with native Apple Applications (Mail, Safari, Key Chain, File Vault, etc.)

No configuration

This tokend will try every PKCS#11 library present in the standard directory /usr/lib/pkcs11/ and try to use them. You do not have to configure a specific PKCS#11 library to use.

Source code

The source code is hosted by Apple in the smartcardservices on the macosforge serveur. Direct access is at http://smartcardservices.macosforge.org/trac/browser/trunk/Tokend/PKCS11 You can also find instructions to (re)build the Tokend project at http://smartcardservices.macosforge.org/trac/wiki/documentation/building

License

The code is licensed under Apple Public Source License Version 2.0 as the other tokend provided by Apple.

Compiled version

A compiled version can be found in the package installer provided by Gemalto at http://www.gemalto.com/products/dotnet_card/resources/libraries.html. Download the ".NET PKCS#11 libraries for Mac OS X" file. The tokend is then installed as /System/Library/Security/tokend/PKCS11.tokend

Long term plan

The idea is to have this tokend provided by Apple in the next major operating system (Mac OS X 10.7). And I have no idea when that will happen. But I will let you know :-)

Source code of PKCS#11 for .NET cards

Gemalto provides the source code of its PKCS#11 library for its .NET (dot net) cards at http://www.gemalto.com/products/dotnet_card/resources/libraries.html

The software licence is GNU Lesser General Public License version 2.1 of the License, or (at your option) any later version.

You may be interested by this software if you (or your customers) want:


The source code is also available at the subversion repository hosted by Apple at http://smartcardservices.macosforge.org/trac/browser/trunk/SmartCardServices/src/PKCS11dotNetV2

PCSC sample in Perl

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

Installation

Get the source code from http://ludovic.rousseau.free.fr/softwares/pcsc-perl/. The current version is 1.4.8. If you distribution does not provide a package (Debian does with libpcsc-perl) you can install it by hand using:

pcsc-perl-1.4.8$ perl Makefile.PL 
osname: linux
LDDFLAGS: 
INC: `pkg-config --cflags libpcsclite`
Checking if your kit is complete...
Looks good
Writing Makefile for Chipcard::PCSC::Card
Writing Makefile for Chipcard::PCSC
pcsc-perl-1.4.7$ make
[...]
pcsc-perl-1.4.7$ make test
[...]
pcsc-perl-1.4.7$ make install
[...]

The wrapper works on GNU/Linux, Mac OS X and Windows.

API


The API documentation is available online at http://ludovic.rousseau.free.fr/softwares/pcsc-perl/PCSC.html and http://ludovic.rousseau.free.fr/softwares/pcsc-perl/Card.html.

You can also have a look at the project page project page on CPAN (Comprehensive Perl Archive Network).

Source code


#!/usr/bin/perl -w

use Chipcard::PCSC;

# create a new object
$hContext = new Chipcard::PCSC();
die ("Can't create the PCSC object: $Chipcard::PCSC::errno\n")
    unless defined $hContext;

# get the reader list
@ReadersList = $hContext->ListReaders();
die ("Can't get readers' list: $Chipcard::PCSC::errno\n")
    unless defined $ReadersList[0];

# connect to the first reader
$hCard = new Chipcard::PCSC::Card($hContext, $ReadersList[0]);
die ("Can't connect: $Chipcard::PCSC::errno\n")
    unless defined $hCard;

# send the Select Applet APDU
$cmd = Chipcard::PCSC::ascii_to_array("00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01");
$RecvData = $hCard->Transmit($cmd);
die ("Can't transmit data: $Chipcard::PCSC::errno") unless defined $RecvData;
print Chipcard::PCSC::array_to_ascii($RecvData)."\n";

# send the test APDU
$cmd = Chipcard::PCSC::ascii_to_array("00 00 00 00");
$RecvData = $hCard->Transmit($cmd);
die ("Can't transmit data: $Chipcard::PCSC::errno") unless defined $RecvData;
print Chipcard::PCSC::array_to_ascii($RecvData)."\n";

$hCard->Disconnect();

Output


90 00
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00

Lessons learned


Portability


The same code can be used on any plateform. No more #ifdef like in C.

Low level API


The API is still low level and just wrap PC/SC calls from C to Perl.

Higher level API


TransmitWithCheck() is a little more easy to use than Transmit(). This method does the split between data and status word.

In the example above replace the two last blocks with:

# Send the Select Applet APDU
($sw, $RecvData) = $hCard->TransmitWithCheck("00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01", "90 00");
die ("Can't transmit data: $Chipcard::PCSC::errno") unless defined $sw;
print $RecvData."\n";
print Chipcard::PCSC::Card::ISO7816Error($sw) . " ($sw)\n";

# Send the test APDU
($sw, $RecvData) = $hCard->TransmitWithCheck("00 00 00 00", "90 00");
die ("Can't transmit data: $Chipcard::PCSC::errno") unless defined $sw;
print $RecvData."\n";
print map { chr hex $_ } split ' ', $RecvData;
print "\n";
print Chipcard::PCSC::Card::ISO7816Error($sw) . " ($sw)\n";

This sample code also uses Chipcard::PCSC::Card::ISO7816Error($sw) to transform the status word is something human readable like Normal processing. for 90 00.

Output


Normal processing. (90 00)
48 65 6C 6C 6F 20 77 6F 72 6C 64 21 
Hello world!
Normal processing. (90 00)

My Ohloh page

I created an ohloh account account and added some/most of the free software projects I work on.

I have no idea what it can be used for. Maybe just have some nice graphics of the subversion activity on the projects.

Does anybody else use ohloh services?

pcsc-lite and CCID beta versions

Just to remind you that I more or less regularly post beta versions of pcsc-lite and my CCID driver on http://ludovic.rousseau.free.fr/softwares/pcsc-lite/. These versions are beta but should be fully functional. So please test them and report problems.

The ChangeLog (for pcsc-lite) or README (for ccid) file is in general not up-to-date but the file generated with the subversion changelog is always up-to-date.

I may post blog messages describing all the wonderful new features I added in these software.

PCSC sample in C

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

C Source code


#ifdef WIN32
#undef UNICODE
#endif

#include <stdio.h>
#include <stdlib.h>

#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

#ifdef WIN32
static char *pcsc_stringify_error(LONG rv)
{
 static char out[20];
 sprintf_s(out, sizeof(out), "0x%08X", rv);

 return out;
}
#endif

#define CHECK(f, rv) \
 if (SCARD_S_SUCCESS != rv) \
 { \
  printf(f ": %s\n", pcsc_stringify_error(rv)); \
  return -1; \
 }

int main(void)
{
 LONG rv;

 SCARDCONTEXT hContext;
 LPTSTR mszReaders;
 SCARDHANDLE hCard;
 DWORD dwReaders, dwActiveProtocol, dwRecvLength;

 SCARD_IO_REQUEST pioSendPci;
 BYTE pbRecvBuffer[258];
 BYTE cmd1[] = { 0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0,
  0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01 };
 BYTE cmd2[] = { 0x00, 0x00, 0x00, 0x00 };

 unsigned int i;

 rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 CHECK("SCardEstablishContext", rv)

#ifdef SCARD_AUTOALLOCATE
 dwReaders = SCARD_AUTOALLOCATE;

 rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders);
 CHECK("SCardListReaders", rv)
#else
 rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
 CHECK("SCardListReaders", rv)

 mszReaders = calloc(dwReaders, sizeof(char));
 rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
 CHECK("SCardListReaders", rv)
#endif
 printf("reader name: %s\n", mszReaders);

 rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED,
  SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
 CHECK("SCardConnect", rv)

 switch(dwActiveProtocol)
 {
  case SCARD_PROTOCOL_T0:
   pioSendPci = *SCARD_PCI_T0;
   break;

  case SCARD_PROTOCOL_T1:
   pioSendPci = *SCARD_PCI_T1;
   break;
 }
 dwRecvLength = sizeof(pbRecvBuffer);
 rv = SCardTransmit(hCard, &pioSendPci, cmd1, sizeof(cmd1),
  NULL, pbRecvBuffer, &dwRecvLength);
 CHECK("SCardTransmit", rv)

 printf("response: ");
 for(i=0; i<dwRecvLength; i++)
  printf("%02X ", pbRecvBuffer[i]);
 printf("\n");

 dwRecvLength = sizeof(pbRecvBuffer);
 rv = SCardTransmit(hCard, &pioSendPci, cmd2, sizeof(cmd2),
  NULL, pbRecvBuffer, &dwRecvLength);
 CHECK("SCardTransmit", rv)

 printf("response: ");
 for(i=0; i<dwRecvLength; i++)
  printf("%02X ", pbRecvBuffer[i]);
 printf("\n");

 rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 CHECK("SCardDisconnect", rv)

#ifdef SCARD_AUTOALLOCATE
 rv = SCardFreeMemory(hContext, mszReaders);
 CHECK("SCardFreeMemory", rv)

#else
 free(mszReaders);
#endif

 rv = SCardReleaseContext(hContext);

 CHECK("SCardReleaseContext", rv)

 return 0;
}

Makefile

# Linux
PCSC_CFLAGS := $(shell pkg-config --cflags libpcsclite)
LDFLAGS := $(shell pkg-config --libs libpcsclite)

# Mac OS X
#PCSC_CFLAGS := -framework PCSC

CFLAGS += $(PCSC_CFLAGS)

sample: sample.c

clean:
 rm -f sample

Outputs

The output is "Hello world!" which is in hex:

$ echo -n 'Hello world!' | xxd -g 1
0000000: 48 65 6c 6c 6f 20 77 6f 72 6c 64 21   Hello world!

GNU/Linux and Mac OS X

reader name: Gemplus GemPC Twin 00 00
response: 90 00 
response: 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00

Windows

D:\Documents and Settings\lroussea\My Documents\Visual Studio 2008\Projects\
sample\Debug> sample.exe
reader name: Gemplus USB Smart Card Reader 0
response: 90 00
response: 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00

Lessons learned

Reader name

The reader names are different on different plate forms. GNU/Linux and Mac OS X use the same PC/SC driver and the same (mostly) pcsc-lite so the name of an identical reader is the same. But Windows uses a different driver with a different PC/SC reader name.

Do not use the PC/SC reader name to identify a specific reader if you want to be portable.

SCardTransmit

pioRecvPci argument may be NULL (as in the sample) but shall not be left uninitialised on Windows. Otherwise SCardTransmit returns an error 0xE. This error code does not look like a WinSCard error code and is not documented in the SCardTransmit() API AFAIK.

UNICODE

If UNICODE is defined under Windows then the reader name is in UTF-16 and not easy to manipulate in a portable way. But I am not a Windows expert.

Unix uses UTF-8 so the name is the same in ASCII and UTF-8.

SCARD_AUTOALLOCATE

Mac OS X does not (yet) support memory (auto) allocation in the PC/SC layer. So you have to use a double call mechanism. One first call to get the size to allocate and one second call with a buffer allocated at the correct size.

In the example the memory auto allocation is used to get the list of connected readers. We see here that the C language is very low level regarding memory allocation.

Memory allocated by PC/SC has to be free-ed by PC/SC using SCardFreeMemory()

PCSC framework

Mac OS X uses Frameworks instead of classical Unix libraries. The difference (for our example) is the header files are not stored in /usr/include/ but in /System/Library/Frameworks/PCSC.framework/Headers/. You do not specify the header path to the compiler using -Ipath but use -framework PCSC instead. The compiler (gcc) takes care to find and use the correct framework.

pcsc_stringify_error()

pcsc_stringify_error() is a function specific to pcsc-lite. So we have to either not use it at all in a portable source code or re-implement a version on Windows. I decided to rewrite a limited version on Windows.

wintypes.h

Mac OS X does not use the exact same API definition than Windows or GNU/Linux for the WinSCard functions. Only standards types are used instead of Windows ones. For example:

int32_t SCardConnect(SCARDCONTEXT hContext,
        const char *szReader,
        uint32_t dwShareMode,
        uint32_t dwPreferredProtocols,
        LPSCARDHANDLE phCard,
        uint32_t *pdwActiveProtocol);

instead of

LONG SCardConnect(SCARDCONTEXT hContext,
 LPCSTR szReader,
 DWORD dwShareMode,
 DWORD dwPreferredProtocols,
 LPSCARDHANDLE phCard,
 LPDWORD pdwActiveProtocol);

The advantage is that the types used on Mac OS X are clearly defined by inttypes.h.

For example ULONG is defined as

typedef unsigned long ULONG;

but the long type from the C-language is different on Windows and GNU/Linux.

GNU/Linux (and Mac OS X) are LLP64 systems but Windows is a LP64 system. See 64-bit. So on GNU/Linux a long is 64-bits on a 64-bits CPU and on Windows a long is 32-bits on a 64-bits CPU.

To avoid problems Apple decided to explicitly fix the size using uint32_t.

CCID driver in Mac OS X

Apple provides a pcsc-lite versions since Jaguar (10.2) in August 2002.
Different versions of Mac OS X contains different versions and family of CCID driver.

Tiger (10.4)


Contains the ifd-CCID driver from Jean-Luc Giraud. This CCID driver is limited and has not evolved since its inclusion in Mac OS X. This driver does not support T=1 cards withe a TPDU reader for example.

Leopard (10.5)


Leopard also contained the same ifd-CCID driver in the first 10.5.x versions.

Then Apple replaced it by my libccid driver in 10.5.6 using version 1.3.8 of the driver.

Snow Leopard (10.6)


Since Leopard 10.5.6 the same 1.3.8 driver is provided.

The CCID driver binary is still a Universal PowerPC and i386 binary even if Snow Leopard does not support Power PC and the PC/SC framework is 32 and 64-bits. It looks like the CCID driver has not been recompiled for Snow Leopard.

Future


Rumors says that Apple may update the PC/SC layer in a future version of Mac OS X.

PC/SC sample in different languages

The PC/SC API also called WinSCard can be used from a large variety of languages. I will start a serie of blogs to present the same program (functionnally) but using different languages.

Languages:

  1. C
  2. Perl
  3. Python
  4. scriptor
  5. OCaml
  6. Prolog
  7. Ruby
  8. Java
  9. C#
  10. Ada
  11. PHP (dead upstream as 11 January 2015)
  12. PHP5
  13. lua
  14. JavaScript (Node.js)
  15. Python (using python-pcsclite)
  16. Common Lisp
  17. C for UEFI
  18. Objective-C (using Crypto Token Kit)
  19. Swift (using Crypto Token Kit)
  20. go
  21. Smart Card Connector on Chromebook
  22. Rust
  23. Objective-C (synchronous)
  24. Java using intarsys smartcard-io
  25. Kotlin
  26. Scala  
  27. Free Pascal (Lazarus)
  28. Flutter/Dart
  29. Elixir
  30. Erlang
  31. TypeScript (Deno) 
If you know a wrapper for a language not listed above please contact me.

Program

The program shall do:
  1. connect to the first found smart card reader
  2. send APDU: 00 A4 04 00 0A A0 00 00 00 62 03 01 0C 06 01
  3. display result
  4. send APDU: 00 00 00 00
  5. display result
The first APDU is a SELECT command to select a test applet. The second command is the only command known by the applet.


Smart card Applet

The applet only knows one command and returns Hello world! (yes, it is a test applet). It was described in a French magazine GNU/Linux Magazine HS n°39.

Parsing an ATR

If you have an ATR and would like to transfrom it in a human readable format you can use an online application I developped (using Google App Engine): http://smartcard-atr.appspot.com/
The output will look like this:


Parsing ATR: 3B FA 13 00 FF 81 31 80 45 00 31 C1 73 C0 01 00 00 90 00 B1

TS = 0x3B Direct Convention
T0 = 0xFA Y(1): b1111, K: 10 (historical bytes)
TA(1) = 0x13 Fi=372, Di=4, 93 cycles/ETU (43010 bits/s at 4.00 MHz, 53763 bits/s for fMax=5 MHz)
TB(1) = 0x00 VPP is not electrically connected
TC(1) = 0xFF Extra guard time: 255 (special value)
TD(1) = 0x81 Y(i+1) = b1000, Protocol T=1
----
TD(2) = 0x31 Y(i+1) = b0011, Protocol T=1
----
TA(3) = 0x80 IFSC: 128
TB(3) = 0x45 Block Waiting Integer: 4 - Character Waiting Integer: 5
----
Historical bytes 00 31 C1 73 C0 01 00 00 90 00
Category indicator byte: 0x00
(compact TLV data object)
    Tag: 3, Len: 1 (card service data byte)
      Card service data byte: 193
        - Application selection: by full DF name
        - Application selection: by partial DF name
        - EF.DIR and EF.ATR access services: by GET RECORD(s) command
        - Card without MF
    Tag: 7, Len: 3 (card capabilities)
      Selection methods: 192
        - DF selection by partial DF name
        - DF selection by full DF name
      Data coding byte: 1
        - Behaviour of write functions: one-time write
        - Value 'FF' for the first byte of BER-TLV tag fields: valid
        - Data unit in quartets: 1
     Command chaining, length fields and logical channels: 0
        - Logical channel number assignment: No logical channel
        - Maximum number of logical channels: 0
    Mandatory status indicator (3 last bytes)
      LCS (life card cycle): 0 (No information given)
      SW: 9000 ()
TCK = 0xB1 (correct checksum)

Possibly identified card: OpenPGP