Add initial ISO7816 support (#3681)

* Add initial ISO7816 support
* Format sources and sync API Symbols version
* Debug: change VID/PID in ccid test app to opensc detectable generic one

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Filipe Paz Rodrigues 2024-06-01 11:45:27 -05:00 committed by GitHub
parent c93d164785
commit 0d4ead8fbd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 253 additions and 68 deletions

View File

@ -6,8 +6,9 @@
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <gui/gui.h>
#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);

View File

@ -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_

View File

@ -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 <stdint.h>
#include <stddef.h>
#include <furi.h>
#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);
}

View File

@ -0,0 +1,28 @@
#ifndef _ISO7816_CALLBACKS_H_
#define _ISO7816_CALLBACKS_H_
#include <stdint.h>
#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_

View File

@ -4,22 +4,14 @@
#include <furi.h>
#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;
}

View File

@ -2,6 +2,8 @@
#define _ISO7816_T0_APDU_H_
#include <stdint.h>
#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_

View File

@ -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"

1 entry status name type params
2 Version + 62.3 63.0
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h
1157 Function + furi_hal_bus_reset void FuriHalBus
1158 Function + furi_hal_ccid_ccid_insert_smartcard void
1159 Function + furi_hal_ccid_ccid_remove_smartcard void
1160 Function + furi_hal_ccid_set_callbacks void CcidCallbacks* CcidCallbacks*, void*
1161 Function + furi_hal_cdc_get_ctrl_line_state uint8_t uint8_t
1162 Function + furi_hal_cdc_get_port_settings usb_cdc_line_coding* uint8_t
1163 Function + furi_hal_cdc_receive int32_t uint8_t, uint8_t*, uint16_t

View File

@ -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,,
@ -1256,7 +1256,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"

1 entry status name type params
2 Version + 62.3 63.0
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
1256 Function + furi_hal_bus_reset void FuriHalBus
1257 Function + furi_hal_ccid_ccid_insert_smartcard void
1258 Function + furi_hal_ccid_ccid_remove_smartcard void
1259 Function + furi_hal_ccid_set_callbacks void CcidCallbacks* CcidCallbacks*, void*
1260 Function + furi_hal_cdc_get_ctrl_line_state uint8_t uint8_t
1261 Function + furi_hal_cdc_get_port_settings usb_cdc_line_coding* uint8_t
1262 Function + furi_hal_cdc_receive int32_t uint8_t, uint8_t*, uint16_t

View File

@ -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) {

View File

@ -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);