From a089aeb2bd60d4c39905a21af87bfb7603bb4e9d Mon Sep 17 00:00:00 2001 From: Filipe Paz Rodrigues Date: Thu, 21 Sep 2023 02:09:00 -0700 Subject: [PATCH] Add Initial CCID support (#3048) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Initial CCID support * Sync api symbols * Format sources Co-authored-by: あく --- applications/debug/ccid_test/application.fam | 16 + applications/debug/ccid_test/ccid_test_app.c | 159 ++++++ .../debug/ccid_test/iso7816_t0_apdu.c | 36 ++ .../debug/ccid_test/iso7816_t0_apdu.h | 32 ++ firmware/targets/f18/api_symbols.csv | 8 +- firmware/targets/f7/api_symbols.csv | 8 +- .../targets/f7/furi_hal/furi_hal_usb_ccid.c | 498 ++++++++++++++++++ firmware/targets/furi_hal_include/furi_hal.h | 1 + .../targets/furi_hal_include/furi_hal_usb.h | 1 + .../furi_hal_include/furi_hal_usb_ccid.h | 31 ++ lib/libusb_stm32 | 2 +- 11 files changed, 789 insertions(+), 3 deletions(-) create mode 100644 applications/debug/ccid_test/application.fam create mode 100644 applications/debug/ccid_test/ccid_test_app.c create mode 100644 applications/debug/ccid_test/iso7816_t0_apdu.c create mode 100644 applications/debug/ccid_test/iso7816_t0_apdu.h create mode 100644 firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c create mode 100644 firmware/targets/furi_hal_include/furi_hal_usb_ccid.h diff --git a/applications/debug/ccid_test/application.fam b/applications/debug/ccid_test/application.fam new file mode 100644 index 00000000..e0cbc8d8 --- /dev/null +++ b/applications/debug/ccid_test/application.fam @@ -0,0 +1,16 @@ +App( + appid="ccid_test", + name="CCID Debug", + apptype=FlipperAppType.DEBUG, + entry_point="ccid_test_app", + cdefines=["CCID_TEST"], + requires=[ + "gui", + ], + provides=[ + "ccid_test", + ], + stack_size=1 * 1024, + order=120, + fap_category="Debug", +) diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c new file mode 100644 index 00000000..a2f936d7 --- /dev/null +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -0,0 +1,159 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "iso7816_t0_apdu.h" + +typedef enum { + EventTypeInput, +} EventType; + +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriMessageQueue* event_queue; + FuriHalUsbCcidConfig ccid_cfg; +} CcidTestApp; + +typedef struct { + union { + InputEvent input; + }; + EventType type; +} CcidTestAppEvent; + +typedef enum { + CcidTestSubmenuIndexInsertSmartcard, + CcidTestSubmenuIndexRemoveSmartcard, + CcidTestSubmenuIndexInsertSmartcardReader +} SubmenuIndex; + +void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { + UNUSED(context); + + iso7816_answer_to_reset(atrBuffer, atrlen); +} + +void xfr_datablock_callback(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context) { + UNUSED(context); + + struct ISO7816_Response_APDU responseAPDU; + //class not supported + responseAPDU.SW1 = 0x6E; + responseAPDU.SW2 = 0x00; + + iso7816_write_response_apdu(&responseAPDU, dataBlock, dataBlockLen); +} + +static const CcidCallbacks ccid_cb = { + icc_power_on_callback, + xfr_datablock_callback, +}; + +static void ccid_test_app_render_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "CCID Test App"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void ccid_test_app__input_callback(InputEvent* input_event, void* ctx) { + FuriMessageQueue* event_queue = ctx; + + CcidTestAppEvent event; + event.type = EventTypeInput; + event.input = *input_event; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +uint32_t ccid_test_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CcidTestApp* ccid_test_app_alloc() { + CcidTestApp* app = malloc(sizeof(CcidTestApp)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + //viewport + app->view_port = view_port_alloc(); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + view_port_draw_callback_set(app->view_port, ccid_test_app_render_callback, NULL); + + //message queue + app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent)); + furi_check(app->event_queue); + view_port_input_callback_set(app->view_port, ccid_test_app__input_callback, app->event_queue); + + return app; +} + +void ccid_test_app_free(CcidTestApp* app) { + furi_assert(app); + + //message queue + furi_message_queue_free(app->event_queue); + + //view port + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + + // Close gui record + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t ccid_test_app(void* p) { + UNUSED(p); + + //setup view + CcidTestApp* app = ccid_test_app_alloc(); + + //setup CCID USB + // On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist + app->ccid_cfg.vid = 0x1234; + app->ccid_cfg.pid = 0x5678; + + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb); + furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); + + //handle button events + CcidTestAppEvent event; + while(1) { + FuriStatus event_status = + furi_message_queue_get(app->event_queue, &event, FuriWaitForever); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { + break; + } + } + } + view_port_update(app->view_port); + } + + //tear down USB + furi_hal_usb_set_config(usb_mode_prev, NULL); + furi_hal_ccid_set_callbacks(NULL); + + //teardown view + ccid_test_app_free(app); + return 0; +} diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816_t0_apdu.c new file mode 100644 index 00000000..29f5f7a8 --- /dev/null +++ b/applications/debug/ccid_test/iso7816_t0_apdu.c @@ -0,0 +1,36 @@ +/* Implements rudimentary iso7816-3 support for APDU (T=0) */ +#include +#include +#include +#include "iso7816_t0_apdu.h" + +void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen) { + //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00 + uint8_t AtrBuffer[2] = { + 0x3B, //TS (direct convention) + 0x00 // T0 (Y(1): b0000, K: 0 (historical bytes)) + }; + *atrlen = 2; + + memcpy(atrBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); +} + +void iso7816_read_command_apdu( + struct ISO7816_Command_APDU* command, + const uint8_t* dataBuffer, + uint32_t dataLen) { + furi_assert(dataLen <= 4); + command->CLA = dataBuffer[0]; + command->INS = dataBuffer[1]; + command->P1 = dataBuffer[2]; + command->P2 = dataBuffer[3]; +} + +void iso7816_write_response_apdu( + const struct ISO7816_Response_APDU* response, + uint8_t* dataBuffer, + uint32_t* dataLen) { + dataBuffer[0] = response->SW1; + dataBuffer[1] = response->SW2; + *dataLen = 2; +} \ No newline at end of file diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816_t0_apdu.h new file mode 100644 index 00000000..8a8c99f8 --- /dev/null +++ b/applications/debug/ccid_test/iso7816_t0_apdu.h @@ -0,0 +1,32 @@ +#ifndef _ISO7816_T0_APDU_H_ +#define _ISO7816_T0_APDU_H_ + +#include + +struct ISO7816_Command_APDU { + //header + uint8_t CLA; + uint32_t INS; + uint8_t P1; + uint8_t P2; + + //body + uint8_t Nc; + uint8_t Ne; +} __attribute__((packed)); + +struct ISO7816_Response_APDU { + uint8_t SW1; + uint32_t SW2; +} __attribute__((packed)); + +void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_read_command_apdu( + struct ISO7816_Command_APDU* command, + const uint8_t* dataBuffer, + uint32_t dataLen); +void iso7816_write_response_apdu( + const struct ISO7816_Response_APDU* response, + uint8_t* dataBuffer, + uint32_t* dataLen); +#endif //_ISO7816_T0_APDU_H_ diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index f4e990be..338697ad 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,39.0,, +Version,+,39.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -76,6 +76,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_ccid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, @@ -105,6 +106,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, Header,-,lib/libusb_stm32/inc/stm32_compat.h,, Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_ccid.h,, Header,+,lib/libusb_stm32/inc/usb_cdc.h,, Header,+,lib/libusb_stm32/inc/usb_cdca.h,, Header,+,lib/libusb_stm32/inc/usb_cdce.h,, @@ -1008,6 +1010,9 @@ Function,+,furi_hal_bus_enable,void,FuriHalBus Function,+,furi_hal_bus_init_early,void, Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus Function,+,furi_hal_bus_reset,void,FuriHalBus +Function,+,furi_hal_ccid_ccid_insert_smartcard,void, +Function,+,furi_hal_ccid_ccid_remove_smartcard,void, +Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -2692,6 +2697,7 @@ Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, Variable,-,suboptarg,char*, +Variable,+,usb_ccid,FuriHalUsbInterface, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d37d5f33..f3f42d0f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,39.0,, +Version,+,39.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -82,6 +82,7 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_ccid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, @@ -123,6 +124,7 @@ Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, Header,-,lib/libusb_stm32/inc/stm32_compat.h,, Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_ccid.h,, Header,+,lib/libusb_stm32/inc/usb_cdc.h,, Header,+,lib/libusb_stm32/inc/usb_cdca.h,, Header,+,lib/libusb_stm32/inc/usb_cdce.h,, @@ -1079,6 +1081,9 @@ Function,+,furi_hal_bus_enable,void,FuriHalBus Function,+,furi_hal_bus_init_early,void, Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus Function,+,furi_hal_bus_reset,void,FuriHalBus +Function,+,furi_hal_ccid_ccid_insert_smartcard,void, +Function,+,furi_hal_ccid_ccid_remove_smartcard,void, +Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -3479,6 +3484,7 @@ Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,suboptarg,char*, +Variable,+,usb_ccid,FuriHalUsbInterface, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c new file mode 100644 index 00000000..559713d0 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_ccid.c @@ -0,0 +1,498 @@ +#include +#include +#include +#include +#include + +#include "usb.h" +#include "usb_ccid.h" + +static const uint8_t USB_DEVICE_NO_CLASS = 0x0; +static const uint8_t USB_DEVICE_NO_SUBCLASS = 0x0; +static const uint8_t USB_DEVICE_NO_PROTOCOL = 0x0; + +#define FIXED_CONTROL_ENDPOINT_SIZE 8 +#define IF_NUM_MAX 1 + +#define CCID_VID_DEFAULT 0x1234 +#define CCID_PID_DEFAULT 0xABCD +#define CCID_TOTAL_SLOTS 1 +#define CCID_SLOT_INDEX 0 + +#define CCID_DATABLOCK_SIZE 256 + +#define ENDPOINT_DIR_IN 0x80 +#define ENDPOINT_DIR_OUT 0x00 + +#define INTERFACE_ID_CCID 0 + +#define CCID_IN_EPADDR (ENDPOINT_DIR_IN | 2) + +/** Endpoint address of the CCID data OUT endpoint, for host-to-device data transfers. */ +#define CCID_OUT_EPADDR (ENDPOINT_DIR_OUT | 1) + +/** Endpoint size in bytes of the CCID data being sent between IN and OUT endpoints. */ +#define CCID_EPSIZE 64 + +struct CcidIntfDescriptor { + struct usb_interface_descriptor ccid; + struct usb_ccid_descriptor ccid_desc; + struct usb_endpoint_descriptor ccid_bulk_in; + struct usb_endpoint_descriptor ccid_bulk_out; +} __attribute__((packed)); + +struct CcidConfigDescriptor { + struct usb_config_descriptor config; + struct CcidIntfDescriptor intf_0; +} __attribute__((packed)); + +enum CCID_Features_Auto_t { + CCID_Features_Auto_None = 0x0, + CCID_Features_Auto_ParameterConfiguration = 0x2, + CCID_Features_Auto_ICCActivation = 0x4, + CCID_Features_Auto_VoltageSelection = 0x8, + + CCID_Features_Auto_ICCClockFrequencyChange = 0x10, + CCID_Features_Auto_ICCBaudRateChange = 0x20, + CCID_Features_Auto_ParameterNegotiation = 0x40, + CCID_Features_Auto_PPS = 0x80, +}; + +enum CCID_Features_ExchangeLevel_t { + CCID_Features_ExchangeLevel_TPDU = 0x00010000, + CCID_Features_ExchangeLevel_ShortAPDU = 0x00020000, + CCID_Features_ExchangeLevel_ShortExtendedAPDU = 0x00040000 +}; + +/* Device descriptor */ +static struct usb_device_descriptor ccid_device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_DEVICE_NO_CLASS, + .bDeviceSubClass = USB_DEVICE_NO_SUBCLASS, + .bDeviceProtocol = USB_DEVICE_NO_PROTOCOL, + .bMaxPacketSize0 = FIXED_CONTROL_ENDPOINT_SIZE, + .idVendor = CCID_VID_DEFAULT, + .idProduct = CCID_PID_DEFAULT, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = UsbDevManuf, + .iProduct = UsbDevProduct, + .iSerialNumber = UsbDevSerial, + .bNumConfigurations = 1, +}; + +/* Device configuration descriptor*/ +static const struct CcidConfigDescriptor ccid_cfg_desc = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct CcidConfigDescriptor), + .bNumInterfaces = 1, + + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf_0 = + { + .ccid = + {.bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + + .bInterfaceNumber = INTERFACE_ID_CCID, + .bAlternateSetting = 0x00, + .bNumEndpoints = 2, + + .bInterfaceClass = USB_CLASS_CCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + + .iInterface = NO_DESCRIPTOR + + }, + .ccid_desc = + {.bLength = sizeof(struct usb_ccid_descriptor), + .bDescriptorType = USB_DTYPE_CCID_FUNCTIONAL, + .bcdCCID = CCID_CURRENT_SPEC_RELEASE_NUMBER, + .bMaxSlotIndex = 0x00, + .bVoltageSupport = CCID_VOLTAGESUPPORT_5V, + .dwProtocols = 0x01, //T0 + .dwDefaultClock = 16000, //16MHz + .dwMaximumClock = 16000, //16MHz + .bNumClockSupported = 0, + .dwDataRate = 307200, + .dwMaxDataRate = 307200, + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 2038, + .dwSynchProtocols = 0, + .dwMechanical = 0, + .dwFeatures = CCID_Features_ExchangeLevel_ShortAPDU | + CCID_Features_Auto_ParameterConfiguration | + CCID_Features_Auto_ICCActivation | + CCID_Features_Auto_VoltageSelection, + .dwMaxCCIDMessageLength = 0x0c00, + .bClassGetResponse = 0xff, + .bClassEnvelope = 0xff, + .wLcdLayout = 0, + .bPINSupport = 0, + .bMaxCCIDBusySlots = 1}, + .ccid_bulk_in = + {.bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CCID_IN_EPADDR, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CCID_EPSIZE, + .bInterval = 0x05 + + }, + .ccid_bulk_out = + {.bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CCID_OUT_EPADDR, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CCID_EPSIZE, + .bInterval = 0x05}, + }, +}; + +static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void ccid_deinit(usbd_device* dev); +static void ccid_on_wakeup(usbd_device* dev); +static void ccid_on_suspend(usbd_device* dev); + +FuriHalUsbInterface usb_ccid = { + .init = ccid_init, + .deinit = ccid_deinit, + .wakeup = ccid_on_wakeup, + .suspend = ccid_on_suspend, + + .dev_descr = (struct usb_device_descriptor*)&ccid_device_desc, + + .str_manuf_descr = NULL, + .str_prod_descr = NULL, + .str_serial_descr = NULL, + + .cfg_descr = (void*)&ccid_cfg_desc, +}; + +static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); +static usbd_device* usb_dev; +static bool connected = false; +static bool smartcard_inserted = true; +static CcidCallbacks* callbacks[CCID_TOTAL_SLOTS] = {NULL}; + +static void* ccid_set_string_descr(char* str) { + furi_assert(str); + + size_t len = strlen(str); + struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); + dev_str_desc->bLength = len * 2 + 2; + dev_str_desc->bDescriptorType = USB_DTYPE_STRING; + for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; + + return dev_str_desc; +} + +static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + + FuriHalUsbCcidConfig* cfg = (FuriHalUsbCcidConfig*)ctx; + + usb_dev = dev; + + usb_ccid.dev_descr->iManufacturer = 0; + usb_ccid.dev_descr->iProduct = 0; + usb_ccid.str_manuf_descr = NULL; + usb_ccid.str_prod_descr = NULL; + usb_ccid.dev_descr->idVendor = CCID_VID_DEFAULT; + usb_ccid.dev_descr->idProduct = CCID_PID_DEFAULT; + + if(cfg != NULL) { + usb_ccid.dev_descr->idVendor = cfg->vid; + usb_ccid.dev_descr->idProduct = cfg->pid; + + if(cfg->manuf[0] != '\0') { + usb_ccid.str_manuf_descr = ccid_set_string_descr(cfg->manuf); + usb_ccid.dev_descr->iManufacturer = UsbDevManuf; + } + + if(cfg->product[0] != '\0') { + usb_ccid.str_prod_descr = ccid_set_string_descr(cfg->product); + usb_ccid.dev_descr->iProduct = UsbDevProduct; + } + } + + usbd_reg_config(dev, ccid_ep_config); + usbd_reg_control(dev, ccid_control); + + usbd_connect(dev, true); +} + +static void ccid_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + free(usb_ccid.str_prod_descr); + free(usb_ccid.str_serial_descr); +} + +static void ccid_on_wakeup(usbd_device* dev) { + UNUSED(dev); + connected = true; +} + +static void ccid_on_suspend(usbd_device* dev) { + UNUSED(dev); + connected = false; +} + +struct ccid_bulk_message_header { + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; +} __attribute__((packed)); + +static struct rdr_to_pc_slot_status responseSlotStatus; +static struct rdr_to_pc_data_block responseDataBlock; +static struct rdr_to_pc_parameters_t0 responseParameters; +uint8_t SendDataBlock[CCID_DATABLOCK_SIZE]; + +uint8_t CALLBACK_CCID_GetSlotStatus(uint8_t slot, uint8_t* error) { + if(slot == CCID_SLOT_INDEX) { + *error = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + *error = CCID_ERROR_SLOTNOTFOUND; + return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +uint8_t + CALLBACK_CCID_IccPowerOn(uint8_t slot, uint8_t* atrBuffer, uint32_t* atrlen, uint8_t* error) { + if(slot == CCID_SLOT_INDEX) { + *error = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + if(callbacks[CCID_SLOT_INDEX] != NULL) { + callbacks[CCID_SLOT_INDEX]->icc_power_on_callback(atrBuffer, atrlen, NULL); + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDINACTIVE; + } + + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + *error = CCID_ERROR_SLOTNOTFOUND; + return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +uint8_t CALLBACK_CCID_XfrBlock( + uint8_t slot, + uint8_t* dataBlock, + uint32_t* dataBlockLen, + uint8_t* error) { + if(slot == CCID_SLOT_INDEX) { + *error = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + if(callbacks[CCID_SLOT_INDEX] != NULL) { + callbacks[CCID_SLOT_INDEX]->xfr_datablock_callback(dataBlock, dataBlockLen, NULL); + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDINACTIVE; + } + + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + return CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + *error = CCID_ERROR_SLOTNOTFOUND; + return CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +void furi_hal_ccid_ccid_insert_smartcard() { + smartcard_inserted = true; +} + +void furi_hal_ccid_ccid_remove_smartcard() { + smartcard_inserted = false; +} + +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb) { + callbacks[CCID_SLOT_INDEX] = cb; +} + +static void ccid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); +} + +static void ccid_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + + if(event == usbd_evt_eprx) { + if(connected == false) return; + + struct ccid_bulk_message_header message; + usbd_ep_read(usb_dev, ep, &message, sizeof(message)); + + uint8_t Status; + uint8_t Error = CCID_ERROR_NOERROR; + + uint32_t dataBlockLen = 0; + uint8_t* dataBlockBuffer = NULL; + + if(message.bMessageType == PC_TO_RDR_GETSLOTSTATUS) { + responseSlotStatus.bMessageType = RDR_TO_PC_SLOTSTATUS; + responseSlotStatus.dwLength = 0; + responseSlotStatus.bSlot = message.bSlot; + responseSlotStatus.bSeq = message.bSeq; + + responseSlotStatus.bClockStatus = 0; + + Status = CALLBACK_CCID_GetSlotStatus(message.bSlot, &Error); + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, &responseSlotStatus, sizeof(responseSlotStatus)); + } else if(message.bMessageType == PC_TO_RDR_ICCPOWERON) { + responseDataBlock.bMessageType = RDR_TO_PC_DATABLOCK; + responseDataBlock.bSlot = message.bSlot; + responseDataBlock.bSeq = message.bSeq; + responseDataBlock.bChainParameter = 0; + + dataBlockLen = 0; + dataBlockBuffer = (uint8_t*)SendDataBlock; + Status = CALLBACK_CCID_IccPowerOn( + message.bSlot, (uint8_t*)dataBlockBuffer, &dataBlockLen, &Error); + + furi_assert(dataBlockLen < CCID_DATABLOCK_SIZE); + responseDataBlock.dwLength = dataBlockLen; + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + memcpy(responseDataBlock.abData, SendDataBlock, dataBlockLen); + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + &responseDataBlock, + sizeof(struct rdr_to_pc_data_block) + (sizeof(uint8_t) * dataBlockLen)); + } else if(message.bMessageType == PC_TO_RDR_ICCPOWEROFF) { + responseSlotStatus.bMessageType = RDR_TO_PC_SLOTSTATUS; + responseSlotStatus.dwLength = 0; + responseSlotStatus.bSlot = message.bSlot; + responseSlotStatus.bSeq = message.bSeq; + + responseSlotStatus.bClockStatus = 0; + + uint8_t Status; + uint8_t Error = CCID_ERROR_NOERROR; + Status = CALLBACK_CCID_GetSlotStatus(message.bSlot, &Error); + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, &responseSlotStatus, sizeof(responseSlotStatus)); + } else if(message.bMessageType == PC_TO_RDR_SETPARAMETERS) { + responseParameters.bMessageType = RDR_TO_PC_PARAMETERS; + responseParameters.bSlot = message.bSlot; + responseParameters.bSeq = message.bSeq; + responseParameters.bProtocolNum = 0; //T0 + + uint8_t Status = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR; + uint8_t Error = CCID_ERROR_NOERROR; + + responseParameters.bStatus = Status; + responseParameters.bError = Error; + + responseParameters.dwLength = sizeof(struct rdr_to_pc_parameters_t0); + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, &responseParameters, sizeof(responseParameters)); + } else if(message.bMessageType == PC_TO_RDR_XFRBLOCK) { + responseDataBlock.bMessageType = RDR_TO_PC_DATABLOCK; + responseDataBlock.bSlot = message.bSlot; + responseDataBlock.bSeq = message.bSeq; + responseDataBlock.bChainParameter = 0; + + dataBlockLen = 0; + dataBlockBuffer = (uint8_t*)SendDataBlock; + Status = CALLBACK_CCID_XfrBlock( + message.bSlot, (uint8_t*)dataBlockBuffer, &dataBlockLen, &Error); + + furi_assert(dataBlockLen < CCID_DATABLOCK_SIZE); + responseDataBlock.dwLength = dataBlockLen; + + responseSlotStatus.bStatus = Status; + responseSlotStatus.bError = Error; + + memcpy(responseDataBlock.abData, SendDataBlock, dataBlockLen); + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + &responseDataBlock, + sizeof(struct rdr_to_pc_data_block) + (sizeof(uint8_t) * dataBlockLen)); + } + } +} + +/* Configure endpoints */ +static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: + /* deconfiguring device */ + usbd_ep_deconfig(dev, CCID_IN_EPADDR); + usbd_ep_deconfig(dev, CCID_OUT_EPADDR); + usbd_reg_endpoint(dev, CCID_IN_EPADDR, 0); + usbd_reg_endpoint(dev, CCID_OUT_EPADDR, 0); + return usbd_ack; + case 1: + /* configuring device */ + usbd_ep_config(dev, CCID_IN_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE); + usbd_ep_config(dev, CCID_OUT_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE); + usbd_reg_endpoint(dev, CCID_IN_EPADDR, ccid_rx_ep_callback); + usbd_reg_endpoint(dev, CCID_OUT_EPADDR, ccid_tx_ep_callback); + return usbd_ack; + default: + return usbd_fail; + } +} + +/* Control requests handler */ +static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + /* CDC control requests */ + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + (req->wIndex == 0 || req->wIndex == 2)) { + switch(req->bRequest) { + case CCID_ABORT: + return usbd_fail; + case CCID_GET_CLOCK_FREQUENCIES: + dev->status.data_ptr = (void*)&(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock); + dev->status.data_count = sizeof(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock); + return usbd_ack; + default: + return usbd_fail; + } + } + return usbd_fail; +} \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 9341dcce..e6fd9eb1 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -35,6 +35,7 @@ struct STOP_EXTERNING_ME {}; #include #include #include +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/firmware/targets/furi_hal_include/furi_hal_usb.h index 8b49f6c6..a9879795 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb.h @@ -28,6 +28,7 @@ extern FuriHalUsbInterface usb_cdc_single; extern FuriHalUsbInterface usb_cdc_dual; extern FuriHalUsbInterface usb_hid; extern FuriHalUsbInterface usb_hid_u2f; +extern FuriHalUsbInterface usb_ccid; typedef enum { FuriHalUsbStateEventReset, diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_ccid.h b/firmware/targets/furi_hal_include/furi_hal_usb_ccid.h new file mode 100644 index 00000000..e3ee0dfc --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_usb_ccid.h @@ -0,0 +1,31 @@ +#pragma once +#include "hid_usage_desktop.h" +#include "hid_usage_button.h" +#include "hid_usage_keyboard.h" +#include "hid_usage_consumer.h" +#include "hid_usage_led.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t vid; + uint16_t pid; + char manuf[32]; + char product[32]; +} FuriHalUsbCcidConfig; + +typedef struct { + void (*icc_power_on_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); + void (*xfr_datablock_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); +} CcidCallbacks; + +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb); + +void furi_hal_ccid_ccid_insert_smartcard(); +void furi_hal_ccid_ccid_remove_smartcard(); + +#ifdef __cplusplus +} +#endif diff --git a/lib/libusb_stm32 b/lib/libusb_stm32 index 9168e2a3..6ca28575 160000 --- a/lib/libusb_stm32 +++ b/lib/libusb_stm32 @@ -1 +1 @@ -Subproject commit 9168e2a31db946326fb84016a74ea2ab5bf87f54 +Subproject commit 6ca2857519f996244f7b324dd227fdf0a075fffb