diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 3422e91af..6d0eadb2b 100644 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -85,6 +85,13 @@ Nfc* nfc_alloc() { nfc->view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box)); string_init(nfc->text_box_store); + // Variable Item List + nfc->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + nfc->view_dispatcher, + NfcViewVarItemList, + variable_item_list_get_view(nfc->variable_item_list)); + // Custom Widget nfc->widget = widget_alloc(); view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget)); @@ -150,6 +157,10 @@ void nfc_free(Nfc* nfc) { text_box_free(nfc->text_box); string_clear(nfc->text_box_store); + // Variable Item List + view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewVarItemList); + variable_item_list_free(nfc->variable_item_list); + // Custom Widget view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget); widget_free(nfc->widget); diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h index bcfe4a219..305920885 100644 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,7 @@ struct Nfc { TextInput* text_input; ByteInput* byte_input; TextBox* text_box; + VariableItemList* variable_item_list; Widget* widget; DictAttack* dict_attack; @@ -83,6 +85,7 @@ typedef enum { NfcViewTextInput, NfcViewByteInput, NfcViewTextBox, + NfcViewVarItemList, NfcViewWidget, NfcViewDictAttack, } NfcView; diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index ff34a11d8..9a20de3b8 100644 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -35,6 +35,11 @@ ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) +ADD_SCENE(nfc, passport_read, PassportReadSuccess) +ADD_SCENE(nfc, passport_menu, PassportMenu) +ADD_SCENE(nfc, passport_bac, PassportBac) +//ADD_SCENE(nfc, passport_pace, PassportPace) +ADD_SCENE(nfc, passport_date, PassportDate) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) ADD_SCENE(nfc, device_info, DeviceInfo) ADD_SCENE(nfc, delete, Delete) diff --git a/applications/nfc/scenes/nfc_scene_passport_bac.c b/applications/nfc/scenes/nfc_scene_passport_bac.c new file mode 100644 index 000000000..0998d0445 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_passport_bac.c @@ -0,0 +1,105 @@ +#include "../nfc_i.h" + +#define TAG "PassportBac" + +#define MRTD_AUTH_METHOD_COUNT 2 +// Indexes must match MrtdAuthMethod (lib/nfc/protocols/mrtd.h) +const char* const mrtd_auth_method_text[MRTD_AUTH_METHOD_COUNT] = { + "BAC", + "PACE", +}; + +typedef enum { + NfcScenePassportAuthSelectDob, + NfcScenePassportAuthSelectDoe, + NfcScenePassportAuthSelectDocNr, + NfcScenePassportAuthSelectMethod, + NfcScenePassportAuthSelectAuth, +} NfcScenePassportAuthSelect; + +void nfc_scene_passport_bac_var_list_enter_callback(void* context, uint32_t index) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_passport_bac_auth_method_changed(VariableItem* item) { + Nfc* nfc = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + nfc->dev->dev_data.mrtd_data.auth.method = index; + variable_item_set_current_value_text(item, mrtd_auth_method_text[index]); +} + +void nfc_scene_passport_bac_on_enter(void* context) { + Nfc* nfc = context; + + VariableItemList* variable_item_list = nfc->variable_item_list; + + VariableItem* item; + uint8_t value_index; + + variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL); + //TODO: show dates in menu? + + variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); + + variable_item_list_add(variable_item_list, "Document Nr.", 1, NULL, NULL); + //TODO: add scene to enter docnr, based on nfc_scene_passport_date.c + + item = variable_item_list_add( + variable_item_list, + "Method", + MRTD_AUTH_METHOD_COUNT, + nfc_scene_passport_bac_auth_method_changed, + nfc); + + value_index = nfc->dev->dev_data.mrtd_data.auth.method; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, mrtd_auth_method_text[value_index]); + + variable_item_list_add(variable_item_list, "Authenticate and read", 1, NULL, NULL); + + variable_item_list_set_enter_callback( + variable_item_list, nfc_scene_passport_bac_var_list_enter_callback, nfc); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewVarItemList); +} + +bool nfc_scene_passport_bac_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + FURI_LOG_D(TAG, "event.event: %d", event.event); + switch(event.event) { + case NfcScenePassportAuthSelectDob: + scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 0); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate); + consumed = true; + break; + case NfcScenePassportAuthSelectDoe: + scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 1); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate); + consumed = true; + break; + case NfcScenePassportAuthSelectDocNr: + consumed = true; + break; + case NfcScenePassportAuthSelectMethod: + consumed = true; + break; + case NfcScenePassportAuthSelectAuth: + consumed = true; + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_passport_bac_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + variable_item_list_reset(nfc->variable_item_list); +} diff --git a/applications/nfc/scenes/nfc_scene_passport_date.c b/applications/nfc/scenes/nfc_scene_passport_date.c new file mode 100644 index 000000000..2b553cc89 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_passport_date.c @@ -0,0 +1,126 @@ +#include "../nfc_i.h" +#include "m-string.h" +#include + +#define TAG "PassportDate" + +#define DATE_LENGTH 6 + +//TODO: use types in .h file? also in nfc_scene_passport_bac.c +#define NFC_PASSPORT_DATE_BIRTH 0 +#define NFC_PASSPORT_DATE_EXPIRY 1 + +void nfc_scene_passport_date_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_passport_date_on_enter(void* context) { + Nfc* nfc = context; + + MrtdDate date_value; + + uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate); + + //TODO: numbers only + TextInput* text_input = nfc->text_input; + + switch(date_type) { + case NFC_PASSPORT_DATE_BIRTH: + text_input_set_header_text(text_input, "Birth Date"); + date_value = nfc->dev->dev_data.mrtd_data.auth.bac.birth_date; + break; + case NFC_PASSPORT_DATE_EXPIRY: + text_input_set_header_text(text_input, "Expiry Date"); + date_value = nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date; + break; + } + + bool date_empty = false; + if(date_value.year == 0 || date_value.month == 0 || date_value.day == 0 || + date_value.year > 100 || date_value.month > 13 || date_value.day > 31) { + nfc_text_store_set(nfc, "YYMMDD"); + date_empty = true; + } else { + snprintf( + nfc->text_store, + DATE_LENGTH + 1, + "%02u%02u%02u", + date_value.year % 100, + date_value.month % 112, + date_value.day % 31); // incl. '\x00' + } + + text_input_set_result_callback( + text_input, + nfc_scene_passport_date_text_input_callback, + nfc, + nfc->text_store, + DATE_LENGTH + 1, // incl. '\x00' + date_empty); // Use as template + + //TODO: add validator for valid date (YYMMDD) + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); +} + +bool nfc_scene_passport_date_save(Nfc* nfc) { + FURI_LOG_D(TAG, "Value2: %s", nfc->text_store); + + int year; + int month; + int day; + int ret = sscanf(nfc->text_store, "%02d%02d%02d", &year, &month, &day); + + if(ret != 3) { + FURI_LOG_E(TAG, "Invalid date entered (YYMMDD): %s", nfc->text_store); + return false; + } + + MrtdDate date_value; + date_value.year = year; + date_value.month = month; + date_value.day = day; + + uint32_t date_type = scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportDate); + + //TODO: use types in .h file? also in nfc_scene_passport_bac.c + switch(date_type) { + case NFC_PASSPORT_DATE_BIRTH: + nfc->dev->dev_data.mrtd_data.auth.bac.birth_date = date_value; + break; + case NFC_PASSPORT_DATE_EXPIRY: + nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date = date_value; + break; + } + + return true; +} + +bool nfc_scene_passport_date_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + FURI_LOG_D(TAG, "Value: %s", nfc->text_store); + + nfc_scene_passport_date_save(nfc); + //TODO: handle invalid date (returned false) + + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportBac); + } + } + return consumed; +} + +void nfc_scene_passport_date_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + // TODO: clear validator + + text_input_reset(nfc->text_input); +} diff --git a/applications/nfc/scenes/nfc_scene_passport_menu.c b/applications/nfc/scenes/nfc_scene_passport_menu.c new file mode 100644 index 000000000..f17c5f2a1 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_passport_menu.c @@ -0,0 +1,59 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexBac, + SubmenuIndexPace, +}; + +void nfc_scene_passport_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_passport_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + //TODO: enable/disable available methods + + submenu_add_item( + submenu, "BAC", SubmenuIndexBac, nfc_scene_passport_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "PACE", SubmenuIndexPace, nfc_scene_passport_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_passport_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexBac) { + nfc->dev->format = NfcDeviceSaveFormatUid; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportBac); + consumed = true; + } else if(event.event == SubmenuIndexPace) { + //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPace); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportMenu, event.event); + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_passport_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_passport_read.c b/applications/nfc/scenes/nfc_scene_passport_read.c new file mode 100644 index 000000000..37547224d --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_passport_read.c @@ -0,0 +1,73 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_passport_read_widget_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_passport_read_on_enter(void* context) { + Nfc* nfc = context; + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + //MrtdBacDAta* bac_data = &nfc->dev->dev_data.mrtd_data.bac; + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + Widget* widget = nfc->widget; + + // Setup Custom Widget view + string_t temp_str; + string_init_printf(temp_str, "\e#Passport\n"); + char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; + //TODO: NFC-B? + string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + string_cat_printf(temp_str, " %02X", data->uid[i]); + } + string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); + string_cat_printf(temp_str, " SAK: %02X", data->sak); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); + string_clear(temp_str); + + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_widget_callback, nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_passport_read_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportBac); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_passport_read_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_read.c b/applications/nfc/scenes/nfc_scene_read.c index 00b7c8fac..5a0b632a5 100644 --- a/applications/nfc/scenes/nfc_scene_read.c +++ b/applications/nfc/scenes/nfc_scene_read.c @@ -83,6 +83,10 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadPassport) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index f9a0b24ba..5a143ebd4 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ typedef void (*NfcLoadingCallback)(void* context, bool state); typedef enum { NfcDeviceProtocolUnknown, NfcDeviceProtocolEMV, + NfcDeviceProtocolMRTD, NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, @@ -56,6 +58,7 @@ typedef struct { }; union { EmvData emv_data; + MrtdData mrtd_data; MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f07ea616c..c2165695d 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -179,7 +179,9 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont return read_success; } -static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +//TODO: remove unused attribute +static bool __attribute__((unused)) +nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; EmvApplication emv_app = {}; EmvData* result = &nfc_worker->dev_data->emv_data; @@ -214,6 +216,44 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte return read_success; } +static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + MrtdApplication mrtd_app = {}; + //EmvData* result = &nfc_worker->dev_data->emv_data; + + nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + do { + // Read passport + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; + if(!mrtd_select_lds1(tx_rx, &mrtd_app)) break; + + /* + // Copy data + // TODO Set EmvData to reader or like in mifare ultralight! + result->number_len = emv_app.card_number_len; + memcpy(result->number, emv_app.card_number, result->number_len); + result->aid_len = emv_app.aid_len; + memcpy(result->aid, emv_app.aid, result->aid_len); + if(emv_app.name_found) { + memcpy(result->name, emv_app.name, sizeof(emv_app.name)); + } + if(emv_app.exp_month) { + result->exp_mon = emv_app.exp_month; + result->exp_year = emv_app.exp_year; + } + if(emv_app.country_code) { + result->country_code = emv_app.country_code; + } + if(emv_app.currency_code) { + result->currency_code = emv_app.currency_code; + } + */ + read_success = true; + } while(false); + + return read_success; +} + static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; @@ -239,8 +279,14 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t card_read = true; } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { FURI_LOG_I(TAG, "ISO14443-4 card detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; - if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) { + //TODO: EMV read on MRTD results in states: 0, 10, 11, 13, 30, 33? + /*if(nfc_worker_read_bank_card(nfc_worker, tx_rx)) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; + } else*/ + if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { + FURI_LOG_I(TAG, "MRTD reading"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; + } else { FURI_LOG_I(TAG, "Unknown card. Save UID"); nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; } @@ -283,6 +329,9 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } else if(dev_data->protocol == NfcDeviceProtocolEMV) { event = NfcWorkerEventReadBankCard; break; + } else if(dev_data->protocol == NfcDeviceProtocolMRTD) { + event = NfcWorkerEventReadPassport; + break; } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { event = NfcWorkerEventReadUidNfcA; break; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 22cbc3dcc..cc8058061 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -38,6 +38,7 @@ typedef enum { NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, NfcWorkerEventReadBankCard, + NfcWorkerEventReadPassport, // Nfc worker common events NfcWorkerEventSuccess, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 1e98879a7..024febb55 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c new file mode 100644 index 000000000..f70ed69d4 --- /dev/null +++ b/lib/nfc/protocols/mrtd.c @@ -0,0 +1,88 @@ +#include "mrtd.h" + +#define TAG "Mrtd" + +//TODO: Check EF.DIR first? Before LDS1 +//TODO: ICAO 9303 p11 ยง4.2 steps + +static void mrtd_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { + if(furi_log_get_level() == FuriLogLevelTrace) { + FURI_LOG_T(TAG, "%s", message); + printf("TX: "); + for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) { + printf("%02X ", tx_rx->tx_data[i]); + } + printf("\r\nRX: "); + for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { + printf("%02X ", tx_rx->rx_data[i]); + } + printf("\r\n"); + } +} + +uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) { + if(len != 2) { + FURI_LOG_E(TAG, "Expecting 2 byte responses only"); + return 0xffff; + } + + return (buffer[0] << 8) | buffer[1]; +} + +bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { + UNUSED(mrtd_app); + + uint8_t select_emrtd_cmd[] = { + 0x00, + 0xa4, // SELECT + 0x04, + 0x0C, // P1,P2: DF + 0x07, // Lc: Data length + 0xa0, + 0x00, + 0x00, + 0x02, + 0x47, + 0x10, + 0x01, // Data: LDS1 eMRTD Application + 0x00, // Le + }; + + memcpy(tx_rx->tx_data, select_emrtd_cmd, sizeof(select_emrtd_cmd)); + tx_rx->tx_bits = sizeof(select_emrtd_cmd) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + bool lds1_success = false; + + FURI_LOG_D(TAG, "Send select LDS1 eMRTD"); + if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + mrtd_trace(tx_rx, "Select LDS1:"); + if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { + lds1_success = true; + } else { + FURI_LOG_D(TAG, "Select LDS1 eMRTD response is not 0x9000"); + } + } else { + FURI_LOG_E(TAG, "Failed select LDS1"); + } + + return lds1_success; +} + +int mrtd_bac_keyhandshake(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { + UNUSED(tx_rx); + UNUSED(mrtd_app); + + return 0; +} + +bool mrtd_read(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { + furi_assert(tx_rx); + furi_assert(mrtd_app); + bool mrtd_read = false; + + memset(mrtd_app, 0, sizeof(MrtdApplication)); + + mrtd_read = mrtd_bac_keyhandshake(tx_rx, mrtd_app); + return mrtd_read; +} diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h new file mode 100644 index 000000000..6963439b0 --- /dev/null +++ b/lib/nfc/protocols/mrtd.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +typedef struct { + uint8_t* kmrz; + uint8_t ksenc[16]; + uint8_t ksmac[16]; + uint64_t ssc_long; +} MrtdApplication; + +typedef struct { + uint8_t year; + uint8_t month; + uint8_t day; +} MrtdDate; + +// NULL terminated document ID +#define MRTD_DOCNR_MAX_LENGTH 21 + +typedef struct { + // BAC input fields + MrtdDate birth_date; + MrtdDate expiry_date; + char doc_number[MRTD_DOCNR_MAX_LENGTH]; +} MrtdBacData; + +typedef enum { + MrtdAuthMethodBac, + MrtdAuthMethodPace, +} MrtdAuthMethod; + +typedef struct { + MrtdAuthMethod method; + union { + MrtdBacData bac; + }; +} MrtdAuthData; + +typedef struct { + MrtdAuthData auth; +} MrtdData; + +/** Select the LDS1 eMRTD application + * @note Can be used to detect presence of Passport/ID-card + * + * @param tx_rx FuriHalNfcTxRxContext instance + * @param emv_app MrtdApplication instance + * + * @return true on success + */ +bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app);