Files
openpgp/pc/ccid.cpp
2019-09-25 20:28:02 +03:00

530 lines
19 KiB
C++

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include "ccid.h"
#include "usbip.h"
/* Device Descriptor */
const USB_DEVICE_DESCRIPTOR dev_dsc=
{
0x12, // Size of this descriptor in bytes
0x01, // DEVICE descriptor type
0x0200, // USB Spec Release Number in BCD format
0x00, // Class Code
0x00, // Subclass code
0x00, // Protocol code
0x10, // Max packet size for EP0, see usb_config.h
0x072f, // Vendor ID (1209)
0x90cc, // Product ID (5070)
0x0100, // Device release number in BCD format
0x01, // Manufacturer string index
0x03, // Product string index
0x04, // Device serial number string index
0x01 // Number of possible configurations
};
const USB_DEVICE_QUALIFIER_DESCRIPTOR dev_qua = { // A high-speed capable device that has different device information for full-speed and high-speed must have a Device Qualifier Descriptor
0x0A, // bLength
0x06, // bDescriptorType
0x0200, // bcdUSB
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
CCID_DATA_PACKET_SIZE, // bMaxPacketSize
0x01, // bNumConfigurations
0x00 // RFU == 0
};
/* Configuration 1 Descriptor */
const CONFIG_CCID configuration_ccid={{
/* Configuration Descriptor */
0x09,//sizeof(USB_CFG_DSC), // Size of this descriptor in bytes
USB_DESCRIPTOR_CONFIGURATION, // CONFIGURATION descriptor type
sizeof(CONFIG_CCID), // Total length of data for this cfg
1, // Number of interfaces in this cfg
1, // Index value of this configuration
0, // Configuration string index
0xC0, // b8 = 1 mandatory, b7=1 self powered
50, // Max power consumption (2X mA). 50 = 100mA
},{
/* Interface Descriptor */
0x09,//sizeof(USB_INTF_DSC), // Size of this descriptor in bytes
USB_DESCRIPTOR_INTERFACE, // INTERFACE descriptor type
0, // Interface Number
0, // Alternate Setting Number
3, // Number of endpoints in this intf
0x0b, // Class code (CCID class)
0x00, // Subclass code
0x00, // Protocol code
0 // Interface string index
},{
/* ICC Descriptor */
54, // bLength:
USB_DESCRIPTOR_ICC, // bDescriptorType: USBDESCR_ICC
0x0100, // bcdCCID: revision 1.1 (of CCID)
0x00, // bMaxSlotIndex: 0
0x01, // bVoltageSupport: 5V-only
0x00000002, // dwProtocols: T=1
0x00000fa0, // dwDefaultClock: 4000
0x00000fa0, // dwMaximumClock: 4000
0x00, // bNumClockSupported: 0x00
0x00002580, // dwDataRate: 9600
0x00002580, // dwMaxDataRate: 9600
0x00, // bNumDataRateSupported: 0x00
0x000000fe, // dwMaxIFSD: 254
0x00000000, // dwSynchProtocols: 0
0x00000000, // dwMechanical: 0
0x0002047a, /* dwFeatures:
* Short and extended APDU level: 0x40000 ----
* Short APDU level : 0x20000 *
* (ICCD?) : 0x00800 ----
* Automatic IFSD : 0x00400 *
* NAD value other than 0x00 : 0x00200
* Can set ICC in clock stop : 0x00100
* Automatic PPS CUR : 0x00080
* Automatic PPS PROP : 0x00040 *
* Auto baud rate change : 0x00020 *
* Auto clock change : 0x00010 *
* Auto voltage selection : 0x00008 *
* Auto activaction of ICC : 0x00004
* Automatic conf. based on ATR : 0x00002 *
*/
0x0000010f, // dwMaxCCIDMessageLength: 271
0xff, // bClassGetResponse: 0xff
0x00, // bClassEnvelope: 0
0x0000, // wLCDLayout: 0
0x00, // bPinSupport: No PIN pad
0x01, // bMaxCCIDBusySlots: 1
},{
/* Endpoint Descriptors */
/* Endpoint IN1 Descriptor */
sizeof(USB_ENDPOINT_DESCRIPTOR),
USB_DESCRIPTOR_ENDPOINT, //Endpoint Descriptor
CCID_IN_EP, //EndpointAddress
0x02, //bmAttributes: Bulk
CCID_DATA_PACKET_SIZE, //size // was 34U!!!
0x00 //Interval
},{
/* Endpoint OUT1 Descriptor */
0x07,/*sizeof(USB_EP_DSC)*/
USB_DESCRIPTOR_ENDPOINT, //Endpoint Descriptor
CCID_OUT_EP, //EndpointAddress
0x02, //bmAttributes: Bulk
CCID_DATA_PACKET_SIZE, //size
0x00 //Interval
},{
/* Endpoint IN2 Descriptor */
0x07,/*sizeof(USB_EP_DSC)*/
USB_DESCRIPTOR_ENDPOINT, //Endpoint Descriptor
CCID_CMD_EP, //EndpointAddress
0x03, //bmAttributes: Interrupt
0x0004, //wMaxPacketSize: 4
0xff //Interval 255ms
}
};
const unsigned char string_0[] = { // available languages descriptor
0x04,
USB_DESCRIPTOR_STRING,
0x09, // 0x0409 (English - United States)
0x04
};
const unsigned char string_1[] = { // Manufacturer
0x10,
USB_DESCRIPTOR_STRING, // bLength, bDscType
'S', 0x00,
'o', 0x00,
'l', 0x00,
'o', 0x00,
'D', 0x00,
'e', 0x00,
'v', 0x00,
};
const unsigned char string_2[] = {
0x12,
USB_DESCRIPTOR_STRING,
'U', 0x00,
'S', 0x00,
'B', 0x00,
' ', 0x00,
'C', 0x00,
'C', 0x00,
'I', 0x00,
'D', 0x00,
};
const unsigned char string_3[] = { // product
0x18,
USB_DESCRIPTOR_STRING,
'V', 0x00,
'i', 0x00,
'r', 0x00,
't', 0x00,
'u', 0x00,
'a', 0x00,
'l', 0x00,
' ', 0x00,
'U', 0x00,
'S', 0x00,
'B', 0x00,
};
const unsigned char string_4[] = { // serial number
0x18,
USB_DESCRIPTOR_STRING,
'1', 0x00,
'2', 0x00,
'3', 0x00,
'4', 0x00,
'5', 0x00,
'6', 0x00,
'7', 0x00,
'8', 0x00,
'9', 0x00,
'A', 0x00,
'B', 0x00,
};
const char *configuration = (const char *)&configuration_ccid;
const USB_INTERFACE_DESCRIPTOR *interfaces[] = {&configuration_ccid.dev_int0};
const unsigned char *strings[] = {string_0, string_1, string_2, string_3, string_4};
#define BSIZE 2048
static uint8_t buffer[BSIZE + 1];
static size_t bsize = 0;
static uint8_t bufferout[BSIZE + 1];
static size_t bsizeout = 0;
bool ICCStateChanged = true;
bool ICCPowered = false;
bool ProcessCCIDTransfer(uint8_t *datain, size_t datainlen, uint8_t *dataout, size_t *dataoutlen);
void handle_data(int sockfd, USBIP_RET_SUBMIT *usb_req, int bl) {
// data channel
if(usb_req->ep == 0x04)
{
#ifdef _DEBUGCLI
printf("##Data (EP4) received \n");
#endif // _DEBUGCLI
if(usb_req->direction == 0) //input
{
#ifdef _DEBUGCLI
printf("EP4 direction=input\n");
#endif // _DEBUGCLI
bsize=recv (sockfd, (char *)buffer, bl, 0);
bool res = ProcessCCIDTransfer(buffer, bsize, bufferout, &bsizeout);
// ACK
send_usb_req(sockfd, usb_req, nullptr, 0, res ? 0 : 1);
}
else
{
#ifdef _DEBUGCLI
printf("EP4 direction=output\n");
#endif // _DEBUGCLI
send_usb_req(sockfd, usb_req, (char *)bufferout, bsizeout, 0);
bsizeout = 0;
}
}
// Interrupt channel
if((usb_req->ep == 0x05)) {
#ifdef _DEBUGCLI
printf("##Interrupt (EP5) received \n");
#endif // _DEBUGCLI
if(usb_req->direction == 0) {
printf("EP5 direction=input. WARNNING!!!!\n");
//not supported
send_usb_req(sockfd, usb_req, nullptr, 0, 0);
//usleep(500);
} else {
#ifdef _DEBUGCLI
printf("EP5 direction=output\n");
#endif // _DEBUGCLI
// b0 - slot0 current state b1 - slot0 changed state
uint8_t state = (ICCPowered ? ICC_PRESENT : ICC_NOT_PRESENT) | (ICCStateChanged ? ICC_CHANGE : 0x00);
uint8_t data[] = {RDR_TO_PC_NOTIFYSLOTCHANGE, state};
ICCStateChanged = false;
send_usb_req(sockfd, usb_req, (char*)data, 2, 0);
}
}
};
typedef struct _LINE_CODING
{
word dwDTERate; //in bits per second
byte bCharFormat;//0-1 stop; 1-1.5 stop; 2-2 stop bits
byte ParityType; //0 none; 1- odd; 2 -even; 3-mark; 4 -space
byte bDataBits; //5,6,7,8 or 16
}LINE_CODING;
LINE_CODING linec;
unsigned short linecs=0;
void handle_unknown_control(int sockfd, StandardDeviceRequest * control_req, USBIP_RET_SUBMIT *usb_req)
{
if(control_req->bmRequestType == 0x21)//Abstract Control Model Requests
{
if(control_req->bRequest == 0x20) //SET_LINE_CODING
{
printf("SET_LINE_CODING\n");
if ((recv (sockfd, (char *) &linec , control_req->wLength, 0)) != control_req->wLength)
{
printf ("receive error : %s \n", strerror (errno));
exit(-1);
};
send_usb_req(sockfd,usb_req,nullptr,0,0);
}
if(control_req->bRequest == 0x21) //GET_LINE_CODING
{
printf("GET_LINE_CODING\n");
send_usb_req(sockfd,usb_req,(char *)&linec,7,0);
}
if(control_req->bRequest == 0x22) //SET_LINE_CONTROL_STATE
{
linecs=control_req->wValue0;
printf("SET_LINE_CONTROL_STATE 0x%02X\n", linecs);
send_usb_req(sockfd,usb_req,nullptr,0,0);
}
if(control_req->bRequest == 0x23) //SEND_BREAK
{
printf("SEND_BREAK\n");
send_usb_req(sockfd,usb_req,nullptr,0,0);
}
}
};
static ex_cb exchange_callback = nullptr;
int usbip_ccid_start(ex_cb cb) {
exchange_callback = cb;
printf("ccid started....\n");
usbip_run(&dev_dsc);
printf("ccid stopped....\n");
return 0;
}
#define ABDATA_SIZE 261
typedef struct {
uint8_t bMessageType; /* Offset = 0*/
uint32_t dwLength; /* Offset = 1, The length field (dwLength) is the length
of the message not including the 10-byte header.*/
uint8_t bSlot; /* Offset = 5*/
uint8_t bSeq; /* Offset = 6*/
uint8_t bSpecific_0; /* Offset = 7*/
uint8_t bSpecific_1; /* Offset = 8*/
uint8_t bSpecific_2; /* Offset = 9*/
uint8_t abData [ABDATA_SIZE]; /* Offset = 10, For reference, the absolute
maximum block size for a TPDU T=0 block is 260 bytes
(5 bytes command; 255 bytes data),
or for a TPDU T=1 block is 259 bytes,
or for a short APDU T=1 block is 261 bytes,
or for an extended APDU T=1 block is 65544 bytes.*/
} __attribute__((packed, aligned(1))) CCID_bulkin_data_t;
typedef struct {
uint8_t bMessageType; /* Offset = 0*/
uint32_t dwLength; /* Offset = 1*/
uint8_t bSlot; /* Offset = 5, Same as Bulk-OUT message */
uint8_t bSeq; /* Offset = 6, Same as Bulk-OUT message */
uint8_t bStatus; /* Offset = 7, Slot status as defined in § 6.2.6*/
uint8_t bError; /* Offset = 8, Slot error as defined in § 6.2.6*/
uint8_t bSpecific; /* Offset = 9*/
uint8_t abData[ABDATA_SIZE]; /* Offset = 10*/
uint16_t u16SizeToSend;
} __attribute__((packed, aligned(1))) CCID_bulkout_data_t;
static const uint8_t atrconst[] = {
0x3B, 0xDA, 0x11, 0xFF, 0x81, 0xB1, 0xFE, 0x55,
0x1F, 0x03, 0x00, 0x31, 0x84, 0x73, 0x80, 0x01,
0x80, 0x00, 0x90, 0x00, 0xE4 };
void CCID_UpdateResponseStatus(CCID_bulkout_data_t *pckout, uint8_t status, uint8_t error) {
pckout->bStatus = status;
pckout->bError = error;
};
void PC_to_RDR_IccPowerOn(CCID_bulkin_data_t *pckin, CCID_bulkout_data_t *pckout) {
uint8_t voltage = pckin->bSpecific_0;
if (voltage >= VOLTS_1_8) {
/* The Voltage specified is out of Spec */
CCID_UpdateResponseStatus(pckout, BM_COMMAND_STATUS_FAILED | BM_ICC_PRESENT_ACTIVE, SLOTERROR_BAD_POWERSELECT);
return;
}
ICCPowered = true;
ICCStateChanged = true;
pckout->dwLength = sizeof(atrconst);
memmove(pckout->abData, atrconst, sizeof(atrconst));
CCID_UpdateResponseStatus(pckout, BM_COMMAND_STATUS_NO_ERROR | BM_ICC_PRESENT_ACTIVE, SLOT_NO_ERROR);
};
void PC_to_RDR_IccPowerOff(CCID_bulkin_data_t *pckin, CCID_bulkout_data_t *pckout) {
ICCPowered = false;
ICCStateChanged = true;
CCID_UpdateResponseStatus(pckout, BM_COMMAND_STATUS_NO_ERROR | BM_ICC_NO_ICC_PRESENT, SLOT_NO_ERROR);
};
void PC_to_RDR_GetSlotStatus(CCID_bulkin_data_t *pckin, CCID_bulkout_data_t *pckout) {
CCID_UpdateResponseStatus(pckout, BM_COMMAND_STATUS_NO_ERROR | BM_ICC_PRESENT_ACTIVE, SLOT_NO_ERROR);
};
void PC_to_RDR_XfrBlock(CCID_bulkin_data_t *pckin, CCID_bulkout_data_t *pckout) {
size_t len = 0;
exchange_callback(pckin->abData, pckin->dwLength, pckout->abData, &len);
pckout->dwLength = len;
CCID_UpdateResponseStatus(pckout, BM_COMMAND_STATUS_NO_ERROR | BM_ICC_PRESENT_ACTIVE, SLOT_NO_ERROR);
};
void RDR_to_PC_NotifySlotChange(void) {
};
void RDR_to_PC_SlotStatus(CCID_bulkout_data_t *pckout) {
pckout->bMessageType = RDR_TO_PC_SLOTSTATUS;
pckout->dwLength = 0;
pckout->bSpecific = 0; /* bClockStatus = 00h Clock running
01h Clock stopped in state L
02h Clock stopped in state H
03h Clock stopped in an unknown state
All other values are RFU. */
};
void RDR_to_PC_DataBlock(CCID_bulkout_data_t *pckout) {
pckout->bMessageType = RDR_TO_PC_DATABLOCK;
pckout->bSpecific = 0; /* bChainParameter */
// if error - no data send
if(pckout->bError != SLOT_NO_ERROR) {
pckout->dwLength = 0;
}
};
bool ProcessCCIDTransfer(uint8_t *datain, size_t datainlen, uint8_t *dataout, size_t *dataoutlen) {
*dataoutlen = 0;
if (datainlen < 10)
return false;
#ifdef _DEBUGCLI
printf("<<<[%ld]: ", datainlen);
for (size_t i = 0; i < datainlen; i++)
printf("%02x ",datain[i]);
printf("\n");
#endif // _DEBUGCLI
CCID_bulkin_data_t *sdatain = (CCID_bulkin_data_t *)datain;
if (sdatain->dwLength + CCID_HEADER_SIZE != datainlen)
return false;
// structures vice versa!
CCID_bulkout_data_t *sdataout = (CCID_bulkout_data_t *)dataout;
memset(dataout, 0x00, CCID_HEADER_SIZE);
sdataout->bSlot = sdatain->bSlot;
sdataout->bSeq = sdatain->bSeq;
switch (sdatain->bMessageType) {
case PC_TO_RDR_ICCPOWERON:
PC_to_RDR_IccPowerOn(sdatain, sdataout);
RDR_to_PC_DataBlock(sdataout);
break;
case PC_TO_RDR_ICCPOWEROFF:
PC_to_RDR_IccPowerOff(sdatain, sdataout);
RDR_to_PC_SlotStatus(sdataout);
break;
case PC_TO_RDR_GETSLOTSTATUS:
PC_to_RDR_GetSlotStatus(sdatain, sdataout);
RDR_to_PC_SlotStatus(sdataout);
break;
case PC_TO_RDR_XFRBLOCK:
PC_to_RDR_XfrBlock(sdatain, sdataout);
RDR_to_PC_DataBlock(sdataout);
break;
/*
case PC_TO_RDR_GETPARAMETERS:
errorCode = PC_to_RDR_GetParameters();
RDR_to_PC_Parameters(errorCode);
break;
case PC_TO_RDR_RESETPARAMETERS:
errorCode = PC_to_RDR_ResetParameters();
RDR_to_PC_Parameters(errorCode);
break;
case PC_TO_RDR_SETPARAMETERS:
errorCode = PC_to_RDR_SetParameters();
RDR_to_PC_Parameters(errorCode);
break;
case PC_TO_RDR_ESCAPE:
errorCode = PC_to_RDR_Escape();
RDR_to_PC_Escape(errorCode);
break;
case PC_TO_RDR_ICCCLOCK:
errorCode = PC_to_RDR_IccClock();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_ABORT:
errorCode = PC_to_RDR_Abort();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_T0APDU:
errorCode = PC_TO_RDR_T0Apdu();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_MECHANICAL:
errorCode = PC_TO_RDR_Mechanical();
RDR_to_PC_SlotStatus(errorCode);
break;
case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY:
errorCode = PC_TO_RDR_SetDataRateAndClockFrequency();
RDR_to_PC_DataRateAndClockFrequency(errorCode);
break;
case PC_TO_RDR_SECURE:
errorCode = PC_TO_RDR_Secure();
RDR_to_PC_DataBlock(errorCode);
break;
*/
default:
CCID_UpdateResponseStatus(sdataout, BM_COMMAND_STATUS_FAILED | BM_ICC_PRESENT_ACTIVE, SLOTERROR_CMD_NOT_SUPPORTED);
RDR_to_PC_SlotStatus(sdataout);
break;
};
*dataoutlen = CCID_HEADER_SIZE + sdataout->dwLength;
#ifdef _DEBUGCLI
printf(">>>[%ld]: ", *dataoutlen);
for (size_t i = 0; i < *dataoutlen; i++)
printf("%02x ",dataout[i]);
printf("\n");
#endif // _DEBUGCLI
return true;
}