diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c index 46a1237f9..abb8ad3dd 100644 --- a/applications/debug/ccid_test/ccid_test_app.c +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -6,10 +6,13 @@ #include #include #include -#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; diff --git a/applications/debug/ccid_test/ccid_test_app_commands.c b/applications/debug/ccid_test/ccid_test_app_commands.c new file mode 100644 index 000000000..1daaa70c3 --- /dev/null +++ b/applications/debug/ccid_test/ccid_test_app_commands.c @@ -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); + } +} diff --git a/applications/debug/ccid_test/ccid_test_app_commands.h b/applications/debug/ccid_test/ccid_test_app_commands.h new file mode 100644 index 000000000..ca3275aec --- /dev/null +++ b/applications/debug/ccid_test/ccid_test_app_commands.h @@ -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); diff --git a/applications/debug/ccid_test/iso7816_atr.h b/applications/debug/ccid_test/iso7816/iso7816_atr.h similarity index 100% rename from applications/debug/ccid_test/iso7816_atr.h rename to applications/debug/ccid_test/iso7816/iso7816_atr.h diff --git a/applications/debug/ccid_test/iso7816/iso7816_handler.c b/applications/debug/ccid_test/iso7816/iso7816_handler.c new file mode 100644 index 000000000..97214d1b2 --- /dev/null +++ b/applications/debug/ccid_test/iso7816/iso7816_handler.c @@ -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 +#include +#include +#include + +#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; +} diff --git a/applications/debug/ccid_test/iso7816/iso7816_handler.h b/applications/debug/ccid_test/iso7816/iso7816_handler.h new file mode 100644 index 000000000..d67118ce6 --- /dev/null +++ b/applications/debug/ccid_test/iso7816/iso7816_handler.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#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(); diff --git a/applications/debug/ccid_test/iso7816_response.c b/applications/debug/ccid_test/iso7816/iso7816_response.c similarity index 100% rename from applications/debug/ccid_test/iso7816_response.c rename to applications/debug/ccid_test/iso7816/iso7816_response.c diff --git a/applications/debug/ccid_test/iso7816_response.h b/applications/debug/ccid_test/iso7816/iso7816_response.h similarity index 100% rename from applications/debug/ccid_test/iso7816_response.h rename to applications/debug/ccid_test/iso7816/iso7816_response.h diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c similarity index 85% rename from applications/debug/ccid_test/iso7816_t0_apdu.c rename to applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c index 3de5555f4..216f2582f 100644 --- a/applications/debug/ccid_test/iso7816_t0_apdu.c +++ b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.c @@ -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; } diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h similarity index 81% rename from applications/debug/ccid_test/iso7816_t0_apdu.h rename to applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h index 50eb476a9..a21dfbafc 100644 --- a/applications/debug/ccid_test/iso7816_t0_apdu.h +++ b/applications/debug/ccid_test/iso7816/iso7816_t0_apdu.h @@ -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); diff --git a/applications/debug/ccid_test/iso7816_callbacks.c b/applications/debug/ccid_test/iso7816_callbacks.c deleted file mode 100644 index 6c1bb106a..000000000 --- a/applications/debug/ccid_test/iso7816_callbacks.c +++ /dev/null @@ -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 -#include -#include -#include - -#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); -} diff --git a/applications/debug/ccid_test/iso7816_callbacks.h b/applications/debug/ccid_test/iso7816_callbacks.h deleted file mode 100644 index 6b408c7f5..000000000 --- a/applications/debug/ccid_test/iso7816_callbacks.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#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); diff --git a/applications/main/nfc/helpers/nfc_detected_protocols.c b/applications/main/nfc/helpers/nfc_detected_protocols.c new file mode 100644 index 000000000..339d4ddc2 --- /dev/null +++ b/applications/main/nfc/helpers/nfc_detected_protocols.c @@ -0,0 +1,85 @@ +#include "nfc_detected_protocols.h" + +#include + +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; +} diff --git a/applications/main/nfc/helpers/nfc_detected_protocols.h b/applications/main/nfc/helpers/nfc_detected_protocols.h new file mode 100644 index 000000000..2ab46add3 --- /dev/null +++ b/applications/main/nfc/helpers/nfc_detected_protocols.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +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); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 063e0ece3..c56b55390 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -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) { diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index ce444982d..68b5d0c6f 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -50,6 +50,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(); @@ -142,6 +143,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); @@ -433,23 +435,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); diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 9a9c81004..867d0eb4f 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -26,6 +26,7 @@ #include "views/dict_attack.h" #include +#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; @@ -194,8 +193,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); diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index 11b60d6fa..36f745697 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -229,7 +229,7 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { } furi_string_printf( - parsed_data, "\e#Plantain\nNo.: %llu?\nBalance:%lu\n", card_number, balance); + parsed_data, "\e#Plantain\nNo.: %lluX\nBalance: %lu\n", card_number, balance); parsed = true; } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index 2f92030d9..dc87d3072 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -157,7 +157,7 @@ static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) { furi_string_printf( parsed_data, - "\e#Troika+Plantain\nPN: %llu?\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n", + "\e#Troika+Plantain\nPN: %lluX\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n", card_number, balance, troika_number, diff --git a/applications/main/nfc/scenes/nfc_scene_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c index 3ef153657..7ef3f9d87 100644 --- a/applications/main/nfc/scenes/nfc_scene_detect.c +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -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 { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 4df8a6289..a0b6986d1 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -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; diff --git a/applications/main/nfc/scenes/nfc_scene_select_protocol.c b/applications/main/nfc/scenes/nfc_scene_select_protocol.c index af644035e..f2c92b631 100644 --- a/applications/main/nfc/scenes/nfc_scene_select_protocol.c +++ b/applications/main/nfc/scenes/nfc_scene_select_protocol.c @@ -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)) { diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index e8774b4aa..53857b849 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -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( diff --git a/documentation/file_formats/LfRfidFileFormat.md b/documentation/file_formats/LfRfidFileFormat.md index 2463195e4..6bad4a3c0 100644 --- a/documentation/file_formats/LfRfidFileFormat.md +++ b/documentation/file_formats/LfRfidFileFormat.md @@ -47,3 +47,4 @@ The file stores a single RFID key of the type defined by the `Key type` paramete | PAC/Stanley | PAC/Stanley | | Keri | Keri | | Gallagher | Gallagher | +| GProxII | Guardall GProx II | diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index e8d630668..0e65dc045 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -19,6 +19,7 @@ #include "protocol_gallagher.h" #include "protocol_nexwatch.h" #include "protocol_securakey.h" +#include "protocol_gproxii.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -43,4 +44,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolGallagher] = &protocol_gallagher, [LFRFIDProtocolNexwatch] = &protocol_nexwatch, [LFRFIDProtocolSecurakey] = &protocol_securakey, + [LFRFIDProtocolGProxII] = &protocol_gproxii, }; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index b9ef5c518..12ac2dddd 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -30,6 +30,7 @@ typedef enum { LFRFIDProtocolGallagher, LFRFIDProtocolNexwatch, LFRFIDProtocolSecurakey, + LFRFIDProtocolGProxII, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_gproxii.c b/lib/lfrfid/protocols/protocol_gproxii.c new file mode 100644 index 000000000..73cbe8f39 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_gproxii.c @@ -0,0 +1,261 @@ +#include +#include "toolbox/level_duration.h" +#include "protocol_gproxii.h" +#include +#include +#include "lfrfid_protocols.h" + +#define GPROXII_PREAMBLE_BIT_SIZE (6) +#define GPROXII_ENCODED_BIT_SIZE (90) +#define GPROXII_ENCODED_BYTE_FULL_SIZE \ + (((GPROXII_PREAMBLE_BIT_SIZE + GPROXII_ENCODED_BIT_SIZE) / 8)) + +#define GPROXII_DATA_SIZE (12) + +#define GPROXII_SHORT_TIME (256) +#define GPROXII_LONG_TIME (512) +#define GPROXII_JITTER_TIME (120) + +#define GPROXII_SHORT_TIME_LOW (GPROXII_SHORT_TIME - GPROXII_JITTER_TIME) +#define GPROXII_SHORT_TIME_HIGH (GPROXII_SHORT_TIME + GPROXII_JITTER_TIME) +#define GPROXII_LONG_TIME_LOW (GPROXII_LONG_TIME - GPROXII_JITTER_TIME) +#define GPROXII_LONG_TIME_HIGH (GPROXII_LONG_TIME + GPROXII_JITTER_TIME) + +typedef struct { + bool last_short; + bool last_level; + size_t encoded_index; + uint8_t decoded_data[GPROXII_ENCODED_BYTE_FULL_SIZE]; + uint8_t data[GPROXII_ENCODED_BYTE_FULL_SIZE]; +} ProtocolGProxII; + +ProtocolGProxII* protocol_gproxii_alloc(void) { + ProtocolGProxII* protocol = malloc(sizeof(ProtocolGProxII)); + return protocol; +} + +void protocol_gproxii_free(ProtocolGProxII* protocol) { + free(protocol); +} + +uint8_t* protocol_gproxii_get_data(ProtocolGProxII* proto) { + return proto->data; +} + +void protocol_gproxii_decoder_start(ProtocolGProxII* protocol) { + memset(protocol->data, 0, GPROXII_ENCODED_BYTE_FULL_SIZE); + protocol->last_short = false; +} + +static bool protocol_gproxii_can_be_decoded(ProtocolGProxII* protocol) { + // 96 bit with 5 bit zero parity + // 0 10 20 30 40 50 60 70 80 90 + // | | | | | | | | | | + // 012345 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 6789 0 1234 5 + // ------------------------------------------------------------------------------------------------------------------------------------ + // 111110 0000 0 1001 0 1101 0 1111 0 1000 0 1001 0 0000 0 1001 0 0000 0 1001 0 0000 0 1001 0 0000 0 1001 0 0000 0 1000 0 0000 0 1001 0 + + // Remove header and reverse bytes on the remaining 72 bits + // + // 0 10 20 30 40 50 60 70 + // | | | | | | | | + // 01234567 89012345 67890123 45678901 23456789 01234567 89012345 67890123 45678901 + // -------------------------------------------------------------------------------- + // 00001001 11011111 10001001 00001001 00001001 00001001 00001001 00001000 00001001 - Without parity + // 10010000 11111011 10010001 10010000 10010000 10010000 10010000 00010000 10010000 - Reversed + // 10010000 01101011 00000001 00000000 00000000 00000000 00000000 10000000 00000000 - XOR all bytes from 1 using byte 0 + + // 72 Bit Guardall/Verex/Chubb GProx II 26 bit key with 16 bit profile + // 0 10 20 30 40 50 60 70 + // | | | | | | | | + // 01234567 890123 45 6789012345678901 2 34567890 1234567890123456 7 89012345678901 + // -------------------------------------------------------------------------------- + // XORVALUE LLLLLL DD PPPPPPPPPPPPPPPP E FFFFFFFF CCCCCCCCCCCCCCCC O UUUUUUUUUUUUUU + // 10010000 011010 11 0000000100000000 0 00000000 0000000000000001 0 00000000000000 - Profile: 256 FC: 0 Card: 1 + + // 72 Bit Guardall/Verex/Chubb GProx II 36 bit key with 26 bit profile + // 0 10 20 30 40 50 60 70 + // | | | | | | | | + // 01234567 890123 45 67890123456789012345678901 2 34567890 1234567890123456 7 8901 + // -------------------------------------------------------------------------------- + // XORVALUE LLLLLL DD PPPPPPPPPPPPPPPPPPPPPPPPPP E FFFFFFFF CCCCCCCCCCCCCCCC O UUUU + // 10111000 100100 10 00000001000000000000000000 1 01000000 1000100010111000 1 0000 - Profile: 262144 FC: 64 Card: 35000 + + // X = XOR Key, L = Message length, D = 2 bit check digits, P = Profile, E = Wiegand leading even parity + // F = Faclity code, C = Card number, O = Wiegand trailing odd parity, U = Unused bits + + // Check 6 bits preamble 111110 + if(bit_lib_get_bits(protocol->data, 0, 6) != 0b111110) return false; + + // Check always 0 parity on every 5th bit after preamble + if(bit_lib_test_parity(protocol->data, 5, GPROXII_ENCODED_BIT_SIZE, BitLibParityAlways0, 5)) + return false; + + // Start GProx II decode + bit_lib_copy_bits(protocol->decoded_data, 0, GPROXII_ENCODED_BIT_SIZE, protocol->data, 6); + + // Remove parity + bit_lib_remove_bit_every_nth(protocol->decoded_data, 0, GPROXII_ENCODED_BIT_SIZE, 5); + + // Reverse bytes + for(int i = 0; i < 9; i++) { + protocol->decoded_data[i] = bit_lib_reverse_8_fast(protocol->decoded_data[i]); + } + + // DeXOR from byte 1 using byte 0 + for(int i = 1; i < 9; i++) { + protocol->decoded_data[i] = protocol->decoded_data[0] ^ protocol->decoded_data[i]; + } + + // Check card length is either 26 or 36 + int card_len = bit_lib_get_bits(protocol->decoded_data, 8, 6); + if(card_len == 26 || card_len == 36) { + return true; + } else { + return false; // If we don't get a 26 or 36 it's not a known card type + } +} + +bool protocol_gproxii_decoder_feed(ProtocolGProxII* protocol, bool level, uint32_t duration) { + UNUSED(level); + bool pushed = false; + + // Bi-Phase Manchester decoding inverse. Short = 1, Long = 0 + if(duration >= GPROXII_SHORT_TIME_LOW && duration <= GPROXII_SHORT_TIME_HIGH) { + if(protocol->last_short == false) { + protocol->last_short = true; + } else { + pushed = true; + bit_lib_push_bit(protocol->data, GPROXII_ENCODED_BYTE_FULL_SIZE, true); + protocol->last_short = false; + } + } else if(duration >= GPROXII_LONG_TIME_LOW && duration <= GPROXII_LONG_TIME_HIGH) { + if(protocol->last_short == false) { + pushed = true; + bit_lib_push_bit(protocol->data, GPROXII_ENCODED_BYTE_FULL_SIZE, false); + } else { + // reset + protocol->last_short = false; + } + } else { + // reset + protocol->last_short = false; + } + + if(pushed && protocol_gproxii_can_be_decoded(protocol)) { + return true; + } + + return false; +} + +bool protocol_gproxii_encoder_start(ProtocolGProxII* protocol) { + protocol->encoded_index = 0; + protocol->last_short = false; + protocol->last_level = false; + return true; +} + +LevelDuration protocol_gproxii_encoder_yield(ProtocolGProxII* protocol) { + uint32_t duration; + protocol->last_level = !protocol->last_level; + + bool bit = bit_lib_get_bit(protocol->data, protocol->encoded_index); + + // Bi-Phase Manchester encoder inverted + if(bit) { + // two short pulses for 1 + duration = GPROXII_SHORT_TIME / 8; + if(protocol->last_short) { + bit_lib_increment_index(protocol->encoded_index, 96); + protocol->last_short = false; + } else { + protocol->last_short = true; + } + } else { + // one long pulse for 0 + duration = GPROXII_LONG_TIME / 8; + bit_lib_increment_index(protocol->encoded_index, 96); + } + return level_duration_make(protocol->last_level, duration); +} + +void protocol_gproxii_render_data(ProtocolGProxII* protocol, FuriString* result) { + int xor_code = bit_lib_get_bits(protocol->decoded_data, 0, 8); + int card_len = bit_lib_get_bits(protocol->decoded_data, 8, 6); + int crc_code = bit_lib_get_bits(protocol->decoded_data, 14, 2); + + if(card_len == 26) { // 26 Bit card + // Print FC, Card and Length + furi_string_cat_printf( + result, + "FC: %hhu Card: %hu LEN: %hhu\n", + bit_lib_get_bits(protocol->decoded_data, 33, 8), + bit_lib_get_bits_16(protocol->decoded_data, 41, 16), + card_len); + // XOR Key, CRC and Profile + furi_string_cat_printf( + result, + "XOR: %hhu CRC: %hhu P: %04hX", + xor_code, + crc_code, + bit_lib_get_bits_16(protocol->decoded_data, 16, 16)); + } else if(card_len == 36) { // 36 Bit card + // Print FC, Card and Length + furi_string_cat_printf( + result, + "FC: %hhu Card: %hu LEN: %hhu\n", + bit_lib_get_bits(protocol->decoded_data, 43, 8), + bit_lib_get_bits_16(protocol->decoded_data, 51, 16), + card_len); + // XOR Key, CRC and Profile + furi_string_cat_printf( + result, + "XOR: %hhu CRC: %hhu P: %06lX", + xor_code, + crc_code, + bit_lib_get_bits_32(protocol->decoded_data, 16, 26)); + } else { + furi_string_cat_printf(result, "Read Error\n"); + } +} + +bool protocol_gproxii_write_data(ProtocolGProxII* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_BIPHASE | LFRFID_T5577_BITRATE_RF_64 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +} + +const ProtocolBase protocol_gproxii = { + .name = "GProxII", + .manufacturer = "Guardall", + .data_size = GPROXII_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_gproxii_alloc, + .free = (ProtocolFree)protocol_gproxii_free, + .get_data = (ProtocolGetData)protocol_gproxii_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_gproxii_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_gproxii_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_gproxii_encoder_start, + .yield = (ProtocolEncoderYield)protocol_gproxii_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_gproxii_render_data, + .render_brief_data = (ProtocolRenderData)protocol_gproxii_render_data, + .write_data = (ProtocolWriteData)protocol_gproxii_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_gproxii.h b/lib/lfrfid/protocols/protocol_gproxii.h new file mode 100644 index 000000000..002c3024f --- /dev/null +++ b/lib/lfrfid/protocols/protocol_gproxii.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_gproxii;