diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c index 50b356696..be7f7f9e6 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -6,8 +6,9 @@ #include #include #include - +#include "iso7816_callbacks.h" #include "iso7816_t0_apdu.h" +#include "iso7816_atr.h" typedef enum { EventTypeInput, @@ -33,38 +34,6 @@ typedef enum { CcidTestSubmenuIndexInsertSmartcardReader } SubmenuIndex; -void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { - UNUSED(context); - - iso7816_answer_to_reset(atrBuffer, atrlen); -} - -//dataBlock points to the buffer -//dataBlockLen tells reader how nany bytes should be read -void xfr_datablock_callback( - const uint8_t* dataBlock, - uint32_t dataBlockLen, - uint8_t* responseDataBlock, - uint32_t* responseDataBlockLen, - void* context) { - UNUSED(context); - - struct ISO7816_Command_APDU commandAPDU; - iso7816_read_command_apdu(&commandAPDU, dataBlock, dataBlockLen); - - struct ISO7816_Response_APDU responseAPDU; - //class not supported - responseAPDU.SW1 = 0x6E; - responseAPDU.SW2 = 0x00; - - iso7816_write_response_apdu(&responseAPDU, responseDataBlock, responseDataBlockLen); -} - -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); @@ -127,6 +96,86 @@ void ccid_test_app_free(CcidTestApp* app) { free(app); } +void ccid_icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { + UNUSED(context); + + iso7816_icc_power_on_callback(atrBuffer, atrlen); +} + +void ccid_xfr_datablock_callback( + const uint8_t* pcToReaderDataBlock, + uint32_t pcToReaderDataBlockLen, + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, + void* context) { + UNUSED(context); + + iso7816_xfr_datablock_callback( + pcToReaderDataBlock, pcToReaderDataBlockLen, readerToPcDataBlock, readerToPcDataBlockLen); +} + +static const CcidCallbacks ccid_cb = { + ccid_icc_power_on_callback, + ccid_xfr_datablock_callback, +}; + +void iso7816_answer_to_reset(Iso7816Atr* atr) { + //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00 + atr->TS = 0x3B; + atr->T0 = 0x00; +} + +void iso7816_process_command( + const struct ISO7816_Command_APDU* commandAPDU, + struct ISO7816_Response_APDU* responseAPDU, + const uint8_t* commandApduDataBuffer, + uint8_t commandApduDataBufferLen, + uint8_t* responseApduDataBuffer, + uint8_t* responseApduDataBufferLen) { + //example 1: sends a command with no body, receives a response with no body + //sends APDU 0x01:0x02:0x00:0x00 + //receives SW1=0x90, SW2=0x00 + if(commandAPDU->CLA == 0x01 && commandAPDU->INS == 0x01) { + responseAPDU->SW1 = 0x90; + responseAPDU->SW2 = 0x00; + } + //example 2: sends a command with no body, receives a response with a body with two bytes + //sends APDU 0x01:0x02:0x00:0x00 + //receives 'bc' (0x62, 0x63) SW1=0x80, SW2=0x10 + else if(commandAPDU->CLA == 0x01 && commandAPDU->INS == 0x02) { + responseApduDataBuffer[0] = 0x62; + responseApduDataBuffer[1] = 0x63; + + *responseApduDataBufferLen = 2; + + responseAPDU->SW1 = 0x90; + responseAPDU->SW2 = 0x00; + } + //example 3: ends a command with a body with two bytes, receives a response with a body with two bytes + //sends APDU 0x01:0x03:0x00:0x00:0x02:CA:FE + //receives (0xCA, 0xFE) SW1=0x90, SW2=0x02 + else if( + commandAPDU->CLA == 0x01 && commandAPDU->INS == 0x03 && commandApduDataBufferLen == 2 && + commandAPDU->Lc == 2) { + //echo command body to response body + responseApduDataBuffer[0] = commandApduDataBuffer[0]; + responseApduDataBuffer[1] = commandApduDataBuffer[1]; + + *responseApduDataBufferLen = 2; + + responseAPDU->SW1 = 0x90; + responseAPDU->SW2 = 0x00; + } else { + responseAPDU->SW1 = 0x6A; + responseAPDU->SW2 = 0x00; + } +} + +static const Iso7816Callbacks iso87816_cb = { + iso7816_answer_to_reset, + iso7816_process_command, +}; + int32_t ccid_test_app(void* p) { UNUSED(p); @@ -135,14 +184,16 @@ int32_t ccid_test_app(void* p) { //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; + app->ccid_cfg.vid = 0x076B; + app->ccid_cfg.pid = 0x3A21; FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_unlock(); - furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb); + furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb, NULL); furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); + iso7816_set_callbacks((Iso7816Callbacks*)&iso87816_cb); + //handle button events CcidTestAppEvent event; while(1) { @@ -161,7 +212,9 @@ int32_t ccid_test_app(void* p) { //tear down USB furi_hal_usb_set_config(usb_mode_prev, NULL); - furi_hal_ccid_set_callbacks(NULL); + furi_hal_ccid_set_callbacks(NULL, NULL); + + iso7816_set_callbacks(NULL); //teardown view ccid_test_app_free(app); diff --git a/applications/debug/ccid_test/iso7816_atr.h b/applications/debug/ccid_test/iso7816_atr.h new file mode 100644 index 000000000..050457f8c --- /dev/null +++ b/applications/debug/ccid_test/iso7816_atr.h @@ -0,0 +1,9 @@ +#ifndef _ISO7816_ATR_H_ +#define _ISO7816_ATR_H_ + +typedef struct { + uint8_t TS; + uint8_t T0; +} Iso7816Atr; + +#endif //_ISO7816_ATR_H_ diff --git a/applications/debug/ccid_test/iso7816_callbacks.c b/applications/debug/ccid_test/iso7816_callbacks.c new file mode 100644 index 000000000..1a66fa775 --- /dev/null +++ b/applications/debug/ccid_test/iso7816_callbacks.c @@ -0,0 +1,76 @@ +// transforms low level calls such as XFRCallback or ICC Power on to a structured one +// an application can register these calls and listen for the callbacks defined in Iso7816Callbacks + +#include "iso7816_t0_apdu.h" +#include "iso7816_atr.h" +#include "iso7816_callbacks.h" +#include +#include +#include + +#define ISO7816_RESPONSE_BUFFER_SIZE 255 + +static Iso7816Callbacks* callbacks = NULL; + +void iso7816_set_callbacks(Iso7816Callbacks* cb) { + callbacks = cb; +} + +void iso7816_icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen) { + Iso7816Atr atr; + callbacks->iso7816_answer_to_reset(&atr); + + furi_assert(atr.T0 == 0x00); + + uint8_t AtrBuffer[2] = {atr.TS, atr.T0}; + + *atrlen = 2; + + memcpy(atrBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); +} + +//dataBlock points to the buffer +//dataBlockLen tells reader how nany bytes should be read +void iso7816_xfr_datablock_callback( + const uint8_t* pcToReaderDataBlock, + uint32_t pcToReaderDataBlockLen, + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen) { + struct ISO7816_Response_APDU responseAPDU; + uint8_t responseApduDataBuffer[ISO7816_RESPONSE_BUFFER_SIZE]; + uint8_t responseApduDataBufferLen = 0; + + if(callbacks != NULL) { + struct ISO7816_Command_APDU commandAPDU; + + const uint8_t* commandApduDataBuffer = NULL; + uint8_t commandApduDataBufferLen = 0; + + iso7816_read_command_apdu(&commandAPDU, pcToReaderDataBlock, pcToReaderDataBlockLen); + + if(commandAPDU.Lc > 0) { + commandApduDataBufferLen = commandAPDU.Lc; + commandApduDataBuffer = &pcToReaderDataBlock[5]; + } + + callbacks->iso7816_process_command( + &commandAPDU, + &responseAPDU, + commandApduDataBuffer, + commandApduDataBufferLen, + responseApduDataBuffer, + &responseApduDataBufferLen); + + } else { + //class not supported + responseAPDU.SW1 = 0x6E; + responseAPDU.SW2 = 0x00; + } + + iso7816_write_response_apdu( + &responseAPDU, + readerToPcDataBlock, + readerToPcDataBlockLen, + responseApduDataBuffer, + responseApduDataBufferLen); +} diff --git a/applications/debug/ccid_test/iso7816_callbacks.h b/applications/debug/ccid_test/iso7816_callbacks.h new file mode 100644 index 000000000..3d337d23a --- /dev/null +++ b/applications/debug/ccid_test/iso7816_callbacks.h @@ -0,0 +1,28 @@ +#ifndef _ISO7816_CALLBACKS_H_ +#define _ISO7816_CALLBACKS_H_ + +#include +#include "iso7816_atr.h" +#include "iso7816_t0_apdu.h" + +typedef struct { + void (*iso7816_answer_to_reset)(Iso7816Atr* atr); + void (*iso7816_process_command)( + const struct ISO7816_Command_APDU* command, + struct ISO7816_Response_APDU* response, + const uint8_t* commandApduDataBuffer, + uint8_t commandApduDataBufferLen, + uint8_t* responseApduDataBuffer, + uint8_t* responseApduDataBufferLen); +} Iso7816Callbacks; + +void iso7816_set_callbacks(Iso7816Callbacks* cb); + +void iso7816_icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_xfr_datablock_callback( + const uint8_t* dataBlock, + uint32_t dataBlockLen, + uint8_t* responseDataBlock, + uint32_t* responseDataBlockLen); + +#endif //_ISO7816_CALLBACKS_H_ \ No newline at end of file diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816_t0_apdu.c index 7c690a694..5fb695af1 100644 --- a/applications/debug/ccid_test/iso7816_t0_apdu.c +++ b/applications/debug/ccid_test/iso7816_t0_apdu.c @@ -4,22 +4,14 @@ #include #include "iso7816_t0_apdu.h" -void iso7816_answer_to_reset(uint8_t* dataBuffer, 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(dataBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); -} - +//reads dataBuffer with dataLen size, translate it into a ISO7816_Command_APDU type +//extra data will be pointed to commandDataBuffer void iso7816_read_command_apdu( struct ISO7816_Command_APDU* command, const uint8_t* dataBuffer, uint32_t dataLen) { UNUSED(dataLen); + command->CLA = dataBuffer[0]; command->INS = dataBuffer[1]; command->P1 = dataBuffer[2]; @@ -27,11 +19,30 @@ void iso7816_read_command_apdu( command->Lc = dataBuffer[4]; } +//data buffer countains the whole APU response (response + trailer (SW1+SW2)) 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; + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, + uint8_t* responseDataBuffer, + uint32_t responseDataLen) { + uint32_t responseDataBufferIndex = 0; + + //response body + if(responseDataLen > 0) { + while(responseDataBufferIndex < responseDataLen) { + readerToPcDataBlock[responseDataBufferIndex] = + responseDataBuffer[responseDataBufferIndex]; + responseDataBufferIndex++; + } + } + + //trailer + readerToPcDataBlock[responseDataBufferIndex] = response->SW1; + responseDataBufferIndex++; + + readerToPcDataBlock[responseDataBufferIndex] = response->SW2; + responseDataBufferIndex++; + + *readerToPcDataBlockLen = responseDataBufferIndex; } \ 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 index 5ca13eb60..48a189440 100644 --- a/applications/debug/ccid_test/iso7816_t0_apdu.h +++ b/applications/debug/ccid_test/iso7816_t0_apdu.h @@ -2,6 +2,8 @@ #define _ISO7816_T0_APDU_H_ #include +#include "iso7816_atr.h" +#include "core/common_defines.h" struct ISO7816_Command_APDU { //header @@ -20,13 +22,15 @@ struct ISO7816_Response_APDU { uint8_t SW2; } FURI_PACKED; -void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_answer_to_reset(Iso7816Atr* atr); 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); + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, + uint8_t* responseDataBuffer, + uint32_t responseDataLen); #endif //_ISO7816_T0_APDU_H_ diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 04ceb928c..7eda31675 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.3,, +Version,+,63.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -1157,7 +1157,7 @@ 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_ccid_set_callbacks,void,"CcidCallbacks*, void*" 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" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 9071f63b8..550f07426 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.3,, +Version,+,63.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -1290,7 +1290,7 @@ 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_ccid_set_callbacks,void,"CcidCallbacks*, void*" 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" diff --git a/targets/f7/furi_hal/furi_hal_usb_ccid.c b/targets/f7/furi_hal/furi_hal_usb_ccid.c index d8b43fc6c..f7ab2365d 100644 --- a/targets/f7/furi_hal/furi_hal_usb_ccid.c +++ b/targets/f7/furi_hal/furi_hal_usb_ccid.c @@ -184,6 +184,7 @@ static usbd_device* usb_dev; static bool connected = false; static bool smartcard_inserted = true; static CcidCallbacks* callbacks[CCID_TOTAL_SLOTS] = {NULL}; +static void* cb_ctx[CCID_TOTAL_SLOTS]; static void* ccid_set_string_descr(char* str) { furi_check(str); @@ -330,7 +331,9 @@ void CALLBACK_CCID_IccPowerOn( if(smartcard_inserted) { if(callbacks[CCID_SLOT_INDEX] != NULL) { callbacks[CCID_SLOT_INDEX]->icc_power_on_callback( - responseDataBlock->abData, &responseDataBlock->dwLength, NULL); + responseDataBlock->abData, + &responseDataBlock->dwLength, + cb_ctx[CCID_SLOT_INDEX]); responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; } else { @@ -364,7 +367,7 @@ void CALLBACK_CCID_XfrBlock( receivedXfrBlock->dwLength, responseDataBlock->abData, &responseDataBlock->dwLength, - NULL); + cb_ctx[CCID_SLOT_INDEX]); responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | CCID_ICCSTATUS_PRESENTANDACTIVE; } else { @@ -389,8 +392,9 @@ void furi_hal_ccid_ccid_remove_smartcard(void) { smartcard_inserted = false; } -void furi_hal_ccid_set_callbacks(CcidCallbacks* cb) { +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb, void* context) { callbacks[CCID_SLOT_INDEX] = cb; + cb_ctx[CCID_SLOT_INDEX] = context; } static void ccid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { diff --git a/targets/furi_hal_include/furi_hal_usb_ccid.h b/targets/furi_hal_include/furi_hal_usb_ccid.h index 500bafa3d..cbd0bf092 100644 --- a/targets/furi_hal_include/furi_hal_usb_ccid.h +++ b/targets/furi_hal_include/furi_hal_usb_ccid.h @@ -19,14 +19,14 @@ typedef struct { typedef struct { void (*icc_power_on_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); void (*xfr_datablock_callback)( - const uint8_t* dataBlock, - uint32_t dataBlockLen, - uint8_t* responseDataBlock, - uint32_t* responseDataBlockLen, + const uint8_t* pcToReaderDataBlock, + uint32_t pcToReaderDataBlockLen, + uint8_t* readerToPcDataBlock, + uint32_t* readerToPcDataBlockLen, void* context); } CcidCallbacks; -void furi_hal_ccid_set_callbacks(CcidCallbacks* cb); +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb, void* context); void furi_hal_ccid_ccid_insert_smartcard(void); void furi_hal_ccid_ccid_remove_smartcard(void);