-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathccid.cpp
537 lines (470 loc) · 18.9 KB
/
ccid.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
/*
Copyright 2019 SoloKeys Developers
Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
http://opensource.org/licenses/MIT>, at your option. This file may not be
copied, modified, or distributed except according to those terms.
*/
#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;
}