mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'ofw/dev' into mntm-dev
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
- Seader: Remove some optional asn1 fields (by @bettse)
|
||||
- NFC Playlist: Fix extension check and error messages (by @acegoal07)
|
||||
- Various app fixes for `-Wundef` option (by @Willy-JL)
|
||||
- OFW: NFC: Refactor detected protocols list (by @Astrrra)
|
||||
- OFW: CCID: App refactor (by @kidbomb)
|
||||
- OFW: Furi: Update string documentation (by @skotopes)
|
||||
- OFW: FBT: Toolchain v39 (by @hedger)
|
||||
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
#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"
|
||||
#include "iso7816_response.h"
|
||||
|
||||
#include "iso7816/iso7816_handler.h"
|
||||
#include "iso7816/iso7816_t0_apdu.h"
|
||||
#include "iso7816/iso7816_atr.h"
|
||||
#include "iso7816/iso7816_response.h"
|
||||
|
||||
#include "ccid_test_app_commands.h"
|
||||
|
||||
typedef enum {
|
||||
EventTypeInput,
|
||||
@@ -20,6 +23,7 @@ typedef struct {
|
||||
ViewPort* view_port;
|
||||
FuriMessageQueue* event_queue;
|
||||
FuriHalUsbCcidConfig ccid_cfg;
|
||||
Iso7816Handler* iso7816_handler;
|
||||
} CcidTestApp;
|
||||
|
||||
typedef struct {
|
||||
@@ -63,6 +67,15 @@ uint32_t ccid_test_exit(void* context) {
|
||||
CcidTestApp* ccid_test_app_alloc(void) {
|
||||
CcidTestApp* app = malloc(sizeof(CcidTestApp));
|
||||
|
||||
//setup CCID USB
|
||||
// On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist
|
||||
app->ccid_cfg.vid = 0x076B;
|
||||
app->ccid_cfg.pid = 0x3A21;
|
||||
|
||||
app->iso7816_handler = iso7816_handler_alloc();
|
||||
app->iso7816_handler->iso7816_answer_to_reset = iso7816_answer_to_reset;
|
||||
app->iso7816_handler->iso7816_process_command = iso7816_process_command;
|
||||
|
||||
// Gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
@@ -92,174 +105,26 @@ void ccid_test_app_free(CcidTestApp* app) {
|
||||
furi_record_close(RECORD_GUI);
|
||||
app->gui = NULL;
|
||||
|
||||
free(app->iso7816_handler);
|
||||
|
||||
// Free rest
|
||||
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,
|
||||
};
|
||||
|
||||
//Instruction 1: returns an OK response unconditionally
|
||||
//APDU example: 0x01:0x01:0x00:0x00
|
||||
//response: SW1=0x90, SW2=0x00
|
||||
void handle_instruction_01(ISO7816_Response_APDU* responseAPDU) {
|
||||
responseAPDU->DataLen = 0;
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_OK);
|
||||
}
|
||||
|
||||
//Instruction 2: expect command with no body, replies wit with a body with two bytes
|
||||
//APDU example: 0x01:0x02:0x00:0x00:0x02
|
||||
//response: 'bc' (0x62, 0x63) SW1=0x90, SW2=0x00
|
||||
void handle_instruction_02(
|
||||
uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint16_t lc,
|
||||
uint16_t le,
|
||||
ISO7816_Response_APDU* responseAPDU) {
|
||||
if(p1 == 0 && p2 == 0 && lc == 0 && le >= 2) {
|
||||
responseAPDU->Data[0] = 0x62;
|
||||
responseAPDU->Data[1] = 0x63;
|
||||
|
||||
responseAPDU->DataLen = 2;
|
||||
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_OK);
|
||||
} else if(p1 != 0 || p2 != 0) {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);
|
||||
} else {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
//Instruction 3: sends a command with a body with two bytes, receives a response with no bytes
|
||||
//APDU example: 0x01:0x03:0x00:0x00:0x02:CA:FE
|
||||
//response SW1=0x90, SW2=0x00
|
||||
void handle_instruction_03(
|
||||
uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint16_t lc,
|
||||
ISO7816_Response_APDU* responseAPDU) {
|
||||
if(p1 == 0 && p2 == 0 && lc == 2) {
|
||||
responseAPDU->DataLen = 0;
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_OK);
|
||||
} else if(p1 != 0 || p2 != 0) {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);
|
||||
} else {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
//instruction 4: sends a command with a body with 'n' bytes, receives a response with 'n' bytes
|
||||
//APDU example: 0x01:0x04:0x00:0x00:0x04:0x01:0x02:0x03:0x04:0x04
|
||||
//receives (0x01, 0x02, 0x03, 0x04) SW1=0x90, SW2=0x00
|
||||
void handle_instruction_04(
|
||||
uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint16_t lc,
|
||||
uint16_t le,
|
||||
const uint8_t* commandApduDataBuffer,
|
||||
ISO7816_Response_APDU* responseAPDU) {
|
||||
if(p1 == 0 && p2 == 0 && lc > 0 && le > 0 && le >= lc) {
|
||||
for(uint16_t i = 0; i < lc; i++) {
|
||||
responseAPDU->Data[i] = commandApduDataBuffer[i];
|
||||
}
|
||||
|
||||
responseAPDU->DataLen = lc;
|
||||
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_OK);
|
||||
} else if(p1 != 0 || p2 != 0) {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);
|
||||
} else {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
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 ISO7816_Command_APDU* commandAPDU,
|
||||
ISO7816_Response_APDU* responseAPDU) {
|
||||
//example 1: sends a command with no body, receives a response with no body
|
||||
//sends APDU 0x01:0x01:0x00:0x00
|
||||
//receives SW1=0x90, SW2=0x00
|
||||
|
||||
if(commandAPDU->CLA == 0x01) {
|
||||
switch(commandAPDU->INS) {
|
||||
case 0x01:
|
||||
handle_instruction_01(responseAPDU);
|
||||
break;
|
||||
case 0x02:
|
||||
handle_instruction_02(
|
||||
commandAPDU->P1, commandAPDU->P2, commandAPDU->Lc, commandAPDU->Le, responseAPDU);
|
||||
break;
|
||||
case 0x03:
|
||||
handle_instruction_03(commandAPDU->P1, commandAPDU->P2, commandAPDU->Lc, responseAPDU);
|
||||
break;
|
||||
case 0x04:
|
||||
handle_instruction_04(
|
||||
commandAPDU->P1,
|
||||
commandAPDU->P2,
|
||||
commandAPDU->Lc,
|
||||
commandAPDU->Le,
|
||||
commandAPDU->Data,
|
||||
responseAPDU);
|
||||
break;
|
||||
default:
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_INSTRUCTION_NOT_SUPPORTED);
|
||||
}
|
||||
} else {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_CLASS_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
static const Iso7816Callbacks iso87816_cb = {
|
||||
iso7816_answer_to_reset,
|
||||
iso7816_process_command,
|
||||
};
|
||||
|
||||
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 = 0x076B;
|
||||
app->ccid_cfg.pid = 0x3A21;
|
||||
|
||||
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
||||
furi_hal_usb_unlock();
|
||||
|
||||
furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true);
|
||||
furi_hal_usb_ccid_set_callbacks((CcidCallbacks*)&ccid_cb, NULL);
|
||||
furi_hal_usb_ccid_set_callbacks(
|
||||
(CcidCallbacks*)&app->iso7816_handler->ccid_callbacks, app->iso7816_handler);
|
||||
furi_hal_usb_ccid_insert_smartcard();
|
||||
|
||||
iso7816_set_callbacks((Iso7816Callbacks*)&iso87816_cb);
|
||||
|
||||
//handle button events
|
||||
CcidTestAppEvent event;
|
||||
while(1) {
|
||||
@@ -280,8 +145,6 @@ int32_t ccid_test_app(void* p) {
|
||||
furi_hal_usb_ccid_set_callbacks(NULL, NULL);
|
||||
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
||||
|
||||
iso7816_set_callbacks(NULL);
|
||||
|
||||
//teardown view
|
||||
ccid_test_app_free(app);
|
||||
return 0;
|
||||
|
||||
123
applications/debug/ccid_test/ccid_test_app_commands.c
Normal file
123
applications/debug/ccid_test/ccid_test_app_commands.c
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "iso7816/iso7816_t0_apdu.h"
|
||||
#include "iso7816/iso7816_response.h"
|
||||
|
||||
//Instruction 1: returns an OK response unconditionally
|
||||
//APDU example: 0x01:0x01:0x00:0x00
|
||||
//response: SW1=0x90, SW2=0x00
|
||||
void handle_instruction_01(ISO7816_Response_APDU* response_apdu) {
|
||||
response_apdu->DataLen = 0;
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);
|
||||
}
|
||||
|
||||
//Instruction 2: expect command with no body, replies wit with a body with two bytes
|
||||
//APDU example: 0x01:0x02:0x00:0x00:0x02
|
||||
//response: 'bc' (0x62, 0x63) SW1=0x90, SW2=0x00
|
||||
void handle_instruction_02(
|
||||
uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint16_t lc,
|
||||
uint16_t le,
|
||||
ISO7816_Response_APDU* response_apdu) {
|
||||
if(p1 == 0 && p2 == 0 && lc == 0 && le >= 2) {
|
||||
response_apdu->Data[0] = 0x62;
|
||||
response_apdu->Data[1] = 0x63;
|
||||
|
||||
response_apdu->DataLen = 2;
|
||||
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);
|
||||
} else if(p1 != 0 || p2 != 0) {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);
|
||||
} else {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
//Instruction 3: sends a command with a body with two bytes, receives a response with no bytes
|
||||
//APDU example: 0x01:0x03:0x00:0x00:0x02:CA:FE
|
||||
//response SW1=0x90, SW2=0x00
|
||||
void handle_instruction_03(
|
||||
uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint16_t lc,
|
||||
ISO7816_Response_APDU* response_apdu) {
|
||||
if(p1 == 0 && p2 == 0 && lc == 2) {
|
||||
response_apdu->DataLen = 0;
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);
|
||||
} else if(p1 != 0 || p2 != 0) {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);
|
||||
} else {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
//instruction 4: sends a command with a body with 'n' bytes, receives a response with 'n' bytes
|
||||
//APDU example: 0x01:0x04:0x00:0x00:0x04:0x01:0x02:0x03:0x04:0x04
|
||||
//receives (0x01, 0x02, 0x03, 0x04) SW1=0x90, SW2=0x00
|
||||
void handle_instruction_04(
|
||||
uint8_t p1,
|
||||
uint8_t p2,
|
||||
uint16_t lc,
|
||||
uint16_t le,
|
||||
const uint8_t* command_apdu_data_buffer,
|
||||
ISO7816_Response_APDU* response_apdu) {
|
||||
if(p1 == 0 && p2 == 0 && lc > 0 && le > 0 && le >= lc) {
|
||||
for(uint16_t i = 0; i < lc; i++) {
|
||||
response_apdu->Data[i] = command_apdu_data_buffer[i];
|
||||
}
|
||||
|
||||
response_apdu->DataLen = lc;
|
||||
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_OK);
|
||||
} else if(p1 != 0 || p2 != 0) {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_PARAMETERS_P1_P2);
|
||||
} else {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
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 ISO7816_Command_APDU* command_apdu,
|
||||
ISO7816_Response_APDU* response_apdu) {
|
||||
//example 1: sends a command with no body, receives a response with no body
|
||||
//sends APDU 0x01:0x01:0x00:0x00
|
||||
//receives SW1=0x90, SW2=0x00
|
||||
|
||||
if(command_apdu->CLA == 0x01) {
|
||||
switch(command_apdu->INS) {
|
||||
case 0x01:
|
||||
handle_instruction_01(response_apdu);
|
||||
break;
|
||||
case 0x02:
|
||||
handle_instruction_02(
|
||||
command_apdu->P1,
|
||||
command_apdu->P2,
|
||||
command_apdu->Lc,
|
||||
command_apdu->Le,
|
||||
response_apdu);
|
||||
break;
|
||||
case 0x03:
|
||||
handle_instruction_03(
|
||||
command_apdu->P1, command_apdu->P2, command_apdu->Lc, response_apdu);
|
||||
break;
|
||||
case 0x04:
|
||||
handle_instruction_04(
|
||||
command_apdu->P1,
|
||||
command_apdu->P2,
|
||||
command_apdu->Lc,
|
||||
command_apdu->Le,
|
||||
command_apdu->Data,
|
||||
response_apdu);
|
||||
break;
|
||||
default:
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_INSTRUCTION_NOT_SUPPORTED);
|
||||
}
|
||||
} else {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_CLASS_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
7
applications/debug/ccid_test/ccid_test_app_commands.h
Normal file
7
applications/debug/ccid_test/ccid_test_app_commands.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "iso7816/iso7816_t0_apdu.h"
|
||||
|
||||
void iso7816_answer_to_reset(Iso7816Atr* atr);
|
||||
|
||||
void iso7816_process_command(
|
||||
const ISO7816_Command_APDU* command_apdu,
|
||||
ISO7816_Response_APDU* response_apdu);
|
||||
68
applications/debug/ccid_test/iso7816/iso7816_handler.c
Normal file
68
applications/debug/ccid_test/iso7816/iso7816_handler.c
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include "iso7816_t0_apdu.h"
|
||||
#include "iso7816_atr.h"
|
||||
#include "iso7816_handler.h"
|
||||
#include "iso7816_response.h"
|
||||
|
||||
void iso7816_icc_power_on_callback(uint8_t* atr_data, uint32_t* atr_data_len, void* context) {
|
||||
furi_check(context);
|
||||
|
||||
Iso7816Handler* handler = (Iso7816Handler*)context;
|
||||
|
||||
Iso7816Atr iso7816_atr;
|
||||
handler->iso7816_answer_to_reset(&iso7816_atr);
|
||||
|
||||
furi_assert(iso7816_atr.T0 == 0x00);
|
||||
|
||||
uint8_t atr_buffer[2] = {iso7816_atr.TS, iso7816_atr.T0};
|
||||
|
||||
*atr_data_len = 2;
|
||||
|
||||
memcpy(atr_data, atr_buffer, sizeof(uint8_t) * (*atr_data_len));
|
||||
}
|
||||
|
||||
//dataBlock points to the buffer
|
||||
//dataBlockLen tells reader how nany bytes should be read
|
||||
void iso7816_xfr_datablock_callback(
|
||||
const uint8_t* pc_to_reader_datablock,
|
||||
uint32_t pc_to_reader_datablock_len,
|
||||
uint8_t* reader_to_pc_datablock,
|
||||
uint32_t* reader_to_pc_datablock_len,
|
||||
void* context) {
|
||||
furi_check(context);
|
||||
|
||||
Iso7816Handler* handler = (Iso7816Handler*)context;
|
||||
|
||||
ISO7816_Response_APDU* response_apdu = (ISO7816_Response_APDU*)&handler->response_apdu_buffer;
|
||||
|
||||
ISO7816_Command_APDU* command_apdu = (ISO7816_Command_APDU*)&handler->command_apdu_buffer;
|
||||
|
||||
uint8_t result = iso7816_read_command_apdu(
|
||||
command_apdu, pc_to_reader_datablock, pc_to_reader_datablock_len);
|
||||
|
||||
if(result == ISO7816_READ_COMMAND_APDU_OK) {
|
||||
handler->iso7816_process_command(command_apdu, response_apdu);
|
||||
|
||||
furi_assert(response_apdu->DataLen < CCID_SHORT_APDU_SIZE);
|
||||
} else if(result == ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LE) {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LE);
|
||||
} else if(result == ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH) {
|
||||
iso7816_set_response(response_apdu, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
|
||||
iso7816_write_response_apdu(response_apdu, reader_to_pc_datablock, reader_to_pc_datablock_len);
|
||||
}
|
||||
|
||||
Iso7816Handler* iso7816_handler_alloc() {
|
||||
Iso7816Handler* handler = malloc(sizeof(Iso7816Handler));
|
||||
handler->ccid_callbacks.icc_power_on_callback = iso7816_icc_power_on_callback;
|
||||
handler->ccid_callbacks.xfr_datablock_callback = iso7816_xfr_datablock_callback;
|
||||
return handler;
|
||||
}
|
||||
18
applications/debug/ccid_test/iso7816/iso7816_handler.h
Normal file
18
applications/debug/ccid_test/iso7816/iso7816_handler.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "iso7816_atr.h"
|
||||
#include "iso7816_t0_apdu.h"
|
||||
|
||||
typedef struct {
|
||||
CcidCallbacks ccid_callbacks;
|
||||
void (*iso7816_answer_to_reset)(Iso7816Atr* atr);
|
||||
void (*iso7816_process_command)(
|
||||
const ISO7816_Command_APDU* command,
|
||||
ISO7816_Response_APDU* response);
|
||||
|
||||
uint8_t command_apdu_buffer[sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE];
|
||||
uint8_t response_apdu_buffer[sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE];
|
||||
} Iso7816Handler;
|
||||
|
||||
Iso7816Handler* iso7816_handler_alloc();
|
||||
@@ -61,24 +61,25 @@ uint8_t iso7816_read_command_apdu(
|
||||
//data buffer contains the whole APU response (response + trailer (SW1+SW2))
|
||||
void iso7816_write_response_apdu(
|
||||
const ISO7816_Response_APDU* response,
|
||||
uint8_t* readerToPcDataBlock,
|
||||
uint32_t* readerToPcDataBlockLen) {
|
||||
uint8_t* reader_to_pc_datablock,
|
||||
uint32_t* reader_to_pc_datablock_len) {
|
||||
uint32_t responseDataBufferIndex = 0;
|
||||
|
||||
//response body
|
||||
if(response->DataLen > 0) {
|
||||
while(responseDataBufferIndex < response->DataLen) {
|
||||
readerToPcDataBlock[responseDataBufferIndex] = response->Data[responseDataBufferIndex];
|
||||
reader_to_pc_datablock[responseDataBufferIndex] =
|
||||
response->Data[responseDataBufferIndex];
|
||||
responseDataBufferIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
//trailer
|
||||
readerToPcDataBlock[responseDataBufferIndex] = response->SW1;
|
||||
reader_to_pc_datablock[responseDataBufferIndex] = response->SW1;
|
||||
responseDataBufferIndex++;
|
||||
|
||||
readerToPcDataBlock[responseDataBufferIndex] = response->SW2;
|
||||
reader_to_pc_datablock[responseDataBufferIndex] = response->SW2;
|
||||
responseDataBufferIndex++;
|
||||
|
||||
*readerToPcDataBlockLen = responseDataBufferIndex;
|
||||
*reader_to_pc_datablock_len = responseDataBufferIndex;
|
||||
}
|
||||
@@ -31,12 +31,11 @@ typedef struct {
|
||||
uint8_t Data[0];
|
||||
} FURI_PACKED ISO7816_Response_APDU;
|
||||
|
||||
void iso7816_answer_to_reset(Iso7816Atr* atr);
|
||||
uint8_t iso7816_read_command_apdu(
|
||||
ISO7816_Command_APDU* command,
|
||||
const uint8_t* pcToReaderDataBlock,
|
||||
uint32_t pcToReaderDataBlockLen);
|
||||
const uint8_t* pc_to_reader_datablock,
|
||||
uint32_t pc_to_reader_datablock_len);
|
||||
void iso7816_write_response_apdu(
|
||||
const ISO7816_Response_APDU* response,
|
||||
uint8_t* readerToPcDataBlock,
|
||||
uint32_t* readerToPcDataBlockLen);
|
||||
uint8_t* reader_to_pc_datablock,
|
||||
uint32_t* reader_to_pc_datablock_len);
|
||||
@@ -1,65 +0,0 @@
|
||||
// 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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include "iso7816_t0_apdu.h"
|
||||
#include "iso7816_atr.h"
|
||||
#include "iso7816_callbacks.h"
|
||||
#include "iso7816_response.h"
|
||||
|
||||
static Iso7816Callbacks* callbacks = NULL;
|
||||
|
||||
static uint8_t commandApduBuffer[sizeof(ISO7816_Command_APDU) + CCID_SHORT_APDU_SIZE];
|
||||
static uint8_t responseApduBuffer[sizeof(ISO7816_Response_APDU) + CCID_SHORT_APDU_SIZE];
|
||||
|
||||
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) {
|
||||
ISO7816_Response_APDU* responseAPDU = (ISO7816_Response_APDU*)&responseApduBuffer;
|
||||
|
||||
if(callbacks != NULL) {
|
||||
ISO7816_Command_APDU* commandAPDU = (ISO7816_Command_APDU*)&commandApduBuffer;
|
||||
|
||||
uint8_t result =
|
||||
iso7816_read_command_apdu(commandAPDU, pcToReaderDataBlock, pcToReaderDataBlockLen);
|
||||
|
||||
if(result == ISO7816_READ_COMMAND_APDU_OK) {
|
||||
callbacks->iso7816_process_command(commandAPDU, responseAPDU);
|
||||
|
||||
furi_assert(responseAPDU->DataLen < CCID_SHORT_APDU_SIZE);
|
||||
} else if(result == ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LE) {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_LE);
|
||||
} else if(result == ISO7816_READ_COMMAND_APDU_ERROR_WRONG_LENGTH) {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_WRONG_LENGTH);
|
||||
}
|
||||
} else {
|
||||
iso7816_set_response(responseAPDU, ISO7816_RESPONSE_INTERNAL_EXCEPTION);
|
||||
}
|
||||
|
||||
iso7816_write_response_apdu(responseAPDU, readerToPcDataBlock, readerToPcDataBlockLen);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#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 ISO7816_Command_APDU* command,
|
||||
ISO7816_Response_APDU* response);
|
||||
} 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);
|
||||
85
applications/main/nfc/helpers/nfc_detected_protocols.c
Normal file
85
applications/main/nfc/helpers/nfc_detected_protocols.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "nfc_detected_protocols.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
struct NfcDetectedProtocols {
|
||||
uint32_t protocols_detected_num;
|
||||
NfcProtocol protocols_detected[NfcProtocolNum];
|
||||
uint32_t selected_idx;
|
||||
};
|
||||
|
||||
NfcDetectedProtocols* nfc_detected_protocols_alloc(void) {
|
||||
NfcDetectedProtocols* instance = malloc(sizeof(NfcDetectedProtocols));
|
||||
|
||||
instance->protocols_detected_num = 0;
|
||||
instance->selected_idx = 0;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_detected_protocols_free(NfcDetectedProtocols* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_detected_protocols_reset(NfcDetectedProtocols* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->protocols_detected_num = 0;
|
||||
memset(instance->protocols_detected, 0, sizeof(instance->protocols_detected));
|
||||
instance->selected_idx = 0;
|
||||
}
|
||||
|
||||
void nfc_detected_protocols_select(NfcDetectedProtocols* instance, uint32_t idx) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->selected_idx = idx;
|
||||
}
|
||||
|
||||
void nfc_detected_protocols_set(
|
||||
NfcDetectedProtocols* instance,
|
||||
const NfcProtocol* types,
|
||||
uint32_t count) {
|
||||
furi_assert(instance);
|
||||
furi_assert(types);
|
||||
furi_assert(count < NfcProtocolNum);
|
||||
|
||||
memcpy(instance->protocols_detected, types, count);
|
||||
instance->protocols_detected_num = count;
|
||||
instance->selected_idx = 0;
|
||||
}
|
||||
|
||||
uint32_t nfc_detected_protocols_get_num(NfcDetectedProtocols* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->protocols_detected_num;
|
||||
}
|
||||
|
||||
NfcProtocol nfc_detected_protocols_get_protocol(NfcDetectedProtocols* instance, uint32_t idx) {
|
||||
furi_assert(instance);
|
||||
furi_assert(idx < instance->protocols_detected_num);
|
||||
|
||||
return instance->protocols_detected[idx];
|
||||
}
|
||||
|
||||
void nfc_detected_protocols_fill_all_protocols(NfcDetectedProtocols* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->protocols_detected_num = NfcProtocolNum;
|
||||
for(uint32_t i = 0; i < NfcProtocolNum; i++) {
|
||||
instance->protocols_detected[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
NfcProtocol nfc_detected_protocols_get_selected(NfcDetectedProtocols* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->protocols_detected[instance->selected_idx];
|
||||
}
|
||||
|
||||
uint32_t nfc_detected_protocols_get_selected_idx(NfcDetectedProtocols* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->selected_idx;
|
||||
}
|
||||
29
applications/main/nfc/helpers/nfc_detected_protocols.h
Normal file
29
applications/main/nfc/helpers/nfc_detected_protocols.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <nfc/protocols/nfc_protocol.h>
|
||||
|
||||
typedef struct NfcDetectedProtocols NfcDetectedProtocols;
|
||||
|
||||
NfcDetectedProtocols* nfc_detected_protocols_alloc(void);
|
||||
|
||||
void nfc_detected_protocols_free(NfcDetectedProtocols* instance);
|
||||
|
||||
void nfc_detected_protocols_reset(NfcDetectedProtocols* instance);
|
||||
|
||||
void nfc_detected_protocols_select(NfcDetectedProtocols* instance, uint32_t idx);
|
||||
|
||||
void nfc_detected_protocols_set(
|
||||
NfcDetectedProtocols* instance,
|
||||
const NfcProtocol* types,
|
||||
uint32_t count);
|
||||
|
||||
uint32_t nfc_detected_protocols_get_num(NfcDetectedProtocols* instance);
|
||||
|
||||
NfcProtocol nfc_detected_protocols_get_protocol(NfcDetectedProtocols* instance, uint32_t idx);
|
||||
|
||||
void nfc_detected_protocols_fill_all_protocols(NfcDetectedProtocols* instance);
|
||||
|
||||
NfcProtocol nfc_detected_protocols_get_selected(NfcDetectedProtocols* instance);
|
||||
|
||||
uint32_t nfc_detected_protocols_get_selected_idx(NfcDetectedProtocols* instance);
|
||||
@@ -150,8 +150,7 @@ static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) {
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
|
||||
const NfcProtocol protocol =
|
||||
instance->protocols_detected[instance->protocols_detected_selected_idx];
|
||||
const NfcProtocol protocol = nfc_detected_protocols_get_selected(instance->detected_protocols);
|
||||
instance->poller = nfc_poller_alloc(instance->nfc, protocol);
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
@@ -186,7 +185,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
|
||||
consumed = true;
|
||||
} else {
|
||||
const NfcProtocol protocol =
|
||||
instance->protocols_detected[instance->protocols_detected_selected_idx];
|
||||
nfc_detected_protocols_get_selected(instance->detected_protocols);
|
||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);
|
||||
}
|
||||
} else if(event.event == NfcCustomEventPollerFailure) {
|
||||
@@ -199,7 +198,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventCardDetected) {
|
||||
const NfcProtocol protocol =
|
||||
instance->protocols_detected[instance->protocols_detected_selected_idx];
|
||||
nfc_detected_protocols_get_selected(instance->detected_protocols);
|
||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
|
||||
@@ -51,6 +51,7 @@ NfcApp* nfc_app_alloc(void) {
|
||||
|
||||
instance->nfc = nfc_alloc();
|
||||
|
||||
instance->detected_protocols = nfc_detected_protocols_alloc();
|
||||
instance->felica_auth = felica_auth_alloc();
|
||||
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
||||
instance->slix_unlock = slix_unlock_alloc();
|
||||
@@ -143,6 +144,7 @@ void nfc_app_free(NfcApp* instance) {
|
||||
|
||||
nfc_free(instance->nfc);
|
||||
|
||||
nfc_detected_protocols_free(instance->detected_protocols);
|
||||
felica_auth_free(instance->felica_auth);
|
||||
mf_ultralight_auth_free(instance->mf_ul_auth);
|
||||
slix_unlock_free(instance->slix_unlock);
|
||||
@@ -434,23 +436,6 @@ void nfc_show_loading_popup(void* context, bool show) {
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count) {
|
||||
furi_assert(instance);
|
||||
furi_assert(types);
|
||||
furi_assert(count < NfcProtocolNum);
|
||||
|
||||
memcpy(instance->protocols_detected, types, count);
|
||||
instance->protocols_detected_num = count;
|
||||
instance->protocols_detected_selected_idx = 0;
|
||||
}
|
||||
|
||||
void nfc_app_reset_detected_protocols(NfcApp* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->protocols_detected_selected_idx = 0;
|
||||
instance->protocols_detected_num = 0;
|
||||
}
|
||||
|
||||
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string) {
|
||||
furi_assert(instance);
|
||||
furi_assert(string);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "views/dict_attack.h"
|
||||
|
||||
#include <nfc/scenes/nfc_scene.h>
|
||||
#include "helpers/nfc_detected_protocols.h"
|
||||
#include "helpers/nfc_custom_event.h"
|
||||
#include "helpers/mf_ultralight_auth.h"
|
||||
#include "helpers/mf_user_dict.h"
|
||||
@@ -107,9 +108,7 @@ struct NfcApp {
|
||||
FuriString* text_box_store;
|
||||
uint8_t byte_input_store[NFC_BYTE_INPUT_STORE_SIZE];
|
||||
|
||||
uint32_t protocols_detected_num;
|
||||
NfcProtocol protocols_detected[NfcProtocolNum];
|
||||
uint32_t protocols_detected_selected_idx;
|
||||
NfcDetectedProtocols* detected_protocols;
|
||||
|
||||
RpcAppSystem* rpc_ctx;
|
||||
NfcRpcState rpc_state;
|
||||
@@ -196,8 +195,4 @@ bool nfc_save_file(NfcApp* instance, FuriString* path);
|
||||
|
||||
void nfc_make_app_folder(NfcApp* instance);
|
||||
|
||||
void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count);
|
||||
|
||||
void nfc_app_reset_detected_protocols(NfcApp* instance);
|
||||
|
||||
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string);
|
||||
|
||||
@@ -7,7 +7,8 @@ void nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) {
|
||||
NfcApp* instance = context;
|
||||
|
||||
if(event.type == NfcScannerEventTypeDetected) {
|
||||
nfc_app_set_detected_protocols(instance, event.data.protocols, event.data.protocol_num);
|
||||
nfc_detected_protocols_set(
|
||||
instance->detected_protocols, event.data.protocols, event.data.protocol_num);
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +24,7 @@ void nfc_scene_detect_on_enter(void* context) {
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
|
||||
nfc_app_reset_detected_protocols(instance);
|
||||
nfc_detected_protocols_reset(instance->detected_protocols);
|
||||
|
||||
instance->scanner = nfc_scanner_alloc(instance->nfc);
|
||||
nfc_scanner_start(instance->scanner, nfc_scene_detect_scan_callback, instance);
|
||||
@@ -37,7 +38,7 @@ bool nfc_scene_detect_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventWorkerExit) {
|
||||
if(instance->protocols_detected_num > 1) {
|
||||
if(nfc_detected_protocols_get_num(instance->detected_protocols) > 1) {
|
||||
notification_message(instance->notifications, &sequence_single_vibro);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol);
|
||||
} else {
|
||||
|
||||
@@ -57,7 +57,8 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultRight) {
|
||||
const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight};
|
||||
nfc_app_set_detected_protocols(nfc, mfu_protocol, COUNT_OF(mfu_protocol));
|
||||
nfc_detected_protocols_set(
|
||||
nfc->detected_protocols, mfu_protocol, COUNT_OF(mfu_protocol));
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
|
||||
dolphin_deed(DolphinDeedNfcRead);
|
||||
consumed = true;
|
||||
@@ -77,7 +78,8 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultCenter) {
|
||||
const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight};
|
||||
nfc_app_set_detected_protocols(nfc, mfu_protocol, COUNT_OF(mfu_protocol));
|
||||
nfc_detected_protocols_set(
|
||||
nfc->detected_protocols, mfu_protocol, COUNT_OF(mfu_protocol));
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
|
||||
dolphin_deed(DolphinDeedNfcRead);
|
||||
consumed = true;
|
||||
|
||||
@@ -14,21 +14,19 @@ void nfc_scene_select_protocol_on_enter(void* context) {
|
||||
const char* prefix;
|
||||
if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneExtraActions)) {
|
||||
prefix = "Read";
|
||||
instance->protocols_detected_num = NfcProtocolNum;
|
||||
for(uint32_t i = 0; i < NfcProtocolNum; i++) {
|
||||
instance->protocols_detected[i] = i;
|
||||
}
|
||||
nfc_detected_protocols_fill_all_protocols(instance->detected_protocols);
|
||||
} else {
|
||||
prefix = "Read as";
|
||||
submenu_set_header(submenu, "Multi-protocol card");
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < instance->protocols_detected_num; i++) {
|
||||
for(uint32_t i = 0; i < nfc_detected_protocols_get_num(instance->detected_protocols); i++) {
|
||||
furi_string_printf(
|
||||
temp_str,
|
||||
"%s %s",
|
||||
prefix,
|
||||
nfc_device_get_protocol_name(instance->protocols_detected[i]));
|
||||
nfc_device_get_protocol_name(
|
||||
nfc_detected_protocols_get_protocol(instance->detected_protocols, i)));
|
||||
|
||||
furi_string_replace_str(temp_str, "Mifare", "MIFARE");
|
||||
submenu_add_item(
|
||||
@@ -40,9 +38,8 @@ void nfc_scene_select_protocol_on_enter(void* context) {
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
const uint32_t state =
|
||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneSelectProtocol);
|
||||
submenu_set_selected_item(submenu, state);
|
||||
submenu_set_selected_item(
|
||||
submenu, nfc_detected_protocols_get_selected_idx(instance->detected_protocols));
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
@@ -52,10 +49,8 @@ bool nfc_scene_select_protocol_on_event(void* context, SceneManagerEvent event)
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
instance->protocols_detected_selected_idx = event.event;
|
||||
nfc_detected_protocols_select(instance->detected_protocols, event.event);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneRead);
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneSelectProtocol, event.event);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) {
|
||||
|
||||
@@ -25,7 +25,7 @@ void nfc_scene_start_on_enter(void* context) {
|
||||
nfc_device_clear(nfc->nfc_device);
|
||||
iso14443_3a_reset(nfc->iso14443_3a_edit_data);
|
||||
// Reset detected protocols list
|
||||
nfc_app_reset_detected_protocols(nfc);
|
||||
nfc_detected_protocols_reset(nfc->detected_protocols);
|
||||
|
||||
submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc);
|
||||
submenu_add_item(
|
||||
|
||||
Reference in New Issue
Block a user