From a286c7b1a7d107794f54a285804707ec98817f19 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:01 +0200 Subject: [PATCH 01/44] Text input overwrite max size template The TextInput would not allow another key press if the template that is going to be overwritten is over the over the buffer size. This change clears any default text, before checking the length of entered input. --- applications/gui/modules/text_input.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index 26f74e7ac..9dff957f7 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -318,15 +318,17 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b } } else if(selected == BACKSPACE_KEY) { text_input_backspace_cb(model); - } else if(text_length < (model->text_buffer_size - 1)) { + } else { if(model->clear_default_text) { text_length = 0; } - if(text_length == 0 && char_is_lowercase(selected)) { - selected = char_to_uppercase(selected); + if(text_length < (model->text_buffer_size - 1)) { + if(text_length == 0 && char_is_lowercase(selected)) { + selected = char_to_uppercase(selected); + } + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; } - model->text_buffer[text_length] = selected; - model->text_buffer[text_length + 1] = 0; } model->clear_default_text = false; } From 3eb0c43756f50aa36dad98a8e5267456850eb411 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:13 +0200 Subject: [PATCH 02/44] [NFC] Recognize MRTD + initial menus --- applications/nfc/nfc.c | 11 ++ applications/nfc/nfc_i.h | 3 + applications/nfc/scenes/nfc_scene_config.h | 5 + .../nfc/scenes/nfc_scene_passport_bac.c | 105 +++++++++++++++ .../nfc/scenes/nfc_scene_passport_date.c | 126 ++++++++++++++++++ .../nfc/scenes/nfc_scene_passport_menu.c | 59 ++++++++ .../nfc/scenes/nfc_scene_passport_read.c | 73 ++++++++++ applications/nfc/scenes/nfc_scene_read.c | 4 + lib/nfc/nfc_device.h | 3 + lib/nfc/nfc_worker.c | 55 +++++++- lib/nfc/nfc_worker.h | 1 + lib/nfc/nfc_worker_i.h | 1 + lib/nfc/protocols/mrtd.c | 88 ++++++++++++ lib/nfc/protocols/mrtd.h | 52 ++++++++ 14 files changed, 583 insertions(+), 3 deletions(-) create mode 100644 applications/nfc/scenes/nfc_scene_passport_bac.c create mode 100644 applications/nfc/scenes/nfc_scene_passport_date.c create mode 100644 applications/nfc/scenes/nfc_scene_passport_menu.c create mode 100644 applications/nfc/scenes/nfc_scene_passport_read.c create mode 100644 lib/nfc/protocols/mrtd.c create mode 100644 lib/nfc/protocols/mrtd.h 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); From 67eff89a092e70781994d9f32c9b599feed99ba7 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:46 +0200 Subject: [PATCH 03/44] Show set dates in MRTD auth menu --- .../nfc/scenes/nfc_scene_passport_bac.c | 19 ++++++++++++++++--- .../nfc/scenes/nfc_scene_passport_date.c | 18 +++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/applications/nfc/scenes/nfc_scene_passport_bac.c b/applications/nfc/scenes/nfc_scene_passport_bac.c index 0998d0445..379fea4f5 100644 --- a/applications/nfc/scenes/nfc_scene_passport_bac.c +++ b/applications/nfc/scenes/nfc_scene_passport_bac.c @@ -37,10 +37,23 @@ void nfc_scene_passport_bac_on_enter(void* context) { VariableItem* item; uint8_t value_index; - variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL); - //TODO: show dates in menu? + const size_t temp_str_size = 10; + char temp_str[temp_str_size]; + snprintf(temp_str, temp_str_size, "%02u%02u%02u", + nfc->dev->dev_data.mrtd_data.auth.bac.birth_date.year, + nfc->dev->dev_data.mrtd_data.auth.bac.birth_date.month, + nfc->dev->dev_data.mrtd_data.auth.bac.birth_date.day); - variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); + item = variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL); + variable_item_set_current_value_text(item, temp_str); + + snprintf(temp_str, temp_str_size, "%02u%02u%02u", + nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date.year, + nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date.month, + nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date.day); + + item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); + variable_item_set_current_value_text(item, temp_str); 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 diff --git a/applications/nfc/scenes/nfc_scene_passport_date.c b/applications/nfc/scenes/nfc_scene_passport_date.c index 2b553cc89..45ef86486 100644 --- a/applications/nfc/scenes/nfc_scene_passport_date.c +++ b/applications/nfc/scenes/nfc_scene_passport_date.c @@ -43,13 +43,17 @@ void nfc_scene_passport_date_on_enter(void* context) { nfc_text_store_set(nfc, "YYMMDD"); date_empty = true; } else { + char temp_str[10]; snprintf( - nfc->text_store, - DATE_LENGTH + 1, + temp_str, + 10, "%02u%02u%02u", - date_value.year % 100, - date_value.month % 112, - date_value.day % 31); // incl. '\x00' + date_value.year, + date_value.month, + date_value.day); + + memcpy(nfc->text_store, temp_str, DATE_LENGTH); + nfc->text_store[DATE_LENGTH] = '\x00'; } text_input_set_result_callback( @@ -66,8 +70,6 @@ void nfc_scene_passport_date_on_enter(void* context) { } bool nfc_scene_passport_date_save(Nfc* nfc) { - FURI_LOG_D(TAG, "Value2: %s", nfc->text_store); - int year; int month; int day; @@ -104,8 +106,6 @@ bool nfc_scene_passport_date_on_event(void* context, SceneManagerEvent event) { 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) From 9b861e2313a548664df781827bc1cfbebdbcbf74 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:52 +0200 Subject: [PATCH 04/44] MRTD popup "PACE todo" --- applications/nfc/scenes/nfc_scene_config.h | 4 +- .../nfc/scenes/nfc_scene_passport_bac.c | 3 ++ .../nfc/scenes/nfc_scene_passport_pace_todo.c | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 applications/nfc/scenes/nfc_scene_passport_pace_todo.c diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index 9a20de3b8..711281093 100644 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -37,9 +37,9 @@ 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_bac, PassportBac) //TODO: rename to Auth ADD_SCENE(nfc, passport_date, PassportDate) +ADD_SCENE(nfc, passport_pace_todo, PassportPaceTodo) 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 index 379fea4f5..bc27a91f5 100644 --- a/applications/nfc/scenes/nfc_scene_passport_bac.c +++ b/applications/nfc/scenes/nfc_scene_passport_bac.c @@ -100,6 +100,9 @@ bool nfc_scene_passport_bac_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case NfcScenePassportAuthSelectAuth: + if(nfc->dev->dev_data.mrtd_data.auth.method == MrtdAuthMethodPace) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPaceTodo); + } consumed = true; break; } diff --git a/applications/nfc/scenes/nfc_scene_passport_pace_todo.c b/applications/nfc/scenes/nfc_scene_passport_pace_todo.c new file mode 100644 index 000000000..92dfaed97 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_passport_pace_todo.c @@ -0,0 +1,40 @@ +#include "../nfc_i.h" + +void nfc_scene_passport_pace_todo_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_passport_pace_todo_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 0, 2, &I_DolphinCommon_56x48); + popup_set_header(popup, "PACE not yet implemented", 0, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 2000); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_passport_pace_todo_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_passport_pace_todo_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportBac); + } + } + return consumed; +} + +void nfc_scene_passport_pace_todo_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} From 82352705dd0121c8e53bf7cfb13f50e4137df736 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:54 +0200 Subject: [PATCH 05/44] MRTD rename mrtd_data.auth.bac to mrtd_data.auth --- .../nfc/scenes/nfc_scene_passport_bac.c | 12 ++++++------ .../nfc/scenes/nfc_scene_passport_date.c | 8 ++++---- lib/nfc/protocols/mrtd.h | 17 +++++++---------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/applications/nfc/scenes/nfc_scene_passport_bac.c b/applications/nfc/scenes/nfc_scene_passport_bac.c index bc27a91f5..ba3b3dfd0 100644 --- a/applications/nfc/scenes/nfc_scene_passport_bac.c +++ b/applications/nfc/scenes/nfc_scene_passport_bac.c @@ -40,17 +40,17 @@ void nfc_scene_passport_bac_on_enter(void* context) { const size_t temp_str_size = 10; char temp_str[temp_str_size]; snprintf(temp_str, temp_str_size, "%02u%02u%02u", - nfc->dev->dev_data.mrtd_data.auth.bac.birth_date.year, - nfc->dev->dev_data.mrtd_data.auth.bac.birth_date.month, - nfc->dev->dev_data.mrtd_data.auth.bac.birth_date.day); + nfc->dev->dev_data.mrtd_data.auth.birth_date.year, + nfc->dev->dev_data.mrtd_data.auth.birth_date.month, + nfc->dev->dev_data.mrtd_data.auth.birth_date.day); item = variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL); variable_item_set_current_value_text(item, temp_str); snprintf(temp_str, temp_str_size, "%02u%02u%02u", - nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date.year, - nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date.month, - nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date.day); + nfc->dev->dev_data.mrtd_data.auth.expiry_date.year, + nfc->dev->dev_data.mrtd_data.auth.expiry_date.month, + nfc->dev->dev_data.mrtd_data.auth.expiry_date.day); item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); variable_item_set_current_value_text(item, temp_str); diff --git a/applications/nfc/scenes/nfc_scene_passport_date.c b/applications/nfc/scenes/nfc_scene_passport_date.c index 45ef86486..cee59038f 100644 --- a/applications/nfc/scenes/nfc_scene_passport_date.c +++ b/applications/nfc/scenes/nfc_scene_passport_date.c @@ -29,11 +29,11 @@ void nfc_scene_passport_date_on_enter(void* context) { 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; + date_value = nfc->dev->dev_data.mrtd_data.auth.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; + date_value = nfc->dev->dev_data.mrtd_data.auth.expiry_date; break; } @@ -90,10 +90,10 @@ bool nfc_scene_passport_date_save(Nfc* nfc) { //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; + nfc->dev->dev_data.mrtd_data.auth.birth_date = date_value; break; case NFC_PASSPORT_DATE_EXPIRY: - nfc->dev->dev_data.mrtd_data.auth.bac.expiry_date = date_value; + nfc->dev->dev_data.mrtd_data.auth.expiry_date = date_value; break; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 6963439b0..87f856b08 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -18,13 +18,6 @@ typedef struct { // 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, @@ -32,9 +25,13 @@ typedef enum { typedef struct { MrtdAuthMethod method; - union { - MrtdBacData bac; - }; + + // BAC input fields + MrtdDate birth_date; + MrtdDate expiry_date; + char doc_number[MRTD_DOCNR_MAX_LENGTH]; + + //TODO: PACE } MrtdAuthData; typedef struct { From d585eec50ef4272a8e24af653e5dba9c601f0eb4 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:56 +0200 Subject: [PATCH 06/44] MRTD configure docnr for auth --- applications/nfc/scenes/nfc_scene_config.h | 1 + .../nfc/scenes/nfc_scene_passport_bac.c | 18 ++++- .../nfc/scenes/nfc_scene_passport_docnr.c | 67 +++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 applications/nfc/scenes/nfc_scene_passport_docnr.c diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index 711281093..92dea6394 100644 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -39,6 +39,7 @@ ADD_SCENE(nfc, passport_read, PassportReadSuccess) ADD_SCENE(nfc, passport_menu, PassportMenu) ADD_SCENE(nfc, passport_bac, PassportBac) //TODO: rename to Auth ADD_SCENE(nfc, passport_date, PassportDate) +ADD_SCENE(nfc, passport_docnr, PassportDocNr) ADD_SCENE(nfc, passport_pace_todo, PassportPaceTodo) ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) ADD_SCENE(nfc, device_info, DeviceInfo) diff --git a/applications/nfc/scenes/nfc_scene_passport_bac.c b/applications/nfc/scenes/nfc_scene_passport_bac.c index ba3b3dfd0..ca4efb6bd 100644 --- a/applications/nfc/scenes/nfc_scene_passport_bac.c +++ b/applications/nfc/scenes/nfc_scene_passport_bac.c @@ -37,7 +37,7 @@ void nfc_scene_passport_bac_on_enter(void* context) { VariableItem* item; uint8_t value_index; - const size_t temp_str_size = 10; + const size_t temp_str_size = 15; char temp_str[temp_str_size]; snprintf(temp_str, temp_str_size, "%02u%02u%02u", nfc->dev->dev_data.mrtd_data.auth.birth_date.year, @@ -55,8 +55,19 @@ void nfc_scene_passport_bac_on_enter(void* context) { item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); variable_item_set_current_value_text(item, temp_str); - 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, "Document Nr.", 1, NULL, NULL); + + strncpy(temp_str, nfc->dev->dev_data.mrtd_data.auth.doc_number, temp_str_size); + temp_str[temp_str_size] = '\x00'; + if(strlen(temp_str) > 8) { + temp_str[8] = '.'; + temp_str[9] = '.'; + temp_str[10] = '.'; + temp_str[11] = '\x00'; + } + variable_item_set_current_value_text( + item, + temp_str); item = variable_item_list_add( variable_item_list, @@ -94,6 +105,7 @@ bool nfc_scene_passport_bac_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case NfcScenePassportAuthSelectDocNr: + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDocNr); consumed = true; break; case NfcScenePassportAuthSelectMethod: diff --git a/applications/nfc/scenes/nfc_scene_passport_docnr.c b/applications/nfc/scenes/nfc_scene_passport_docnr.c new file mode 100644 index 000000000..a4ee1c2cd --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_passport_docnr.c @@ -0,0 +1,67 @@ +#include "../nfc_i.h" +#include "m-string.h" +#include + +#define TAG "PassportDocnr" + +void nfc_scene_passport_docnr_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_passport_docnr_on_enter(void* context) { + Nfc* nfc = context; + + TextInput* text_input = nfc->text_input; + text_input_set_header_text(text_input, "Document Nr."); + + char* docnr = nfc->dev->dev_data.mrtd_data.auth.doc_number; + bool docnr_empty = false; + + if(*docnr) { + nfc_text_store_set(nfc, docnr); + docnr_empty = false; + } else { + nfc_text_store_set(nfc, "PA7HJ34M8"); + docnr_empty = true; + } + + text_input_set_result_callback( + text_input, + nfc_scene_passport_docnr_text_input_callback, + nfc, + nfc->text_store, + MRTD_DOCNR_MAX_LENGTH, // incl. '\x00' + docnr_empty); // Use as template + + //TODO: add validator? + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); +} + +bool nfc_scene_passport_docnr_save(Nfc* nfc) { + strncpy(nfc->dev->dev_data.mrtd_data.auth.doc_number, nfc->text_store, MRTD_DOCNR_MAX_LENGTH); + return true; +} + +bool nfc_scene_passport_docnr_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + nfc_scene_passport_docnr_save(nfc); + + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportBac); + } + } + return consumed; +} + +void nfc_scene_passport_docnr_on_exit(void* context) { + Nfc* nfc = context; + + text_input_reset(nfc->text_input); +} From c82b55575a47a45ab6e66b264fbed7534be84da7 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:57 +0200 Subject: [PATCH 07/44] Make MRTD work along with EMV --- lib/nfc/nfc_worker.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index c2165695d..aa63b0d6d 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -179,9 +179,7 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont return read_success; } -//TODO: remove unused attribute -static bool __attribute__((unused)) -nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +static bool 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; @@ -279,17 +277,24 @@ 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"); - //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 { + //TODO: thoughts on improving logic/readability here? + do { + FURI_LOG_D(TAG, "Try reading EMV"); + if(nfc_worker_read_bank_card(nfc_worker, tx_rx)) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; + break; + } + + furi_hal_nfc_sleep(); // Needed between checks + FURI_LOG_D(TAG, "Try reading MRTD"); + if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; + break; + } + FURI_LOG_I(TAG, "Unknown card. Save UID"); nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - } + } while(false); card_read = true; } else { nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; From fc9b37850d3f1593ae8a4acb5be2b93d124c1342 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:12:59 +0200 Subject: [PATCH 08/44] Minor improvements --- .../nfc/scenes/nfc_scene_nfc_data_info.c | 4 +++- .../nfc/scenes/nfc_scene_passport_menu.c | 20 +++++++++---------- .../nfc/scenes/nfc_scene_passport_pace_todo.c | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/applications/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/nfc/scenes/nfc_scene_nfc_data_info.c index 33f5e44af..c1c486a8a 100644 --- a/applications/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/nfc/scenes/nfc_scene_nfc_data_info.c @@ -32,6 +32,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { // Set tag type if(protocol == NfcDeviceProtocolEMV) { string_cat_printf(temp_str, "\e#EMV Bank Card\n"); + } else if(protocol == NfcDeviceProtocolMRTD) { + string_cat_printf(temp_str, "\e#Passport/ID\n"); } else if(protocol == NfcDeviceProtocolMifareUl) { string_cat_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true)); } else if(protocol == NfcDeviceProtocolMifareClassic) { @@ -130,4 +132,4 @@ void nfc_scene_nfc_data_info_on_exit(void* context) { Nfc* nfc = context; widget_reset(nfc->widget); -} \ No newline at end of file +} diff --git a/applications/nfc/scenes/nfc_scene_passport_menu.c b/applications/nfc/scenes/nfc_scene_passport_menu.c index f17c5f2a1..27febcb18 100644 --- a/applications/nfc/scenes/nfc_scene_passport_menu.c +++ b/applications/nfc/scenes/nfc_scene_passport_menu.c @@ -1,8 +1,8 @@ #include "../nfc_i.h" enum SubmenuIndex { - SubmenuIndexBac, - SubmenuIndexPace, + SubmenuIndexSave, + SubmenuIndexInfo, }; void nfc_scene_passport_menu_submenu_callback(void* context, uint32_t index) { @@ -15,13 +15,10 @@ 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, "Save", SubmenuIndexSave, nfc_scene_passport_menu_submenu_callback, nfc); submenu_add_item( - submenu, "PACE", SubmenuIndexPace, nfc_scene_passport_menu_submenu_callback, nfc); - + submenu, "Info", SubmenuIndexInfo, nfc_scene_passport_menu_submenu_callback, nfc); submenu_set_selected_item( nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcScenePassportMenu)); @@ -33,14 +30,15 @@ bool nfc_scene_passport_menu_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexBac) { + if(event.event == SubmenuIndexSave) { + //TODO: save more than just UID nfc->dev->format = NfcDeviceSaveFormatUid; // Clear device name nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcScenePassportBac); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; - } else if(event.event == SubmenuIndexPace) { - //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPace); + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportMenu, event.event); diff --git a/applications/nfc/scenes/nfc_scene_passport_pace_todo.c b/applications/nfc/scenes/nfc_scene_passport_pace_todo.c index 92dfaed97..e3a5fe0cb 100644 --- a/applications/nfc/scenes/nfc_scene_passport_pace_todo.c +++ b/applications/nfc/scenes/nfc_scene_passport_pace_todo.c @@ -10,8 +10,8 @@ void nfc_scene_passport_pace_todo_on_enter(void* context) { // Setup view Popup* popup = nfc->popup; - popup_set_icon(popup, 0, 2, &I_DolphinCommon_56x48); - popup_set_header(popup, "PACE not yet implemented", 0, 19, AlignLeft, AlignBottom); + popup_set_icon(popup, 64, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "PACE not yet implemented", 4, 4, AlignLeft, AlignTop); popup_set_timeout(popup, 2000); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_passport_pace_todo_popup_callback); From 315294f2cf899434bf2d5c24ec0c12b640294878 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:00 +0200 Subject: [PATCH 09/44] MRTD work on reading EF.DIR and EF.CardAccess --- lib/nfc/nfc_worker.c | 2 + lib/nfc/protocols/mrtd.c | 87 ++++++++++++++++++++++++++++++++++++++-- lib/nfc/protocols/mrtd.h | 4 ++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index aa63b0d6d..321de5b2c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -223,6 +223,8 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t do { // Read passport if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; + mrtd_select_efcardaccess(tx_rx, &mrtd_app); + mrtd_select_efdir(tx_rx, &mrtd_app); if(!mrtd_select_lds1(tx_rx, &mrtd_app)) break; /* diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index f70ed69d4..a70e86c22 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -4,6 +4,12 @@ //TODO: Check EF.DIR first? Before LDS1 //TODO: ICAO 9303 p11 §4.2 steps +//- Read EF.CardAccess (REQUIRED) +// If not available or does not contain PACE params, try BAC +//- Read EF.DIR (OPTIONAL) +// Check list of applications present +//- PACE (CONDITIONAL) +//- BAC (CONDITIONAL) static void mrtd_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { if(furi_log_get_level() == FuriLogLevelTrace) { @@ -29,14 +35,87 @@ uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) { return (buffer[0] << 8) | buffer[1]; } +bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { + UNUSED(mrtd_app); + + // Chris: short: 0x6700, long: yes (0x9000) + // Anna: short: 0x6700, long: yes (0x9000) + + // ICAO 9303 p10 §3.6.2 + uint8_t select_efcardaccess_cmd[] = { + 0x00, // CLA + 0xA4, // INS + 0x02, // P1 + 0x0C, // P2 + 0x02, // Lc: Data length + 0x01, 0x1C, // Data: EF.CardAccess File ID + 0x00, // Le + }; + + memcpy(tx_rx->tx_data, select_efcardaccess_cmd, sizeof(select_efcardaccess_cmd)); + tx_rx->tx_bits = sizeof(select_efcardaccess_cmd) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + bool efcardaccess_success = false; + + FURI_LOG_D(TAG, "Send select EF.CardAccess"); + if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + mrtd_trace(tx_rx, "Select EF.CardAccess:"); + if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { + efcardaccess_success = true; + } else { + FURI_LOG_D(TAG, "Select EF.CardAccess response is not 0x9000"); + } + } else { + FURI_LOG_E(TAG, "Failed select EF.CardAccess"); + } + return efcardaccess_success; +} + +bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { + UNUSED(mrtd_app); + + // Chris: short: no, long: no (0x6A82) + // Anna: short: no, long: yes (0x9000) + + uint8_t select_efdir_cmd[] = { + 0x00, // CLA + 0xA4, // INS + 0x02, // P1 + 0x0C, // P2 + 0x02, // Lc: Data length + 0x2F, 0x00, // Data: EF.DIR File ID + 0x00, // Le + }; + + memcpy(tx_rx->tx_data, select_efdir_cmd, sizeof(select_efdir_cmd)); + tx_rx->tx_bits = sizeof(select_efdir_cmd) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + bool efdir_success = false; + + FURI_LOG_D(TAG, "Send select EF.DIR"); + if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + mrtd_trace(tx_rx, "Select EF.DIR:"); + if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { + efdir_success = true; + } else { + FURI_LOG_D(TAG, "Select EF.DIR response is not 0x9000"); + } + } else { + FURI_LOG_E(TAG, "Failed select EF.DIR"); + } + return efdir_success; +} + 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 + 0x00, // CLA + 0xA4, // INS + 0x04, // P1 + 0x0C, // P2: DF 0x07, // Lc: Data length 0xa0, 0x00, diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 87f856b08..972d427bb 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -38,6 +38,10 @@ typedef struct { MrtdAuthData auth; } MrtdData; +//TODO: description +bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app); +bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app); + /** Select the LDS1 eMRTD application * @note Can be used to detect presence of Passport/ID-card * From 08afd7c88360f232ed965ea94a8fd065a979055a Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:01 +0200 Subject: [PATCH 10/44] MRTD select and read file basics --- lib/nfc/nfc_worker.c | 11 ++- lib/nfc/protocols/mrtd.c | 203 ++++++++++++++++++++++++++++++++++----- lib/nfc/protocols/mrtd.h | 47 ++++++++- 3 files changed, 227 insertions(+), 34 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 321de5b2c..f0491a4f1 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -216,16 +216,19 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; - MrtdApplication mrtd_app = {}; + MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx); //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; - mrtd_select_efcardaccess(tx_rx, &mrtd_app); - mrtd_select_efdir(tx_rx, &mrtd_app); - if(!mrtd_select_lds1(tx_rx, &mrtd_app)) break; + //mrtd_select(mrtd_app, EF.CardAccess); + //mrtd_select(mrtd_app, EF.DIR); + //mrtd_select_efcardaccess(mrtd_app); + //mrtd_select_efdir(mrtd_app); + mrtd_test(mrtd_app); + if(!mrtd_select_lds1(mrtd_app)) break; /* // Copy data diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index a70e86c22..63ffcbc8e 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -11,9 +11,9 @@ //- PACE (CONDITIONAL) //- BAC (CONDITIONAL) -static void mrtd_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { +static void mrtd_trace(MrtdApplication* app) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; 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]); @@ -27,20 +27,173 @@ static void mrtd_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { } 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]; + // Last two bytes are return code + return (buffer[len-2] << 8) | buffer[len-1]; } -bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { - UNUSED(mrtd_app); +struct EFFormat EF = { + .ATR = {.file_id = 0x2F01, .short_id = 0x01 }, + .DIR = {.file_id = 0x2F00, .short_id = 0x1E }, + .CardAccess = {.file_id = 0x011C, .short_id = 0x1C }, + .CardSecurity = {.file_id = 0x011D, .short_id = 0x1D }, + .COM = {.file_id = 0x011E, .short_id = 0x1E }, + .SOD = {.file_id = 0X011D, .short_id = 0X1D }, + .DG1 = {.file_id = 0X0101, .short_id = 0X01 }, + .DG2 = {.file_id = 0X0102, .short_id = 0X02 }, + .DG3 = {.file_id = 0X0103, .short_id = 0X03 }, + .DG4 = {.file_id = 0X0104, .short_id = 0X04 }, + .DG5 = {.file_id = 0X0105, .short_id = 0X05 }, + .DG6 = {.file_id = 0X0106, .short_id = 0X06 }, + .DG7 = {.file_id = 0X0107, .short_id = 0X07 }, + .DG8 = {.file_id = 0X0108, .short_id = 0X08 }, + .DG9 = {.file_id = 0X0109, .short_id = 0X09 }, + .DG10 = {.file_id = 0X010A, .short_id = 0X0A }, + .DG11 = {.file_id = 0X010B, .short_id = 0X0B }, + .DG12 = {.file_id = 0X010C, .short_id = 0X0C }, + .DG13 = {.file_id = 0X010D, .short_id = 0X0D }, + .DG14 = {.file_id = 0X010E, .short_id = 0X0E }, + .DG15 = {.file_id = 0X010F, .short_id = 0X0F }, + .DG16 = {.file_id = 0X0110, .short_id = 0X10 }, +}; + +/*bool mrtd_send_apdu(MrtdApplication* app, uint8_t* buffer, size_t length) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + + memcpy(tx_rx->tx_data, buffer, length); + tx_rx->tx_bits = length * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + //TODO: timeout as param? + if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + mrtd_trace(app); + uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); + if(ret_code == 0x9000) { + return true; + } else { + FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); + return false; + } + } + return false; +}*/ + +bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + + FURI_LOG_D(TAG, "Send APDU, lc: %d, le: %d", lc, le); + + size_t idx = 0; + tx_rx->tx_data[idx++] = cla; + tx_rx->tx_data[idx++] = ins; + tx_rx->tx_data[idx++] = p1; + tx_rx->tx_data[idx++] = p2; + if(lc > 0) { + tx_rx->tx_data[idx++] = lc; + memcpy(tx_rx->tx_data + idx, data, lc); + idx += lc; + } + if(le >= 0) { + tx_rx->tx_data[idx++] = le&0xff; + } + + tx_rx->tx_bits = idx * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + + //TODO: timeout as param? + if(furi_hal_nfc_tx_rx(tx_rx, 300)) { + mrtd_trace(app); + uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); + //TODO: handle other return codes? + if(ret_code == 0x9000) { + return true; + } else { + FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); + return false; + } + } + return false; +} + +bool mrtd_select(MrtdApplication* app, EFFile file) { + uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; + FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id); + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1)) { + FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id); + return false; + } + + return true; +} + +size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) { + UNUSED(buffer); + UNUSED(bufsize); + // 00 B0 offst - + FURI_LOG_D(TAG, "Read binary, offset: %d", offset); + if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, 0)) { + FURI_LOG_E(TAG, "Failed to read"); + return 0; + } + + //TODO: copy data to buffer + //TODO: return read amount + + return 0; +} + +void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { + FURI_LOG_D(TAG, "Read and dump %s:", descr); + if(!mrtd_select(app, file)) { + return; + } + uint8_t data[2048]; + size_t read = 0; + do { + read = mrtd_read_binary(app, data+read, sizeof(data)-read, read); + } while(read > 0); +} + +void mrtd_test(MrtdApplication* app) { + FURI_LOG_D(TAG, "Mrtd Test"); + mrtd_read_dump(app, EF.ATR, "EF.ATR"); + mrtd_read_dump(app, EF.DIR, "EF.DIR"); + mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess"); + mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity"); +} + +MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { + MrtdApplication* app = malloc(sizeof(MrtdApplication)); + + app->tx_rx = tx_rx; + + return app; +} + +void mrtd_free(MrtdApplication* app) { + furi_assert(app); + free(app); +} + +/* +size_t mrtd_read_data(MrtdApplication* app, uint8_t* buffer, size_t bufsize) { + //TODO: fill buffer with data from file + + //TODO: use protected APDU if authenticated + + + + return read; +} +*/ + +bool mrtd_select_efcardaccess(MrtdApplication* app) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; // Chris: short: 0x6700, long: yes (0x9000) // Anna: short: 0x6700, long: yes (0x9000) + EFFile file = EF.CardAccess; + // ICAO 9303 p10 §3.6.2 uint8_t select_efcardaccess_cmd[] = { 0x00, // CLA @@ -48,7 +201,8 @@ bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrt 0x02, // P1 0x0C, // P2 0x02, // Lc: Data length - 0x01, 0x1C, // Data: EF.CardAccess File ID + file.file_id >> 8 | (file.file_id & 0xFF), + //0x01, 0x1C, // Data: EF.CardAccess File ID 0x00, // Le }; @@ -60,7 +214,7 @@ bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrt FURI_LOG_D(TAG, "Send select EF.CardAccess"); if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - mrtd_trace(tx_rx, "Select EF.CardAccess:"); + mrtd_trace(app); if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { efcardaccess_success = true; } else { @@ -72,8 +226,8 @@ bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrt return efcardaccess_success; } -bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { - UNUSED(mrtd_app); +bool mrtd_select_efdir(MrtdApplication* app) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; // Chris: short: no, long: no (0x6A82) // Anna: short: no, long: yes (0x9000) @@ -96,7 +250,7 @@ bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) FURI_LOG_D(TAG, "Send select EF.DIR"); if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - mrtd_trace(tx_rx, "Select EF.DIR:"); + mrtd_trace(app); if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { efdir_success = true; } else { @@ -108,8 +262,8 @@ bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) return efdir_success; } -bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { - UNUSED(mrtd_app); +bool mrtd_select_lds1(MrtdApplication* app) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; uint8_t select_emrtd_cmd[] = { 0x00, // CLA @@ -135,7 +289,7 @@ bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { FURI_LOG_D(TAG, "Send select LDS1 eMRTD"); if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - mrtd_trace(tx_rx, "Select LDS1:"); + mrtd_trace(app); if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { lds1_success = true; } else { @@ -148,20 +302,17 @@ bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { return lds1_success; } -int mrtd_bac_keyhandshake(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { - UNUSED(tx_rx); - UNUSED(mrtd_app); +int mrtd_bac_keyhandshake(MrtdApplication* app) { + UNUSED(app); return 0; } -bool mrtd_read(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) { - furi_assert(tx_rx); - furi_assert(mrtd_app); +bool mrtd_read(MrtdApplication* app) { bool mrtd_read = false; - memset(mrtd_app, 0, sizeof(MrtdApplication)); + memset(app, 0, sizeof(MrtdApplication)); - mrtd_read = mrtd_bac_keyhandshake(tx_rx, mrtd_app); + mrtd_read = mrtd_bac_keyhandshake(app); return mrtd_read; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 972d427bb..ace1e833b 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -3,6 +3,8 @@ #include typedef struct { + FuriHalNfcTxRxContext* tx_rx; + uint16_t file_offset; uint8_t* kmrz; uint8_t ksenc[16]; uint8_t ksmac[16]; @@ -38,16 +40,53 @@ typedef struct { MrtdAuthData auth; } MrtdData; +typedef struct { + const uint8_t short_id; + const uint16_t file_id; +} EFFile; + +struct EFFormat { + // Under Master File (MF) + EFFile ATR; + EFFile DIR; + EFFile CardAccess; + EFFile CardSecurity; + + // Under LDS1 eMRTD Application + EFFile COM; + EFFile SOD; + EFFile DG1; + EFFile DG2; + EFFile DG3; + EFFile DG4; + EFFile DG5; + EFFile DG6; + EFFile DG7; + EFFile DG8; + EFFile DG9; + EFFile DG10; + EFFile DG11; + EFFile DG12; + EFFile DG13; + EFFile DG14; + EFFile DG15; + EFFile DG16; +}; + +extern struct EFFormat EF; + //TODO: description -bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app); -bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app); +MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); +bool mrtd_select(MrtdApplication* app, EFFile file); +bool mrtd_select_efcardaccess(MrtdApplication* mrtd_app); +bool mrtd_select_efdir(MrtdApplication* mrtd_app); +void mrtd_test(MrtdApplication* app); /** 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); +bool mrtd_select_lds1(MrtdApplication* mrtd_app); From 0871d274ebeccad4e2909973c4606a044311bf5a Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:02 +0200 Subject: [PATCH 11/44] MRTD add helper functions: - Check digit - KMRZ --- lib/nfc/nfc_worker.c | 1 + lib/nfc/protocols/mrtd.h | 27 +----------- lib/nfc/protocols/mrtd_helpers.c | 70 ++++++++++++++++++++++++++++++++ lib/nfc/protocols/mrtd_helpers.h | 37 +++++++++++++++++ test_mrtd_helpers.c | 59 +++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 25 deletions(-) create mode 100644 lib/nfc/protocols/mrtd_helpers.c create mode 100644 lib/nfc/protocols/mrtd_helpers.h create mode 100644 test_mrtd_helpers.c diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f0491a4f1..efbeafa94 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -292,6 +292,7 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_hal_nfc_sleep(); // Needed between checks FURI_LOG_D(TAG, "Try reading MRTD"); + //TODO: support NFC-B? if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; break; diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index ace1e833b..4c466c806 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -2,6 +2,8 @@ #include +#include "mrtd_helpers.h" + typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; @@ -11,31 +13,6 @@ typedef struct { 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 enum { - MrtdAuthMethodBac, - MrtdAuthMethodPace, -} MrtdAuthMethod; - -typedef struct { - MrtdAuthMethod method; - - // BAC input fields - MrtdDate birth_date; - MrtdDate expiry_date; - char doc_number[MRTD_DOCNR_MAX_LENGTH]; - - //TODO: PACE -} MrtdAuthData; - typedef struct { MrtdAuthData auth; } MrtdData; diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c new file mode 100644 index 000000000..b27b832e0 --- /dev/null +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -0,0 +1,70 @@ +#include "mrtd_helpers.h" + +uint8_t mrtd_bac_check_digit(const uint8_t* input, const size_t length) { + const size_t num_weights = 3; + uint8_t weights[] = {7, 3, 1}; + uint8_t check_digit = 0; + uint8_t idx; + + for(size_t i=0; i= 'A' && c <= 'Z') { + idx = c - 'A' + 10; + } else if(c >= 'a' && c <= 'z') { + idx = c - 'a' + 10; + } else if(c >= '0' && c <= '9') { + idx = c - '0'; + } else { + idx = 0; + } + check_digit = (check_digit + idx * weights[i%num_weights]) % 10; + } + return check_digit; +} + +void mrtd_print_date(uint8_t* output, MrtdDate* date) { + output[0] = (date->year / 10) + '0'; + output[1] = (date->year % 10) + '0'; + output[2] = (date->month / 10) + '0'; + output[3] = (date->month % 10) + '0'; + output[4] = (date->day / 10) + '0'; + output[5] = (date->day % 10) + '0'; +} + +// Safe size: MRTD_DOCNR_MAX_LENGTH + 1 (CD) + 6 (DOB) + 1 (CD) + 6 (DOE) + 1 (CD) + 1 (0x00) +// Test with: +// - DOCNR of size 9 +// - DOCNR of size <9 +// - DOCNR of size >9 +// - DOCNR of size MRTD_DOCNR_MAX_LENGTH +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, size_t output_size) { + size_t idx = 0; + size_t docnr_length = strlen(auth->doc_number); + size_t cd_idx = 0; + if(output_size < docnr_length + 16) { + return false; + } + + cd_idx = idx; + memcpy(output+idx, auth->doc_number, docnr_length); + idx += docnr_length; + if(docnr_length < 9) { + memset(output+idx, '<', 9-docnr_length); + idx += 9-docnr_length; + } + + output[idx++] = mrtd_bac_check_digit(output+cd_idx, docnr_length) + '0'; + + cd_idx = idx; + mrtd_print_date(output+idx, &auth->birth_date); + idx += 6; + output[idx++] = mrtd_bac_check_digit(output+cd_idx, 6) + '0'; + + cd_idx = idx; + mrtd_print_date(output+idx, &auth->expiry_date); + idx += 6; + output[idx++] = mrtd_bac_check_digit(output+cd_idx, 6) + '0'; + + output[idx++] = '\x00'; + return true; +} diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h new file mode 100644 index 000000000..83a398d4f --- /dev/null +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct { + uint8_t year; + uint8_t month; + uint8_t day; +} MrtdDate; + +// NULL terminated document ID +#define MRTD_DOCNR_MAX_LENGTH 21 + +typedef enum { + MrtdAuthMethodBac, + MrtdAuthMethodPace, +} MrtdAuthMethod; + +typedef struct { + MrtdAuthMethod method; + + // BAC input fields + MrtdDate birth_date; + MrtdDate expiry_date; + char doc_number[MRTD_DOCNR_MAX_LENGTH]; + + //TODO: PACE +} MrtdAuthData; + +uint8_t mrtd_bac_check_digit(const uint8_t* input, const size_t length); + +void mrtd_print_date(uint8_t* output, MrtdDate* date); + +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, size_t output_size); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c new file mode 100644 index 000000000..b10196b3b --- /dev/null +++ b/test_mrtd_helpers.c @@ -0,0 +1,59 @@ +#include + +#include "lib/nfc/protocols/mrtd_helpers.h" + +#define COLOR_RED "\033[0;31m" +#define COLOR_GREEN "\033[0;32m" +#define COLOR_RESET "\033[0;0m" + +void test_mrtd_bac_check_digit(const uint8_t* input, uint8_t exp_output) { + uint8_t output = mrtd_bac_check_digit(input, strlen(input)); + if(output != exp_output) { + printf(COLOR_RED "FAILED - mrtd_bac_check_digit for %s is not %d, but %d\n" COLOR_RESET, + input, exp_output, output); + return; + } + + printf(COLOR_GREEN "SUCCESS - mrtd_bac_check_digit for %s is %d\n" COLOR_RESET, + input, output); +} + +void test_bac_get_kmrz(MrtdAuthData* auth, uint8_t* exp_output) { + bool result; + uint8_t buffer[1000]; + + result = mrtd_bac_get_kmrz(auth, buffer, 1000); + if(!result) { + printf(COLOR_RED "FAILED - mrtd_bac_get_kmrz returned FALSE for" COLOR_RESET); + return; + } + + if(strcmp(exp_output, buffer)) { + printf(COLOR_RED "FAILED - mrtd_bac_get_kmrz expected:\n%s, result:\n%s\n" COLOR_RESET, + exp_output, + buffer); + } + + printf(COLOR_GREEN "SUCCESS - mrtd_bac_get_kmrz is: %s\n" COLOR_RESET, + buffer); +} + +int main(int argc, char** argv) { + test_mrtd_bac_check_digit("D23145890734", 9); + test_mrtd_bac_check_digit("340712", 7); + test_mrtd_bac_check_digit("950712", 2); + + MrtdAuthData mad1 = { + .doc_number = "D23145890734", + .birth_date = {34, 7, 12}, + .expiry_date = {95, 7, 12}, + }; + test_bac_get_kmrz(&mad1, "D23145890734934071279507122"); + test_bac_get_kmrz(&(MrtdAuthData){ + .doc_number = "L898902C", + .birth_date = {69, 8, 6}, + .expiry_date = {94, 6, 23}, + }, "L898902C<369080619406236"); + + return 0; +} From 44c2299b70a15ec3bb66c932b358918c4bbf5d8b Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:04 +0200 Subject: [PATCH 12/44] MRTD use smaller data type of lengths, add sha1 test --- lib/nfc/protocols/mrtd_helpers.c | 14 +++++++------- lib/nfc/protocols/mrtd_helpers.h | 4 ++-- test_mrtd_helpers.c | 33 ++++++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index b27b832e0..97d9eb69a 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,12 +1,12 @@ #include "mrtd_helpers.h" -uint8_t mrtd_bac_check_digit(const uint8_t* input, const size_t length) { - const size_t num_weights = 3; +uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { + const uint8_t num_weights = 3; uint8_t weights[] = {7, 3, 1}; uint8_t check_digit = 0; uint8_t idx; - for(size_t i=0; i= 'A' && c <= 'Z') { idx = c - 'A' + 10; @@ -37,10 +37,10 @@ void mrtd_print_date(uint8_t* output, MrtdDate* date) { // - DOCNR of size <9 // - DOCNR of size >9 // - DOCNR of size MRTD_DOCNR_MAX_LENGTH -bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, size_t output_size) { - size_t idx = 0; - size_t docnr_length = strlen(auth->doc_number); - size_t cd_idx = 0; +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) { + uint8_t idx = 0; + uint8_t docnr_length = strlen(auth->doc_number); + uint8_t cd_idx = 0; if(output_size < docnr_length + 16) { return false; } diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index 83a398d4f..6ad283c68 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -30,8 +30,8 @@ typedef struct { //TODO: PACE } MrtdAuthData; -uint8_t mrtd_bac_check_digit(const uint8_t* input, const size_t length); +uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length); void mrtd_print_date(uint8_t* output, MrtdDate* date); -bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, size_t output_size); +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index b10196b3b..633dd39c7 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -1,12 +1,15 @@ #include +#include #include "lib/nfc/protocols/mrtd_helpers.h" +// gcc -o test_mrtd_helpers -Ilib/mbedtls/include lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c + #define COLOR_RED "\033[0;31m" #define COLOR_GREEN "\033[0;32m" #define COLOR_RESET "\033[0;0m" -void test_mrtd_bac_check_digit(const uint8_t* input, uint8_t exp_output) { +void test_mrtd_bac_check_digit(const uint8_t* input, const uint8_t exp_output) { uint8_t output = mrtd_bac_check_digit(input, strlen(input)); if(output != exp_output) { printf(COLOR_RED "FAILED - mrtd_bac_check_digit for %s is not %d, but %d\n" COLOR_RESET, @@ -18,11 +21,11 @@ void test_mrtd_bac_check_digit(const uint8_t* input, uint8_t exp_output) { input, output); } -void test_bac_get_kmrz(MrtdAuthData* auth, uint8_t* exp_output) { +void test_bac_get_kmrz(MrtdAuthData* auth, const uint8_t* exp_output) { bool result; - uint8_t buffer[1000]; + uint8_t buffer[255]; - result = mrtd_bac_get_kmrz(auth, buffer, 1000); + result = mrtd_bac_get_kmrz(auth, buffer, 255); if(!result) { printf(COLOR_RED "FAILED - mrtd_bac_get_kmrz returned FALSE for" COLOR_RESET); return; @@ -38,6 +41,26 @@ void test_bac_get_kmrz(MrtdAuthData* auth, uint8_t* exp_output) { buffer); } +void test_sha1(const uint8_t* data, const uint8_t* exp_output) { + uint8_t hash[20]; + mbedtls_sha1(data, strlen(data), hash); + + if(memcmp(hash, exp_output, 20)) { + printf(COLOR_RED "FAILED - sha1 of %s, expected:\n", data); + for(uint8_t i=0; i<20; ++i) { + printf("%02X", exp_output[i]); + } + printf(", result:\n"); + } else { + printf(COLOR_GREEN "SUCCESS - sha1 of %s is: ", data); + } + + for(uint8_t i=0; i<20; ++i) { + printf("%02X", hash[i]); + } + printf("\n" COLOR_RESET); +} + int main(int argc, char** argv) { test_mrtd_bac_check_digit("D23145890734", 9); test_mrtd_bac_check_digit("340712", 7); @@ -55,5 +78,7 @@ int main(int argc, char** argv) { .expiry_date = {94, 6, 23}, }, "L898902C<369080619406236"); + test_sha1("L898902C<369080619406236", "\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae\xdf\x47\x75\x65"); + return 0; } From c4499e221f6277f475b26c77fac863f7f357e8d0 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:06 +0200 Subject: [PATCH 13/44] MRTD add mrtd_bac_keys --- lib/nfc/protocols/mrtd.h | 1 - lib/nfc/protocols/mrtd_helpers.c | 41 +++++++++++++++++---- lib/nfc/protocols/mrtd_helpers.h | 2 ++ test_mrtd_helpers.c | 61 +++++++++++++++++++++++++++----- 4 files changed, 89 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 4c466c806..1e775f436 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -7,7 +7,6 @@ typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; - uint8_t* kmrz; uint8_t ksenc[16]; uint8_t ksmac[16]; uint64_t ssc_long; diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 97d9eb69a..fd6f201ae 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,5 +1,8 @@ #include "mrtd_helpers.h" +#include +#include + uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { const uint8_t num_weights = 3; uint8_t weights[] = {7, 3, 1}; @@ -31,12 +34,6 @@ void mrtd_print_date(uint8_t* output, MrtdDate* date) { output[5] = (date->day % 10) + '0'; } -// Safe size: MRTD_DOCNR_MAX_LENGTH + 1 (CD) + 6 (DOB) + 1 (CD) + 6 (DOE) + 1 (CD) + 1 (0x00) -// Test with: -// - DOCNR of size 9 -// - DOCNR of size <9 -// - DOCNR of size >9 -// - DOCNR of size MRTD_DOCNR_MAX_LENGTH bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) { uint8_t idx = 0; uint8_t docnr_length = strlen(auth->doc_number); @@ -68,3 +65,35 @@ bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) output[idx++] = '\x00'; return true; } + +bool mrtd_bac_keys(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) { + uint8_t hash[20]; + mbedtls_sha1_context ctx; + mbedtls_sha1_init(&ctx); + + do { + for(uint8_t i=1; i<=2; ++i) { + if(mbedtls_sha1_starts(&ctx)) break; + if(mbedtls_sha1_update(&ctx, kseed, 16)) break; + if(mbedtls_sha1_update(&ctx, "\x00\x00\x00", 3)) break; + if(mbedtls_sha1_update(&ctx, &i, 1)) break; + if(mbedtls_sha1_finish(&ctx, hash)) break; + + switch(i) { + case 1: + memcpy(ksenc, hash, 16); + mbedtls_des_key_set_parity(ksenc); + mbedtls_des_key_set_parity(ksenc+8); + break; + case 2: + memcpy(ksmac, hash, 16); + mbedtls_des_key_set_parity(ksmac); + mbedtls_des_key_set_parity(ksmac+8); + break; + } + } + } while(false); + + mbedtls_sha1_free(&ctx); + return true; +} diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index 6ad283c68..d5d237319 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -35,3 +35,5 @@ uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length); void mrtd_print_date(uint8_t* output, MrtdDate* date); bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size); + +bool mrtd_bac_keys(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksmac); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index 633dd39c7..978c6071f 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -9,6 +9,12 @@ #define COLOR_GREEN "\033[0;32m" #define COLOR_RESET "\033[0;0m" +void print_hex(const uint8_t* data, size_t length) { + for(uint8_t i=0; i Date: Tue, 11 Oct 2022 22:13:07 +0200 Subject: [PATCH 14/44] MRTD generalize app selection --- lib/nfc/nfc_worker.c | 5 +- lib/nfc/protocols/mrtd.c | 177 +++++++------------------------ lib/nfc/protocols/mrtd.h | 25 ++--- lib/nfc/protocols/mrtd_helpers.c | 4 +- 4 files changed, 56 insertions(+), 155 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index efbeafa94..3012f121e 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -228,7 +228,10 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t //mrtd_select_efcardaccess(mrtd_app); //mrtd_select_efdir(mrtd_app); mrtd_test(mrtd_app); - if(!mrtd_select_lds1(mrtd_app)) break; + if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; + + //TODO: read general informatie + //TODO: after auth scene, do auth (BAC / PACE) /* // Copy data diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 63ffcbc8e..eefaa15d9 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -1,3 +1,5 @@ +#include + #include "mrtd.h" #define TAG "Mrtd" @@ -56,6 +58,13 @@ struct EFFormat EF = { .DG16 = {.file_id = 0X0110, .short_id = 0X10 }, }; +struct AIDSet AID = { + .eMRTDApplication = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}, + .TravelRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x01}, + .VisaRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x02}, + .AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03}, +}; + /*bool mrtd_send_apdu(MrtdApplication* app, uint8_t* buffer, size_t length) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; @@ -114,7 +123,17 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, return false; } -bool mrtd_select(MrtdApplication* app, EFFile file) { +bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { + FURI_LOG_D(TAG, "Send select App: %02X %02X %02X %02X %02X %02X %02X", + aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]); + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1)) { + FURI_LOG_E(TAG, "Failed select App"); + return false; + } + return true; +} + +bool mrtd_select_file(MrtdApplication* app, EFFile file) { uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id); if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1)) { @@ -143,7 +162,7 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { FURI_LOG_D(TAG, "Read and dump %s:", descr); - if(!mrtd_select(app, file)) { + if(!mrtd_select_file(app, file)) { return; } uint8_t data[2048]; @@ -174,145 +193,21 @@ void mrtd_free(MrtdApplication* app) { free(app); } -/* -size_t mrtd_read_data(MrtdApplication* app, uint8_t* buffer, size_t bufsize) { - //TODO: fill buffer with data from file - - //TODO: use protected APDU if authenticated - - - - return read; -} -*/ - -bool mrtd_select_efcardaccess(MrtdApplication* app) { - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - - // Chris: short: 0x6700, long: yes (0x9000) - // Anna: short: 0x6700, long: yes (0x9000) - - EFFile file = EF.CardAccess; - - // ICAO 9303 p10 §3.6.2 - uint8_t select_efcardaccess_cmd[] = { - 0x00, // CLA - 0xA4, // INS - 0x02, // P1 - 0x0C, // P2 - 0x02, // Lc: Data length - file.file_id >> 8 | (file.file_id & 0xFF), - //0x01, 0x1C, // Data: EF.CardAccess File ID - 0x00, // Le - }; - - memcpy(tx_rx->tx_data, select_efcardaccess_cmd, sizeof(select_efcardaccess_cmd)); - tx_rx->tx_bits = sizeof(select_efcardaccess_cmd) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - bool efcardaccess_success = false; - - FURI_LOG_D(TAG, "Send select EF.CardAccess"); - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - mrtd_trace(app); - if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { - efcardaccess_success = true; - } else { - FURI_LOG_D(TAG, "Select EF.CardAccess response is not 0x9000"); - } - } else { - FURI_LOG_E(TAG, "Failed select EF.CardAccess"); - } - return efcardaccess_success; -} - -bool mrtd_select_efdir(MrtdApplication* app) { - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - - // Chris: short: no, long: no (0x6A82) - // Anna: short: no, long: yes (0x9000) - - uint8_t select_efdir_cmd[] = { - 0x00, // CLA - 0xA4, // INS - 0x02, // P1 - 0x0C, // P2 - 0x02, // Lc: Data length - 0x2F, 0x00, // Data: EF.DIR File ID - 0x00, // Le - }; - - memcpy(tx_rx->tx_data, select_efdir_cmd, sizeof(select_efdir_cmd)); - tx_rx->tx_bits = sizeof(select_efdir_cmd) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - bool efdir_success = false; - - FURI_LOG_D(TAG, "Send select EF.DIR"); - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - mrtd_trace(app); - if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) { - efdir_success = true; - } else { - FURI_LOG_D(TAG, "Select EF.DIR response is not 0x9000"); - } - } else { - FURI_LOG_E(TAG, "Failed select EF.DIR"); - } - return efdir_success; -} - -bool mrtd_select_lds1(MrtdApplication* app) { - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - - uint8_t select_emrtd_cmd[] = { - 0x00, // CLA - 0xA4, // INS - 0x04, // P1 - 0x0C, // 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(app); - 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(MrtdApplication* app) { +bool mrtd_bac(MrtdApplication* app) { UNUSED(app); - return 0; -} - -bool mrtd_read(MrtdApplication* app) { - bool mrtd_read = false; - - memset(app, 0, sizeof(MrtdApplication)); - - mrtd_read = mrtd_bac_keyhandshake(app); - return mrtd_read; + static bool rand_generator_inited = false; + uint8_t rnd_ifd[8]; + uint8_t k_ifd[16]; + + if(!rand_generator_inited) { + // TODO: should this maybe be system wide? + srand(DWT->CYCCNT); + rand_generator_inited = true; + } + + furi_hal_random_fill_buf(rnd_ifd, 8); + furi_hal_random_fill_buf(k_ifd, 16); + + return false; //TODO: return true } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 1e775f436..7d8c19bf9 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -51,18 +51,19 @@ struct EFFormat { extern struct EFFormat EF; +typedef uint8_t AIDValue[7]; + +struct AIDSet { + AIDValue eMRTDApplication; + AIDValue TravelRecords; + AIDValue VisaRecords; + AIDValue AdditionalBiometrics; +}; + +extern struct AIDSet AID; + //TODO: description MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); -bool mrtd_select(MrtdApplication* app, EFFile file); -bool mrtd_select_efcardaccess(MrtdApplication* mrtd_app); -bool mrtd_select_efdir(MrtdApplication* mrtd_app); +bool mrtd_select_app(MrtdApplication* app, AIDValue aid); +bool mrtd_select_file(MrtdApplication* app, EFFile file); void mrtd_test(MrtdApplication* app); - -/** Select the LDS1 eMRTD application - * @note Can be used to detect presence of Passport/ID-card - * - * @param emv_app MrtdApplication instance - * - * @return true on success - */ -bool mrtd_select_lds1(MrtdApplication* mrtd_app); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index fd6f201ae..f8adb0cf7 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -3,6 +3,8 @@ #include #include +static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; } + uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { const uint8_t num_weights = 3; uint8_t weights[] = {7, 3, 1}; @@ -75,7 +77,7 @@ bool mrtd_bac_keys(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16] for(uint8_t i=1; i<=2; ++i) { if(mbedtls_sha1_starts(&ctx)) break; if(mbedtls_sha1_update(&ctx, kseed, 16)) break; - if(mbedtls_sha1_update(&ctx, "\x00\x00\x00", 3)) break; + if(mbedtls_sha1_update(&ctx, ucstr("\x00\x00\x00"), 3)) break; if(mbedtls_sha1_update(&ctx, &i, 1)) break; if(mbedtls_sha1_finish(&ctx, hash)) break; From 66a507ba535d2f134fd1bb06bf9c163fdb58232f Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:08 +0200 Subject: [PATCH 15/44] Add ISO7816 tester --- lib/nfc/helpers/iso7816.c | 53 +++++++++++++++++++++++++++++++++++++ lib/nfc/helpers/iso7816.h | 18 +++++++++++++ test_iso7816_helpers.c | 55 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 lib/nfc/helpers/iso7816.c create mode 100644 lib/nfc/helpers/iso7816.h create mode 100644 test_iso7816_helpers.c diff --git a/lib/nfc/helpers/iso7816.c b/lib/nfc/helpers/iso7816.c new file mode 100644 index 000000000..5187aabbb --- /dev/null +++ b/lib/nfc/helpers/iso7816.c @@ -0,0 +1,53 @@ +#include "iso7816.h" + +// ISO7816-5 +// Simple-TLV (§5.2.1) +// BER-TLV (§5.2.2) +TlvInfo iso7816_tlv_parse(const uint8_t* data) { + TlvInfo tlv; + + const uint8_t* org = data; + + // Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF') + // BER-TLV: TODO describe + // 00000 - 11110 => 0 - 30 (single byte) + // 11111 00011111 - 11111 01111111 => 31 - 127 (2 byte) + // 11111 10000001 00000001 - 11111 11111111 01111111 => 128 - 16383 (3 byte) + + tlv.tag = *(data++); + if ((tlv.tag & 0x1f) == 0x1f) { + // BER-TLV, multi byte tag + tlv.tag = *(data++); + if(tlv.tag & 0x80) { + // BER-TLV, 3 byte tag + tlv.tag &= ~0x80; + tlv.tag <<= 7; + tlv.tag |= *(data++) & 0x7f; + } + } + + //TODO: check for invalid 'indefinite length' + tlv.length = *(data++); + if (tlv.length == 0xff) { + // Simple-TLV 2 byte length + tlv.length = *(data++) << 8; + tlv.length += *(data++); + } else if(tlv.length > 0x7f) { + uint8_t length_bytes = tlv.length & 0x7f; + //printf("BER length of %d bytes\n", length_bytes); + if (length_bytes < 1 || length_bytes > 4) { + //TODO: error: ISO7816 doesn't support more than 4 length bytes + return (TlvInfo){.tag = 0}; + } + tlv.length = 0; + for(uint8_t i=0; i +#include +#include +#include + +typedef struct { + uint16_t tag; + size_t length; + const uint8_t* value; + + const uint8_t* next; +} TlvInfo; + +// ISO7816-5 §5.2 +// Simple-TLV and BER-TLV parsing +TlvInfo iso7816_tlv_parse(const uint8_t* data); diff --git a/test_iso7816_helpers.c b/test_iso7816_helpers.c new file mode 100644 index 000000000..afaedde75 --- /dev/null +++ b/test_iso7816_helpers.c @@ -0,0 +1,55 @@ +#include + +#include "lib/nfc/helpers/iso7816.h" + +#define COLOR_RED "\033[0;31m" +#define COLOR_GREEN "\033[0;32m" +#define COLOR_RESET "\033[0;0m" + +void print_hex(const uint8_t* data, size_t length) { + for(size_t i=0; i Date: Tue, 11 Oct 2022 22:13:10 +0200 Subject: [PATCH 16/44] MRTD BAC support and some other improvements --- lib/nfc/helpers/iso7816.c | 2 - lib/nfc/helpers/iso7816.h | 17 ++- lib/nfc/nfc_worker.c | 6 +- lib/nfc/protocols/mrtd.c | 179 ++++++++++++++++++++++++++-- lib/nfc/protocols/mrtd.h | 36 ++++-- lib/nfc/protocols/mrtd_helpers.c | 119 ++++++++++++++++++- lib/nfc/protocols/mrtd_helpers.h | 39 ++++++- test_iso7816_helpers.c | 70 +++++++++++ test_mrtd_helpers.c | 195 +++++++++++++++++++++++++++---- 9 files changed, 607 insertions(+), 56 deletions(-) diff --git a/lib/nfc/helpers/iso7816.c b/lib/nfc/helpers/iso7816.c index 5187aabbb..2cbff0809 100644 --- a/lib/nfc/helpers/iso7816.c +++ b/lib/nfc/helpers/iso7816.c @@ -6,8 +6,6 @@ TlvInfo iso7816_tlv_parse(const uint8_t* data) { TlvInfo tlv; - const uint8_t* org = data; - // Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF') // BER-TLV: TODO describe // 00000 - 11110 => 0 - 30 (single byte) diff --git a/lib/nfc/helpers/iso7816.h b/lib/nfc/helpers/iso7816.h index 21e9bac34..883da0d33 100644 --- a/lib/nfc/helpers/iso7816.h +++ b/lib/nfc/helpers/iso7816.h @@ -5,8 +5,23 @@ #include #include +#define BER_CLASS_UNIVERSAL 0x0 +#define BER_CLASS_APPLICATION 0x1 +#define BER_CLASS_CONTEXT 0x2 +#define BER_CLASS_PRIVATE 0x3 + typedef struct { - uint16_t tag; + union { + uint16_t tag; + struct { + // LSB + uint8_t tag : 5; + uint8_t constructed : 1; + uint8_t class : 2; + // MSB + } ber; + //TODO: currently only works for 1-byte tags + }; size_t length; const uint8_t* value; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 3012f121e..44a63c816 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -214,7 +214,7 @@ 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) { +static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx); //EmvData* result = &nfc_worker->dev_data->emv_data; @@ -227,7 +227,7 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t //mrtd_select(mrtd_app, EF.DIR); //mrtd_select_efcardaccess(mrtd_app); //mrtd_select_efdir(mrtd_app); - mrtd_test(mrtd_app); + mrtd_test(mrtd_app, mrtd_data); if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; //TODO: read general informatie @@ -296,7 +296,7 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_hal_nfc_sleep(); // Needed between checks FURI_LOG_D(TAG, "Try reading MRTD"); //TODO: support NFC-B? - if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { + if(nfc_worker_read_mrtd(nfc_worker, &nfc_worker->dev_data->mrtd_data, tx_rx)) { nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; break; } diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index eefaa15d9..17fdfdf7a 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -1,5 +1,7 @@ #include +#include "../helpers/iso7816.h" + #include "mrtd.h" #define TAG "Mrtd" @@ -13,6 +15,16 @@ //- PACE (CONDITIONAL) //- BAC (CONDITIONAL) +static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) { + if(furi_log_get_level() >= level) { + printf("%s ", prefix); + for(size_t i = 0; i < length; i++) { + printf("%02X ", ((uint8_t*)data)[i]); + } + printf("\r\n"); + } +} + static void mrtd_trace(MrtdApplication* app) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; if(furi_log_get_level() == FuriLogLevelTrace) { @@ -20,7 +32,7 @@ static void mrtd_trace(MrtdApplication* app) { for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) { printf("%02X ", tx_rx->tx_data[i]); } - printf("\r\nRX: "); + printf("\r\nRX: ", tx_rx->rx_data); for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { printf("%02X ", tx_rx->rx_data[i]); } @@ -116,23 +128,53 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, if(ret_code == 0x9000) { return true; } else { - FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); + FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); return false; } } return false; } +//TODO: rename commands to "mrtd_cmd_..." bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { FURI_LOG_D(TAG, "Send select App: %02X %02X %02X %02X %02X %02X %02X", aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]); if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1)) { - FURI_LOG_E(TAG, "Failed select App"); + FURI_LOG_W(TAG, "Failed select App"); return false; } return true; } +bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[8]) { + FURI_LOG_D(TAG, "Send Get Challenge"); + if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08)) { + FURI_LOG_W(TAG, "Failed get challenge"); + return false; + } + + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + memcpy(challenge, tx_rx->rx_data, 8); + + return true; +} + +bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t cmd_size, uint8_t* out_data, size_t out_size) { + furi_assert(cmd_size == 0x28); + furi_assert(out_size >= 0x28); + + FURI_LOG_D(TAG, "Send External Authenticate"); + if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28)) { + FURI_LOG_W(TAG, "Failed External Authenticate"); + return false; + } + + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + memcpy(out_data, tx_rx->rx_data, 0x28); + + return true; +} + bool mrtd_select_file(MrtdApplication* app, EFFile file) { uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id); @@ -144,6 +186,7 @@ bool mrtd_select_file(MrtdApplication* app, EFFile file) { return true; } +//TODO: use out parameter to point to rx_data buffer instead of require allocating another size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) { UNUSED(buffer); UNUSED(bufsize); @@ -160,6 +203,9 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s return 0; } +//TODO: use short id to read, because it's mandatory for eMRTD +//TODO: check for support of extended length in EF.ATR/INFO, see ISO7816-4 + void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { FURI_LOG_D(TAG, "Read and dump %s:", descr); if(!mrtd_select_file(app, file)) { @@ -168,16 +214,70 @@ void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { uint8_t data[2048]; size_t read = 0; do { - read = mrtd_read_binary(app, data+read, sizeof(data)-read, read); + read = mrtd_read_binary(app, data + read, sizeof(data) - read, read); } while(read > 0); } -void mrtd_test(MrtdApplication* app) { +void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { + size_t offset = 0; + uint8_t app_idx = 0; + + memset(EF_DIR->applications, 0x00, sizeof(EF_DIR->applications)); + EF_DIR->applications_count = 0; + + while(offset < length) { + TlvInfo tlv = iso7816_tlv_parse(data + offset); + + if(tlv.tag != 0x61 || tlv.length != 0x09) { + FURI_LOG_E(TAG, "Invalid EF.DIR, tag at offset %d must be '61' and length 9. Got '%02X' and %d", offset, tlv.tag, tlv.length); + return; + } + + tlv = iso7816_tlv_parse(tlv.value); + if(tlv.tag != 0x4F || tlv.length != 0x07) { + FURI_LOG_E(TAG, "Invalid EF.DIR, subtag at offset %d must be '4F' and length 7", offset); + return; + } + + memcpy(EF_DIR->applications[app_idx], tlv.value, tlv.length); + EF_DIR->applications_count = ++app_idx; + + offset = tlv.next - data; + } + + //TODO: remove testing block: + FURI_LOG_D(TAG, "EF.DIR applications: %d", EF_DIR->applications_count); + if(furi_log_get_level() >= FuriLogLevelDebug) { + for(uint8_t i=0; iapplications_count; ++i) { + printf("- "); + for(uint8_t n=0; napplications[i][n]); + } + printf("\r\n"); + } + } +} + +//TODO: remove testing function +void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { + FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + FURI_LOG_D(TAG, "Mrtd Test"); mrtd_read_dump(app, EF.ATR, "EF.ATR"); mrtd_read_dump(app, EF.DIR, "EF.DIR"); + parse_ef_dir(&app->files.EF_DIR, tx_rx->rx_data, tx_rx->rx_bits / 8 - 2); // bits to bytes, and exclude the 2 byte return code mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess"); mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity"); + + mrtd_select_app(app, AID.eMRTDApplication); + + //TODO: remove details + /* + mrtd_data->auth.birth_date = (MrtdDate){.year=69, .month=8, .day=6}; + mrtd_data->auth.expiry_date = (MrtdDate){.year=94, .month=6, .day=23}; + memcpy(mrtd_data->auth.doc_number, "L898902C<", 9); + */ + mrtd_bac(app, &mrtd_data->auth); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { @@ -193,21 +293,84 @@ void mrtd_free(MrtdApplication* app) { free(app); } -bool mrtd_bac(MrtdApplication* app) { +bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { UNUSED(app); static bool rand_generator_inited = false; + uint8_t rnd_ic[8]; uint8_t rnd_ifd[8]; uint8_t k_ifd[16]; if(!rand_generator_inited) { - // TODO: should this maybe be system wide? + // TODO: should random initialization maybe be system wide? srand(DWT->CYCCNT); rand_generator_inited = true; } + mrtd_get_challenge(app, rnd_ic); + //TODO: remove memcpy rnd_ic + //memcpy(rnd_ic, "\x46\x08\xF9\x19\x88\x70\x22\x12", 8); + furi_hal_random_fill_buf(rnd_ifd, 8); furi_hal_random_fill_buf(k_ifd, 16); + //TODO: remove testing code: + //memcpy(rnd_ifd, "\x78\x17\x23\x86\x0C\x06\xC2\x26", 8); + //memcpy(k_ifd, "\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", 16); - return false; //TODO: return true + uint8_t kenc[16]; + uint8_t kmac[16]; + + if(!mrtd_bac_keys(auth, kenc, kmac)) { + FURI_LOG_E(TAG, "Failed to calculate BAC keys"); + return false; + } + + uint8_t S[32]; + memcpy(S, rnd_ifd, 8); + memcpy(S+8, rnd_ic, 8); + memcpy(S+16, k_ifd, 16); + + hexdump(FuriLogLevelDebug, "S:", S, 32); + + uint8_t cmd_data[40]; + uint8_t *eifd = cmd_data; + uint8_t *kifd = cmd_data+32; + mrtd_bac_encrypt(S, 32, kenc, eifd); + mrtd_bac_padded_mac(eifd, 32, kmac, kifd); + + uint8_t response[40]; + if(!mrtd_external_authenticate(app, cmd_data, 40, response, 40)) { + FURI_LOG_E(TAG, "BAC External Authenticate failed"); + return false; + } + + uint8_t buffer[32]; // Received R = RND.IC (8) || RND.IFD (8) || KIC (16) + if(!mrtd_bac_decrypt_verify(response, 40, kenc, kmac, buffer)) { + FURI_LOG_W(TAG, "BAC DecryptVerify failed"); + } + + uint8_t *rnd_ifd_recv = buffer + 8; + uint8_t *kic = buffer + 16; + + if(memcmp(rnd_ifd, rnd_ifd_recv, 8)) { + FURI_LOG_W(TAG, "BAC RND.IFD sent and received mismatch."); + } + + uint8_t kseed[16]; + for(uint8_t i=0; i<16; ++i) { + kseed[i] = kifd[i] ^ kic[i]; + } + + if(!mrtd_bac_keys_from_seed(kseed, app->ksenc, app->ksmac)) { + FURI_LOG_E(TAG, "BAC error, could not derive KSenc and KSmac"); + return false; + } + + hexdump(FuriLogLevelTrace, "RND.IC:", rnd_ic, 8); + hexdump(FuriLogLevelTrace, "RND.IFS:", rnd_ifd, 8); + + app->ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + FURI_LOG_D(TAG, "SSC: %01llX", app->ssc_long); + + return true; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 7d8c19bf9..81210c986 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -4,12 +4,34 @@ #include "mrtd_helpers.h" +#define MAX_EFDIR_APPS 4 + +typedef uint8_t AIDValue[7]; + +struct AIDSet { + AIDValue eMRTDApplication; + AIDValue TravelRecords; + AIDValue VisaRecords; + AIDValue AdditionalBiometrics; +}; + +extern struct AIDSet AID; + +typedef struct { + AIDValue applications[MAX_EFDIR_APPS]; + uint8_t applications_count; +} EF_DIR_contents; + typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; uint8_t ksenc[16]; uint8_t ksmac[16]; uint64_t ssc_long; + + struct { + EF_DIR_contents EF_DIR; + } files; } MrtdApplication; typedef struct { @@ -51,19 +73,9 @@ struct EFFormat { extern struct EFFormat EF; -typedef uint8_t AIDValue[7]; - -struct AIDSet { - AIDValue eMRTDApplication; - AIDValue TravelRecords; - AIDValue VisaRecords; - AIDValue AdditionalBiometrics; -}; - -extern struct AIDSet AID; - //TODO: description MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); bool mrtd_select_app(MrtdApplication* app, AIDValue aid); bool mrtd_select_file(MrtdApplication* app, EFFile file); -void mrtd_test(MrtdApplication* app); +void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data); +bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index f8adb0cf7..d5b529734 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,18 +1,20 @@ #include "mrtd_helpers.h" +#include //TODO: remove + #include #include static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; } -uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { +uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length) { const uint8_t num_weights = 3; uint8_t weights[] = {7, 3, 1}; uint8_t check_digit = 0; uint8_t idx; for(uint8_t i=0; i= 'A' && c <= 'Z') { idx = c - 'A' + 10; } else if(c >= 'a' && c <= 'z') { @@ -27,7 +29,7 @@ uint8_t mrtd_bac_check_digit(const uint8_t* input, const uint8_t length) { return check_digit; } -void mrtd_print_date(uint8_t* output, MrtdDate* date) { +void mrtd_print_date(char* output, MrtdDate* date) { output[0] = (date->year / 10) + '0'; output[1] = (date->year % 10) + '0'; output[2] = (date->month / 10) + '0'; @@ -36,7 +38,7 @@ void mrtd_print_date(uint8_t* output, MrtdDate* date) { output[5] = (date->day % 10) + '0'; } -bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) { +bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) { uint8_t idx = 0; uint8_t docnr_length = strlen(auth->doc_number); uint8_t cd_idx = 0; @@ -68,7 +70,7 @@ bool mrtd_bac_get_kmrz(MrtdAuthData* auth, uint8_t* output, uint8_t output_size) return true; } -bool mrtd_bac_keys(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) { +bool mrtd_bac_keys_from_seed(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16]) { uint8_t hash[20]; mbedtls_sha1_context ctx; mbedtls_sha1_init(&ctx); @@ -99,3 +101,110 @@ bool mrtd_bac_keys(const uint8_t kseed[16], uint8_t ksenc[16], uint8_t ksmac[16] mbedtls_sha1_free(&ctx); return true; } + +bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]) { + uint8_t kmrz_max_length = MRTD_DOCNR_MAX_LENGTH + 16; + char kmrz[kmrz_max_length]; + if(!mrtd_bac_get_kmrz(auth, kmrz, kmrz_max_length)) { + return false; + } + + printf("kmrz: %s\r\n", kmrz); //TODO: remove + + uint8_t hash[20]; + mbedtls_sha1((uint8_t*)kmrz, strlen(kmrz), hash); + + if(!mrtd_bac_keys_from_seed(hash, ksenc, ksmac)) { + return false; + } + + return true; +} + +//NOTE: output size will be ((data_length+8)/8)*8 +bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_enc(&ctx, key); + if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, data_length, IV, data, output)) { + return false; + } + mbedtls_des3_free(&ctx); + + return true; +} + +bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_dec(&ctx, key); + if(mbedtls_des3_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, data_length, IV, data, output)) { + return false; + } + mbedtls_des3_free(&ctx); + + return true; +} + +bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* output) { + mrtd_bac_decrypt(data, data_length - 8, key_enc, output); + + uint8_t mac_calc[8]; + mrtd_bac_padded_mac(data, data_length - 8, key_mac, mac_calc); + + if(memcmp(mac_calc, data + data_length - 8, 8)) { + printf( "MAC failed\n"); + return false; + } + return true; +} + +bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + // MAC + uint8_t mac[8]; + uint8_t xormac[8]; + uint8_t tmp[8]; + mbedtls_des_context ctx; + + mbedtls_des_init(&ctx); + mbedtls_des_setkey_enc(&ctx, key); + + memset(mac, 0, 8); + for(size_t i=0; i #include +#include #include "lib/nfc/protocols/mrtd_helpers.h" -// gcc -o test_mrtd_helpers -Ilib/mbedtls/include lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c +// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c #define COLOR_RED "\033[0;31m" #define COLOR_GREEN "\033[0;32m" @@ -15,7 +16,7 @@ void print_hex(const uint8_t* data, size_t length) { } } -void test_mrtd_bac_check_digit(const uint8_t* input, const uint8_t exp_output) { +void test_mrtd_bac_check_digit(const char* input, const uint8_t exp_output) { uint8_t output = mrtd_bac_check_digit(input, strlen(input)); if(output != exp_output) { printf(COLOR_RED "FAILED - mrtd_bac_check_digit for %s is not %d, but %d\n" COLOR_RESET, @@ -27,9 +28,9 @@ void test_mrtd_bac_check_digit(const uint8_t* input, const uint8_t exp_output) { input, output); } -void test_bac_get_kmrz(MrtdAuthData* auth, const uint8_t* exp_output) { +void test_bac_get_kmrz(MrtdAuthData* auth, const char* exp_output) { bool result; - uint8_t buffer[255]; + char buffer[255]; result = mrtd_bac_get_kmrz(auth, buffer, 255); if(!result) { @@ -49,7 +50,7 @@ void test_bac_get_kmrz(MrtdAuthData* auth, const uint8_t* exp_output) { void test_sha1(const uint8_t* data, const uint8_t* exp_output) { uint8_t hash[20]; - mbedtls_sha1(data, strlen(data), hash); + mbedtls_sha1(data, strlen((char*)data), hash); if(memcmp(hash, exp_output, 20)) { printf(COLOR_RED "FAILED - sha1 of %s, expected:\n", data); @@ -63,18 +64,18 @@ void test_sha1(const uint8_t* data, const uint8_t* exp_output) { printf("\n" COLOR_RESET); } -void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], const uint8_t exp_ksmac[16]) { +void test_mrtd_bac_keys_from_seed(const uint8_t kseed[16], const uint8_t exp_ksenc[16], const uint8_t exp_ksmac[16]) { uint8_t ksenc[16]; uint8_t ksmac[16]; - if(!mrtd_bac_keys(kseed, ksenc, ksmac)) { - printf(COLOR_RED "FAILED - mrtd_bac_keys returned FALSE for "); + if(!mrtd_bac_keys_from_seed(kseed, ksenc, ksmac)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys_from_seed returned FALSE for "); print_hex(kseed, 16); printf(COLOR_RESET "\n"); return; } if(memcmp(exp_ksenc, ksenc, 16)) { - printf(COLOR_RED "FAILED - mrtd_bac_keys of "); + printf(COLOR_RED "FAILED - mrtd_bac_keys_from_seed of "); print_hex(kseed, 16); printf(", expected ksenc:\n"); print_hex(exp_ksenc, 16); @@ -82,7 +83,7 @@ void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], co print_hex(ksenc, 16); return; } else if(memcmp(exp_ksmac, ksmac, 16)) { - printf(COLOR_RED "FAILED - mrtd_bac_keys of "); + printf(COLOR_RED "FAILED - mrtd_bac_keys_from_seed of "); print_hex(kseed, 16); printf(", expected ksmac:\n"); print_hex(exp_ksmac, 16); @@ -90,7 +91,7 @@ void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], co print_hex(ksmac, 16); return; } else { - printf(COLOR_GREEN "SUCCESS - mrtd_bac_keys of "); + printf(COLOR_GREEN "SUCCESS - mrtd_bac_keys_from_seed of "); print_hex(kseed, 16); printf(" ksenc: "); print_hex(ksenc, 16); @@ -100,6 +101,107 @@ void test_mrtd_bac_keys(const uint8_t kseed[16], const uint8_t exp_ksenc[16], co } } +void test_mrtd_bac_keys(MrtdAuthData* auth, const uint8_t exp_ksenc[16], const uint8_t exp_ksmac[16]) { + uint8_t ksenc[16]; + uint8_t ksmac[16]; + if(!mrtd_bac_keys(auth, ksenc, ksmac)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys returned FALSE\n" COLOR_RESET); + return; + } + + if(memcmp(exp_ksenc, ksenc, 16)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys, expected ksenc:\n"); + print_hex(exp_ksenc, 16); + printf(" is:\n"); + print_hex(ksenc, 16); + return; + } else if(memcmp(exp_ksmac, ksmac, 16)) { + printf(COLOR_RED "FAILED - mrtd_bac_keys, expected ksmac:\n"); + print_hex(exp_ksmac, 16); + printf(" is:\n"); + print_hex(ksmac, 16); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_keys ksenc: "); + print_hex(ksenc, 16); + printf(" ksmac: "); + print_hex(ksmac, 16); + printf(COLOR_RESET "\n"); + } +} + +void test_mrtd_bac_encrypt(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + uint8_t buffer[256]; + + // buffer size must be at least ((data_length+8)/8)*8 + mrtd_bac_encrypt(data, data_length, key, buffer); + + if(memcmp(exp_output, buffer, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_encrypt, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(buffer, exp_output_length); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_encrypt output: "); + print_hex(buffer, exp_output_length); + printf(COLOR_RESET "\n"); + } +} + +void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + uint8_t mac[8]; + if(!mrtd_bac_padded_mac(data, data_length, key, mac)) { + printf("ERROR BAC MAC"); + return; + } + + if(memcmp(exp_output, mac, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_padded_mac, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(mac, 8); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_padded_mac output: "); + print_hex(mac, 8); + printf(COLOR_RESET "\n"); + } +} + +void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* exp_output, size_t exp_output_length, bool should_verify) { + uint8_t buffer[256]; + + bool result = mrtd_bac_decrypt_verify(data, data_length, key_enc, key_mac, buffer); + if(result != should_verify) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify, expected verify: %d, but is: %d\n", should_verify, result); + return; + } + + if(memcmp(exp_output, buffer, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(buffer, 32); + return; + } + + printf(COLOR_GREEN "SUCCESS - mrtd_bac_decrypt_verify output: "); + print_hex(buffer, exp_output_length); + printf(COLOR_RESET "\n"); +} + +void test_mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd, uint64_t exp_ssc) { + uint64_t ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + + if(ssc_long != exp_ssc) { + printf(COLOR_RED "FAILED - mrtd_ssc_from_data, expected ssc: %016lx, but is: %016lx\n" COLOR_RESET, exp_ssc, ssc_long); + return; + } + + printf(COLOR_GREEN "SUCCESS - mrtd_ssc_from_data output: %016lx\n" COLOR_RESET, ssc_long); +} + int main(int argc, char** argv) { test_mrtd_bac_check_digit("D23145890734", 9); test_mrtd_bac_check_digit("340712", 7); @@ -109,19 +211,70 @@ int main(int argc, char** argv) { .doc_number = "D23145890734", .birth_date = {34, 7, 12}, .expiry_date = {95, 7, 12}, - }, "D23145890734934071279507122"); + }, "D23145890734934071279507122" + ); test_bac_get_kmrz(&(MrtdAuthData){ - .doc_number = "L898902C", - .birth_date = {69, 8, 6}, - .expiry_date = {94, 6, 23}, - }, "L898902C<369080619406236"); + .doc_number = "L898902C", + .birth_date = {69, 8, 6}, + .expiry_date = {94, 6, 23}, + }, "L898902C<369080619406236" + ); - test_sha1("L898902C<369080619406236", "\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae\xdf\x47\x75\x65"); + test_sha1((uint8_t*)"L898902C<369080619406236", (uint8_t*)"\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae\xdf\x47\x75\x65"); - test_mrtd_bac_keys( - "\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae", - "\xab\x94\xfd\xec\xf2\x67\x4f\xdf\xb9\xb3\x91\xf8\x5d\x7f\x76\xf2", - "\x79\x62\xd9\xec\xe0\x3d\x1a\xcd\x4c\x76\x08\x9d\xce\x13\x15\x43"); + test_mrtd_bac_keys_from_seed( + (uint8_t*)"\x23\x9a\xb9\xcb\x28\x2d\xaf\x66\x23\x1d\xc5\xa4\xdf\x6b\xfb\xae", + (uint8_t*)"\xab\x94\xfd\xec\xf2\x67\x4f\xdf\xb9\xb3\x91\xf8\x5d\x7f\x76\xf2", + (uint8_t*)"\x79\x62\xd9\xec\xe0\x3d\x1a\xcd\x4c\x76\x08\x9d\xce\x13\x15\x43" + ); + + test_mrtd_bac_keys(&(MrtdAuthData){ + .doc_number = "L898902C", + .birth_date = {69, 8, 6}, + .expiry_date = {94, 6, 23}, + }, + (uint8_t*)"\xab\x94\xfd\xec\xf2\x67\x4f\xdf\xb9\xb3\x91\xf8\x5d\x7f\x76\xf2", + (uint8_t*)"\x79\x62\xd9\xec\xe0\x3d\x1a\xcd\x4c\x76\x08\x9d\xce\x13\x15\x43" + ); + + test_mrtd_bac_encrypt( + /*input*/ (uint8_t*)"\x78\x17\x23\x86\x0C\x06\xC2\x26\x46\x08\xF9\x19\x88\x70\x22\x12\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", + /*size*/ 32, + /*key*/ (uint8_t*)"\xAB\x94\xFD\xEC\xF2\x67\x4F\xDF\xB9\xB3\x91\xF8\x5D\x7F\x76\xF2", + /*exp output*/ (uint8_t*)"\x72\xC2\x9C\x23\x71\xCC\x9B\xDB\x65\xB7\x79\xB8\xE8\xD3\x7B\x29\xEC\xC1\x54\xAA\x56\xA8\x79\x9F\xAE\x2F\x49\x8F\x76\xED\x92\xF2", + /*exp_output_size*/ 32 + ); + + test_mrtd_bac_padded_mac( + /*input*/ (uint8_t*)"\x72\xC2\x9C\x23\x71\xCC\x9B\xDB\x65\xB7\x79\xB8\xE8\xD3\x7B\x29\xEC\xC1\x54\xAA\x56\xA8\x79\x9F\xAE\x2F\x49\x8F\x76\xED\x92\xF2", + /*size*/ 32, + /*key*/ (uint8_t*)"\x79\x62\xD9\xEC\xE0\x3D\x1A\xCD\x4C\x76\x08\x9D\xCE\x13\x15\x43", + /*exp output*/ (uint8_t*)"\x5F\x14\x48\xEE\xA8\xAD\x90\xA7", + /*exp_output_size*/ 8 + ); + + test_mrtd_bac_decrypt_verify( + /*input*/ (uint8_t*)"\x46\xB9\x34\x2A\x41\x39\x6C\xD7\x38\x6B\xF5\x80\x31\x04\xD7\xCE\xDC\x12\x2B\x91\x32\x13\x9B\xAF\x2E\xED\xC9\x4E\xE1\x78\x53\x4F\x2F\x2D\x23\x5D\x07\x4D\x74\x49", + /*size*/ 40, + /*key_enc*/ (uint8_t*)"\xAB\x94\xFD\xEC\xF2\x67\x4F\xDF\xB9\xB3\x91\xF8\x5D\x7F\x76\xF2", + /*key_mac*/ (uint8_t*)"\x79\x62\xD9\xEC\xE0\x3D\x1A\xCD\x4C\x76\x08\x9D\xCE\x13\x15\x43", + /*exp output*/ (uint8_t*)"\x46\x08\xF9\x19\x88\x70\x22\x12\x78\x17\x23\x86\x0C\x06\xC2\x26\x0B\x4F\x80\x32\x3E\xB3\x19\x1C\xB0\x49\x70\xCB\x40\x52\x79\x0B", + /*exp_output_size*/ 32, + /*should_verify*/ 1 + ); + + //TODO: test that does not verify + + test_mrtd_bac_keys_from_seed( + (uint8_t*)"\x00\x36\xD2\x72\xF5\xC3\x50\xAC\xAC\x50\xC3\xF5\x72\xD2\x36\x00", + (uint8_t*)"\x97\x9E\xC1\x3B\x1C\xBF\xE9\xDC\xD0\x1A\xB0\xFE\xD3\x07\xEA\xE5", + (uint8_t*)"\xF1\xCB\x1F\x1F\xB5\xAD\xF2\x08\x80\x6B\x89\xDC\x57\x9D\xC1\xF8" + ); + + uint8_t* rnd_ic = (uint8_t*)"\x46\x08\xF9\x19\x88\x70\x22\x12"; + uint8_t* rnd_ifd = (uint8_t*)"\x78\x17\x23\x86\x0C\x06\xC2\x26"; + + test_mrtd_ssc_from_data(rnd_ic, rnd_ifd, 0x887022120C06C226); return 0; } From 0b8a0c705c4219d6fd0e875026178ef00d408dc4 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:11 +0200 Subject: [PATCH 17/44] Minor warning fix in MRTD --- lib/littlefs | 2 +- lib/nfc/protocols/mrtd.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/littlefs b/lib/littlefs index 1863dc788..40dba4a55 160000 --- a/lib/littlefs +++ b/lib/littlefs @@ -1 +1 @@ -Subproject commit 1863dc7883d82bd6ca79faa164b65341064d1c16 +Subproject commit 40dba4a556e0d81dfbe64301a6aa4e18ceca896c diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 17fdfdf7a..8eec491f5 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -32,7 +32,7 @@ static void mrtd_trace(MrtdApplication* app) { for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) { printf("%02X ", tx_rx->tx_data[i]); } - printf("\r\nRX: ", tx_rx->rx_data); + printf("\r\nRX: "); for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { printf("%02X ", tx_rx->rx_data[i]); } From e9bb94254f7517bfb884d42fca18bd909a6cb6c2 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:13 +0200 Subject: [PATCH 18/44] MRTD move scenes to new directory --- applications/{ => main}/nfc/scenes/nfc_scene_passport_bac.c | 0 applications/{ => main}/nfc/scenes/nfc_scene_passport_date.c | 0 applications/{ => main}/nfc/scenes/nfc_scene_passport_docnr.c | 0 applications/{ => main}/nfc/scenes/nfc_scene_passport_menu.c | 0 applications/{ => main}/nfc/scenes/nfc_scene_passport_pace_todo.c | 0 applications/{ => main}/nfc/scenes/nfc_scene_passport_read.c | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename applications/{ => main}/nfc/scenes/nfc_scene_passport_bac.c (100%) rename applications/{ => main}/nfc/scenes/nfc_scene_passport_date.c (100%) rename applications/{ => main}/nfc/scenes/nfc_scene_passport_docnr.c (100%) rename applications/{ => main}/nfc/scenes/nfc_scene_passport_menu.c (100%) rename applications/{ => main}/nfc/scenes/nfc_scene_passport_pace_todo.c (100%) rename applications/{ => main}/nfc/scenes/nfc_scene_passport_read.c (100%) diff --git a/applications/nfc/scenes/nfc_scene_passport_bac.c b/applications/main/nfc/scenes/nfc_scene_passport_bac.c similarity index 100% rename from applications/nfc/scenes/nfc_scene_passport_bac.c rename to applications/main/nfc/scenes/nfc_scene_passport_bac.c diff --git a/applications/nfc/scenes/nfc_scene_passport_date.c b/applications/main/nfc/scenes/nfc_scene_passport_date.c similarity index 100% rename from applications/nfc/scenes/nfc_scene_passport_date.c rename to applications/main/nfc/scenes/nfc_scene_passport_date.c diff --git a/applications/nfc/scenes/nfc_scene_passport_docnr.c b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c similarity index 100% rename from applications/nfc/scenes/nfc_scene_passport_docnr.c rename to applications/main/nfc/scenes/nfc_scene_passport_docnr.c diff --git a/applications/nfc/scenes/nfc_scene_passport_menu.c b/applications/main/nfc/scenes/nfc_scene_passport_menu.c similarity index 100% rename from applications/nfc/scenes/nfc_scene_passport_menu.c rename to applications/main/nfc/scenes/nfc_scene_passport_menu.c diff --git a/applications/nfc/scenes/nfc_scene_passport_pace_todo.c b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c similarity index 100% rename from applications/nfc/scenes/nfc_scene_passport_pace_todo.c rename to applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c diff --git a/applications/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c similarity index 100% rename from applications/nfc/scenes/nfc_scene_passport_read.c rename to applications/main/nfc/scenes/nfc_scene_passport_read.c From 2e586338a1cec5418d1fcf2ba14a44a12b1c1836 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:15 +0200 Subject: [PATCH 19/44] MRTD use reader_analyzer instead of deprecated debug_pcap_worker --- lib/nfc/nfc_worker.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 50520bb3f..1c61a192c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -271,7 +271,11 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, Fur MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx); //EmvData* result = &nfc_worker->dev_data->emv_data; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Read passport if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; From b1f95c5c54d6b0af9735f318352b56f832b4f0fb Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:17 +0200 Subject: [PATCH 20/44] MRTD add worker method for auhtenticated read --- lib/nfc/nfc_worker.c | 21 ++++++++++++++++++++- lib/nfc/nfc_worker.h | 1 + lib/nfc/nfc_worker_i.h | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 1c61a192c..f72767144 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -91,6 +91,8 @@ int32_t nfc_worker_task(void* context) { if(nfc_worker->state == NfcWorkerStateRead) { nfc_worker_read(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateReadMrtdAuth) { + nfc_worker_read_mrtd_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { @@ -283,9 +285,10 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, Fur //mrtd_select(mrtd_app, EF.DIR); //mrtd_select_efcardaccess(mrtd_app); //mrtd_select_efdir(mrtd_app); - mrtd_test(mrtd_app, mrtd_data); if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; + mrtd_test(mrtd_app, mrtd_data); + //TODO: read general informatie //TODO: after auth scene, do auth (BAC / PACE) @@ -316,6 +319,22 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, Fur return read_success; } +bool nfc_worker_read_mrtd_auth(NfcWorker* nfc_worker) { + MrtdData* mrtd_data = &nfc_worker->dev_data->mrtd_data; + FuriHalNfcTxRxContext tx_rx = {}; + MrtdApplication* mrtd_app = mrtd_alloc_init(&tx_rx); + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; + + if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; + + if(!mrtd_bac(mrtd_app, &mrtd_data->auth)) break; + } while(false); + + return false; +} + static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index df0f53176..044e30875 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -11,6 +11,7 @@ typedef enum { NfcWorkerStateReady, // Main worker states NfcWorkerStateRead, + NfcWorkerStateReadMrtdAuth, NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index b3f11d9eb..193fc04bb 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -36,6 +36,8 @@ int32_t nfc_worker_task(void* context); void nfc_worker_read(NfcWorker* nfc_worker); +bool nfc_worker_read_mrtd_auth(NfcWorker* nfc_worker); + void nfc_worker_emulate_uid(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); From 2c4e2b4aad7ed4f5e9dde44109c7e7f895d00b3e Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:19 +0200 Subject: [PATCH 21/44] MRTD rename scene Bac to Auth --- applications/main/nfc/scenes/nfc_scene_config.h | 2 +- ..._passport_bac.c => nfc_scene_passport_auth.c} | 16 ++++++++-------- .../main/nfc/scenes/nfc_scene_passport_date.c | 2 +- .../main/nfc/scenes/nfc_scene_passport_docnr.c | 2 +- .../nfc/scenes/nfc_scene_passport_pace_todo.c | 2 +- .../main/nfc/scenes/nfc_scene_passport_read.c | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) rename applications/main/nfc/scenes/{nfc_scene_passport_bac.c => nfc_scene_passport_auth.c} (89%) diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 71c257d20..76ce419c0 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -40,7 +40,7 @@ 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) //TODO: rename to Auth +ADD_SCENE(nfc, passport_auth, PassportAuth) ADD_SCENE(nfc, passport_date, PassportDate) ADD_SCENE(nfc, passport_docnr, PassportDocNr) ADD_SCENE(nfc, passport_pace_todo, PassportPaceTodo) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_bac.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c similarity index 89% rename from applications/main/nfc/scenes/nfc_scene_passport_bac.c rename to applications/main/nfc/scenes/nfc_scene_passport_auth.c index ca4efb6bd..79c74a954 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_bac.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -#define TAG "PassportBac" +#define TAG "PassportAuth" #define MRTD_AUTH_METHOD_COUNT 2 // Indexes must match MrtdAuthMethod (lib/nfc/protocols/mrtd.h) @@ -17,19 +17,19 @@ typedef enum { NfcScenePassportAuthSelectAuth, } NfcScenePassportAuthSelect; -void nfc_scene_passport_bac_var_list_enter_callback(void* context, uint32_t index) { +void nfc_scene_passport_auth_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) { +void nfc_scene_passport_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) { +void nfc_scene_passport_auth_on_enter(void* context) { Nfc* nfc = context; VariableItemList* variable_item_list = nfc->variable_item_list; @@ -73,7 +73,7 @@ void nfc_scene_passport_bac_on_enter(void* context) { variable_item_list, "Method", MRTD_AUTH_METHOD_COUNT, - nfc_scene_passport_bac_auth_method_changed, + nfc_scene_passport_auth_method_changed, nfc); value_index = nfc->dev->dev_data.mrtd_data.auth.method; @@ -83,11 +83,11 @@ void nfc_scene_passport_bac_on_enter(void* context) { 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); + variable_item_list, nfc_scene_passport_auth_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) { +bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; @@ -125,7 +125,7 @@ bool nfc_scene_passport_bac_on_event(void* context, SceneManagerEvent event) { return consumed; } -void nfc_scene_passport_bac_on_exit(void* context) { +void nfc_scene_passport_auth_on_exit(void* context) { Nfc* nfc = context; // Clear view diff --git a/applications/main/nfc/scenes/nfc_scene_passport_date.c b/applications/main/nfc/scenes/nfc_scene_passport_date.c index cee59038f..d2d2f92a9 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_date.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_date.c @@ -110,7 +110,7 @@ bool nfc_scene_passport_date_on_event(void* context, SceneManagerEvent event) { //TODO: handle invalid date (returned false) consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcScenePassportBac); + nfc->scene_manager, NfcScenePassportAuth); } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_passport_docnr.c b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c index a4ee1c2cd..109415759 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_docnr.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_docnr.c @@ -54,7 +54,7 @@ bool nfc_scene_passport_docnr_on_event(void* context, SceneManagerEvent event) { nfc_scene_passport_docnr_save(nfc); consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcScenePassportBac); + nfc->scene_manager, NfcScenePassportAuth); } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c index e3a5fe0cb..c6d7d7f6c 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_pace_todo.c @@ -26,7 +26,7 @@ bool nfc_scene_passport_pace_todo_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcScenePassportBac); + nfc->scene_manager, NfcScenePassportAuth); } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c index 37547224d..55b9cae0b 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read.c @@ -52,7 +52,7 @@ bool nfc_scene_passport_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); consumed = true; } else if(event.event == GuiButtonTypeCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcScenePassportBac); + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuth); consumed = true; } else if(event.event == GuiButtonTypeRight) { scene_manager_next_scene(nfc->scene_manager, NfcScenePassportMenu); From 6b968f7fea1db31f500f4350042add6939da05e9 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:20 +0200 Subject: [PATCH 22/44] MRTD reread with auth --- .../main/nfc/scenes/nfc_scene_passport_auth.c | 12 ++++++++++-- lib/nfc/protocols/mrtd.c | 17 ++++++++++++++++- lib/nfc/protocols/mrtd_helpers.h | 2 ++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c index 79c74a954..0d6242d01 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -2,9 +2,11 @@ #define TAG "PassportAuth" -#define MRTD_AUTH_METHOD_COUNT 2 -// Indexes must match MrtdAuthMethod (lib/nfc/protocols/mrtd.h) +#define MRTD_AUTH_METHOD_COUNT 4 +// Indexes must match MrtdAuthMethod (lib/nfc/protocols/mrtd_helpers.h) const char* const mrtd_auth_method_text[MRTD_AUTH_METHOD_COUNT] = { + "None", + "Any", "BAC", "PACE", }; @@ -77,6 +79,9 @@ void nfc_scene_passport_auth_on_enter(void* context) { nfc); value_index = nfc->dev->dev_data.mrtd_data.auth.method; + if(value_index == MrtdAuthMethodNone) { + value_index = MrtdAuthMethodAny; + } variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, mrtd_auth_method_text[value_index]); @@ -114,6 +119,9 @@ bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) { case NfcScenePassportAuthSelectAuth: if(nfc->dev->dev_data.mrtd_data.auth.method == MrtdAuthMethodPace) { scene_manager_next_scene(nfc->scene_manager, NfcScenePassportPaceTodo); + } else { + nfc_device_clear(nfc->dev); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); } consumed = true; break; diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 8eec491f5..712c829b8 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -277,7 +277,22 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { mrtd_data->auth.expiry_date = (MrtdDate){.year=94, .month=6, .day=23}; memcpy(mrtd_data->auth.doc_number, "L898902C<", 9); */ - mrtd_bac(app, &mrtd_data->auth); + + MrtdAuthMethod method = mrtd_data->auth.method; + FURI_LOG_D(TAG, "Auth method: %d", method); + switch(method) { + case MrtdAuthMethodAny: + //TODO: try PACE, then BAC + case MrtdAuthMethodBac: + mrtd_bac(app, &mrtd_data->auth); + break; + case MrtdAuthMethodPace: + FURI_LOG_E(TAG, "Auth method PACE not implemented"); + break; + case MrtdAuthMethodNone: + default: + break; + } } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index 030ab0863..2c83e2cc6 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -15,6 +15,8 @@ typedef struct { #define MRTD_DOCNR_MAX_LENGTH 21 typedef enum { + MrtdAuthMethodNone, + MrtdAuthMethodAny, MrtdAuthMethodBac, MrtdAuthMethodPace, } MrtdAuthMethod; From d9399c6559f963a2c2757d68fbdb49f7892d942c Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:21 +0200 Subject: [PATCH 23/44] fixup! MRTD use reader_analyzer instead of deprecated debug_pcap_worker Clean up reader_analyzer after usage --- lib/nfc/nfc_worker.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f72767144..ff7a00831 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -316,6 +316,10 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, Fur read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } From 0f242984fbbb37e485d359df56508ae0af6991a4 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:22 +0200 Subject: [PATCH 24/44] MRTD fix KMRZ for BAC --- lib/nfc/protocols/mrtd_helpers.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index d5b529734..3484010ad 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -47,8 +47,14 @@ bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) { } cd_idx = idx; - memcpy(output+idx, auth->doc_number, docnr_length); - idx += docnr_length; + for(uint8_t i=0; idoc_number[i]; + if(c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + output[idx++] = c; + } + if(docnr_length < 9) { memset(output+idx, '<', 9-docnr_length); idx += 9-docnr_length; From 6abd967966c00801605b9266ee6e166cc34f3354 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:24 +0200 Subject: [PATCH 25/44] MRTD set auth method + cleanup nfc_worker --- .../main/nfc/scenes/nfc_scene_passport_auth.c | 11 +++++++---- lib/nfc/nfc_worker.c | 11 ++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c index 0d6242d01..6e7fd7525 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -34,6 +34,12 @@ void nfc_scene_passport_auth_method_changed(VariableItem* item) { void nfc_scene_passport_auth_on_enter(void* context) { Nfc* nfc = context; + // By entering the Auth menu, we default to Auth: Any + MrtdAuthMethod* auth_method = &nfc->dev->dev_data.mrtd_data.auth.method; + if(*auth_method == MrtdAuthMethodNone) { + *auth_method = MrtdAuthMethodAny; + } + VariableItemList* variable_item_list = nfc->variable_item_list; VariableItem* item; @@ -78,10 +84,7 @@ void nfc_scene_passport_auth_on_enter(void* context) { nfc_scene_passport_auth_method_changed, nfc); - value_index = nfc->dev->dev_data.mrtd_data.auth.method; - if(value_index == MrtdAuthMethodNone) { - value_index = MrtdAuthMethodAny; - } + value_index = *auth_method; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, mrtd_auth_method_text[value_index]); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index ff7a00831..deda0db2b 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -268,10 +268,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte return read_success; } -static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, FuriHalNfcTxRxContext* tx_rx) { +static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx); - //EmvData* result = &nfc_worker->dev_data->emv_data; + MrtdData* mrtd_data = &nfc_worker->dev_data->mrtd_data; if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); @@ -281,10 +281,7 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, MrtdData* mrtd_data, Fur do { // Read passport if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; - //mrtd_select(mrtd_app, EF.CardAccess); - //mrtd_select(mrtd_app, EF.DIR); - //mrtd_select_efcardaccess(mrtd_app); - //mrtd_select_efdir(mrtd_app); + if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; mrtd_test(mrtd_app, mrtd_data); @@ -375,7 +372,7 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_hal_nfc_sleep(); // Needed between checks FURI_LOG_D(TAG, "Try reading MRTD"); //TODO: support NFC-B? - if(nfc_worker_read_mrtd(nfc_worker, &nfc_worker->dev_data->mrtd_data, tx_rx)) { + if(nfc_worker_read_mrtd(nfc_worker, tx_rx)) { nfc_worker->dev_data->protocol = NfcDeviceProtocolMRTD; break; } From 8796195a631f053765dbf27833ba655f8c8071be Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:25 +0200 Subject: [PATCH 26/44] Revert "MRTD add worker method for auhtenticated read" This reverts commit 3458ded128a3c13ddd18484c2e1be2945e951a28. --- lib/nfc/nfc_worker.c | 18 ------------------ lib/nfc/nfc_worker.h | 1 - lib/nfc/nfc_worker_i.h | 2 -- 3 files changed, 21 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index deda0db2b..44ee06e19 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -91,8 +91,6 @@ int32_t nfc_worker_task(void* context) { if(nfc_worker->state == NfcWorkerStateRead) { nfc_worker_read(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadMrtdAuth) { - nfc_worker_read_mrtd_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { @@ -320,22 +318,6 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t return read_success; } -bool nfc_worker_read_mrtd_auth(NfcWorker* nfc_worker) { - MrtdData* mrtd_data = &nfc_worker->dev_data->mrtd_data; - FuriHalNfcTxRxContext tx_rx = {}; - MrtdApplication* mrtd_app = mrtd_alloc_init(&tx_rx); - - do { - if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; - - if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; - - if(!mrtd_bac(mrtd_app, &mrtd_data->auth)) break; - } while(false); - - return false; -} - static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 044e30875..df0f53176 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -11,7 +11,6 @@ typedef enum { NfcWorkerStateReady, // Main worker states NfcWorkerStateRead, - NfcWorkerStateReadMrtdAuth, NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 193fc04bb..b3f11d9eb 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -36,8 +36,6 @@ int32_t nfc_worker_task(void* context); void nfc_worker_read(NfcWorker* nfc_worker); -bool nfc_worker_read_mrtd_auth(NfcWorker* nfc_worker); - void nfc_worker_emulate_uid(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); From c221c8947e086db0bdf171ab17d5b453a05c0bce Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:26 +0200 Subject: [PATCH 27/44] MRTD scene for auth. read --- .gitignore | 3 + .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_passport_auth.c | 25 ++++-- .../main/nfc/scenes/nfc_scene_passport_read.c | 1 - .../nfc/scenes/nfc_scene_passport_read_auth.c | 77 +++++++++++++++++++ applications/main/nfc/scenes/nfc_scene_read.c | 8 +- lib/nfc/protocols/mrtd.c | 3 +- lib/nfc/protocols/mrtd.h | 1 + 8 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_passport_read_auth.c diff --git a/.gitignore b/.gitignore index 38a31bf01..e5633531c 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ openocd.log # PVS Studio temporary files .PVS-Studio/ PVS-Studio.log + +#TODO: remove +applications/main/nfc/test_bac_creds.h diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 76ce419c0..cb28a07f9 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -39,6 +39,7 @@ 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_read_auth, PassportReadAuthSuccess) ADD_SCENE(nfc, passport_menu, PassportMenu) ADD_SCENE(nfc, passport_auth, PassportAuth) ADD_SCENE(nfc, passport_date, PassportDate) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c index 6e7fd7525..05afad97d 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -1,5 +1,7 @@ #include "../nfc_i.h" +#include "../test_bac_creds.h" //TODO: remove + #define TAG "PassportAuth" #define MRTD_AUTH_METHOD_COUNT 4 @@ -33,11 +35,18 @@ void nfc_scene_passport_auth_method_changed(VariableItem* item) { void nfc_scene_passport_auth_on_enter(void* context) { Nfc* nfc = context; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; // By entering the Auth menu, we default to Auth: Any - MrtdAuthMethod* auth_method = &nfc->dev->dev_data.mrtd_data.auth.method; + MrtdAuthMethod* auth_method = &mrtd_data->auth.method; if(*auth_method == MrtdAuthMethodNone) { *auth_method = MrtdAuthMethodAny; + + //TODO: remove testing credentials: + mrtd_data->auth.birth_date = TODO_REMOVE_ID_DOB; + mrtd_data->auth.expiry_date = TODO_REMOVE_ID_DOE; + memcpy(mrtd_data->auth.doc_number, TODO_REMOVE_ID_DOC, 9); + //TODO: remove testing credentials ^^ } VariableItemList* variable_item_list = nfc->variable_item_list; @@ -48,24 +57,24 @@ void nfc_scene_passport_auth_on_enter(void* context) { const size_t temp_str_size = 15; char temp_str[temp_str_size]; snprintf(temp_str, temp_str_size, "%02u%02u%02u", - nfc->dev->dev_data.mrtd_data.auth.birth_date.year, - nfc->dev->dev_data.mrtd_data.auth.birth_date.month, - nfc->dev->dev_data.mrtd_data.auth.birth_date.day); + mrtd_data->auth.birth_date.year, + mrtd_data->auth.birth_date.month, + mrtd_data->auth.birth_date.day); item = variable_item_list_add(variable_item_list, "Birth Date", 1, NULL, NULL); variable_item_set_current_value_text(item, temp_str); snprintf(temp_str, temp_str_size, "%02u%02u%02u", - nfc->dev->dev_data.mrtd_data.auth.expiry_date.year, - nfc->dev->dev_data.mrtd_data.auth.expiry_date.month, - nfc->dev->dev_data.mrtd_data.auth.expiry_date.day); + mrtd_data->auth.expiry_date.year, + mrtd_data->auth.expiry_date.month, + mrtd_data->auth.expiry_date.day); item = variable_item_list_add(variable_item_list, "Expiry Date", 1, NULL, NULL); variable_item_set_current_value_text(item, temp_str); item = variable_item_list_add(variable_item_list, "Document Nr.", 1, NULL, NULL); - strncpy(temp_str, nfc->dev->dev_data.mrtd_data.auth.doc_number, temp_str_size); + strncpy(temp_str, mrtd_data->auth.doc_number, temp_str_size); temp_str[temp_str_size] = '\x00'; if(strlen(temp_str) > 8) { temp_str[8] = '.'; diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c index 55b9cae0b..441d34584 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read.c @@ -11,7 +11,6 @@ void nfc_scene_passport_read_widget_callback(GuiButtonType result, InputType typ 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); diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c new file mode 100644 index 000000000..6bff4b00e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -0,0 +1,77 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_passport_read_auth_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_auth_on_enter(void* context) { + Nfc* nfc = context; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + + Widget* widget = nfc->widget; + + // Setup Custom Widget view + string_t temp_str; + string_init_printf(temp_str, "\e#Passport\n"); + string_cat_printf(temp_str, "Authenticated: %d", mrtd_data->auth_success); + // TODO: indicate BAC / PACE used + + /* + 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_auth_widget_callback, nfc); + /* + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "Auth", nfc_scene_passport_read_auth_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "More", nfc_scene_passport_read_auth_widget_callback, nfc); + */ + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_passport_read_auth_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, NfcScenePassportAuth); + //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_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 48fef9ddc..6dd06e8f3 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -85,7 +85,13 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == NfcWorkerEventReadPassport) { notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadSuccess); + if(nfc->dev->dev_data.mrtd_data.auth_success) { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadAuthSuccess); + //TODO: } else if(nfc->dev->dev_data.mrtd_data.auth.method != MrtdAuthMethodNone) { + //scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadAuthFailed); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportReadSuccess); + } consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 712c829b8..323b0440c 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -279,12 +279,13 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { */ MrtdAuthMethod method = mrtd_data->auth.method; + mrtd_data->auth_success = false; FURI_LOG_D(TAG, "Auth method: %d", method); switch(method) { case MrtdAuthMethodAny: //TODO: try PACE, then BAC case MrtdAuthMethodBac: - mrtd_bac(app, &mrtd_data->auth); + mrtd_data->auth_success = mrtd_bac(app, &mrtd_data->auth); break; case MrtdAuthMethodPace: FURI_LOG_E(TAG, "Auth method PACE not implemented"); diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 81210c986..b44b26d99 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -36,6 +36,7 @@ typedef struct { typedef struct { MrtdAuthData auth; + bool auth_success; } MrtdData; typedef struct { From ab5e5644104628639e48fe271e0087ff7e4bafa7 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:27 +0200 Subject: [PATCH 28/44] MRTD WIP secure messaging --- lib/nfc/protocols/mrtd.c | 51 +++++++--- lib/nfc/protocols/mrtd.h | 10 +- lib/nfc/protocols/mrtd_helpers.c | 168 +++++++++++++++++++++++++++++++ lib/nfc/protocols/mrtd_helpers.h | 23 +++++ test_mrtd_helpers.c | 85 +++++++++++++++- 5 files changed, 320 insertions(+), 17 deletions(-) diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 323b0440c..c0ac5215d 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -100,21 +100,29 @@ struct AIDSet AID = { bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + size_t idx = 0; FURI_LOG_D(TAG, "Send APDU, lc: %d, le: %d", lc, le); - size_t idx = 0; - tx_rx->tx_data[idx++] = cla; - tx_rx->tx_data[idx++] = ins; - tx_rx->tx_data[idx++] = p1; - tx_rx->tx_data[idx++] = p2; - if(lc > 0) { - tx_rx->tx_data[idx++] = lc; - memcpy(tx_rx->tx_data + idx, data, lc); - idx += lc; - } - if(le >= 0) { - tx_rx->tx_data[idx++] = le&0xff; + if(app->secure_messaging) { + FURI_LOG_D(TAG, "Protect APDU"); + + app->ssc_long++; + idx = mrtd_protect_apdu(cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data); + + } else { + tx_rx->tx_data[idx++] = cla; + tx_rx->tx_data[idx++] = ins; + tx_rx->tx_data[idx++] = p1; + tx_rx->tx_data[idx++] = p2; + if(lc > 0) { + tx_rx->tx_data[idx++] = lc; + memcpy(tx_rx->tx_data + idx, data, lc); + idx += lc; + } + if(le >= 0) { + tx_rx->tx_data[idx++] = le&0xff; + } } tx_rx->tx_bits = idx * 8; @@ -208,6 +216,7 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { FURI_LOG_D(TAG, "Read and dump %s:", descr); + if(!mrtd_select_file(app, file)) { return; } @@ -258,12 +267,22 @@ void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { } } +void parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) { + UNUSED(EF_COM); //TODO + UNUSED(length); //TODO + size_t offset = 0; + + TlvInfo tlv = iso7816_tlv_parse(data + offset); + UNUSED(tlv); //TODO +} + //TODO: remove testing function void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; FURI_LOG_D(TAG, "Mrtd Test"); mrtd_read_dump(app, EF.ATR, "EF.ATR"); + mrtd_read_dump(app, EF.COM, "EF.COM"); mrtd_read_dump(app, EF.DIR, "EF.DIR"); parse_ef_dir(&app->files.EF_DIR, tx_rx->rx_data, tx_rx->rx_bits / 8 - 2); // bits to bytes, and exclude the 2 byte return code mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess"); @@ -294,6 +313,12 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { default: break; } + + if(!mrtd_data->auth_success) { + return; + } + + mrtd_read_dump(app, EF.COM, "EF.COM"); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { @@ -388,5 +413,7 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { app->ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); FURI_LOG_D(TAG, "SSC: %01llX", app->ssc_long); + app->secure_messaging = true; + return true; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index b44b26d99..f207fd97f 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -22,12 +22,20 @@ typedef struct { uint8_t applications_count; } EF_DIR_contents; +typedef struct { + uint16_t lds_version; + uint16_t unicode_version; + //TODO: taglist +} EF_COM_contents; + typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; uint8_t ksenc[16]; uint8_t ksmac[16]; - uint64_t ssc_long; + uint64_t ssc_long; // TODO: rename without _long + + bool secure_messaging; struct { EF_DIR_contents EF_DIR; diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 3484010ad..22ce7e6c8 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,6 +1,7 @@ #include "mrtd_helpers.h" #include //TODO: remove +#include #include #include @@ -169,6 +170,96 @@ bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* k return true; } +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]) { + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_enc(&ctx->des, key); + memset(ctx->mac, 0, 8); + ctx->idx_in = 0; + memcpy(ctx->key, key, 16); + return true; +} + +bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length) { + size_t data_idx = 0; + //uint8_t* xormac = ctx->xormac; + + if(ctx->idx_in != 0) { + uint8_t buff_add = 8 - ctx->idx_in; + if(data_length < buff_add) { + buff_add = data_length; + } + memcpy(ctx->buffer_in + ctx->idx_in, data, buff_add); + ctx->idx_in = (ctx->idx_in + buff_add) % 8; + data_idx += buff_add; + + if(ctx->idx_in == 0) { // buffer_in filled + for(uint8_t j=0; j<8; ++j) { + ctx->xormac[j] = ctx->mac[j] ^ ctx->buffer_in[j]; + } + mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); + + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + ctx->buffer_in[0], ctx->buffer_in[1], ctx->buffer_in[2], ctx->buffer_in[3], + ctx->buffer_in[4], ctx->buffer_in[5], ctx->buffer_in[6], ctx->buffer_in[7]); + + //printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + //xormac[0], xormac[1], xormac[2], xormac[3], + //xormac[4], xormac[5], xormac[6], xormac[7]); + } + } + + while(true) { + if(data_idx + 8 > data_length) { + // Not a full block + break; + } + for(uint8_t j=0; j<8; ++j) { + ctx->xormac[j] = ctx->mac[j] ^ data[data_idx++]; + } + + mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + data[data_idx - 8 + 0], data[data_idx - 8 + 1], data[data_idx - 8 + 2], data[data_idx - 8 + 3], + data[data_idx - 8 + 4], data[data_idx - 8 + 5], data[data_idx - 8 + 6], data[data_idx - 8 + 7]); + + //printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + //xormac[0], xormac[1], xormac[2], xormac[3], + //xormac[4], xormac[5], xormac[6], xormac[7]); + } + + if(data_idx < data_length) { + ctx->idx_in = data_length - data_idx; + memcpy(ctx->buffer_in, data + data_idx, ctx->idx_in); + } + + return true; +} + +bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx) { + memset(ctx->buffer_in + ctx->idx_in, 0x00, 8 - ctx->idx_in); + ctx->buffer_in[ctx->idx_in] = 0x80; + ctx->idx_in = 8; + + mrtd_bac_mac_update(ctx, NULL, 0); // Force processing the buffer_in + return true; +} + +bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) { + mrtd_bac_mac_pad(ctx); + + uint8_t tmp[8]; + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_dec(&ctx->des, ctx->key+8); + mbedtls_des_crypt_ecb(&ctx->des, ctx->mac, tmp); + + mbedtls_des_init(&ctx->des); + mbedtls_des_setkey_enc(&ctx->des, ctx->key); + mbedtls_des_crypt_ecb(&ctx->des, tmp, output); + + mbedtls_des_free(&ctx->des); + return true; +} + bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { // MAC uint8_t mac[8]; @@ -184,7 +275,11 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t for(uint8_t j=0; j<8; ++j) { xormac[j] = mac[j] ^ data[i * 8 + j]; } + mbedtls_des_crypt_ecb(&ctx, xormac, mac); + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + xormac[0], xormac[1], xormac[2], xormac[3], + xormac[4], xormac[5], xormac[6], xormac[7]); } mbedtls_des_init(&ctx); @@ -201,6 +296,7 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t } bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { + //TODO: bufferless padding should be possible with 3DES size_t newlength = ((data_length+8)/8)*8; // TODO: return this value too? uint8_t padded[newlength]; //TODO: input parameter memset(padded, 0, newlength); @@ -214,3 +310,75 @@ bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, return true; } +size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output) { + //TODO: max size on output? + size_t idx = 0; + + // CC = MAC( SSC || CmdHeader || DO'87 ) + mrtd_bac_mac_ctx mac_ctx; + mrtd_bac_mac_init(&mac_ctx, key_mac); + uint64_t ssc_n = htonll(ssc); + mrtd_bac_mac_update(&mac_ctx, (uint8_t*)&ssc_n, 8); + + // Mask cla + output[idx++] = cla | 0x0c; + output[idx++] = ins; + output[idx++] = p1; + output[idx++] = p2; + + // Pad Header + mrtd_bac_mac_update(&mac_ctx, output, idx); + mrtd_bac_mac_pad(&mac_ctx); + + size_t idx_lc = idx; + output[idx++] = 0xff; // place holder for Lc + + // Build DO'87 + // TODO: condition on data presence + { + size_t newlength = ((lc+8)/8)*8; + uint8_t padded[newlength]; + size_t idx_do87 = idx; + + output[idx++] = 0x87; // Header + output[idx++] = newlength + 1; // Length + output[idx++] = 0x01; //TODO: check this value + + memset(padded, 0, newlength); + memcpy(padded, data, lc); + padded[lc] = 0x80; + + mrtd_bac_encrypt(padded, newlength, key_enc, output + idx); + idx += newlength; + + mrtd_bac_mac_update(&mac_ctx, output + idx_do87, idx - idx_do87); + } + + // Build DO'8E + // TODO: conditions? + { + output[idx++] = 0x8E; // Header + output[idx++] = 0x08; // Length + + printf("idx: %d\n", idx); + + uint8_t mac[8]; + mrtd_bac_mac_finalize(&mac_ctx, output + idx); + idx += 8; + printf("MAC: "); + for(uint8_t i=0; i<8; ++i) { + printf("%02X ", mac[i]); + } + printf("\n"); + } + + output[idx_lc] = idx - idx_lc - 1; // Set Lc + + output[idx++] = 0x00; + + if(le) { + //TODO: le? + } + + return idx; +} diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index 2c83e2cc6..1566be068 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -5,6 +5,8 @@ #include #include +#include + typedef struct { uint8_t year; uint8_t month; @@ -32,6 +34,16 @@ typedef struct { //TODO: PACE } MrtdAuthData; +typedef struct { + mbedtls_des_context des; + uint8_t key[16]; + uint8_t mac[8]; + uint8_t xormac[8]; + + uint8_t buffer_in[8]; + uint8_t idx_in; +} mrtd_bac_mac_ctx; + uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); //TODO: swap order, all other functions have output last @@ -47,12 +59,21 @@ bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uin bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]); + +bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length); + +bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]); + bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* output); +#include +#define htonll(x) ((((uint64_t)__htonl(x)) << 32) + __htonl((x) >> 32)) + static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd) { #if _BYTE_ORDER == _LITTLE_ENDIAN return @@ -70,3 +91,5 @@ static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t (*((uint64_t*)(rnd_ifd + 4)) * 0x100000000); #endif } + +size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index 6c16084cb..b3e5e462c 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -4,7 +4,7 @@ #include "lib/nfc/protocols/mrtd_helpers.h" -// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c +// gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include -Itoolchain/x86_64-linux/arm-none-eabi/include/ lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c #define COLOR_RED "\033[0;31m" #define COLOR_GREEN "\033[0;32m" @@ -152,7 +152,7 @@ void test_mrtd_bac_encrypt(uint8_t* data, size_t data_length, uint8_t* key, uint void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { uint8_t mac[8]; if(!mrtd_bac_padded_mac(data, data_length, key, mac)) { - printf("ERROR BAC MAC"); + printf("ERROR BAC MAC\n"); return; } @@ -161,6 +161,7 @@ void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, u print_hex(exp_output, exp_output_length); printf(" is:\n"); print_hex(mac, 8); + printf(COLOR_RESET "\n"); return; } else { printf(COLOR_GREEN "SUCCESS - mrtd_bac_padded_mac output: "); @@ -169,6 +170,45 @@ void test_mrtd_bac_padded_mac(uint8_t* data, size_t data_length, uint8_t* key, u } } +void test_mrtd_bac_mac_calls(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + mrtd_bac_mac_ctx ctx; + + for(int break_at=0; break_at Date: Tue, 11 Oct 2022 22:13:28 +0200 Subject: [PATCH 29/44] MRTD Secure Messaging added --- lib/nfc/protocols/mrtd.c | 37 ++++++- lib/nfc/protocols/mrtd_helpers.c | 75 +++++++++++--- test_mrtd_helpers.c | 163 ++++++++++++++++++++++++++++++- 3 files changed, 255 insertions(+), 20 deletions(-) diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index c0ac5215d..615b34506 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -132,11 +132,32 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, if(furi_hal_nfc_tx_rx(tx_rx, 300)) { mrtd_trace(app); uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); + //TODO: handle other return codes? if(ret_code == 0x9000) { + + if(app->secure_messaging) { + //TODO: decrypt and verify + app->ssc_long++; + + mrtd_bac_decrypt_verify + } + return true; } else { FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); + + switch(ret_code) { + case 0x6987: + FURI_LOG_I(TAG, "'expected secure messaging data objects are missing'"); + app->secure_messaging = false; + break; + case 0x6988: + FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'"); + app->secure_messaging = false; + break; + } + return false; } } @@ -358,6 +379,9 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { //memcpy(rnd_ifd, "\x78\x17\x23\x86\x0C\x06\xC2\x26", 8); //memcpy(k_ifd, "\x0B\x79\x52\x40\xCB\x70\x49\xB0\x1C\x19\xB3\x3E\x32\x80\x4F\x0B", 16); + hexdump(FuriLogLevelDebug, "rnd_ifd:", rnd_ifd, 8); + hexdump(FuriLogLevelDebug, "k_ifd:", k_ifd, 16); + uint8_t kenc[16]; uint8_t kmac[16]; @@ -375,9 +399,9 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { uint8_t cmd_data[40]; uint8_t *eifd = cmd_data; - uint8_t *kifd = cmd_data+32; + uint8_t *mifd = cmd_data+32; mrtd_bac_encrypt(S, 32, kenc, eifd); - mrtd_bac_padded_mac(eifd, 32, kmac, kifd); + mrtd_bac_padded_mac(eifd, 32, kmac, mifd); uint8_t response[40]; if(!mrtd_external_authenticate(app, cmd_data, 40, response, 40)) { @@ -393,19 +417,26 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { uint8_t *rnd_ifd_recv = buffer + 8; uint8_t *kic = buffer + 16; + hexdump(FuriLogLevelDebug, "kic:", kic, 16); + if(memcmp(rnd_ifd, rnd_ifd_recv, 8)) { FURI_LOG_W(TAG, "BAC RND.IFD sent and received mismatch."); } uint8_t kseed[16]; for(uint8_t i=0; i<16; ++i) { - kseed[i] = kifd[i] ^ kic[i]; + kseed[i] = k_ifd[i] ^ kic[i]; + printf("seed %2d = %02X ^ %02X = %02X\r\n", i, k_ifd[i], kic[i], kseed[i]); } + hexdump(FuriLogLevelDebug, "kseed:", kseed, 16); + if(!mrtd_bac_keys_from_seed(kseed, app->ksenc, app->ksmac)) { FURI_LOG_E(TAG, "BAC error, could not derive KSenc and KSmac"); return false; } + hexdump(FuriLogLevelDebug, "ksenc:", app->ksenc, 16); + hexdump(FuriLogLevelDebug, "ksmac:", app->ksmac, 16); hexdump(FuriLogLevelTrace, "RND.IC:", rnd_ic, 8); hexdump(FuriLogLevelTrace, "RND.IFS:", rnd_ifd, 8); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 22ce7e6c8..ada117af1 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -146,6 +146,12 @@ bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uin bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; + printf("Decrypt: "); + for(uint8_t i=0; i %02X\r\n", mac_calc[i], data[data_length - 8 + i]); + } + return false; + } + return true; +} + +bool mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output, uint16_t* ret_code) { + // Message: [DO'85 or DO'87] || [DO'99] || DO'8E + // Lengths: Var 1+1+2=4 1+1+8=10 + + printf("ret_code: %02X %02X\n", data[data_length - 10 - 2], data[data_length - 10 - 2 + 1]); + *ret_code = data[data_length - 10 - 2] <<8 | data[data_length - 10 - 1]; + //ntohs(data + data_length - 10 - 2); + printf("set to: %04X\n", *ret_code); + + if(data[0] == 0x87) { + uint8_t do87_length = data[1] - 1; + mrtd_bac_decrypt(data + 3, do87_length, key_enc, output); + } + + mrtd_bac_mac_ctx ctx; + mrtd_bac_mac_init(&ctx, key_mac); + uint64_t ssc_n = htonll(ssc); + mrtd_bac_mac_update(&ctx, (uint8_t*)&ssc_n, 8); + mrtd_bac_mac_update(&ctx, data, data_length - 10); // 10 = len(DO'8E) = len(header + length + MAC) = 1 + 1 + 8 + uint8_t mac_calc[8]; + mrtd_bac_mac_finalize(&ctx, mac_calc); + + if(memcmp(mac_calc, data + data_length - 8, 8)) { + printf( "SM MAC failed\r\n"); + for(uint8_t i=0; i<8; ++i) { + printf("%02X <=> %02X\r\n", mac_calc[i], data[data_length - 8 + i]); + } return false; } return true; @@ -180,6 +221,7 @@ bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]) { } bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length) { + //printf("MAC add %d: ", data_length); print_hex(data, data_length); printf("\n"); size_t data_idx = 0; //uint8_t* xormac = ctx->xormac; @@ -198,7 +240,7 @@ bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data } mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); - printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + printf("DES buf: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", ctx->buffer_in[0], ctx->buffer_in[1], ctx->buffer_in[2], ctx->buffer_in[3], ctx->buffer_in[4], ctx->buffer_in[5], ctx->buffer_in[6], ctx->buffer_in[7]); @@ -218,7 +260,7 @@ bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data } mbedtls_des_crypt_ecb(&ctx->des, ctx->xormac, ctx->mac); - printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + printf("DES add: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", data[data_idx - 8 + 0], data[data_idx - 8 + 1], data[data_idx - 8 + 2], data[data_idx - 8 + 3], data[data_idx - 8 + 4], data[data_idx - 8 + 5], data[data_idx - 8 + 6], data[data_idx - 8 + 7]); @@ -277,7 +319,7 @@ bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t } mbedtls_des_crypt_ecb(&ctx, xormac, mac); - printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\n", + printf("DES1: %02X %02X %02X %02X %02X %02X %02X %02X\r\n", xormac[0], xormac[1], xormac[2], xormac[3], xormac[4], xormac[5], xormac[6], xormac[7]); } @@ -318,6 +360,8 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8 mrtd_bac_mac_ctx mac_ctx; mrtd_bac_mac_init(&mac_ctx, key_mac); uint64_t ssc_n = htonll(ssc); + printf("ssc: %016llx\r\n", ssc); + //printf("ssc_n: "); print_hex(ssc_n, 8); printf("\n"); mrtd_bac_mac_update(&mac_ctx, (uint8_t*)&ssc_n, 8); // Mask cla @@ -335,10 +379,10 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8 // Build DO'87 // TODO: condition on data presence - { + // TODO: if ins is odd, use 0x85 + if(lc > 0) { size_t newlength = ((lc+8)/8)*8; uint8_t padded[newlength]; - size_t idx_do87 = idx; output[idx++] = 0x87; // Header output[idx++] = newlength + 1; // Length @@ -350,26 +394,31 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8 mrtd_bac_encrypt(padded, newlength, key_enc, output + idx); idx += newlength; - - mrtd_bac_mac_update(&mac_ctx, output + idx_do87, idx - idx_do87); } + // Build DO'97 + if(le >= 0) { + output[idx++] = 0x97; // Header + output[idx++] = 0x01; // Length + output[idx++] = le; + } + + mrtd_bac_mac_update(&mac_ctx, output + idx_lc + 1, idx - idx_lc - 1); + // Build DO'8E // TODO: conditions? { output[idx++] = 0x8E; // Header output[idx++] = 0x08; // Length - printf("idx: %d\n", idx); - - uint8_t mac[8]; mrtd_bac_mac_finalize(&mac_ctx, output + idx); idx += 8; + printf("MAC: "); for(uint8_t i=0; i<8; ++i) { - printf("%02X ", mac[i]); + printf("%02X ", output[idx - 8 + i]); } - printf("\n"); + printf("\r\n"); } output[idx_lc] = idx - idx_lc - 1; // Set Lc diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index b3e5e462c..568d2c248 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -1,7 +1,10 @@ #include +#include #include #include +#include "applications/main/nfc/test_bac_creds.h" //TODO: remove + #include "lib/nfc/protocols/mrtd_helpers.h" // gcc -o test_mrtd_helpers -Wall -Ilib/mbedtls/include -Itoolchain/x86_64-linux/arm-none-eabi/include/ lib/nfc/protocols/mrtd_helpers.c lib/mbedtls/library/sha1.c lib/mbedtls/library/des.c lib/mbedtls/library/platform_util.c test_mrtd_helpers.c @@ -209,12 +212,58 @@ void test_mrtd_bac_mac_calls(uint8_t* data, size_t data_length, uint8_t* key, ui } } +void test_mrtd_bac_mac_steps(uint8_t* data, size_t data_length, uint8_t* key, uint8_t* exp_output, size_t exp_output_length) { + mrtd_bac_mac_ctx ctx; + + printf("A\n"); + + if(!mrtd_bac_mac_init(&ctx, key)) { + printf("ERROR mrtd_bac_mac_init (steps)\n"); + return; + } + + if(!mrtd_bac_mac_update(&ctx, data, 8)) { + printf("ERROR mrtd_bac_mac_update 1 (steps)\n"); + } + + if(!mrtd_bac_mac_update(&ctx, data + 8, 4)) { + printf("ERROR mrtd_bac_mac_update 2 (steps)\n"); + } + + if(!mrtd_bac_mac_pad(&ctx)) { + printf("ERROR mrtd_bac_mac_pad (steps)\n"); + } + + if(!mrtd_bac_mac_update(&ctx, data + 16, 11)) { + printf("ERROR mrtd_bac_mac_update 3 (steps)\n"); + } + + uint8_t mac[8]; + if(!mrtd_bac_mac_finalize(&ctx, mac)) { + printf("ERROR mrtd_bac_mac_finalize (steps)\n"); + return; + } + + if(memcmp(exp_output, mac, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_mac (steps), expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(mac, 8); + printf(COLOR_RESET "\n"); + return; + } else { + printf(COLOR_GREEN "SUCCESS - mrtd_bac_mac (steps) output: "); + print_hex(mac, 8); + printf(COLOR_RESET "\n"); + } +} + void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* exp_output, size_t exp_output_length, bool should_verify) { uint8_t buffer[256]; bool result = mrtd_bac_decrypt_verify(data, data_length, key_enc, key_mac, buffer); if(result != should_verify) { - printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify, expected verify: %d, but is: %d\n", should_verify, result); + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify, expected verify: %d, but is: %d\n" COLOR_RESET, should_verify, result); return; } @@ -223,6 +272,7 @@ void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8 print_hex(exp_output, exp_output_length); printf(" is:\n"); print_hex(buffer, 32); + printf(COLOR_RESET "\n"); return; } @@ -231,6 +281,35 @@ void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8 printf(COLOR_RESET "\n"); } +void test_mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* exp_output, size_t exp_output_length, bool should_verify) { + uint8_t buffer[256]; + uint16_t ret_code; + + bool result = mrtd_bac_decrypt_verify_sm(data, data_length, key_enc, key_mac, ssc, buffer, &ret_code); + if(result != should_verify) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected verify: %d, but is: %d\n" COLOR_RESET, should_verify, result); + return; + } + + if(ret_code != 0x9000) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected ret_code: %04X, but is: %04X\n" COLOR_RESET, 0x9000, ret_code); + return; + } + + if(memcmp(exp_output, buffer, exp_output_length)) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected output:\n"); + print_hex(exp_output, exp_output_length); + printf(" is:\n"); + print_hex(buffer, 32); + printf(COLOR_RESET "\n"); + return; + } + + printf(COLOR_GREEN "SUCCESS - mrtd_bac_decrypt_verify_sm output: "); + print_hex(buffer, exp_output_length); + printf(COLOR_RESET "\n"); +} + void test_mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t* rnd_ifd, uint64_t exp_ssc) { uint64_t ssc_long = mrtd_ssc_from_data(rnd_ic, rnd_ifd); @@ -253,10 +332,11 @@ void test_mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, ui } if(memcmp(buffer, exp_output, ret)) { - printf(COLOR_RED "FAILED - mrtd_protect_apdu, expected output:\n" COLOR_RESET); + printf(COLOR_RED "FAILED - mrtd_protect_apdu, expected output:\n"); print_hex(exp_output, exp_output_length); printf(" is:\n"); print_hex(buffer, ret); + printf("\n" COLOR_RESET); return; } @@ -349,9 +429,84 @@ int main(int argc, char** argv) { uint8_t* rnd_ic = (uint8_t*)"\x46\x08\xF9\x19\x88\x70\x22\x12"; uint8_t* rnd_ifd = (uint8_t*)"\x78\x17\x23\x86\x0C\x06\xC2\x26"; - test_mrtd_ssc_from_data(rnd_ic, rnd_ifd, 0x887022120C06C226); + uint64_t ssc = 0x887022120C06C226; + test_mrtd_ssc_from_data(rnd_ic, rnd_ifd, ssc); - test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x1e", -1, ks_enc, ks_mac, 0x887022120C06C227, (uint8_t*)"\x0C\xA4\x02\x0C\x15\x87\x09\x01\x63\x75\x43\x29\x08\xC0\x44\xF6\x8E\x08\xBF\x8B\x92\xD6\x35\xFF\x24\xF8\x00", 27); + ssc++; + + test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x1e", -1, ks_enc, ks_mac, ssc, (uint8_t*)"\x0C\xA4\x02\x0C\x15\x87\x09\x01\x63\x75\x43\x29\x08\xC0\x44\xF6\x8E\x08\xBF\x8B\x92\xD6\x35\xFF\x24\xF8\x00", 27); + + ssc++; // Increment for decrypt, verify + + test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x99\x02\x90\x00\x8E\x08\xFA\x85\x5A\x5D\x4C\x50\xA8\xED", 14, ks_enc, ks_mac, ssc, NULL, 0, 1); + + ssc++; // Increment for encrypt, sign + + test_mrtd_protect_apdu(0x00, 0xB0, 0x00, 0x00, 0x00, NULL, 0x04, ks_enc, ks_mac, ssc, (uint8_t*)"\x0C\xB0\x00\x00\x0D\x97\x01\x04\x8E\x08\xED\x67\x05\x41\x7E\x96\xBA\x55\x00", 19); + + ssc++; // Increment for decrypt, verify + + test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x87\x09\x01\x9F\xF0\xEC\x34\xF9\x92\x26\x51\x99\x02\x90\x00\x8E\x08\xAD\x55\xCC\x17\x14\x0B\x2D\xED", 25, ks_enc, ks_mac, ssc, (uint8_t*)"\x60\x14\x5F\x01", 4, 1); + + ssc++; // Increment for encrypt, sign + + test_mrtd_protect_apdu(0x00, 0xB0, 0x00, 0x04, 0x00, NULL, 0x12, ks_enc, ks_mac, ssc, (uint8_t*)"\x0C\xB0\x00\x04\x0D\x97\x01\x12\x8E\x08\x2E\xA2\x8A\x70\xF3\xC7\xB5\x35\x00", 19); + + // Verify working against mrtdreader + + /* + printf("=====================================\n\n"); + + //TODO: set auth data + MrtdAuthData auth; + auth.birth_date = TODO_REMOVE_ID_DOB; + auth.expiry_date = TODO_REMOVE_ID_DOE; + memcpy(auth.doc_number, TODO_REMOVE_ID_DOC, 9); + uint8_t kenc[16]; + uint8_t kmac[16]; + mrtd_bac_keys(&auth, kenc, kmac); + + printf("kenc: "); print_hex(kenc, 16); printf("\n"); + printf("kmac: "); print_hex(kmac, 16); printf("\n"); + + uint8_t buffer[32]; // RND.IC || RND.IFD || KIC + + //TODO: set challenge rx + mrtd_bac_decrypt_verify((uint8_t*)"\x3F\xD4\x6B\xA9\xFF\x29\x4B\xF6\x77\x4E\x8F\x1E\xEC\xAE\x2E\x67\xDB\xE7\x70\x53\xB3\xAD\x9C\xDC\xED\x6E\xED\xD6\x04\x2E\xB7\x6B\x74\xDE\x2A\xFB\x4B\xC0\xF7\x24", 40, kenc, kmac, buffer); + //TODO: set kifd + uint8_t *kifd = "\x9F\x10\x40\xEA\x7F\xAE\xF8\xC3\x09\x6E\xAE\x07\x66\x95\x3F\xDC"; + + printf("buffer: "); print_hex(buffer, 32); printf("\n"); + // 8F763C0B1CDF9F9D|0983F7C136155248|7A705FD193C6A6328C42264A3804002C + rnd_ic = buffer; + rnd_ifd = buffer+8; + uint8_t *kic = buffer+16; + printf("kifd: "); print_hex(kifd, 16); printf("\n"); + printf("kicc: "); print_hex(kic, 16); printf("\n"); + uint8_t kseed[16]; + for(uint8_t i=0; i<16; ++i) { + kseed[i] = kifd[i] ^ kic[i]; + printf("seed %2d = %02X ^ %02X = %02X\r\n", i, kifd[i], kic[i], kseed[i]); + } + printf("kseed: "); print_hex(kseed, 16); printf("\n"); + + ks_enc = malloc(16); + ks_mac = malloc(16); + mrtd_bac_keys_from_seed(kseed, ks_enc, ks_mac); + printf("ks_enc: "); print_hex(ks_enc, 16); printf("\n"); + printf("ks_mac: "); print_hex(ks_mac, 16); printf("\n"); + + printf("rnd_ic: "); print_hex(rnd_ic, 16); printf("\n"); + printf("rnd_ifd: "); print_hex(rnd_ifd, 16); printf("\n"); + uint64_t ssc = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + printf("ssc: %016lx", ssc); + + ssc++; + + //TODO: set challenge TX for verification + test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x1e", -1, ks_enc, ks_mac, ssc, + (uint8_t*)"\x0C\xA4\x02\x0C\x15\x87\x09\x01\xC5\x4E\x76\x3A\xD1\x89\xF0\xFA\x8E\x08\x1F\x03\xC2\xB6\xCE\x8A\xE1\x53\x00", 27); + */ return 0; } From 374f2acb6221db9e85611c77ae9322c3a31d74b0 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:29 +0200 Subject: [PATCH 30/44] MRTD decrypt RX APDU --- lib/nfc/protocols/mrtd.c | 57 ++++++++++---------------------- lib/nfc/protocols/mrtd_helpers.c | 6 ---- lib/nfc/protocols/mrtd_helpers.h | 2 ++ 3 files changed, 19 insertions(+), 46 deletions(-) diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 615b34506..61ab50865 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -77,28 +77,9 @@ struct AIDSet AID = { .AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03}, }; -/*bool mrtd_send_apdu(MrtdApplication* app, uint8_t* buffer, size_t length) { - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - memcpy(tx_rx->tx_data, buffer, length); - tx_rx->tx_bits = length * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - //TODO: timeout as param? - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - mrtd_trace(app); - uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); - if(ret_code == 0x9000) { - return true; - } else { - FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); - return false; - } - } - return false; -}*/ - -bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le) { +//TODO: rename to transceive? +bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* output) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; size_t idx = 0; @@ -133,16 +114,18 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, mrtd_trace(app); uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); + if(app->secure_messaging && ret_code == 0x9000) { + app->ssc_long++; + mrtd_bac_decrypt_verify_sm(tx_rx->rx_data, tx_rx->rx_bits / 8 - 2, + app->ksenc, app->ksmac, app->ssc_long, output, &ret_code); + } + //TODO: handle other return codes? if(ret_code == 0x9000) { - - if(app->secure_messaging) { - //TODO: decrypt and verify - app->ssc_long++; - - mrtd_bac_decrypt_verify + if(!app->secure_messaging && le > 0) { + // Secure Messaging sets output while decrypting + memcpy(output, tx_rx->rx_data, le); } - return true; } else { FURI_LOG_I(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code); @@ -168,7 +151,7 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { FURI_LOG_D(TAG, "Send select App: %02X %02X %02X %02X %02X %02X %02X", aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]); - if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1)) { + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1, NULL)) { FURI_LOG_W(TAG, "Failed select App"); return false; } @@ -177,14 +160,11 @@ bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[8]) { FURI_LOG_D(TAG, "Send Get Challenge"); - if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08)) { + if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08, challenge)) { FURI_LOG_W(TAG, "Failed get challenge"); return false; } - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - memcpy(challenge, tx_rx->rx_data, 8); - return true; } @@ -193,21 +173,18 @@ bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t furi_assert(out_size >= 0x28); FURI_LOG_D(TAG, "Send External Authenticate"); - if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28)) { + if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28, out_data)) { FURI_LOG_W(TAG, "Failed External Authenticate"); return false; } - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - memcpy(out_data, tx_rx->rx_data, 0x28); - return true; } bool mrtd_select_file(MrtdApplication* app, EFFile file) { uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id); - if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1)) { + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, NULL)) { FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id); return false; } @@ -221,12 +198,12 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s UNUSED(bufsize); // 00 B0 offst - FURI_LOG_D(TAG, "Read binary, offset: %d", offset); - if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, 0)) { + //TODO: limit reading/buffer fill to max bufsize + if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, 0, buffer)) { FURI_LOG_E(TAG, "Failed to read"); return 0; } - //TODO: copy data to buffer //TODO: return read amount return 0; diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index ada117af1..39a9029fd 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -146,12 +146,6 @@ bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uin bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; - printf("Decrypt: "); - for(uint8_t i=0; i #define htonll(x) ((((uint64_t)__htonl(x)) << 32) + __htonl((x) >> 32)) From b5e420c11b7fd4322bcfcb27e0e578421999d1af Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:31 +0200 Subject: [PATCH 31/44] MRTD handle BAC decrypt+unpad --- lib/nfc/protocols/mrtd.c | 43 ++++++++++++++++++-------------- lib/nfc/protocols/mrtd.h | 1 + lib/nfc/protocols/mrtd_helpers.c | 24 ++++++++++++++++-- 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 61ab50865..6b6f328d4 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -15,6 +15,8 @@ //- PACE (CONDITIONAL) //- BAC (CONDITIONAL) +//TODO: idea - generalize ISO7816 reading. List available apps + static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) { if(furi_log_get_level() >= level) { printf("%s ", prefix); @@ -50,24 +52,24 @@ struct EFFormat EF = { .DIR = {.file_id = 0x2F00, .short_id = 0x1E }, .CardAccess = {.file_id = 0x011C, .short_id = 0x1C }, .CardSecurity = {.file_id = 0x011D, .short_id = 0x1D }, - .COM = {.file_id = 0x011E, .short_id = 0x1E }, - .SOD = {.file_id = 0X011D, .short_id = 0X1D }, - .DG1 = {.file_id = 0X0101, .short_id = 0X01 }, - .DG2 = {.file_id = 0X0102, .short_id = 0X02 }, - .DG3 = {.file_id = 0X0103, .short_id = 0X03 }, - .DG4 = {.file_id = 0X0104, .short_id = 0X04 }, - .DG5 = {.file_id = 0X0105, .short_id = 0X05 }, - .DG6 = {.file_id = 0X0106, .short_id = 0X06 }, - .DG7 = {.file_id = 0X0107, .short_id = 0X07 }, - .DG8 = {.file_id = 0X0108, .short_id = 0X08 }, - .DG9 = {.file_id = 0X0109, .short_id = 0X09 }, - .DG10 = {.file_id = 0X010A, .short_id = 0X0A }, - .DG11 = {.file_id = 0X010B, .short_id = 0X0B }, - .DG12 = {.file_id = 0X010C, .short_id = 0X0C }, - .DG13 = {.file_id = 0X010D, .short_id = 0X0D }, - .DG14 = {.file_id = 0X010E, .short_id = 0X0E }, - .DG15 = {.file_id = 0X010F, .short_id = 0X0F }, - .DG16 = {.file_id = 0X0110, .short_id = 0X10 }, + .COM = {.file_id = 0x011E, .short_id = 0x1E, .tag = 0x60 }, + .SOD = {.file_id = 0X011D, .short_id = 0X1D, .tag = 0x77 }, + .DG1 = {.file_id = 0X0101, .short_id = 0X01, .tag = 0x61 }, + .DG2 = {.file_id = 0X0102, .short_id = 0X02, .tag = 0x75 }, + .DG3 = {.file_id = 0X0103, .short_id = 0X03, .tag = 0x63 }, + .DG4 = {.file_id = 0X0104, .short_id = 0X04, .tag = 0x76 }, + .DG5 = {.file_id = 0X0105, .short_id = 0X05, .tag = 0x65 }, + .DG6 = {.file_id = 0X0106, .short_id = 0X06, .tag = 0x66 }, + .DG7 = {.file_id = 0X0107, .short_id = 0X07, .tag = 0x67 }, + .DG8 = {.file_id = 0X0108, .short_id = 0X08, .tag = 0x68 }, + .DG9 = {.file_id = 0X0109, .short_id = 0X09, .tag = 0x69 }, + .DG10 = {.file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a }, + .DG11 = {.file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b }, + .DG12 = {.file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c }, + .DG13 = {.file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d }, + .DG14 = {.file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e }, + .DG15 = {.file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f }, + .DG16 = {.file_id = 0X0110, .short_id = 0X10, .tag = 0x70 }, }; struct AIDSet AID = { @@ -198,8 +200,11 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s UNUSED(bufsize); // 00 B0 offst - FURI_LOG_D(TAG, "Read binary, offset: %d", offset); + //TODO: read first 4 bytes, determine length, iterate through file //TODO: limit reading/buffer fill to max bufsize - if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, 0, buffer)) { + + int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing' + if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer)) { FURI_LOG_E(TAG, "Failed to read"); return 0; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index f207fd97f..eeef9ca4f 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -50,6 +50,7 @@ typedef struct { typedef struct { const uint8_t short_id; const uint16_t file_id; + const uint8_t tag; } EFFile; struct EFFormat { diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 39a9029fd..9923555fd 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -177,14 +177,34 @@ bool mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t // Message: [DO'85 or DO'87] || [DO'99] || DO'8E // Lengths: Var 1+1+2=4 1+1+8=10 - printf("ret_code: %02X %02X\n", data[data_length - 10 - 2], data[data_length - 10 - 2 + 1]); *ret_code = data[data_length - 10 - 2] <<8 | data[data_length - 10 - 1]; //ntohs(data + data_length - 10 - 2); - printf("set to: %04X\n", *ret_code); if(data[0] == 0x87) { uint8_t do87_length = data[1] - 1; mrtd_bac_decrypt(data + 3, do87_length, key_enc, output); + printf("Decrypted: "); for(uint8_t i=0; i=0; --padidx) { + if(output[padidx] == 0x00) { + continue; + } else if(output[padidx] == 0x80) { + break; + } else { + printf("Invalid padding\r\n"); + return false; + } + } + printf(" "); + for(int i=0; i Date: Tue, 11 Oct 2022 22:13:32 +0200 Subject: [PATCH 32/44] MRTD TLV parsing (and select) --- lib/nfc/helpers/iso7816.c | 34 +++++++++++- lib/nfc/helpers/iso7816.h | 17 +++--- test_iso7816_helpers.c | 109 +++++++++++++++++++++++++++++++------- 3 files changed, 130 insertions(+), 30 deletions(-) diff --git a/lib/nfc/helpers/iso7816.c b/lib/nfc/helpers/iso7816.c index 2cbff0809..1759a67f9 100644 --- a/lib/nfc/helpers/iso7816.c +++ b/lib/nfc/helpers/iso7816.c @@ -13,15 +13,22 @@ TlvInfo iso7816_tlv_parse(const uint8_t* data) { // 11111 10000001 00000001 - 11111 11111111 01111111 => 128 - 16383 (3 byte) tlv.tag = *(data++); + tlv.ber.constructed = ((tlv.tag & 0x20) != 0); + tlv.ber.class = (tlv.tag >> 6) & 0x03; if ((tlv.tag & 0x1f) == 0x1f) { // BER-TLV, multi byte tag - tlv.tag = *(data++); + tlv.tag <<= 8; + tlv.tag |= *(data++); + tlv.ber.tag = tlv.tag & 0x7f; if(tlv.tag & 0x80) { // BER-TLV, 3 byte tag tlv.tag &= ~0x80; tlv.tag <<= 7; tlv.tag |= *(data++) & 0x7f; + tlv.ber.tag = tlv.tag & 0x3fff; } + } else { + tlv.ber.tag = tlv.tag & 0x1f; } //TODO: check for invalid 'indefinite length' @@ -49,3 +56,28 @@ TlvInfo iso7816_tlv_parse(const uint8_t* data) { return tlv; } + +TlvInfo iso7816_tlv_select(const uint8_t* data, size_t length, const uint16_t tags[], size_t num_tags) { + TlvInfo tlv; + size_t offset = 0; + + if(num_tags == 0) { + return (TlvInfo){.tag = 0x0000}; + } + + while(offset < length) { + tlv = iso7816_tlv_parse(data + offset); + + if(tlv.tag == tags[0]) { + if(num_tags == 1) { + return tlv; + } else { + return iso7816_tlv_select(tlv.value, tlv.length, tags+1, num_tags - 1); + } + } + + offset = tlv.next - data; // TODO: use some length value of TlvInfo instead of this monstrosity + } + + return (TlvInfo){.tag = 0x0000}; +} diff --git a/lib/nfc/helpers/iso7816.h b/lib/nfc/helpers/iso7816.h index 883da0d33..10d28d11b 100644 --- a/lib/nfc/helpers/iso7816.h +++ b/lib/nfc/helpers/iso7816.h @@ -11,17 +11,12 @@ #define BER_CLASS_PRIVATE 0x3 typedef struct { - union { + uint16_t tag; // TODO: use define/typedef for this data format? + struct { uint16_t tag; - struct { - // LSB - uint8_t tag : 5; - uint8_t constructed : 1; - uint8_t class : 2; - // MSB - } ber; - //TODO: currently only works for 1-byte tags - }; + uint8_t constructed : 1; + uint8_t class : 2; + } ber; size_t length; const uint8_t* value; @@ -31,3 +26,5 @@ typedef struct { // ISO7816-5 §5.2 // Simple-TLV and BER-TLV parsing TlvInfo iso7816_tlv_parse(const uint8_t* data); + +TlvInfo iso7816_tlv_select(const uint8_t* data, size_t length, const uint16_t tags[], size_t num_tags); diff --git a/test_iso7816_helpers.c b/test_iso7816_helpers.c index e617f4713..292fefbf4 100644 --- a/test_iso7816_helpers.c +++ b/test_iso7816_helpers.c @@ -6,19 +6,31 @@ #define COLOR_GREEN "\033[0;32m" #define COLOR_RESET "\033[0;0m" +//TODO: do something with ISO7816-4 Table 9 — Interindustry data objects for tag allocation authority +//0x06 Object identifier (encoding specified in ISO/IEC 8825-1, see examples in annex A) +//0x41 Country code (encoding specified in ISO 3166-1 [1] ) and optional national data +//0x42 Issuer identification number (encoding and registration specified in ISO/IEC 7812-1 [3] ) and optional issuer data +//0x4F Application identifier (AID, encoding specified in 8.2.1.2) + void print_hex(const uint8_t* data, size_t length) { for(size_t i=0; i Date: Tue, 11 Oct 2022 22:13:33 +0200 Subject: [PATCH 33/44] Fix indention --- lib/nfc/helpers/iso7816.c | 126 +++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/lib/nfc/helpers/iso7816.c b/lib/nfc/helpers/iso7816.c index 1759a67f9..cab78bcb7 100644 --- a/lib/nfc/helpers/iso7816.c +++ b/lib/nfc/helpers/iso7816.c @@ -4,80 +4,80 @@ // Simple-TLV (§5.2.1) // BER-TLV (§5.2.2) TlvInfo iso7816_tlv_parse(const uint8_t* data) { - TlvInfo tlv; + TlvInfo tlv; - // Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF') - // BER-TLV: TODO describe - // 00000 - 11110 => 0 - 30 (single byte) - // 11111 00011111 - 11111 01111111 => 31 - 127 (2 byte) - // 11111 10000001 00000001 - 11111 11111111 01111111 => 128 - 16383 (3 byte) + // Simple-TLV: tag can be any value from 1 to 254 (not '00' or 'FF') + // BER-TLV: TODO describe + // 00000 - 11110 => 0 - 30 (single byte) + // 11111 00011111 - 11111 01111111 => 31 - 127 (2 byte) + // 11111 10000001 00000001 - 11111 11111111 01111111 => 128 - 16383 (3 byte) - tlv.tag = *(data++); - tlv.ber.constructed = ((tlv.tag & 0x20) != 0); - tlv.ber.class = (tlv.tag >> 6) & 0x03; - if ((tlv.tag & 0x1f) == 0x1f) { - // BER-TLV, multi byte tag - tlv.tag <<= 8; - tlv.tag |= *(data++); - tlv.ber.tag = tlv.tag & 0x7f; - if(tlv.tag & 0x80) { - // BER-TLV, 3 byte tag - tlv.tag &= ~0x80; - tlv.tag <<= 7; - tlv.tag |= *(data++) & 0x7f; + tlv.tag = *(data++); + tlv.ber.constructed = ((tlv.tag & 0x20) != 0); + tlv.ber.class = (tlv.tag >> 6) & 0x03; + if ((tlv.tag & 0x1f) == 0x1f) { + // BER-TLV, multi byte tag + tlv.tag <<= 8; + tlv.tag |= *(data++); + tlv.ber.tag = tlv.tag & 0x7f; + if(tlv.tag & 0x80) { + // BER-TLV, 3 byte tag + tlv.tag &= ~0x80; + tlv.tag <<= 7; + tlv.tag |= *(data++) & 0x7f; tlv.ber.tag = tlv.tag & 0x3fff; - } - } else { - tlv.ber.tag = tlv.tag & 0x1f; - } + } + } else { + tlv.ber.tag = tlv.tag & 0x1f; + } - //TODO: check for invalid 'indefinite length' - tlv.length = *(data++); - if (tlv.length == 0xff) { - // Simple-TLV 2 byte length - tlv.length = *(data++) << 8; - tlv.length += *(data++); - } else if(tlv.length > 0x7f) { - uint8_t length_bytes = tlv.length & 0x7f; - //printf("BER length of %d bytes\n", length_bytes); - if (length_bytes < 1 || length_bytes > 4) { - //TODO: error: ISO7816 doesn't support more than 4 length bytes - return (TlvInfo){.tag = 0}; - } - tlv.length = 0; - for(uint8_t i=0; i 0x7f) { + uint8_t length_bytes = tlv.length & 0x7f; + //printf("BER length of %d bytes\n", length_bytes); + if (length_bytes < 1 || length_bytes > 4) { + //TODO: error: ISO7816 doesn't support more than 4 length bytes + return (TlvInfo){.tag = 0}; + } + tlv.length = 0; + for(uint8_t i=0; i Date: Tue, 11 Oct 2022 22:13:34 +0200 Subject: [PATCH 34/44] MRTD more tlv_select tests --- test_iso7816_helpers.c | 71 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/test_iso7816_helpers.c b/test_iso7816_helpers.c index 292fefbf4..e433764f2 100644 --- a/test_iso7816_helpers.c +++ b/test_iso7816_helpers.c @@ -1,4 +1,5 @@ #include +#include #include "lib/nfc/helpers/iso7816.h" @@ -6,6 +7,8 @@ #define COLOR_GREEN "\033[0;32m" #define COLOR_RESET "\033[0;0m" +#define num_elements(A) (sizeof(A)/sizeof(A[0])) + //TODO: do something with ISO7816-4 Table 9 — Interindustry data objects for tag allocation authority //0x06 Object identifier (encoding specified in ISO/IEC 8825-1, see examples in annex A) //0x41 Country code (encoding specified in ISO 3166-1 [1] ) and optional national data @@ -24,6 +27,24 @@ void print_tlv(char* fmt, TlvInfo tlv) { printf("\n"); } +int tlv_number(TlvInfo tlv) { + //TODO: negative numbers? + const char* str = tlv.value; + size_t length = tlv.length; + + int value = 0; + while(length--) { + char c = *(str++); + + if(c >= '0' && c <= '9') { + value = value * 10 + (c - '0'); + } else { + //TODO: warning? return? crash? + } + } + return value; +} + void test_iso7816_tlv_parse(const uint8_t* input, size_t input_size, uint16_t exp_tag, size_t exp_length) { TlvInfo tlv = iso7816_tlv_parse(input); @@ -185,12 +206,56 @@ int main(int argc, char** argv) { size_t ef_com_data_len = 24; describe_tlv(ef_com_data, ef_com_data_len, 0); - printf("====\n"); + uint16_t lds_tag_path[] = {0x60, 0x5f01}; + uint16_t unicode_tag_path[] = {0x60, 0x5f36}; + uint16_t tags_tag_path[] = {0x60, 0x5c}; - TlvInfo tlv = iso7816_tlv_select(ef_com_data, ef_com_data_len, (uint16_t[]){0x60, 0x5f36}, 2); - print_tlv("0x60, 0x5f36:", tlv); + TlvInfo tlv_lds_version = iso7816_tlv_select(ef_com_data, ef_com_data_len, lds_tag_path, num_elements(lds_tag_path)); + if(tlv_lds_version.tag) { + int tlv_version = tlv_number(tlv_lds_version); + printf("LDS Version: %d.%d (%.4s)\n", tlv_version/100, tlv_version%100, tlv_lds_version.value); + } else { + printf("Error, LDS info not found!\n"); + } + + TlvInfo tlv_unicode_version = iso7816_tlv_select(ef_com_data, ef_com_data_len, unicode_tag_path, num_elements(unicode_tag_path)); + if(tlv_unicode_version.tag) { + int unicode_version = tlv_number(tlv_unicode_version); + printf("Unicode Version: %d.%d.%d (%.6s)\n", unicode_version/10000, unicode_version/100%100, unicode_version%100, tlv_unicode_version.value); + } else { + printf("Error, Unicode info not found!\n"); + } + + TlvInfo tlv_tag_list = iso7816_tlv_select(ef_com_data, ef_com_data_len, tags_tag_path, num_elements(tags_tag_path)); + if(tlv_tag_list.tag) { + printf("Tag List:\n"); + for(size_t i=0; i Date: Tue, 11 Oct 2022 22:13:35 +0200 Subject: [PATCH 35/44] MRTD add file lookup by tag and EF.COM format --- lib/nfc/protocols/mrtd.c | 72 ++++++++++++++++++++++++++++------------ lib/nfc/protocols/mrtd.h | 8 +++++ 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 6b6f328d4..60bd2ff16 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -47,29 +47,31 @@ uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) { return (buffer[len-2] << 8) | buffer[len-1]; } +EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00 }; + struct EFFormat EF = { - .ATR = {.file_id = 0x2F01, .short_id = 0x01 }, - .DIR = {.file_id = 0x2F00, .short_id = 0x1E }, - .CardAccess = {.file_id = 0x011C, .short_id = 0x1C }, - .CardSecurity = {.file_id = 0x011D, .short_id = 0x1D }, - .COM = {.file_id = 0x011E, .short_id = 0x1E, .tag = 0x60 }, - .SOD = {.file_id = 0X011D, .short_id = 0X1D, .tag = 0x77 }, - .DG1 = {.file_id = 0X0101, .short_id = 0X01, .tag = 0x61 }, - .DG2 = {.file_id = 0X0102, .short_id = 0X02, .tag = 0x75 }, - .DG3 = {.file_id = 0X0103, .short_id = 0X03, .tag = 0x63 }, - .DG4 = {.file_id = 0X0104, .short_id = 0X04, .tag = 0x76 }, - .DG5 = {.file_id = 0X0105, .short_id = 0X05, .tag = 0x65 }, - .DG6 = {.file_id = 0X0106, .short_id = 0X06, .tag = 0x66 }, - .DG7 = {.file_id = 0X0107, .short_id = 0X07, .tag = 0x67 }, - .DG8 = {.file_id = 0X0108, .short_id = 0X08, .tag = 0x68 }, - .DG9 = {.file_id = 0X0109, .short_id = 0X09, .tag = 0x69 }, - .DG10 = {.file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a }, - .DG11 = {.file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b }, - .DG12 = {.file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c }, - .DG13 = {.file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d }, - .DG14 = {.file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e }, - .DG15 = {.file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f }, - .DG16 = {.file_id = 0X0110, .short_id = 0X10, .tag = 0x70 }, + .ATR = {.name = "ATR", .file_id = 0x2F01, .short_id = 0x01 }, + .DIR = {.name = "DIR", .file_id = 0x2F00, .short_id = 0x1E }, + .CardAccess = {.name = "CardAccess", .file_id = 0x011C, .short_id = 0x1C }, + .CardSecurity = {.name = "CardSecurity", .file_id = 0x011D, .short_id = 0x1D }, + .COM = {.name = "COM", .file_id = 0x011E, .short_id = 0x1E, .tag = 0x60 }, + .SOD = {.name = "SOD", .file_id = 0X011D, .short_id = 0X1D, .tag = 0x77 }, + .DG1 = {.name = "DG1", .file_id = 0X0101, .short_id = 0X01, .tag = 0x61 }, + .DG2 = {.name = "DG2", .file_id = 0X0102, .short_id = 0X02, .tag = 0x75 }, + .DG3 = {.name = "DG3", .file_id = 0X0103, .short_id = 0X03, .tag = 0x63 }, + .DG4 = {.name = "DG4", .file_id = 0X0104, .short_id = 0X04, .tag = 0x76 }, + .DG5 = {.name = "DG5", .file_id = 0X0105, .short_id = 0X05, .tag = 0x65 }, + .DG6 = {.name = "DG6", .file_id = 0X0106, .short_id = 0X06, .tag = 0x66 }, + .DG7 = {.name = "DG7", .file_id = 0X0107, .short_id = 0X07, .tag = 0x67 }, + .DG8 = {.name = "DG8", .file_id = 0X0108, .short_id = 0X08, .tag = 0x68 }, + .DG9 = {.name = "DG9", .file_id = 0X0109, .short_id = 0X09, .tag = 0x69 }, + .DG10 = {.name = "DG10", .file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a }, + .DG11 = {.name = "DG11", .file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b }, + .DG12 = {.name = "DG12", .file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c }, + .DG13 = {.name = "DG13", .file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d }, + .DG14 = {.name = "DG14", .file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e }, + .DG15 = {.name = "DG15", .file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f }, + .DG16 = {.name = "DG16", .file_id = 0X0110, .short_id = 0X10, .tag = 0x70 }, }; struct AIDSet AID = { @@ -79,6 +81,32 @@ struct AIDSet AID = { .AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03}, }; +EFFile* mrtd_tag_to_file(uint8_t tag) { + //TODO: generate this code with macros? + switch(tag) { + case 0x60: return &EF.COM; + case 0x77: return &EF.SOD; + case 0x61: return &EF.DG1; + case 0x75: return &EF.DG2; + case 0x63: return &EF.DG3; + case 0x76: return &EF.DG4; + case 0x65: return &EF.DG5; + case 0x66: return &EF.DG6; + case 0x67: return &EF.DG7; + case 0x68: return &EF.DG8; + case 0x69: return &EF.DG9; + case 0x6a: return &EF.DG10; + case 0x6b: return &EF.DG11; + case 0x6c: return &EF.DG12; + case 0x6d: return &EF.DG13; + case 0x6e: return &EF.DG14; + case 0x6f: return &EF.DG15; + case 0x70: return &EF.DG16; + default: + furi_assert(false); + return &EFNone; + } +}; //TODO: rename to transceive? bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* output) { diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index eeef9ca4f..212a7878f 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -48,6 +48,7 @@ typedef struct { } MrtdData; typedef struct { + const char* name; const uint8_t short_id; const uint16_t file_id; const uint8_t tag; @@ -83,6 +84,13 @@ struct EFFormat { extern struct EFFormat EF; +#define MAX_EFCOM_TAGS 18 +typedef struct { + uint16_t lds_version; // xxyy => xx.yy (major.minor) + uint32_t unicode_version; // aabbcc => aa.bb.cc (major.minor.release) + uint8_t tag_list[MAX_EFCOM_TAGS]; +} EFComFormat; + //TODO: description MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); bool mrtd_select_app(MrtdApplication* app, AIDValue aid); From 89d0c315b1ec1c386bf86498425a85c997d37099 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:36 +0200 Subject: [PATCH 36/44] MRTD multiple changes --- .../nfc/scenes/nfc_scene_passport_read_auth.c | 8 +- lib/nfc/protocols/mrtd.c | 193 ++++++++++-------- lib/nfc/protocols/mrtd.h | 77 +------ lib/nfc/protocols/mrtd_helpers.c | 155 ++++++++++---- lib/nfc/protocols/mrtd_helpers.h | 80 +++++++- test_iso7816_helpers.c | 18 -- test_mrtd_helpers.c | 18 +- 7 files changed, 319 insertions(+), 230 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c index 6bff4b00e..d0498bd95 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -17,9 +17,15 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { // Setup Custom Widget view string_t temp_str; string_init_printf(temp_str, "\e#Passport\n"); - string_cat_printf(temp_str, "Authenticated: %d", mrtd_data->auth_success); + string_cat_printf(temp_str, "Authenticated: %d\n", mrtd_data->auth_success); // TODO: indicate BAC / PACE used + uint16_t lds_version = mrtd_data->files.EF_COM.lds_version; + string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version/100, lds_version%100); + + uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version; + string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", unicode_version/10000, unicode_version/100%100, unicode_version%100); + /* char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; //TODO: NFC-B? diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 60bd2ff16..07501abf3 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -17,6 +17,8 @@ //TODO: idea - generalize ISO7816 reading. List available apps +#define num_elements(A) (sizeof(A)/sizeof(A[0])) + static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) { if(furi_log_get_level() >= level) { printf("%s ", prefix); @@ -47,69 +49,9 @@ uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) { return (buffer[len-2] << 8) | buffer[len-1]; } -EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00 }; - -struct EFFormat EF = { - .ATR = {.name = "ATR", .file_id = 0x2F01, .short_id = 0x01 }, - .DIR = {.name = "DIR", .file_id = 0x2F00, .short_id = 0x1E }, - .CardAccess = {.name = "CardAccess", .file_id = 0x011C, .short_id = 0x1C }, - .CardSecurity = {.name = "CardSecurity", .file_id = 0x011D, .short_id = 0x1D }, - .COM = {.name = "COM", .file_id = 0x011E, .short_id = 0x1E, .tag = 0x60 }, - .SOD = {.name = "SOD", .file_id = 0X011D, .short_id = 0X1D, .tag = 0x77 }, - .DG1 = {.name = "DG1", .file_id = 0X0101, .short_id = 0X01, .tag = 0x61 }, - .DG2 = {.name = "DG2", .file_id = 0X0102, .short_id = 0X02, .tag = 0x75 }, - .DG3 = {.name = "DG3", .file_id = 0X0103, .short_id = 0X03, .tag = 0x63 }, - .DG4 = {.name = "DG4", .file_id = 0X0104, .short_id = 0X04, .tag = 0x76 }, - .DG5 = {.name = "DG5", .file_id = 0X0105, .short_id = 0X05, .tag = 0x65 }, - .DG6 = {.name = "DG6", .file_id = 0X0106, .short_id = 0X06, .tag = 0x66 }, - .DG7 = {.name = "DG7", .file_id = 0X0107, .short_id = 0X07, .tag = 0x67 }, - .DG8 = {.name = "DG8", .file_id = 0X0108, .short_id = 0X08, .tag = 0x68 }, - .DG9 = {.name = "DG9", .file_id = 0X0109, .short_id = 0X09, .tag = 0x69 }, - .DG10 = {.name = "DG10", .file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a }, - .DG11 = {.name = "DG11", .file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b }, - .DG12 = {.name = "DG12", .file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c }, - .DG13 = {.name = "DG13", .file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d }, - .DG14 = {.name = "DG14", .file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e }, - .DG15 = {.name = "DG15", .file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f }, - .DG16 = {.name = "DG16", .file_id = 0X0110, .short_id = 0X10, .tag = 0x70 }, -}; - -struct AIDSet AID = { - .eMRTDApplication = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}, - .TravelRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x01}, - .VisaRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x02}, - .AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03}, -}; - -EFFile* mrtd_tag_to_file(uint8_t tag) { - //TODO: generate this code with macros? - switch(tag) { - case 0x60: return &EF.COM; - case 0x77: return &EF.SOD; - case 0x61: return &EF.DG1; - case 0x75: return &EF.DG2; - case 0x63: return &EF.DG3; - case 0x76: return &EF.DG4; - case 0x65: return &EF.DG5; - case 0x66: return &EF.DG6; - case 0x67: return &EF.DG7; - case 0x68: return &EF.DG8; - case 0x69: return &EF.DG9; - case 0x6a: return &EF.DG10; - case 0x6b: return &EF.DG11; - case 0x6c: return &EF.DG12; - case 0x6d: return &EF.DG13; - case 0x6e: return &EF.DG14; - case 0x6f: return &EF.DG15; - case 0x70: return &EF.DG16; - default: - furi_assert(false); - return &EFNone; - } -}; - //TODO: rename to transceive? -bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* output) { +//TODO: PRIO output and output written writing seems to crash flipper, sometimes +bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* output, size_t* output_written) { FuriHalNfcTxRxContext* tx_rx = app->tx_rx; size_t idx = 0; @@ -121,6 +63,8 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, app->ssc_long++; idx = mrtd_protect_apdu(cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data); + FURI_LOG_D(TAG, "Protect APDU - done"); + } else { tx_rx->tx_data[idx++] = cla; tx_rx->tx_data[idx++] = ins; @@ -139,22 +83,25 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, tx_rx->tx_bits = idx * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + FURI_LOG_D(TAG, "Sending..."); //TODO: timeout as param? if(furi_hal_nfc_tx_rx(tx_rx, 300)) { mrtd_trace(app); + FURI_LOG_D(TAG, "Sending - done"); uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); if(app->secure_messaging && ret_code == 0x9000) { app->ssc_long++; - mrtd_bac_decrypt_verify_sm(tx_rx->rx_data, tx_rx->rx_bits / 8 - 2, - app->ksenc, app->ksmac, app->ssc_long, output, &ret_code); + ret_code = mrtd_bac_decrypt_verify_sm(tx_rx->rx_data, tx_rx->rx_bits / 8 - 2, + app->ksenc, app->ksmac, app->ssc_long, output, output_written); + //ret_code = 0x1337; //TODO: remove PRIO } //TODO: handle other return codes? if(ret_code == 0x9000) { if(!app->secure_messaging && le > 0) { // Secure Messaging sets output while decrypting - memcpy(output, tx_rx->rx_data, le); + output_written = memcpy(output, tx_rx->rx_data, le); } return true; } else { @@ -173,6 +120,8 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, return false; } + } else { + FURI_LOG_D(TAG, "Sending - failed"); } return false; } @@ -181,7 +130,7 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { FURI_LOG_D(TAG, "Send select App: %02X %02X %02X %02X %02X %02X %02X", aid[0], aid[1], aid[2], aid[3], aid[4], aid[5], aid[6]); - if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1, NULL)) { + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x04, 0x0C, 0x07, aid, -1, NULL, NULL)) { FURI_LOG_W(TAG, "Failed select App"); return false; } @@ -190,7 +139,8 @@ bool mrtd_select_app(MrtdApplication* app, AIDValue aid) { bool mrtd_get_challenge(MrtdApplication* app, uint8_t challenge[8]) { FURI_LOG_D(TAG, "Send Get Challenge"); - if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08, challenge)) { + size_t chal_size; + if(!mrtd_send_apdu(app, 0x00, 0x84, 0x00, 0x00, 0x00, NULL, 0x08, challenge, &chal_size)) { FURI_LOG_W(TAG, "Failed get challenge"); return false; } @@ -203,7 +153,7 @@ bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t furi_assert(out_size >= 0x28); FURI_LOG_D(TAG, "Send External Authenticate"); - if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28, out_data)) { + if(!mrtd_send_apdu(app, 0x00, 0x82, 0x00, 0x00, cmd_size, cmd_data, 0x28, out_data, &out_size)) { FURI_LOG_W(TAG, "Failed External Authenticate"); return false; } @@ -213,8 +163,12 @@ bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t bool mrtd_select_file(MrtdApplication* app, EFFile file) { uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; - FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id); - if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, NULL)) { + FURI_LOG_D(TAG, "Send select EF: %s (0x%04X)", file.name, file.file_id); + uint8_t buffer[100]; + size_t buffer_written = 0; + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, buffer, &buffer_written)) { + FURI_LOG_D(TAG, "Buffer_written: %d", buffer_written); + hexdump(FuriLogLevelDebug, "Buffer:", buffer, buffer_written); FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id); return false; } @@ -222,7 +176,6 @@ bool mrtd_select_file(MrtdApplication* app, EFFile file) { return true; } -//TODO: use out parameter to point to rx_data buffer instead of require allocating another size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) { UNUSED(buffer); UNUSED(bufsize); @@ -231,15 +184,15 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s //TODO: read first 4 bytes, determine length, iterate through file //TODO: limit reading/buffer fill to max bufsize - int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing' - if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer)) { + //TODO: test with max_read = bufsize (value !0, > file size) + int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing', >0 = amount of bytes + size_t buf_written; + if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer, &buf_written)) { FURI_LOG_E(TAG, "Failed to read"); return 0; } - //TODO: return read amount - - return 0; + return buf_written; } //TODO: use short id to read, because it's mandatory for eMRTD @@ -258,7 +211,7 @@ void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { } while(read > 0); } -void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { +bool parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { size_t offset = 0; uint8_t app_idx = 0; @@ -270,13 +223,13 @@ void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { if(tlv.tag != 0x61 || tlv.length != 0x09) { FURI_LOG_E(TAG, "Invalid EF.DIR, tag at offset %d must be '61' and length 9. Got '%02X' and %d", offset, tlv.tag, tlv.length); - return; + return false; } tlv = iso7816_tlv_parse(tlv.value); if(tlv.tag != 0x4F || tlv.length != 0x07) { FURI_LOG_E(TAG, "Invalid EF.DIR, subtag at offset %d must be '4F' and length 7", offset); - return; + return false; } memcpy(EF_DIR->applications[app_idx], tlv.value, tlv.length); @@ -296,26 +249,89 @@ void parse_ef_dir(EF_DIR_contents* EF_DIR, const uint8_t* data, size_t length) { printf("\r\n"); } } + + return true; } -void parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) { - UNUSED(EF_COM); //TODO - UNUSED(length); //TODO - size_t offset = 0; +bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) { + uint16_t lds_tag_path[] = {0x60, 0x5f01}; + uint16_t unicode_tag_path[] = {0x60, 0x5f36}; + uint16_t tags_tag_path[] = {0x60, 0x5c}; - TlvInfo tlv = iso7816_tlv_parse(data + offset); - UNUSED(tlv); //TODO + TlvInfo tlv_lds_version = iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path)); + if(tlv_lds_version.tag) { + EF_COM->lds_version = tlv_number(tlv_lds_version); + } else { + FURI_LOG_W(TAG, "EF.COM LDS version not found"); + return false; + } + + TlvInfo tlv_unicode_version = iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path)); + if(tlv_unicode_version.tag) { + EF_COM->unicode_version = tlv_number(tlv_unicode_version); + } else { + FURI_LOG_W(TAG, "EF.COM Unicode info not found!"); + return false; + } + + TlvInfo tlv_tag_list = iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path)); + if(tlv_tag_list.tag) { + for(size_t i=0; itag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00; + } + } else { + FURI_LOG_W(TAG, "EF.CO Tag List not found!"); + return false; + } + + return true; +} + +bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file) { + uint8_t buffer[100]; + size_t buf_len; + + FURI_LOG_D(TAG, "Read and parse %s (%04X)", file.name, file.file_id); + + if(!mrtd_select_file(app, file)) { + FURI_LOG_E(TAG, "Could not select %s", file.name); + return false; + } + + FURI_LOG_D(TAG, "Selected %s", file.name); + + buf_len = mrtd_read_binary(app, buffer, num_elements(buffer), 0); + + if(!buf_len) { + FURI_LOG_E(TAG, "Could not read %s", file.name); + return false; + } + + FURI_LOG_D(TAG, "Read %s", file.name); + + bool result = false; + + if(file.file_id == EF.COM.file_id) { + result = parse_ef_com(&mrtd_data->files.EF_COM, buffer, buf_len); + FURI_LOG_D(TAG, "Parsed EF.COM"); + } else if(file.file_id == EF.DIR.file_id) { + result = parse_ef_dir(&mrtd_data->files.EF_DIR, buffer, buf_len); + FURI_LOG_D(TAG, "Parsed EF.DIR"); + } else { + FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id); + } + + return result; } //TODO: remove testing function void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { - FuriHalNfcTxRxContext* tx_rx = app->tx_rx; + //FuriHalNfcTxRxContext* tx_rx = app->tx_rx; FURI_LOG_D(TAG, "Mrtd Test"); mrtd_read_dump(app, EF.ATR, "EF.ATR"); mrtd_read_dump(app, EF.COM, "EF.COM"); mrtd_read_dump(app, EF.DIR, "EF.DIR"); - parse_ef_dir(&app->files.EF_DIR, tx_rx->rx_data, tx_rx->rx_bits / 8 - 2); // bits to bytes, and exclude the 2 byte return code mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess"); mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity"); @@ -349,7 +365,8 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { return; } - mrtd_read_dump(app, EF.COM, "EF.COM"); + mrtd_read_parse_file(app, mrtd_data, EF.COM); + mrtd_read_parse_file(app, mrtd_data, EF.DIR); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 212a7878f..277ad5e0b 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -4,30 +4,6 @@ #include "mrtd_helpers.h" -#define MAX_EFDIR_APPS 4 - -typedef uint8_t AIDValue[7]; - -struct AIDSet { - AIDValue eMRTDApplication; - AIDValue TravelRecords; - AIDValue VisaRecords; - AIDValue AdditionalBiometrics; -}; - -extern struct AIDSet AID; - -typedef struct { - AIDValue applications[MAX_EFDIR_APPS]; - uint8_t applications_count; -} EF_DIR_contents; - -typedef struct { - uint16_t lds_version; - uint16_t unicode_version; - //TODO: taglist -} EF_COM_contents; - typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; @@ -36,61 +12,18 @@ typedef struct { uint64_t ssc_long; // TODO: rename without _long bool secure_messaging; - - struct { - EF_DIR_contents EF_DIR; - } files; } MrtdApplication; typedef struct { MrtdAuthData auth; bool auth_success; + + struct { + EF_DIR_contents EF_DIR; + EF_COM_contents EF_COM; + } files; } MrtdData; -typedef struct { - const char* name; - const uint8_t short_id; - const uint16_t file_id; - const uint8_t tag; -} EFFile; - -struct EFFormat { - // Under Master File (MF) - EFFile ATR; - EFFile DIR; - EFFile CardAccess; - EFFile CardSecurity; - - // Under LDS1 eMRTD Application - EFFile COM; - EFFile SOD; - EFFile DG1; - EFFile DG2; - EFFile DG3; - EFFile DG4; - EFFile DG5; - EFFile DG6; - EFFile DG7; - EFFile DG8; - EFFile DG9; - EFFile DG10; - EFFile DG11; - EFFile DG12; - EFFile DG13; - EFFile DG14; - EFFile DG15; - EFFile DG16; -}; - -extern struct EFFormat EF; - -#define MAX_EFCOM_TAGS 18 -typedef struct { - uint16_t lds_version; // xxyy => xx.yy (major.minor) - uint32_t unicode_version; // aabbcc => aa.bb.cc (major.minor.release) - uint8_t tag_list[MAX_EFCOM_TAGS]; -} EFComFormat; - //TODO: description MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); bool mrtd_select_app(MrtdApplication* app, AIDValue aid); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 9923555fd..51dd8e7d3 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -129,7 +129,7 @@ bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]) { } //NOTE: output size will be ((data_length+8)/8)*8 -bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { +bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output) { uint8_t IV[8] = "\x00\x00\x00\x00\x00\x00\x00\x00"; mbedtls_des3_context ctx; @@ -173,38 +173,49 @@ bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* k return true; } -bool mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output, uint16_t* ret_code) { +// If output or output_written are NULL-pointers, no output is written +// Otherwise, and if DO'87 is present, data is written to *output +// output should have enough room for additional padding (rounded up by 8 bytes) +// output_written will be the length without padding +uint16_t mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output, size_t* output_written) { // Message: [DO'85 or DO'87] || [DO'99] || DO'8E // Lengths: Var 1+1+2=4 1+1+8=10 - *ret_code = data[data_length - 10 - 2] <<8 | data[data_length - 10 - 1]; + //TODO: check for DO'99 presence, instead of assuming + uint16_t ret_code = data[data_length - 10 - 2] <<8 | data[data_length - 10 - 1]; //ntohs(data + data_length - 10 - 2); if(data[0] == 0x87) { - uint8_t do87_length = data[1] - 1; - mrtd_bac_decrypt(data + 3, do87_length, key_enc, output); - printf("Decrypted: "); for(uint8_t i=0; i=0; --padidx) { - if(output[padidx] == 0x00) { - continue; - } else if(output[padidx] == 0x80) { - break; - } else { - printf("Invalid padding\r\n"); - return false; + //TODO: function mrtd_bac_unpad? + int padidx; + for(padidx=do87_length-1; padidx>=0; --padidx) { + if(output[padidx] == 0x00) { + continue; + } else if(output[padidx] == 0x80) { + break; + } else { + printf("Invalid padding\r\n"); + return 0xff01; + } } - } - printf(" "); - for(int i=0; i %02X\r\n", mac_calc[i], data[data_length - 8 + i]); } - return false; + return 0xff02; } - return true; + return ret_code; } -bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]) { +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const uint8_t key[16]) { mbedtls_des_init(&ctx->des); mbedtls_des_setkey_enc(&ctx->des, key); memset(ctx->mac, 0, 8); @@ -316,7 +327,7 @@ bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]) { return true; } -bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output) { +bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output) { // MAC uint8_t mac[8]; uint8_t xormac[8]; @@ -366,7 +377,7 @@ bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, return true; } -size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output) { +size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, const uint8_t* key_enc, const uint8_t* key_mac, uint64_t ssc, uint8_t* output) { //TODO: max size on output? size_t idx = 0; @@ -374,7 +385,7 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8 mrtd_bac_mac_ctx mac_ctx; mrtd_bac_mac_init(&mac_ctx, key_mac); uint64_t ssc_n = htonll(ssc); - printf("ssc: %016llx\r\n", ssc); + //printf("ssc: %016llx\r\n", ssc); //printf("ssc_n: "); print_hex(ssc_n, 8); printf("\n"); mrtd_bac_mac_update(&mac_ctx, (uint8_t*)&ssc_n, 8); @@ -439,9 +450,83 @@ size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8 output[idx++] = 0x00; - if(le) { - //TODO: le? - } - return idx; } + +EFFile EFNone = {.name = NULL, .file_id = 0x0000, .short_id = 0x00, .tag = 0x00 }; + +const struct EFFormat EF = { + .ATR = {.name = "ATR", .file_id = 0x2F01, .short_id = 0x01 }, + .DIR = {.name = "DIR", .file_id = 0x2F00, .short_id = 0x1E }, + .CardAccess = {.name = "CardAccess", .file_id = 0x011C, .short_id = 0x1C }, + .CardSecurity = {.name = "CardSecurity", .file_id = 0x011D, .short_id = 0x1D }, + .COM = {.name = "COM", .file_id = 0x011E, .short_id = 0x1E, .tag = 0x60 }, + .SOD = {.name = "SOD", .file_id = 0X011D, .short_id = 0X1D, .tag = 0x77 }, + .DG1 = {.name = "DG1", .file_id = 0X0101, .short_id = 0X01, .tag = 0x61 }, + .DG2 = {.name = "DG2", .file_id = 0X0102, .short_id = 0X02, .tag = 0x75 }, + .DG3 = {.name = "DG3", .file_id = 0X0103, .short_id = 0X03, .tag = 0x63 }, + .DG4 = {.name = "DG4", .file_id = 0X0104, .short_id = 0X04, .tag = 0x76 }, + .DG5 = {.name = "DG5", .file_id = 0X0105, .short_id = 0X05, .tag = 0x65 }, + .DG6 = {.name = "DG6", .file_id = 0X0106, .short_id = 0X06, .tag = 0x66 }, + .DG7 = {.name = "DG7", .file_id = 0X0107, .short_id = 0X07, .tag = 0x67 }, + .DG8 = {.name = "DG8", .file_id = 0X0108, .short_id = 0X08, .tag = 0x68 }, + .DG9 = {.name = "DG9", .file_id = 0X0109, .short_id = 0X09, .tag = 0x69 }, + .DG10 = {.name = "DG10", .file_id = 0X010A, .short_id = 0X0A, .tag = 0x6a }, + .DG11 = {.name = "DG11", .file_id = 0X010B, .short_id = 0X0B, .tag = 0x6b }, + .DG12 = {.name = "DG12", .file_id = 0X010C, .short_id = 0X0C, .tag = 0x6c }, + .DG13 = {.name = "DG13", .file_id = 0X010D, .short_id = 0X0D, .tag = 0x6d }, + .DG14 = {.name = "DG14", .file_id = 0X010E, .short_id = 0X0E, .tag = 0x6e }, + .DG15 = {.name = "DG15", .file_id = 0X010F, .short_id = 0X0F, .tag = 0x6f }, + .DG16 = {.name = "DG16", .file_id = 0X0110, .short_id = 0X10, .tag = 0x70 }, +}; + +struct AIDSet AID = { + .eMRTDApplication = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01}, + .TravelRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x01}, + .VisaRecords = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x02}, + .AdditionalBiometrics = {0xA0, 0x00, 0x00, 0x02, 0x47, 0x20, 0x03}, +}; + +const EFFile* mrtd_tag_to_file(uint8_t tag) { + //TODO: generate this code with macros? + switch(tag) { + case 0x60: return &EF.COM; + case 0x77: return &EF.SOD; + case 0x61: return &EF.DG1; + case 0x75: return &EF.DG2; + case 0x63: return &EF.DG3; + case 0x76: return &EF.DG4; + case 0x65: return &EF.DG5; + case 0x66: return &EF.DG6; + case 0x67: return &EF.DG7; + case 0x68: return &EF.DG8; + case 0x69: return &EF.DG9; + case 0x6a: return &EF.DG10; + case 0x6b: return &EF.DG11; + case 0x6c: return &EF.DG12; + case 0x6d: return &EF.DG13; + case 0x6e: return &EF.DG14; + case 0x6f: return &EF.DG15; + case 0x70: return &EF.DG16; + default: + return &EFNone; + } +}; + +int tlv_number(TlvInfo tlv) { + //TODO: negative numbers? + const uint8_t* str = tlv.value; + size_t length = tlv.length; + + int value = 0; + while(length--) { + char c = *(str++); + + if(c >= '0' && c <= '9') { + value = value * 10 + (c - '0'); + } else { + //TODO: warning? return? crash? + } + } + return value; +} diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index ff69e25df..ee3eec584 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -7,6 +7,8 @@ #include +#include "../helpers/iso7816.h" + typedef struct { uint8_t year; uint8_t month; @@ -44,6 +46,69 @@ typedef struct { uint8_t idx_in; } mrtd_bac_mac_ctx; +typedef struct { + const char* name; + const uint8_t short_id; + const uint16_t file_id; + const uint8_t tag; +} EFFile; + +struct EFFormat { + // Under Master File (MF) + const EFFile ATR; + const EFFile DIR; + const EFFile CardAccess; + const EFFile CardSecurity; + + // Under LDS1 eMRTD Application + const EFFile COM; + const EFFile SOD; + const EFFile DG1; + const EFFile DG2; + const EFFile DG3; + const EFFile DG4; + const EFFile DG5; + const EFFile DG6; + const EFFile DG7; + const EFFile DG8; + const EFFile DG9; + const EFFile DG10; + const EFFile DG11; + const EFFile DG12; + const EFFile DG13; + const EFFile DG14; + const EFFile DG15; + const EFFile DG16; +}; + +extern const struct EFFormat EF; + +typedef uint8_t AIDValue[7]; + +struct AIDSet { + AIDValue eMRTDApplication; + AIDValue TravelRecords; + AIDValue VisaRecords; + AIDValue AdditionalBiometrics; +}; + +extern struct AIDSet AID; + +#define MAX_EFDIR_APPS 4 + +typedef struct { + AIDValue applications[MAX_EFDIR_APPS]; + uint8_t applications_count; +} EF_DIR_contents; + +#define MAX_EFCOM_TAGS 18 + +typedef struct { + uint16_t lds_version; // xxyy => xx.yy (major.minor) + uint32_t unicode_version; // aabbcc => aa.bb.cc (major.minor.release) + uint8_t tag_list[MAX_EFCOM_TAGS]; +} EF_COM_contents; + uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); //TODO: swap order, all other functions have output last @@ -55,23 +120,26 @@ bool mrtd_bac_keys_from_seed(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksma bool mrtd_bac_keys(MrtdAuthData* auth, uint8_t ksenc[16], uint8_t ksmac[16]); -bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); +bool mrtd_bac_encrypt(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output); -bool mrtd_bac_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); +bool mrtd_bac_mac(const uint8_t* data, size_t data_length, const uint8_t* key, uint8_t* output); -bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, uint8_t key[16]); +bool mrtd_bac_mac_init(mrtd_bac_mac_ctx* ctx, const uint8_t key[16]); bool mrtd_bac_mac_update(mrtd_bac_mac_ctx* ctx, const uint8_t* data, size_t data_length); bool mrtd_bac_mac_finalize(mrtd_bac_mac_ctx* ctx, uint8_t output[8]); +bool mrtd_bac_mac_pad(mrtd_bac_mac_ctx* ctx); // TODO: internal only, remove from .h? + bool mrtd_bac_padded_mac(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); bool mrtd_bac_decrypt(const uint8_t* data, size_t data_length, uint8_t* key, uint8_t* output); bool mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint8_t* output); -bool mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output, uint16_t* ret_code); +//TODO: add some consts +uint16_t mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output, size_t* output_written); #include #define htonll(x) ((((uint64_t)__htonl(x)) << 32) + __htonl((x) >> 32)) @@ -94,4 +162,6 @@ static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t #endif } -size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* output); +size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, const uint8_t* key_enc, const uint8_t* key_mac, uint64_t ssc, uint8_t* output); + +int tlv_number(TlvInfo tlv); diff --git a/test_iso7816_helpers.c b/test_iso7816_helpers.c index e433764f2..91d420dd1 100644 --- a/test_iso7816_helpers.c +++ b/test_iso7816_helpers.c @@ -27,24 +27,6 @@ void print_tlv(char* fmt, TlvInfo tlv) { printf("\n"); } -int tlv_number(TlvInfo tlv) { - //TODO: negative numbers? - const char* str = tlv.value; - size_t length = tlv.length; - - int value = 0; - while(length--) { - char c = *(str++); - - if(c >= '0' && c <= '9') { - value = value * 10 + (c - '0'); - } else { - //TODO: warning? return? crash? - } - } - return value; -} - void test_iso7816_tlv_parse(const uint8_t* input, size_t input_size, uint16_t exp_tag, size_t exp_length) { TlvInfo tlv = iso7816_tlv_parse(input); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index 568d2c248..2449bb2a2 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -281,18 +281,14 @@ void test_mrtd_bac_decrypt_verify(const uint8_t* data, size_t data_length, uint8 printf(COLOR_RESET "\n"); } -void test_mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* exp_output, size_t exp_output_length, bool should_verify) { +void test_mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uint8_t* key_enc, uint8_t* key_mac, uint64_t ssc, uint8_t* exp_output, size_t exp_output_length, uint16_t exp_code) { uint8_t buffer[256]; uint16_t ret_code; + size_t buffer_len; - bool result = mrtd_bac_decrypt_verify_sm(data, data_length, key_enc, key_mac, ssc, buffer, &ret_code); - if(result != should_verify) { - printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected verify: %d, but is: %d\n" COLOR_RESET, should_verify, result); - return; - } - - if(ret_code != 0x9000) { - printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected ret_code: %04X, but is: %04X\n" COLOR_RESET, 0x9000, ret_code); + ret_code = mrtd_bac_decrypt_verify_sm(data, data_length, key_enc, key_mac, ssc, buffer, &buffer_len); + if(ret_code != exp_code) { + printf(COLOR_RED "FAILED - mrtd_bac_decrypt_verify_sm, expected ret_code: %04X, but is: %04X\n" COLOR_RESET, exp_code, ret_code); return; } @@ -438,7 +434,7 @@ int main(int argc, char** argv) { ssc++; // Increment for decrypt, verify - test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x99\x02\x90\x00\x8E\x08\xFA\x85\x5A\x5D\x4C\x50\xA8\xED", 14, ks_enc, ks_mac, ssc, NULL, 0, 1); + test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x99\x02\x90\x00\x8E\x08\xFA\x85\x5A\x5D\x4C\x50\xA8\xED", 14, ks_enc, ks_mac, ssc, NULL, 0, 0x9000); ssc++; // Increment for encrypt, sign @@ -446,7 +442,7 @@ int main(int argc, char** argv) { ssc++; // Increment for decrypt, verify - test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x87\x09\x01\x9F\xF0\xEC\x34\xF9\x92\x26\x51\x99\x02\x90\x00\x8E\x08\xAD\x55\xCC\x17\x14\x0B\x2D\xED", 25, ks_enc, ks_mac, ssc, (uint8_t*)"\x60\x14\x5F\x01", 4, 1); + test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x87\x09\x01\x9F\xF0\xEC\x34\xF9\x92\x26\x51\x99\x02\x90\x00\x8E\x08\xAD\x55\xCC\x17\x14\x0B\x2D\xED", 25, ks_enc, ks_mac, ssc, (uint8_t*)"\x60\x14\x5F\x01", 4, 0x9000); ssc++; // Increment for encrypt, sign From 74faab36d9ee253fd26e29f0449609441b45c02f Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:37 +0200 Subject: [PATCH 37/44] MRTD display AIDs and DGs after auth --- .../nfc/scenes/nfc_scene_passport_read_auth.c | 22 +++++++++++++++++++ lib/nfc/protocols/mrtd.c | 6 +---- lib/nfc/protocols/mrtd.h | 2 +- lib/nfc/protocols/mrtd_helpers.h | 2 ++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c index d0498bd95..6ec777a15 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -26,6 +26,28 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version; string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", unicode_version/10000, unicode_version/100%100, unicode_version%100); + string_cat_printf(temp_str, "Avail.files: "); + for(size_t i=0; ifiles.EF_COM.tag_list[i]; + const EFFile* file = mrtd_tag_to_file(tag); + if(file->tag) { + if(i > 0) string_cat_printf(temp_str, ", "); + string_cat_printf(temp_str, "%s", file->name); + } + } + string_cat_printf(temp_str, "\n"); + + EF_DIR_contents* EF_DIR = &mrtd_data->files.EF_DIR; + if(EF_DIR->applications_count > 0) { + string_cat_printf(temp_str, "Apps:\n"); + for(uint8_t i=0; iapplications_count; ++i) { + for(uint8_t n=0; napplications[i][n]); + } + string_cat_printf(temp_str, "\n"); + } + } + /* char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; //TODO: NFC-B? diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 07501abf3..c68d9b7c6 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -164,11 +164,7 @@ bool mrtd_external_authenticate(MrtdApplication* app, uint8_t* cmd_data, size_t bool mrtd_select_file(MrtdApplication* app, EFFile file) { uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff}; FURI_LOG_D(TAG, "Send select EF: %s (0x%04X)", file.name, file.file_id); - uint8_t buffer[100]; - size_t buffer_written = 0; - if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, buffer, &buffer_written)) { - FURI_LOG_D(TAG, "Buffer_written: %d", buffer_written); - hexdump(FuriLogLevelDebug, "Buffer:", buffer, buffer_written); + if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1, NULL, NULL)) { FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id); return false; } diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 277ad5e0b..1ca5fe4b3 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -16,7 +16,7 @@ typedef struct { typedef struct { MrtdAuthData auth; - bool auth_success; + bool auth_success; //TODO: register (and display) method used BAC/PACE struct { EF_DIR_contents EF_DIR; diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index ee3eec584..c91cf7261 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -165,3 +165,5 @@ static __inline uint64_t mrtd_ssc_from_data(const uint8_t* rnd_ic, const uint8_t size_t mrtd_protect_apdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le, const uint8_t* key_enc, const uint8_t* key_mac, uint64_t ssc, uint8_t* output); int tlv_number(TlvInfo tlv); + +const EFFile* mrtd_tag_to_file(uint8_t tag); From d3d9b675446a942a4bc9eb5acc868f0600adf5fe Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:38 +0200 Subject: [PATCH 38/44] MRTD remove some logs. Read DG1, try DG2 --- lib/nfc/nfc_worker.c | 26 +++------------------ lib/nfc/protocols/mrtd.c | 49 +++++++++++++++++----------------------- test_mrtd_helpers.c | 30 ++++++++++++++++-------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 44ee06e19..7efdda5da 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -280,34 +280,14 @@ static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t // Read passport if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; - if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; + //TODO: if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; - mrtd_test(mrtd_app, mrtd_data); + mrtd_test(mrtd_app, mrtd_data); // Some EFs are only available before Select App + //TODO: try select eMRTDApp first, but when PACE, read CardAccess first! //TODO: read general informatie //TODO: after auth scene, do auth (BAC / PACE) - /* - // 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); diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index c68d9b7c6..1e57c0639 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -55,16 +55,11 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, FuriHalNfcTxRxContext* tx_rx = app->tx_rx; size_t idx = 0; - FURI_LOG_D(TAG, "Send APDU, lc: %d, le: %d", lc, le); + FURI_LOG_T(TAG, "Send APDU, lc: %d, le: %d", lc, le); if(app->secure_messaging) { - FURI_LOG_D(TAG, "Protect APDU"); - app->ssc_long++; idx = mrtd_protect_apdu(cla, ins, p1, p2, lc, data, le, app->ksenc, app->ksmac, app->ssc_long, tx_rx->tx_data); - - FURI_LOG_D(TAG, "Protect APDU - done"); - } else { tx_rx->tx_data[idx++] = cla; tx_rx->tx_data[idx++] = ins; @@ -83,11 +78,9 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, tx_rx->tx_bits = idx * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - FURI_LOG_D(TAG, "Sending..."); //TODO: timeout as param? if(furi_hal_nfc_tx_rx(tx_rx, 300)) { mrtd_trace(app); - FURI_LOG_D(TAG, "Sending - done"); uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8); if(app->secure_messaging && ret_code == 0x9000) { @@ -182,11 +175,12 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s //TODO: test with max_read = bufsize (value !0, > file size) int16_t max_read = 0; // 0 = 'everything', -1 = 'nothing', >0 = amount of bytes - size_t buf_written; + size_t buf_written = 0; if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, max_read, buffer, &buf_written)) { FURI_LOG_E(TAG, "Failed to read"); return 0; } + FURI_LOG_D(TAG, "buf_written: %d\n", buf_written); return buf_written; } @@ -194,16 +188,21 @@ size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, s //TODO: use short id to read, because it's mandatory for eMRTD //TODO: check for support of extended length in EF.ATR/INFO, see ISO7816-4 -void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) { - FURI_LOG_D(TAG, "Read and dump %s:", descr); +void mrtd_read_dump(MrtdApplication* app, EFFile file) { + FURI_LOG_D(TAG, "Read and dump %s:", file.name); if(!mrtd_select_file(app, file)) { return; } + uint8_t data[2048]; size_t read = 0; + size_t offset = 0; do { - read = mrtd_read_binary(app, data + read, sizeof(data) - read, read); + read = mrtd_read_binary(app, data, sizeof(data), offset); + offset += read; + + hexdump(FuriLogLevelDebug, "Data:", data, read); } while(read > 0); } @@ -322,24 +321,15 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file //TODO: remove testing function void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { - //FuriHalNfcTxRxContext* tx_rx = app->tx_rx; - FURI_LOG_D(TAG, "Mrtd Test"); - mrtd_read_dump(app, EF.ATR, "EF.ATR"); - mrtd_read_dump(app, EF.COM, "EF.COM"); - mrtd_read_dump(app, EF.DIR, "EF.DIR"); - mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess"); - mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity"); + mrtd_read_dump(app, EF.ATR); + mrtd_read_dump(app, EF.COM); + mrtd_read_dump(app, EF.DIR); + mrtd_read_dump(app, EF.CardAccess); + mrtd_read_dump(app, EF.CardSecurity); mrtd_select_app(app, AID.eMRTDApplication); - //TODO: remove details - /* - mrtd_data->auth.birth_date = (MrtdDate){.year=69, .month=8, .day=6}; - mrtd_data->auth.expiry_date = (MrtdDate){.year=94, .month=6, .day=23}; - memcpy(mrtd_data->auth.doc_number, "L898902C<", 9); - */ - MrtdAuthMethod method = mrtd_data->auth.method; mrtd_data->auth_success = false; FURI_LOG_D(TAG, "Auth method: %d", method); @@ -362,7 +352,10 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { } mrtd_read_parse_file(app, mrtd_data, EF.COM); - mrtd_read_parse_file(app, mrtd_data, EF.DIR); + //mrtd_read_parse_file(app, mrtd_data, EF.DIR); + + mrtd_read_dump(app, EF.DG1); + mrtd_read_dump(app, EF.DG2); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { @@ -449,7 +442,7 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { uint8_t kseed[16]; for(uint8_t i=0; i<16; ++i) { kseed[i] = k_ifd[i] ^ kic[i]; - printf("seed %2d = %02X ^ %02X = %02X\r\n", i, k_ifd[i], kic[i], kseed[i]); + //printf("seed %2d = %02X ^ %02X = %02X\r\n", i, k_ifd[i], kic[i], kseed[i]); } hexdump(FuriLogLevelDebug, "kseed:", kseed, 16); diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index 2449bb2a2..e0a41aa86 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -450,7 +450,6 @@ int main(int argc, char** argv) { // Verify working against mrtdreader - /* printf("=====================================\n\n"); //TODO: set auth data @@ -468,9 +467,9 @@ int main(int argc, char** argv) { uint8_t buffer[32]; // RND.IC || RND.IFD || KIC //TODO: set challenge rx - mrtd_bac_decrypt_verify((uint8_t*)"\x3F\xD4\x6B\xA9\xFF\x29\x4B\xF6\x77\x4E\x8F\x1E\xEC\xAE\x2E\x67\xDB\xE7\x70\x53\xB3\xAD\x9C\xDC\xED\x6E\xED\xD6\x04\x2E\xB7\x6B\x74\xDE\x2A\xFB\x4B\xC0\xF7\x24", 40, kenc, kmac, buffer); + mrtd_bac_decrypt_verify((uint8_t*)"\x11\x0e\x51\x83\xbe\x78\x94\xcf\x43\x40\x8e\xea\xfe\x99\x54\xbb\x17\x97\x27\x65\xf8\xb4\x51\xa4\x94\x0d\xb2\x5b\xad\x1b\xe3\x64\x16\x53\x2a\xff\xad\xee\x29\xcf", 40, kenc, kmac, buffer); //TODO: set kifd - uint8_t *kifd = "\x9F\x10\x40\xEA\x7F\xAE\xF8\xC3\x09\x6E\xAE\x07\x66\x95\x3F\xDC"; + uint8_t *kifd = (uint8_t*)"\xe0\x01\xf4\x4c\x09\xb1\xb3\x16\x63\xab\x3a\x11\x8d\xa3\x17\xcc"; printf("buffer: "); print_hex(buffer, 32); printf("\n"); // 8F763C0B1CDF9F9D|0983F7C136155248|7A705FD193C6A6328C42264A3804002C @@ -492,16 +491,29 @@ int main(int argc, char** argv) { printf("ks_enc: "); print_hex(ks_enc, 16); printf("\n"); printf("ks_mac: "); print_hex(ks_mac, 16); printf("\n"); - printf("rnd_ic: "); print_hex(rnd_ic, 16); printf("\n"); - printf("rnd_ifd: "); print_hex(rnd_ifd, 16); printf("\n"); - uint64_t ssc = mrtd_ssc_from_data(rnd_ic, rnd_ifd); - printf("ssc: %016lx", ssc); + printf("rnd_ic: "); print_hex(rnd_ic, 8); printf("\n"); + printf("rnd_ifd: "); print_hex(rnd_ifd, 8); printf("\n"); + ssc = mrtd_ssc_from_data(rnd_ic, rnd_ifd); + printf("ssc: %016lx\n", ssc); ssc++; + ssc+=6; + + //test_mrtd_protect_ + //TODO: set challenge TX for verification - test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x1e", -1, ks_enc, ks_mac, ssc, - (uint8_t*)"\x0C\xA4\x02\x0C\x15\x87\x09\x01\xC5\x4E\x76\x3A\xD1\x89\xF0\xFA\x8E\x08\x1F\x03\xC2\xB6\xCE\x8A\xE1\x53\x00", 27); + test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x01", -1, ks_enc, ks_mac, ssc, + (uint8_t*)"\x0c\xa4\x02\x0c\x15\x87\x09\x01\xc8\xcc\x50\x6f\x50\xae\x10\xc7\x8e\x08\xaf\xec\x2e\x03\x90\x26\x8f\xa5\x00", 27); + + /* + uint8_t* select_ef_com = "\x0C\xA4\x02\x0C\x15\x87\x09\x01\xE2\x94\xA2\x9A\xF3\x73\xFD\x20\x8E\x08\x7E\x3B\xA9\xAA\x7C\xB9\x07\x0C\x00"; + uint8_t* select_ef_dg1 = "\x0C\xA4\x02\x0C\x15\x87\x09\x01\x9C\xD7\x89\x94\x97\x05\xB8\xF3\x8E\x08\x6C\xA2\xC1\x48\xA7\x47\xBA\x96\x00"; + uint8_t buffer2[256]; + + mrtd_bac_decrypt(select_ef_dg1 + 8, 8, ks_enc, buffer2); + printf("Decrypted: "); + print_hex(buffer2, 8); */ return 0; From 0ab7d91fb4620cd993d58a7ac73786ad071e5481 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:39 +0200 Subject: [PATCH 39/44] MRTD proper parse DO87 multi byte length --- lib/nfc/protocols/mrtd_helpers.c | 21 ++++++++++++++++----- test_mrtd_helpers.c | 12 ++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 51dd8e7d3..1ca76981e 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -1,4 +1,5 @@ #include "mrtd_helpers.h" +#include "../helpers/iso7816.h" #include //TODO: remove #include @@ -185,15 +186,25 @@ uint16_t mrtd_bac_decrypt_verify_sm(const uint8_t* data, size_t data_length, uin uint16_t ret_code = data[data_length - 10 - 2] <<8 | data[data_length - 10 - 1]; //ntohs(data + data_length - 10 - 2); - if(data[0] == 0x87) { + TlvInfo do87 = iso7816_tlv_select(data, data_length, (uint16_t[]){0x87}, 1); + //printf("DO87.Tag: %X\n", do87.tag); + //printf("DO87.Length: %ld\n", do87.length); + //printf("DO87.Value: "); + //for(uint8_t i=1; i=0; --padidx) { + for(padidx=enclength-1; padidx>=0; --padidx) { if(output[padidx] == 0x00) { continue; } else if(output[padidx] == 0x80) { diff --git a/test_mrtd_helpers.c b/test_mrtd_helpers.c index e0a41aa86..e6fec6bfe 100644 --- a/test_mrtd_helpers.c +++ b/test_mrtd_helpers.c @@ -467,9 +467,9 @@ int main(int argc, char** argv) { uint8_t buffer[32]; // RND.IC || RND.IFD || KIC //TODO: set challenge rx - mrtd_bac_decrypt_verify((uint8_t*)"\x11\x0e\x51\x83\xbe\x78\x94\xcf\x43\x40\x8e\xea\xfe\x99\x54\xbb\x17\x97\x27\x65\xf8\xb4\x51\xa4\x94\x0d\xb2\x5b\xad\x1b\xe3\x64\x16\x53\x2a\xff\xad\xee\x29\xcf", 40, kenc, kmac, buffer); + mrtd_bac_decrypt_verify((uint8_t*)"\xDA\x35\xDF\x28\x7E\x9C\xE1\x25\x39\xD5\x66\xBA\x16\xF7\x16\x46\xCA\x7A\xBC\x0C\x98\x54\x55\x84\x50\x9E\xC1\x91\xB3\x06\x6B\x56\xBD\x10\xD0\xE9\x13\x83\xA9\x97", 40, kenc, kmac, buffer); //TODO: set kifd - uint8_t *kifd = (uint8_t*)"\xe0\x01\xf4\x4c\x09\xb1\xb3\x16\x63\xab\x3a\x11\x8d\xa3\x17\xcc"; + uint8_t *kifd = (uint8_t*)"\x1D\xF3\x5C\xFF\x0F\xF9\xE0\xBA\x36\x89\x63\xAE\xAF\xC8\x26\x64"; printf("buffer: "); print_hex(buffer, 32); printf("\n"); // 8F763C0B1CDF9F9D|0983F7C136155248|7A705FD193C6A6328C42264A3804002C @@ -498,13 +498,13 @@ int main(int argc, char** argv) { ssc++; - ssc+=6; + ssc+=11; - //test_mrtd_protect_ + test_mrtd_bac_decrypt_verify_sm((uint8_t*)"\x87\x81\xE9\x01\x25\xF5\xD5\xB9\x8C\x5D\xF6\xDB\x5C\xC2\x79\x49\x1F\x3B\xDA\xA9\xC3\x55\x95\xE2\x33\xBD\xE6\x1F\xA5\x41\xD7\xF0\x8A\xCB\x01\x6F\xF7\xD3\xCF\x33\x3A\x65\x8C\x40\x37\x06\xDE\xB7\xB6\x1D\x73\x88\x04\x12\xC1\xD1\x52\x04\xC1\xA1\x84\x9F\xD9\x34\x60\x2B\x5F\x30\xD1\xDD\xFB\x37\xE7\x7D\xE8\xC1\x38\x72\x0F\x6C\x69\x12\x14\xB3\x8E\x4C\x19\x8A\x9F\x0F\x39\x08\xD4\xF5\xA4\xBE\x0C\xD0\xD9\x72\x24\xCE\x76\x45\xD3\xCC\xD2\x02\x53\xDE\x49\x77\x0F\xD5\x5E\xBE\x20\x8F\x9F\xFD\x89\x90\xBD\x5C\x44\x74\xE9\x76\xFB\xAA\x81\x35\x6B\xC0\x49\x5D\x5E\x1B\xC9\x18\x85\xFA\xC5\x82\x6F\x7B\x8F\x0F\x1B\x03\x30\xCE\x25\x90\x6E\x3E\xA0\xF4\x01\xA6\xF4\xAE\x02\xF8\x30\x29\x25\xEB\x0A\x10\x31\x8A\x89\xB6\x6B\x8C\xC5\x2E\xE6\xCC\xB8\xFA\xEC\x64\x36\x8D\x5A\x3F\x5A\x31\x67\x26\x01\x85\x19\x98\x0A\x69\x10\x8F\x5F\x71\xAA\x6C\x6E\x1C\xEB\x8A\x40\xD1\x87\xEE\x2A\x0D\xE7\xA3\x61\x92\x6A\x46\x3B\x8C\x79\x5F\x1E\xA2\xE4\x76\x59\x71\xD7\xE4\xFE\x41\xC0\x8A\x99\x02\x90\x00\x8E\x08\x0F\xE2\xD4\x0B\xED\xD6\x66\xA2", 250, ks_enc, ks_mac, ssc, NULL, 0, 0x9000); //TODO: set challenge TX for verification - test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x01", -1, ks_enc, ks_mac, ssc, - (uint8_t*)"\x0c\xa4\x02\x0c\x15\x87\x09\x01\xc8\xcc\x50\x6f\x50\xae\x10\xc7\x8e\x08\xaf\xec\x2e\x03\x90\x26\x8f\xa5\x00", 27); + //test_mrtd_protect_apdu(0x00, 0xA4, 0x02, 0x0C, 0x02, "\x01\x01", -1, ks_enc, ks_mac, ssc, + //(uint8_t*)"\x0c\xa4\x02\x0c\x15\x87\x09\x01\xc8\xcc\x50\x6f\x50\xae\x10\xc7\x8e\x08\xaf\xec\x2e\x03\x90\x26\x8f\xa5\x00", 27); /* uint8_t* select_ef_com = "\x0C\xA4\x02\x0C\x15\x87\x09\x01\xE2\x94\xA2\x9A\xF3\x73\xFD\x20\x8E\x08\x7E\x3B\xA9\xAA\x7C\xB9\x07\x0C\x00"; From d1a1cbff821445acb6e7352fa279778239c04bcf Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:41 +0200 Subject: [PATCH 40/44] MRTD Read DG1 + small changes --- .../nfc/scenes/nfc_scene_passport_read_auth.c | 11 ++ lib/nfc/protocols/mrtd.c | 134 +++++++++++++++--- lib/nfc/protocols/mrtd.h | 1 + lib/nfc/protocols/mrtd_helpers.h | 20 +++ 4 files changed, 149 insertions(+), 17 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c index 6ec777a15..541ac3ee4 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -48,6 +48,17 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { } } + EF_DG1_contents* DG1 = &mrtd_data->files.DG1; + string_cat_printf(temp_str, "\e#DG1\n"); + string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype); + string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state); + string_cat_printf(temp_str, "Name: %s\n", DG1->name); + string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr); + string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality); + string_cat_printf(temp_str, "Birth Date: %s\n", DG1->birth_date); + string_cat_printf(temp_str, "Sex: %s\n", DG1->sex); + string_cat_printf(temp_str, "Expiry Date: %s\n", DG1->expiry_date); + /* char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; //TODO: NFC-B? diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 1e57c0639..a8a3b2f94 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -109,6 +109,14 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'"); app->secure_messaging = false; break; + case 0xff01: + //CUSTOM ERROR CODE from mrtd_helpers.c + FURI_LOG_I(TAG, "'invalid padding'"); + break; + case 0xff02: + //CUSTOM ERROR CODE from mrtd_helpers.c + FURI_LOG_I(TAG, "'verify failed'"); + break; } return false; @@ -254,31 +262,118 @@ bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) { uint16_t tags_tag_path[] = {0x60, 0x5c}; TlvInfo tlv_lds_version = iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path)); - if(tlv_lds_version.tag) { - EF_COM->lds_version = tlv_number(tlv_lds_version); - } else { + if(!tlv_lds_version.tag) { FURI_LOG_W(TAG, "EF.COM LDS version not found"); return false; } + EF_COM->lds_version = tlv_number(tlv_lds_version); + TlvInfo tlv_unicode_version = iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path)); - if(tlv_unicode_version.tag) { - EF_COM->unicode_version = tlv_number(tlv_unicode_version); - } else { + if(!tlv_unicode_version.tag) { FURI_LOG_W(TAG, "EF.COM Unicode info not found!"); return false; } + EF_COM->unicode_version = tlv_number(tlv_unicode_version); + TlvInfo tlv_tag_list = iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path)); - if(tlv_tag_list.tag) { - for(size_t i=0; itag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00; - } - } else { + if(!tlv_tag_list.tag) { FURI_LOG_W(TAG, "EF.CO Tag List not found!"); return false; } + for(size_t i=0; itag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00; + } + + return true; +} + +void mrzcpy(uint8_t* dest, const uint8_t* src, size_t* idx, size_t n) { + //FURI_LOG_D(TAG, "mrzcpy %d: %.*s", n, n, src + *idx); + //memcpy(dest, src + *idx, n); + for(size_t i=0; itype = MrtdTypeTD1; + mrzcpy(DG1->doctype, mrz, &idx, 2); + mrzcpy(DG1->issuing_state, mrz, &idx, 3); + mrzcpy(DG1->docnr, mrz, &idx, 9); + idx += 1; // docnr check digit + idx += 15; // optional data + mrzcpy(DG1->birth_date, mrz, &idx, 6); + idx += 1; // birth date check digit + mrzcpy(DG1->sex, mrz, &idx, 1); + mrzcpy(DG1->expiry_date, mrz, &idx, 6); + idx += 1; // expiry date check digit + mrzcpy(DG1->nationality, mrz, &idx, 3); + idx += 11; // optional data + idx += 1; // check digit + mrzcpy(DG1->name, mrz, &idx, 30); + // 30 + 30 + 30 + break; + case 72: + DG1->type = MrtdTypeTD2; + mrzcpy(DG1->doctype, mrz, &idx, 2); + mrzcpy(DG1->issuing_state, mrz, &idx, 3); + mrzcpy(DG1->name, mrz, &idx, 31); + mrzcpy(DG1->docnr, mrz, &idx, 9); + idx += 1; // docnr check digit + mrzcpy(DG1->nationality, mrz, &idx, 3); + mrzcpy(DG1->birth_date, mrz, &idx, 6); + idx += 1; // birth date check digit + mrzcpy(DG1->sex, mrz, &idx, 1); + mrzcpy(DG1->expiry_date, mrz, &idx, 6); + idx += 1; // expiry date check digit + idx += 7; // optional data + idx += 1; // check digit + // 36 + 36 + break; + case 88: + DG1->type = MrtdTypeTD3; + mrzcpy(DG1->doctype, mrz, &idx, 2); + mrzcpy(DG1->issuing_state, mrz, &idx, 3); + mrzcpy(DG1->name, mrz, &idx, 39); + mrzcpy(DG1->docnr, mrz, &idx, 9); + idx += 1; // docnr check digit + mrzcpy(DG1->nationality, mrz, &idx, 3); + mrzcpy(DG1->birth_date, mrz, &idx, 6); + idx += 1; // birth date check digit + mrzcpy(DG1->sex, mrz, &idx, 1); + mrzcpy(DG1->expiry_date, mrz, &idx, 6); + idx += 1; // expiry date check digit + idx += 14; // optional data + idx += 1; // check digit + idx += 1; // check digit + // 44 + 44 + break; + default: + FURI_LOG_W(TAG, "Unexpected MRZ length in DG1: %d. TD1=90, TD2=72, TD3=88.", tlv_mrz.length); + return false; + } + return true; } @@ -312,6 +407,8 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file } else if(file.file_id == EF.DIR.file_id) { result = parse_ef_dir(&mrtd_data->files.EF_DIR, buffer, buf_len); FURI_LOG_D(TAG, "Parsed EF.DIR"); + } else if(file.file_id == EF.DG1.file_id) { + result = parse_ef_dg1(&mrtd_data->files.DG1, buffer, buf_len); } else { FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id); } @@ -322,11 +419,11 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file //TODO: remove testing function void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { FURI_LOG_D(TAG, "Mrtd Test"); - mrtd_read_dump(app, EF.ATR); - mrtd_read_dump(app, EF.COM); - mrtd_read_dump(app, EF.DIR); - mrtd_read_dump(app, EF.CardAccess); - mrtd_read_dump(app, EF.CardSecurity); + //mrtd_read_dump(app, EF.ATR); + //mrtd_read_dump(app, EF.COM); + //mrtd_read_dump(app, EF.DIR); + //mrtd_read_dump(app, EF.CardAccess); + //mrtd_read_dump(app, EF.CardSecurity); mrtd_select_app(app, AID.eMRTDApplication); @@ -354,8 +451,11 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { mrtd_read_parse_file(app, mrtd_data, EF.COM); //mrtd_read_parse_file(app, mrtd_data, EF.DIR); - mrtd_read_dump(app, EF.DG1); + mrtd_read_parse_file(app, mrtd_data, EF.DG1); + mrtd_read_dump(app, EF.DG2); + mrtd_read_dump(app, EF.DG14); + mrtd_read_dump(app, EF.DG15); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index 1ca5fe4b3..c51f4f660 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -21,6 +21,7 @@ typedef struct { struct { EF_DIR_contents EF_DIR; EF_COM_contents EF_COM; + EF_DG1_contents DG1; } files; } MrtdData; diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index c91cf7261..18fe9f71f 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -25,6 +25,13 @@ typedef enum { MrtdAuthMethodPace, } MrtdAuthMethod; +typedef enum { + MrtdTypeUnknown, + MrtdTypeTD1, + MrtdTypeTD2, + MrtdTypeTD3, +} MrtdType; + typedef struct { MrtdAuthMethod method; @@ -109,6 +116,19 @@ typedef struct { uint8_t tag_list[MAX_EFCOM_TAGS]; } EF_COM_contents; +typedef struct { + MrtdType type; + // ICAO9303 max sizes + 1 for 0-byte + uint8_t doctype[3]; + uint8_t issuing_state[4]; + uint8_t name[40]; + uint8_t docnr[10]; + uint8_t nationality[4]; + uint8_t birth_date[7]; + uint8_t sex[2]; + uint8_t expiry_date[7]; +} EF_DG1_contents; + uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); //TODO: swap order, all other functions have output last From 6003f856018b97a8de8c1ad49e97a68192777f34 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Tue, 11 Oct 2022 22:13:42 +0200 Subject: [PATCH 41/44] MRTD Show month as text --- .../nfc/scenes/nfc_scene_passport_read_auth.c | 20 ++++++++++++++-- lib/nfc/protocols/mrtd.c | 24 ++++++++++++------- lib/nfc/protocols/mrtd_helpers.c | 13 ++++++++++ lib/nfc/protocols/mrtd_helpers.h | 6 +++-- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c index 541ac3ee4..05f642eec 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -1,6 +1,22 @@ #include "../nfc_i.h" #include +const char months[13][4] = { + "---", + "JAN", + "FEB", + "MAR", + "APR", + "MAY", + "JUN", + "JUL", + "AUG", + "SEP", + "OCT", + "NOV", + "DEC", +}; + void nfc_scene_passport_read_auth_widget_callback(GuiButtonType result, InputType type, void* context) { Nfc* nfc = context; if(type == InputTypeShort) { @@ -55,9 +71,9 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { string_cat_printf(temp_str, "Name: %s\n", DG1->name); string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr); string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality); - string_cat_printf(temp_str, "Birth Date: %s\n", DG1->birth_date); + string_cat_printf(temp_str, "Birth Date: %02d %s %02d\n", DG1->birth_date.day, months[DG1->birth_date.month], DG1->birth_date.year); string_cat_printf(temp_str, "Sex: %s\n", DG1->sex); - string_cat_printf(temp_str, "Expiry Date: %s\n", DG1->expiry_date); + string_cat_printf(temp_str, "Expiry Date: %02d %s %02d\n", DG1->expiry_date.day, months[DG1->expiry_date.month], DG1->expiry_date.year); /* char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index a8a3b2f94..2f10584ce 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -323,10 +323,12 @@ bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) { mrzcpy(DG1->docnr, mrz, &idx, 9); idx += 1; // docnr check digit idx += 15; // optional data - mrzcpy(DG1->birth_date, mrz, &idx, 6); + mrtd_parse_date(&DG1->birth_date, mrz + idx); + idx += 6; // birth_date idx += 1; // birth date check digit mrzcpy(DG1->sex, mrz, &idx, 1); - mrzcpy(DG1->expiry_date, mrz, &idx, 6); + mrtd_parse_date(&DG1->expiry_date, mrz + idx); + idx += 6; // expiry_date idx += 1; // expiry date check digit mrzcpy(DG1->nationality, mrz, &idx, 3); idx += 11; // optional data @@ -342,10 +344,12 @@ bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) { mrzcpy(DG1->docnr, mrz, &idx, 9); idx += 1; // docnr check digit mrzcpy(DG1->nationality, mrz, &idx, 3); - mrzcpy(DG1->birth_date, mrz, &idx, 6); + mrtd_parse_date(&DG1->birth_date, mrz + idx); + idx += 6; // birth_date idx += 1; // birth date check digit mrzcpy(DG1->sex, mrz, &idx, 1); - mrzcpy(DG1->expiry_date, mrz, &idx, 6); + mrtd_parse_date(&DG1->expiry_date, mrz + idx); + idx += 6; // expiry_date idx += 1; // expiry date check digit idx += 7; // optional data idx += 1; // check digit @@ -359,10 +363,12 @@ bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) { mrzcpy(DG1->docnr, mrz, &idx, 9); idx += 1; // docnr check digit mrzcpy(DG1->nationality, mrz, &idx, 3); - mrzcpy(DG1->birth_date, mrz, &idx, 6); + mrtd_parse_date(&DG1->birth_date, mrz + idx); idx += 1; // birth date check digit + idx += 6; // birth_date mrzcpy(DG1->sex, mrz, &idx, 1); - mrzcpy(DG1->expiry_date, mrz, &idx, 6); + mrtd_parse_date(&DG1->expiry_date, mrz + idx); + idx += 6; // expiry_date idx += 1; // expiry date check digit idx += 14; // optional data idx += 1; // check digit @@ -453,9 +459,9 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) { mrtd_read_parse_file(app, mrtd_data, EF.DG1); - mrtd_read_dump(app, EF.DG2); - mrtd_read_dump(app, EF.DG14); - mrtd_read_dump(app, EF.DG15); + //mrtd_read_dump(app, EF.DG2); + //mrtd_read_dump(app, EF.DG14); + //mrtd_read_dump(app, EF.DG15); } MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) { diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 1ca76981e..7456830d1 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -40,6 +40,19 @@ void mrtd_print_date(char* output, MrtdDate* date) { output[5] = (date->day % 10) + '0'; } +uint8_t charval(char c) { + if(c >= '0' && c <= '9') { + return c - '0'; + } + return 0; +} + +void mrtd_parse_date(MrtdDate* date, const unsigned char* input) { + date->year = charval(input[0]) * 10 + charval(input[1]); + date->month = charval(input[2]) * 10 + charval(input[3]); + date->day = charval(input[4]) * 10 + charval(input[5]); +} + bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size) { uint8_t idx = 0; uint8_t docnr_length = strlen(auth->doc_number); diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/protocols/mrtd_helpers.h index 18fe9f71f..83197d95e 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -122,11 +122,11 @@ typedef struct { uint8_t doctype[3]; uint8_t issuing_state[4]; uint8_t name[40]; + MrtdDate birth_date; uint8_t docnr[10]; uint8_t nationality[4]; - uint8_t birth_date[7]; uint8_t sex[2]; - uint8_t expiry_date[7]; + MrtdDate expiry_date; } EF_DG1_contents; uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); @@ -134,6 +134,8 @@ uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); //TODO: swap order, all other functions have output last void mrtd_print_date(char* output, MrtdDate* date); +void mrtd_parse_date(MrtdDate* date, const unsigned char* input); + bool mrtd_bac_get_kmrz(MrtdAuthData* auth, char* output, uint8_t output_size); bool mrtd_bac_keys_from_seed(const uint8_t* kseed, uint8_t* ksenc, uint8_t* ksmac); From 033f7f6a0bd7e16fdfacf33a45476c0f2638d786 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Wed, 12 Oct 2022 07:26:29 +0200 Subject: [PATCH 42/44] MRTD fix string_t -> FuriString* --- .../main/nfc/scenes/nfc_scene_passport_read.c | 19 +++--- .../nfc/scenes/nfc_scene_passport_read_auth.c | 59 ++++++++----------- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c index 441d34584..a934b06f3 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read.c @@ -17,20 +17,21 @@ void nfc_scene_passport_read_on_enter(void* context) { Widget* widget = nfc->widget; // Setup Custom Widget view - string_t temp_str; - string_init_printf(temp_str, "\e#Passport\n"); + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_set(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:"); + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_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]); + furi_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); + furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); + furi_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_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_widget_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c index 05f642eec..dbb18eb28 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -31,64 +31,53 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { Widget* widget = nfc->widget; // Setup Custom Widget view - string_t temp_str; - string_init_printf(temp_str, "\e#Passport\n"); - string_cat_printf(temp_str, "Authenticated: %d\n", mrtd_data->auth_success); + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_set(temp_str, "\e#Passport\n"); + furi_string_cat_printf(temp_str, "Authenticated: %d\n", mrtd_data->auth_success); // TODO: indicate BAC / PACE used uint16_t lds_version = mrtd_data->files.EF_COM.lds_version; - string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version/100, lds_version%100); + furi_string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version/100, lds_version%100); uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version; - string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", unicode_version/10000, unicode_version/100%100, unicode_version%100); + furi_string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", unicode_version/10000, unicode_version/100%100, unicode_version%100); - string_cat_printf(temp_str, "Avail.files: "); + furi_string_cat_printf(temp_str, "Avail.files: "); for(size_t i=0; ifiles.EF_COM.tag_list[i]; const EFFile* file = mrtd_tag_to_file(tag); if(file->tag) { - if(i > 0) string_cat_printf(temp_str, ", "); - string_cat_printf(temp_str, "%s", file->name); + if(i > 0) furi_string_cat_printf(temp_str, ", "); + furi_string_cat_printf(temp_str, "%s", file->name); } } - string_cat_printf(temp_str, "\n"); + furi_string_cat_printf(temp_str, "\n"); EF_DIR_contents* EF_DIR = &mrtd_data->files.EF_DIR; if(EF_DIR->applications_count > 0) { - string_cat_printf(temp_str, "Apps:\n"); + furi_string_cat_printf(temp_str, "Apps:\n"); for(uint8_t i=0; iapplications_count; ++i) { for(uint8_t n=0; napplications[i][n]); + furi_string_cat_printf(temp_str, "%02X ", EF_DIR->applications[i][n]); } - string_cat_printf(temp_str, "\n"); + furi_string_cat_printf(temp_str, "\n"); } } EF_DG1_contents* DG1 = &mrtd_data->files.DG1; - string_cat_printf(temp_str, "\e#DG1\n"); - string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype); - string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state); - string_cat_printf(temp_str, "Name: %s\n", DG1->name); - string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr); - string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality); - string_cat_printf(temp_str, "Birth Date: %02d %s %02d\n", DG1->birth_date.day, months[DG1->birth_date.month], DG1->birth_date.year); - string_cat_printf(temp_str, "Sex: %s\n", DG1->sex); - string_cat_printf(temp_str, "Expiry Date: %02d %s %02d\n", DG1->expiry_date.day, months[DG1->expiry_date.month], DG1->expiry_date.year); + furi_string_cat_printf(temp_str, "\e#DG1\n"); + furi_string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype); + furi_string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state); + furi_string_cat_printf(temp_str, "Name: %s\n", DG1->name); + furi_string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr); + furi_string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality); + furi_string_cat_printf(temp_str, "Birth Date: %02d %s %02d\n", DG1->birth_date.day, months[DG1->birth_date.month], DG1->birth_date.year); + furi_string_cat_printf(temp_str, "Sex: %s\n", DG1->sex); + furi_string_cat_printf(temp_str, "Expiry Date: %02d %s %02d\n", DG1->expiry_date.day, months[DG1->expiry_date.month], DG1->expiry_date.year); - /* - 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_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_passport_read_auth_widget_callback, nfc); From 76ef36e62cba505934818cd402fc0b952121060c Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Wed, 12 Oct 2022 07:28:45 +0200 Subject: [PATCH 43/44] MRTD minor format string fixes --- applications/main/nfc/scenes/nfc_scene_passport_auth.c | 2 +- applications/main/nfc/scenes/nfc_scene_passport_read_auth.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c index 05afad97d..78691b771 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -109,7 +109,7 @@ bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - FURI_LOG_D(TAG, "event.event: %d", event.event); + FURI_LOG_D(TAG, "event.event: %ld", event.event); switch(event.event) { case NfcScenePassportAuthSelectDob: scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c index dbb18eb28..0f0f82684 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -41,7 +41,7 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { furi_string_cat_printf(temp_str, "LDS version: %d.%d\n", lds_version/100, lds_version%100); uint32_t unicode_version = mrtd_data->files.EF_COM.unicode_version; - furi_string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", unicode_version/10000, unicode_version/100%100, unicode_version%100); + furi_string_cat_printf(temp_str, "Unicode version: %d.%d.%d\n", (uint8_t)(unicode_version/10000), (uint8_t)(unicode_version/100%100), (uint8_t)(unicode_version%100)); furi_string_cat_printf(temp_str, "Avail.files: "); for(size_t i=0; i Date: Wed, 12 Oct 2022 07:30:25 +0200 Subject: [PATCH 44/44] MRTD remove testing values --- applications/main/nfc/scenes/nfc_scene_passport_auth.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c index 78691b771..fe4bd056b 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -1,7 +1,5 @@ #include "../nfc_i.h" -#include "../test_bac_creds.h" //TODO: remove - #define TAG "PassportAuth" #define MRTD_AUTH_METHOD_COUNT 4 @@ -41,12 +39,6 @@ void nfc_scene_passport_auth_on_enter(void* context) { MrtdAuthMethod* auth_method = &mrtd_data->auth.method; if(*auth_method == MrtdAuthMethodNone) { *auth_method = MrtdAuthMethodAny; - - //TODO: remove testing credentials: - mrtd_data->auth.birth_date = TODO_REMOVE_ID_DOB; - mrtd_data->auth.expiry_date = TODO_REMOVE_ID_DOE; - memcpy(mrtd_data->auth.doc_number, TODO_REMOVE_ID_DOC, 9); - //TODO: remove testing credentials ^^ } VariableItemList* variable_item_list = nfc->variable_item_list;