Chipcard::CTAPI - Perl module for communication with chipcard terminals
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;
Chipcard::CTAPI enables Perl programs to communicate with chipcard terminals based on the low-level CTAPI driver.
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.
This description of methods is sorted in the order you probably will want to use them.
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.
Example: my $memory_size = $ct->getMemorySize();
Example how to read you card's whole memory at once:
my $num_bytes_read = $ct->read(0, $ct->getMemorySize);
Example: my $data = $ct->getData();
An empty string is returned if there is no data available.
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.
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);
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);
Example: $ct->upload(``card_memory_image.bin'');
Returns 1 on success, undef otherwise.
=item B<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.
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.
Returns 1 if successful, undef otherwise.
my ($manufacturer, $model, $revision) = $ct->getTerminalInformation();
Methods for direct communication with the card terminal:
Returns the result of the underlying call to CT_data, which should be 0 on success.
Returns 1 if the last response ended in 0x90 0x00, undef otherwise.
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.
Returns 1 on success, undef otherwise.
Returns 1 on success, undef otherwise.
Returns 1 on success, undef otherwise.
Returns 1 on success, undef otherwise.
Returns 1 on success, undef otherwise.
Methods useful for debugging:
If the debugging mode is turned on, all communication with the card terminal will be dumped.
Returns the number of lines printed.
Examples: my $pos = $ct->getIndex(ord ``\n''); # Returns position of first \n my $first_line = substr($self->getData, 0, $ct->getIndex(ord ``\n''));
Chipcard::CTAPI also provides direct access to the low-level CTAPI functions:
Returns the constant OK on success.
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.
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).
Example:
$ct->submitPIN(``000'') or die ``Sorry, wrong PIN!\n'';
Example: $ct->changePIN(``123'', ``456'') or die ``Changing PIN failed!\n'';
The following options can be passed when creating a new Chipcard::CTAPI object using the method new():
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''; }
Chipcard::CTAPI has currently only been tested with Towitoko card terminals. Please report problems with other terminals and ideas for solving them.
=head1 TODO
Provide convenience methods for handling processor cards (real smartcards).
=head1 SEE ALSO
ctapi(3)
Wolfgang Hommel (wolf (at) code-wizards.com)
Copyright 2003 Wolfgang Hommel
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.