/*
* eID Applet Project.
* Copyright (C) 2008-2014 FedICT.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version
* 3.0 as published by the Free Software Foundation.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, see
* http://www.gnu.org/licenses/.
*/
package test.be.fedict.eid.applet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.beblue.jna.usb.LibUSB;
import org.beblue.jna.usb.usb_dev_handle;
import org.beblue.jna.usb.usb_device;
import org.beblue.jna.usb.usb_endpoint_descriptor;
import org.beblue.jna.usb.usb_interface_descriptor;
public class CCIDTerminal {
static final Log LOG = LogFactory.getLog(CCIDTerminal.class);
private usb_device usbDevice;
private usb_interface_descriptor usbInterfaceDescriptor;
private usb_dev_handle usbDevHandle;
private byte endPointBulkIn;
private byte endPointBulkOut;
private byte endPointInterrupt;
private int busNumber;
private int deviceNumber;
private byte sequenceNumber = 0;
public CCIDTerminal(usb_device usbDevice, usb_interface_descriptor usbInterfaceDescriptor) {
this.usbDevice = usbDevice;
this.usbInterfaceDescriptor = usbInterfaceDescriptor;
this.setEndPoints();
usbDevHandle = null;
}
public usb_device getUsbDevice() {
return usbDevice;
}
public usb_interface_descriptor getUsbInterfaceDescriptor() {
return usbInterfaceDescriptor;
}
public void setBusNumber(int busNumber) {
this.busNumber = busNumber;
}
public void setDeviceNumber(int deviceNumber) {
this.deviceNumber = deviceNumber;
}
public byte getEndPointBulkIn() {
return this.endPointBulkIn;
}
public byte getEndPointBulkOut() {
return this.endPointBulkOut;
}
public byte getEndPointInterrupt() {
return this.endPointInterrupt;
}
private void setEndPoints() {
usb_endpoint_descriptor[] usbEndPointDescriptors = (usb_endpoint_descriptor[]) usbInterfaceDescriptor.endpoint
.toArray(usbInterfaceDescriptor.bNumEndpoints);
for (int i = 0; i < usbEndPointDescriptors.length; i++) {
if ((usbEndPointDescriptors[i].bmAttributes & 3) == 3) // Interrupt
endPointInterrupt = usbEndPointDescriptors[i].bEndpointAddress;
if ((usbEndPointDescriptors[i].bmAttributes & 3) == 2) // Bulk
if ((usbEndPointDescriptors[i].bEndpointAddress & 0x80) == 0x80) // Bulk
// IN
endPointBulkIn = usbEndPointDescriptors[i].bEndpointAddress;
else
// Bulk OUT
endPointBulkOut = usbEndPointDescriptors[i].bEndpointAddress;
}
}
public void open() {
int rv;
if (usbDevHandle == null)
usbDevHandle = LibUSB.libUSB.usb_open(usbDevice);
if (usbDevHandle == null) {
LOG.debug(String.format("usb_open device failed (%d/%d)", busNumber, deviceNumber));
throw new RuntimeException(String.format("usb_open device failed (%d/%d)", busNumber, deviceNumber));
}
rv = LibUSB.libUSB.usb_set_configuration(usbDevHandle, 1);
if (rv != 0) {
LOG.debug(String.format("set configuration failed (%d/%d): %d", busNumber, deviceNumber, rv));
throw new RuntimeException(
String.format("set configuration failed (%d/%d): %d", busNumber, deviceNumber, rv));
}
/*
* rv = LibUSB.libUSB.usb_set_altinterface(usbDevHandle,
* usbInterfaceDescriptor.bAlternateSetting); if (rv != 0) {
* LOG.debug(String .format(
* "set alternative interface failed (%d/%d): %d",
* busNumber,deviceNumber,rv)); throw new
* RuntimeException(String.format(
* "set alternative interface failed (%d/%d): %d",
* busNumber,deviceNumber,rv)); }
*/
rv = LibUSB.libUSB.usb_claim_interface(usbDevHandle, usbInterfaceDescriptor.bInterfaceNumber);
if (rv != 0) {
LOG.debug(String.format("claim interface failed (%d/%d): %d", busNumber, deviceNumber, rv));
throw new RuntimeException(
String.format("claim interface failed (%d/%d): %d", busNumber, deviceNumber, rv));
}
}
public void close() {
if (usbDevHandle != null)
LibUSB.libUSB.usb_close(usbDevHandle);
}
// read from BulkIn Endpoint
public byte[] read() {
byte[] buffer = new byte[256];
int rv = LibUSB.libUSB.usb_bulk_read(usbDevHandle, endPointBulkIn, buffer, buffer.length, 5000); // timeout:
// 5
// secs
if (rv < 0) {
LOG.debug(String.format("read failed (%d/%d): %d", busNumber, deviceNumber, rv & 0xFF));
throw new RuntimeException("read failed");
}
return buffer;
}
// write to BulkOut Endpoint
public void write(byte[] buffer) {
int rv = LibUSB.libUSB.usb_bulk_write(usbDevHandle, endPointBulkOut, buffer, buffer.length, 5000); // timeout:
// 5
// secs
if (rv < 0) {
LOG.debug(String.format("write failed (%d/%d): %d", busNumber, deviceNumber, rv & 0xFF));
throw new RuntimeException("write failed");
}
LOG.debug(String.format("write. Buffer: %s", bytesToString(buffer)));
}
// send xfrblock command to card
public void transmit(byte[] data) {
byte[] cmd = new byte[10 + data.length];
byte seq = this.sequenceNumber++;
cmd[0] = 0x6F; /* XfrBlock */
cmd[1] = (byte) (data.length & 0xFF);
cmd[2] = (byte) ((data.length << 8) & 0xFF);
cmd[3] = (byte) ((data.length << 16) & 0xFF);
cmd[4] = (byte) ((data.length << 24) & 0xFF); /* dwLength */
cmd[5] = 0; // /* slot number */
cmd[6] = seq; // sequence number
cmd[7] = 0; // Extended block waiting timeout
cmd[8] = cmd[9] = 0; /* expected length */
for (int i = 0; i < data.length; i++) {
cmd[10 + i] = data[i];
}
this.write(cmd);
LOG.debug(String.format("transmit. Answer: %s", bytesToString(cmd)));
}
public byte[] receive() {
byte[] answer;
do {
answer = this.read();
if ((answer[7] & (byte) 0x40) == (byte) 0x40) { // 01 0000 00
// bmCommandStatus
// 1: Error
LOG.debug(String.format("receive failed (%d/%d): %x", busNumber, deviceNumber, answer[8]));
throw new RuntimeException(
String.format("receive failed (%d/%d): %x", busNumber, deviceNumber, answer[8]));
}
if ((answer[7] & (byte) 0x80) == (byte) 0x80)
;
{ // 10 0000 00 bmCommandStatus 1: Time extension requested
LOG.debug(String.format("receive: time extension requested (%d/%d)", busNumber, deviceNumber));
}
} while ((answer[7] & (byte) 0x80) == (byte) 0x80);
LOG.debug(String.format("receive. Answer: %s", bytesToString(answer)));
int datalength = (answer[1] & 0xFF) + ((answer[2] & 0xFF) << 8) + ((answer[3] & 0xFF) << 16)
+ ((answer[4] & 0xFF) << 24);
byte[] data = new byte[datalength];
for (int i = 0; i < datalength; i++) {
data[i] = answer[10 + i];
}
return data;
}
// command to power on the card
// returns ATR
public byte[] powerOn() throws Exception {
byte[] cmd = new byte[10];
byte seq = this.sequenceNumber++;
cmd[0] = 0x62; /* IccPowerOn */
cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
cmd[5] = 0; // slot number */
cmd[6] = seq; // sequence number
cmd[7] = 1; // 5V
cmd[8] = cmd[9] = 0; /* RFU */
this.write(cmd);
return this.receive();
}
public byte[] powerOff() throws Exception {
byte[] cmd = new byte[10];
cmd[0] = 0x63; /* IccPowerOff */
cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
cmd[5] = 0; // ccid_descriptor->bCurrentSlotIndex; /* slot number */
cmd[6] = this.sequenceNumber++; // sequence number
cmd[7] = 1; // 5V
cmd[8] = cmd[9] = 0; /* RFU */
this.write(cmd);
return this.receive();
}
public byte[] getSlotStatus() throws Exception {
byte[] cmd = new byte[10];
byte seq = this.sequenceNumber++;
cmd[0] = 0x65; /* GetSlotStatus */
cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
cmd[5] = 0; // slot number */
cmd[6] = seq; // sequence number
cmd[7] = 0; // 5V
cmd[8] = cmd[9] = 0; /* RFU */
this.write(cmd);
return this.receive();
}
private String bytesToString(byte[] data) {
String dataString = "";
for (int j = 0; j < data.length; j++)
dataString += String.format("%2x ", data[j]);
return dataString;
}
}