NAME
Chipcard::CTAPI - Perl module for communication with chipcard terminals
SYNOPSIS
use Chipcard::CTAPI;
my $ct = new Chipcard::CTAPI('interface' => &Chipcard::CTAPI::PORT_COM1)
or die "Can't communicate with card terminal";
my $memory_size = $ct->getMemorySize();
$ct->read(0, $memory_size)
or die "Can't read data from card.\n";
$ct->dumpCommunication("Content: ", $ct->getData, $ct->getDataLength);
my $content = "Hello, world!\n";
$ct->setData($content);
$ct->write(0, $ct->getDataLength)
or die "Can't write new content to card.\n";
$ct->close;
ABSTRACT
Chipcard::CTAPI enables Perl programs to communicate with chipcard terminals based on the low-level CTAPI driver.
DESCRIPTION
Using the CTAPI (card terminal application programming interface) is a simple yet powerful way to communicate with chipcard terminals. There are more advanced APIs available, like PC/SC, but in general they are not as easy and fast to set up as CTAPI. Especially when an application is not all about chipcards but just includes some features which can make use of them, CTAPI is often the best way to go as it implies less overhead for the end user.
Chipcard::CTAPI is a Perl module which provides direct access to the low-level CTAPI functions (which are, in fact, only three), but focusses on a couple of convenience methods for reading and writing memory cards.
METHODS
This description of methods is sorted in the order you probably will want to use them.
- new
-
Creates a new Chipcard::CTAPI object. You must pass at least the interface option which specifies the physical port your card terminal is attached to.
Example: my $ct = new Chipcard::CTAPI('interface' => &Chipcard::CTAPI::PORT_COM1);
A list of all possible PORT-constants can be looked up in CTAPI.pm or your local ctapi.h . Note that there's no port numbers defined for USB card drivers; these are likely to use port numbers greater then 32768. See your card terminal's CTAPI documentation for details.
new returns undef if the communication with the card terminal can't be established. If you can't get it to work, please try other programs based on CTAPI to check whether you've got a hardware problem.
- cardInserted
-
Checks whether there's currently a card inserted into the reader. Returns 1 if a card is available, 0 otherwise.
- getMemorySize
-
Returns the memory size of the currently inserted card in bytes or 0 if there is no card inserted. The size is actually calculated from the second byte of the card's ATR string which is automatically fetched when using new or reset.
Example: my $memory_size = $ct->getMemorySize();
- read (address, size)
-
Reads size bytes from the currently inserted card's memory, starting at address. Returns the number of bytes actually read. The fetched data can be accessed using the getData method.
Example how to read you card's whole memory at once:
my $num_bytes_read = $ct->read(0, $ct->getMemorySize);
- getData
-
Returns the card memory retrieved by the last read method call as string. This also works for binary data.
Example: my $data = $ct->getData();
An empty string is returned if there is no data available.
- getDataLength
-
Returns the length of the most recently set or fetched data in bytes.
- setData (string)
-
Stores data in the object's internal data buffer; this is required before data can be written to the card.
Example: $ct->setData("Hello, world!\n");
Be aware that no size checks are done here. If the length of the data you set exceeds the card's memory, it will be truncated when writing it onto the card.
- write (address, size)
-
Writes size bytes from the currently buffered data, which for example was set using setData, onto the card, starting at address.
Returns 1 if the write access was successful, undef otherwise.
Notes: If the the sum of address and size is greater than your card's memory, the data will be truncated. If size is greater than the length of the currently buffered data, the trailing (size - getDataLength()) bytes will be filled up with null-bytes (chr(0)).
Example: $ct->write(0, $ct->getMemorySize);
- download (filename, [ascii-mode])
-
Fetches the currently inserted card's whole memory and stores it in a file called filename. If the optionally given second parameter is a true value, only the first X bytes of the card's memory will be stored in the file, where X is the index of the first null-byte.
Returns 1 on success, undef otherwise.
Example 1: $ct->download("dump.bin") or die "Can't dump the card.\n";
Example 2: $ct->setData("Hello, world!\n\000"); $ct->write(0, $ct->getDataLength); $ct->download("message.txt", 1);
- upload (filename)
-
Stores the content of the given file in the card's memory. If the file is larger than the card's memory, it will be truncated. If it is smaller, the last (getMemorySize - filesize) bytes of the card's memory will be left untouched.
Example: $ct->upload("card_memory_image.bin");
Returns 1 on success, undef otherwise.
- reset
-
Resets the chipcard. Use this whenever you expect a different card to be inserted in the terminal meanwhile.
Returns 1 if there is a card inserted, undef otherwise.
Notes: This internally calls ejectICC and requestICC and evaluates the currently inserted card's ATR string (see getATR), if present. It thus is required that you call reset whenever the user has inserted a new chipcard in order to get correct data from methods like getMemorySize. If you want to auto-detect card changes, please see checkSixtyTwoOne.
- cardChanged
-
Checks whether the user has exchanged the card in the reader.
Returns 1 if there's a new card in the reader meanwhile. Returns 0 if still the same card is inserted. Returns undef if there's no card inserted now.
Note: if the user removes his card and inserts the same card again, the result is the same as with a new card.
- resetCardTerminal
-
Reset the card terminal. CTBCS specs say that applications should use this only after a communication error between the application and the card terminal. Avoid using it; reset will suffice in most cases.
Returns 1 if successful, undef otherwise.
- getTerminalInformation
-
Returns a triple with information about the card terminal hardware:
my ($manufacturer, $model, $revision) = $ct->getTerminalInformation();
- close
-
Closes the communication with the card terminal. Returns OK if successful.
Methods for direct communication with the card terminal:
- sendCommand ($destination, @command)
-
Sends the command consisting of the sequence of bytes given in command to the given destination. The destination usually is either the constant CT for commands to the card terminal itself or 0 for reading and writing the card's memory.
Returns the result of the underlying call to CT_data, which should be 0 on success.
- getResponse
-
Returns the card terminal's response to the latest command sent by sendCommand. This usually consist of any requested data, followed by two status bytes. Very often, the status bytes 0x90 0x00 signal success, see checkNinetyZero.
- getResponseLength
-
Returns the length of the card terminal's response to the latest command in bytes.
- checkNinetyZero
-
Verifies that the last response from the card terminal ended with 0x90 0x00, which signals the success of the most recent operation.
Returns 1 if the last response ended in 0x90 0x00, undef otherwise.
- checkSixtyTwoOne
-
Checks whether the last response from the card terminal ended with 0x62 0x01. That's the response one usually gets after a call to requestICC if the same card is still inserted.
Thus, if you want to check whether still the same card is inserted, you could use something like this:
$ct->requestCardStatus(); $ct->requestICC(); my $is_same_card = $ct->checkSixtyTwoOne(); die "You didn't insert a new card!\n" if ($is_same_card);
Comparable functionality is implemented by cardChanged.
checkSixtyTwoOne returns 1 if the last response ended in 0x62 0x01, undef otherwise.
- selectFile
-
Sends the "select file" command. This is automatically done by the various read/write methods provided by Chipcard::CTAPI, so you should only need to use it if you send read/write requests manually.
Returns 1 on success, undef otherwise.
- requestICC
-
Sends the "request ICC" command. Fetches the card's ATR (answer to reset) string and calculates the card's memory based on it.
Returns 1 on success, undef otherwise.
- ejectICC
-
Sends the "eject ICC" command. Disconnects the currently inserted card from the card terminal. If the hardware supports it, the card is also ejected.
Returns 1 on success, undef otherwise.
- requestCardStatus
-
Sends the "get status" command to the terminal, requesting information about whether currently a card is inserted and connected or not. The method cardInserted is a more convenient way of figuring that out.
Returns 1 on success, undef otherwise.
- requestTerminalStatus
-
Sends the "get status" command to the terminal, this time requesting information about the card terminal itself. This command is automatically issued on initialization and its result is available through getTerminalInformation.
Returns 1 on success, undef otherwise.
- getATR
-
Returns the currently inserted card's ATR string which can be used to identify the card. Returns undef if there's no card inserted or if there is a new card inserted but reset has not been called yet.
- getProtocol
-
Returns the protocol used by the currently inserted card. Possible values: "ISO", "I2C", "3W", "2W", "unknown". Returns undef if there is no card inserted or a new card is inserted but reset has not been called yet.
- getStructure
-
Returns the card's structure ident. Possible values: "ISO", "common", "proprietary", "special". Returns undef if there is no card inserted or a new card is inserted but reset has not been called yet.
Methods useful for debugging:
- dumpCommunication (prefix, data, length)
-
Pretty-prints a hexdump of the first length bytes of data on STDOUT. Each line of output is prefixed with prefix.
If the debugging mode is turned on, all communication with the card terminal will be dumped.
Returns the number of lines printed.
- setDumpWidth
-
Sets the maximum line width for dumpCommunication output. Defaults to 78. As each byte is printed as a hexadecimal value, followed by a blank (0x20), the should be a multiple of 3. Rounding to a multiple of 3 is automatically done.
- getIndex (ascii-value)
-
Returns the first position in the currently buffered data matching the given value. Returns -1 if there's no match at all.
Examples: my $pos = $ct->getIndex(ord "\n"); # Returns position of first \n my $first_line = substr($self->getData, 0, $ct->getIndex(ord "\n"));
- setDebugLevel (level)
-
Sets the debug level. Currently only used to turn debugging on/off. When turned on (i.e., the level is a true value), all bytes sent to and received from the card terminal via sendCommand will be pretty-printed to STDOUT using dumpCommunication.
Chipcard::CTAPI also provides direct access to the low-level CTAPI functions:
- CT_init (context, port)
-
Initializes the communication with the card terminal. context is a unique short integer (0-255) which must be used on all subsequent calls to CT_data and CT_close. port defines the physical interface on which the card reader is attached to.
Returns the constant OK on success.
- CT_data (context, dest, src, cmd_len, cmd, max_rsp_len)
-
Sends the cmd, which is cmd_len bytes long, over the communication channel identified by context from src to dest and expects an answer which is at most max_rsp_len bytes long.
The source usually is set to the constant HOST, whereas the destination is either the card terminal (constant CT) or 0.
A call to CT_data returns a triple:
my ($result, $response_length, $response) = CT_data(...)
result is the numerical return value from the underlying CT_data() call and should be OK in case of success.
The last two bytes of response represent status information. Often, the sequence 0x90 0x00 signals successful completion of the last requested operation. The checkNinetyZero method can be used to verify whether the last response has ended with that sequence.
- CT_close (context)
-
Closes the connection identified by context. Returns OK on success.
Please consult the CTAPI documentation for more details.
The following methods are EXPERIMENTAL. Use them strictly at your own risc. They are believed to work, if at all, only with 3W cards (see getProtocol).
- submitPIN (pin)
-
Tries to unlock a PIN-protected card with the given PIN. WARNING: submitting a wrong PIN or the right PIN in a wrong way multiple times will render your card useless! As the way PIN verification is implemented in Chipcard::CTAPI might be wrong for your hardware, DO NOT try again if your first attempt fails!
Example: $ct->submitPIN("000") or die "Sorry, wrong PIN!\n";
- changePIN (oldpin, newpin)
-
Tries to change the PIN of a PIN-protected card. Both the old and the new PIN must be passed (in this order). Same warnings as for submitPIN apply here, too.
Example: $ct->changePIN("123", "456") or die "Changing PIN failed!\n";
OPTIONS
The following options can be passed when creating a new Chipcard::CTAPI object using the method new():
- interface
-
Specifies the physical port your card terminal is attached to. The following constants can be used: PORT_COM1 .. PORT_COM4, PORT_LPT1 and PORT_LPT2. For USB devices, your driver probably has assigned numbers greater than 32768. Please consult its documentation.
- debug
-
Used to turn on/off debugging. If set to a true value, each read and write access to the card terminal for which sendCommand is used will be printed on STDOUT using dumpCommunication.
EXAMPLES
The Chipcard::CTAPI distribution archive comes with a couple of demo applications which you should have a look at. Some simple quick-start examples follow.
Establish communication with a card terminal on COM2: my $ct = new Chipcard::CTAPI(interface => &Chipcard::CTAPI::PORT_COM2) or die "Can't initialize card terminal on COM2!\n";
Wait till the user has inserted a card with at least 2kb memory: my $try = 1; while($ct->getMemorySize < 2048) { if ($try == 1) { print "Please insert a memory card with at least 2k capacity."; } elsif ($try < 10) { print "."; } else { die "Timeout.\n"; }
sleep 1;
$ct->reset;
$try++;
}
Read the first 512 byte from the card's memory: $ct->read(0, 512); if ($ct->getDataLength == 512) { # do something with the data my $data = $ct->getData; # ... } else { # error treatment }
Read the bytes 1500 - 2000 from the card's memory: $ct->read(1500, 500); # read 500 bytes, starting at address 1500
Read the whole memory: $ct->read(0, $ct->getMemorySize);
Read the whole memory and store it in a file named card.bin : $ct->download("card.bin");
Store "Hello, world" at the beginning of the card's memory: $ct->setData("Hello, world"); $ct->write(0, $ct->getDataLength);
Store "Hello, world" at the card's memory address 1000: $ct->setData("Hello, world"); $ct->write(1000, $ct->getDataLength);
Erase a card's memory by overwriting it with null-bytes: $ct->setData(''); $ct->write(0, $ct->getMemorySize);
Erase a card's memory by overwriting each byte with a '?': my $mem_size = $ct->getMemorySize; $ct->setData('?' x $mem_size); $ct->write(0, $ct->getMemorySize);
Check whether there's a card inserted at the moment: die "No card, no fun.\n" unless ($ct->cardInserted);
Handling card changes: print "Please insert another card...\n"; sleep 10; # wait for the user to do so ... $ct->reset; # this initializes the new card print "New card's capacity: " . $ct->getMemorySize . "bytes.\n";
Checking whether the card has been exchanged: my $change = $ct->cardChanged(); if (!defined $change) { print "No card inserted now!\n"; } elsif ($change == 0) { print "Still same card inserted!\n"; } else { print "New card inserted!\n"; $ct->reset(); # must do! print "Capacity of new card: " . $ct->getMemorySize . "\n"; }
BUGS
Chipcard::CTAPI has currently only been tested with Towitoko card terminals. Please report problems with other terminals and ideas for solving them.
TODO
Provide convenience methods for handling processor cards (real smartcards).
SEE ALSO
- PCSC::Lite
-
A Perl module to communicate with card terminals using PC/SC-Lite. Requires a running PCSC daemon on your system.
- ctapi(3)
-
Vendor-specific CTAPI library documentation.
AUTHOR
Wolfgang Hommel (wolf (at) code-wizards.com)
COPYRIGHT AND LICENSE
Copyright 2003 Wolfgang Hommel
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.