Communicate with smartcard reader through Android USB host
Asked Answered
M

2

12

I'm trying to send a command to a smart card. I use a Gemalto IDBridge CT30 (PC TWIN reader) and a IDBridge K30 connected to the Android device over USB.

I try to send a SELECT APDU command over USB:

boolean claim = openedConnection.claimInterface(usbInterface, true);
byte[] data = new byte[]{
        (byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x0C,
        (byte) 0x07, (byte) 0xA0, (byte) 0x00, (byte) 0x00,
        (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E};

After that I receive an answer:

final int dataTransferred = this.openedConnection.bulkTransfer(endPointOut, data, data.length, TIMEOUT_MS);
if(!(dataTransferred == 0 || dataTransferred == data.length)) {
    throw new Exception("Error durring sending command [" + dataTransferred + " ; " + data.length + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}

final byte[] responseBuffer = new byte[endPointIn.getMaxPacketSize()];
final int dataTransferred = this.openedConnection.bulkTransfer(this.endPointIn, responseBuffer, responseBuffer.length, TIMEOUT_MS);
Console.writeLine("USB Retrieve: " + dataTransferred + " " + responseBuffer.length);
if(dataTransferred >= 0){
    return responseBuffer;
}
throw new Exception("Error durring receinving response [" + dataTransferred + "]");

That answer is

0x00 0x00 0x00 0x00 0x00 0xA0 0x00 0x41 0x03 0x00

However, I should get an answer of 0x90 0x00 according to the test project here.

What am I doing wrong? Can anybody help me? Do I use the correct approach? I'm not using the default package classes of javax.smartcardio. I use the USB interface classes (e.g. UsbDevice) directly.

Mchale answered 5/12, 2016 at 13:53 Comment(0)
E
18

Your reader device speaks CCID over the USB interface. You cannot simply send an APDU (smartcard command) over the bulk-out endpoint and expect to receive a response APDU over the bulk-in endpoint. Instead you need to implement the CCID device class protocol (see USB Device Class Specifications). The steps are something like:

  1. Send PC_to_RDR_IccPowerOn command to activate the card.
    62 00000000 00 00 00 0000 
    |  |        |  |  |  |    |
    |  |        |  |  |  |    \--> Empty data field
    |  |        |  |  |  \-------> Unused, set to 0x0000
    |  |        |  |  \----------> Power select: 0x00 indicates automatic selection
    |  |        |  \-------------> Sequence number (increment for each command)
    |  |        \----------------> Slot number (seems to be zero for your device)
    |  \-------------------------> Length of data field (LSB first)
    \----------------------------> Message type: 0x62 indicates PC_to_RDR_IccPowerOn
    
  2. Receive the ATR through RDR_to_PC_DataBlock.
    80 18000000 00 00 00 00 00 3BBF11008131FE45455041000000000000000000000000F1 
    |  |        |  |  |  |  |  |
    |  |        |  |  |  |  |  \--> Data field: ATR
    |  |        |  |  |  |  \-----> Level parameter
    |  |        |  |  |  \--------> Error register (should be zero on success)
    |  |        |  |  \-----------> Status register (should be zero on success)
    |  |        |  \--------------> Sequence number (matches the sequence number of the command)
    |  |        \-----------------> Slot number (matches the slot number of the command)
    |  \--------------------------> Length of data field (LSB first)
    \-----------------------------> Message type: 0x80 indicates RDR_to_PC_DataBlock
    
  3. Send command APDU wrapped into PC_to_RDR_XfrBlock command
    6F 0C000000 00 01 00 0000 00A4040C07A000000118454E
    |  |        |  |  |  |    |
    |  |        |  |  |  |    \--> Data field: Command APDU
    |  |        |  |  |  \-------> Level parameter (0x0000 for normal length APDUs)
    |  |        |  |  \----------> Block waiting timeout
    |  |        |  \-------------> Sequence number (increment for each command)
    |  |        \----------------> Slot number (seems to be zero for your device)
    |  \-------------------------> Length of data field (LSB first)
    \----------------------------> Message type: 0x6F indicates PC_to_RDR_XfrBlock
    
  4. Receive response APDU through RDR_to_PC_DataBlock.
    80 02000000 00 01 00 00 00 9000 
    |  |        |  |  |  |  |  |
    |  |        |  |  |  |  |  \--> Data field: Response APDU
    |  |        |  |  |  |  \-----> Level parameter
    |  |        |  |  |  \--------> Error register (should be zero on success)
    |  |        |  |  \-----------> Status register (should be zero on success)
    |  |        |  \--------------> Sequence number (matches the sequence number of the command)
    |  |        \-----------------> Slot number (matches the slot number of the command)
    |  \--------------------------> Length of data field (LSB first)
    \-----------------------------> Message type: 0x80 indicates RDR_to_PC_DataBlock
    
  5. Repeat steps 3 and 4 for each APDU exchange (don't forget to increment the sequence number).

Since the ATR indicates T=1 as first protocol, you might need to wrap your APDU into T=1 TPDUs (depending on the reader configuration). The I-block for the first APDU would look something like:

00 00 0C 00A4040C07A000000118454E 15
|  |  |  |                        |
|  |  |  |                        \--> LRC (due to missing TC in ATR): XOR checksum over all other bytes
|  |  |  \---------------------------> INF: APDU
|  |  \------------------------------> LEN: length of INF field
|  \---------------------------------> PCB: toggle between 0x00 and 0x40 for every other I-block
\------------------------------------> NAD: node addressing

So your PC_to_RDR_XfrBlock command would look like:

6F 10000000 00 01 00 0000  00 00 0C 00A4040C07A000000118454E 15

You would then either receive your answer wrapped in an I-block or an R- or S-block indicating that some special/error treatment is necessary.

Erosion answered 6/12, 2016 at 13:5 Comment(15)
After sending IccPowerOn - I receive: 80 18 00 00 00 00 00 00 00 00 3B BF 11 00 81 31 FE 45 45 50 41 00 00 00 00 00 00 00 00 00 00 00 00 F1 and after sending next command XfrBlock, I receive: 00 00 00 00 00 00 01 40 03 00. But I can't parse the result as ResponseAPDU (because it is part of the package javax.smartcardio) but the result is array of bytes (as above). But the result is still different. I also send the command as array of bytes and not as class CommandAPDU. @MichaelRolandMchale
@Mchale See my updated answer. Obviously you can't use the APDU classes from Java SmartcardIO since this is not easily available on stock Android, though you could implement your own provider on top of the USB classes and the Java SmartcardIO framework. Anyways, you need to speak CCID and not APDUs over USB. You should definitely read the CCID specification.Erosion
Thanks for your answer, but there is probably something missing. The only two commands, that I'm sending are PC_to_RDR_IccPowerOn and when the correct answer with ATR will come, after that command PC_to_RDR_XfrBlock that is still returning only 10 bytes (80 00 00 00 00 00 01 40 F4 00) and not your APDU response. I already tried many implementations, that contains only this two steps and I still get the same result. Is it possible, that I forget something? Some inter-command and so on? @MichaelRolandMchale
@Mchale Have you looked into the CCID specification and checked what the error code 0xF4 means? Did you verify that your card actually supports that command with those specific parameters (specifically the option P2=0x0C)? For instance by connecting the reader to some PC and running some ready-made software for APDU exchange.Erosion
@Mchale Try setting Block waiting timeout to 0x00.Erosion
I tried to prepare probably 16 combinations of Xfr commands for sending after initializing of IccPowerOn and for first comand - it return Error register 0xAA and every next returns 0xFE. I also put a 5 sec. wait until next command was send and increment sequence number, but with no luck. Should be there some command after ecery XfrBlock? Here is the link for command documentation (I'm using ACOS smart card). Many thanks @MichaelRoland for your helpMchale
Let us continue this discussion in chat.Mchale
@Mchale Once you get an error response, you probably need to reset the reader before you can continue sending commands. Error code 0xAA is not defined in the standard and it's probably not that easy to find out what it means. However, looking at the ATR, your card only supports T=1. Depending on your reader configuration, you might need to transmit T=1 TPDUs (including NAD/PCB/LEN and CRC fields) instead of plain APDUs.Erosion
Many thanks for your response Michael. Yes, thanks to you, I could finally managed sending XfrBlocks with IccPowerOn and SetParameters commands. But when I'm trying to get data bigger than 256 bytes (while-do case), it returns me only cca. 40 bytes from max 64bytes, that I send as max response length. For example CommandAPDU commandAPDU = new CommandAPDU(0x00, 0xB0, 0x7F & (0 >> 8), 0 & 0xFF, 256) - converted to XfrBlock 6F 09 00 00 00 00 0A 04 00 00 00 00 05 00 B0 00 00 00 B5 returns smaller byte size than is expected and the data looks fine. @MichaelRolandMchale
@Mchale Is thee more than those 40 bytes in the file that you read? What I-block do you get? Could it be that the data that you expect is actually chained into multiple I-blocks (this typically happens when data would otherwise not fit in transmission buffers at the card side)? Also, since you had to use SetParameters to get it working, could you post your own answer explaining the necessary steps (or if you don't want that you can simply provide the necessary steps to get it working as comments here and I'll happily add those steps to my answer then).Erosion
Well, here is the output pastebin.com/ffiNG7JQ, I hope, it will help you that @MichaelRolandMchale
Does anybody know, where could I find Usb CCID documentation for T=1 protocol? Also the above answer @MichaelRoland, thanksMchale
@Mchale It would have been so helpful if you had posted your solution here ..Hypotension
@MichaelRoland Hi, I am having same issue. I send and receive T=1 protocol TPDU commands successfully. But can't process long responses which being sent back chained. Here are data: sent: 6f 08000000 00 06 00 0000 00 40 04 00 82 00 00 c6 received: 80 24 00 00 00 00 06 00 00 00 00 20 20 76 72 30 81 98 00 81 98 00 00 00 85 80 20 00 23 80 00 00 00 00 00 00 00 00 20 20 06 03 54 19 15 ef Expected data length is 56 but received 32 bytesConventioner
I always get the answer 2. from step 3Political
L
0

What you send is a SELECT command, with a given AID, which easily could produce a result. You clearly indicate, however, that you are not interested in a response, by

  • setting P2 to '0C'
  • not providing an LE byte (assuming a block based protocol, surely reasonable for USB)

So one may conclude, that your card is not compliant to ISO 7816-4; on the other hand, the response does not contain anything looking like an error SW1/SW2 status either, are you sure, to have responsebuffer dumped?

Last answered 5/12, 2016 at 16:27 Comment(2)
This set of bytes (command) was sent in the classic default Java project (link in the main question) through classes of package javax.smartcardio and this same bytes I'm trying to send with Android interface with classes like UsbDevice but the response is quite different. @LastMchale
(Beware,) OP is sending the APDU to the USB endpoint thus have to use CCID -- see Michael Roland's answer (you cant directly send APDUs here)Stricker

© 2022 - 2024 — McMap. All rights reserved.