diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index afd5b284f..670f38efe 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -49,6 +49,7 @@ 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_auth_save_name, PassportAuthSaveName) 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_auth.c b/applications/main/nfc/scenes/nfc_scene_passport_auth.c index 01671942a..0fcabee6e 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -3,13 +3,7 @@ #define TAG "PassportAuth" #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", -}; +// Must match MrtdAuthMethod size (lib/nfc/protocols/mrtd_helpers.h) typedef enum { NfcScenePassportAuthSelectDob, @@ -17,6 +11,8 @@ typedef enum { NfcScenePassportAuthSelectDocNr, NfcScenePassportAuthSelectMethod, NfcScenePassportAuthSelectAuth, + NfcScenePassportAuthSelectSave, + NfcScenePassportAuthSelectLoad, } NfcScenePassportAuthSelect; void nfc_scene_passport_auth_var_list_enter_callback(void* context, uint32_t index) { @@ -28,7 +24,42 @@ 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]); + variable_item_set_current_value_text(item, mrtd_auth_method_string(index)); +} + +bool nfc_scene_passport_auth_load(Nfc* nfc) { + const DialogsFileBrowserOptions browser_options = { + .extension = MRTD_APP_EXTENSION, + .skip_assets = true, + .icon = &I_u2f_10px, + .hide_ext = true, + .item_loader_callback = NULL, + .item_loader_context = NULL, + }; + + FuriString* mrtd_app_folder; + mrtd_app_folder = furi_string_alloc_set(MRTD_APP_FOLDER); + + FuriString* file_path; + file_path = furi_string_alloc(); + + bool res = dialog_file_browser_show(nfc->dev->dialogs, file_path, mrtd_app_folder, &browser_options); + + furi_string_free(mrtd_app_folder); + + if(res) { + mrtd_auth_params_load( + nfc->dev->storage, + nfc->dev->dialogs, + &nfc->dev->dev_data.mrtd_data.auth, + furi_string_get_cstr(file_path), + true); + + nfc_scene_passport_auth_on_enter(nfc); + variable_item_list_set_selected_item(nfc->variable_item_list, NfcScenePassportAuthSelectAuth); + } + + return res; } void nfc_scene_passport_auth_on_enter(void* context) { @@ -42,6 +73,7 @@ void nfc_scene_passport_auth_on_enter(void* context) { } VariableItemList* variable_item_list = nfc->variable_item_list; + variable_item_list_reset(variable_item_list); VariableItem* item; uint8_t value_index; @@ -91,10 +123,13 @@ void nfc_scene_passport_auth_on_enter(void* context) { 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]); + variable_item_set_current_value_text(item, mrtd_auth_method_string(value_index)); variable_item_list_add(variable_item_list, "Authenticate and read", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Save parameters", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Load parameters", 1, NULL, NULL); + variable_item_list_set_enter_callback( variable_item_list, nfc_scene_passport_auth_var_list_enter_callback, nfc); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewVarItemList); @@ -107,6 +142,10 @@ bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { FURI_LOG_D(TAG, "event.event: %ld", event.event); switch(event.event) { + case NfcScenePassportAuthSelectLoad: + nfc_scene_passport_auth_load(nfc); + consumed = true; + break; case NfcScenePassportAuthSelectDob: scene_manager_set_scene_state(nfc->scene_manager, NfcScenePassportDate, 0); scene_manager_next_scene(nfc->scene_manager, NfcScenePassportDate); @@ -133,6 +172,10 @@ bool nfc_scene_passport_auth_on_event(void* context, SceneManagerEvent event) { } consumed = true; break; + case NfcScenePassportAuthSelectSave: + scene_manager_next_scene(nfc->scene_manager, NfcScenePassportAuthSaveName); + consumed = true; + break; } } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_previous_scene(nfc->scene_manager); diff --git a/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c b/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c new file mode 100644 index 000000000..5cfa7d9a3 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c @@ -0,0 +1,81 @@ +#include "../nfc_i.h" +#include +#include +#include + +void nfc_scene_passport_auth_save_name_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_passport_auth_save_name_on_enter(void* context) { + Nfc* nfc = context; + + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + + // Setup view + TextInput* text_input = nfc->text_input; + bool docnr_empty = false; + if(!strcmp(mrtd_data->auth.doc_number, "")) { + set_random_name(nfc->text_store, sizeof(nfc->text_store)); + docnr_empty = true; + } else { + nfc_text_store_set(nfc, mrtd_data->auth.doc_number); + } + text_input_set_header_text(text_input, "Name the parameters"); + text_input_set_result_callback( + text_input, + nfc_scene_passport_auth_save_name_text_input_callback, + nfc, + nfc->text_store, + NFC_DEV_NAME_MAX_LEN, + docnr_empty); + + FuriString* folder_path; + folder_path = furi_string_alloc(); + + if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) { + path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path); + } else { + furi_string_set(folder_path, NFC_APP_FOLDER); + } + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); + + furi_string_free(folder_path); +} + +bool nfc_scene_passport_auth_save_name_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + if(mrtd_auth_params_save(nfc->dev->storage, nfc->dev->dialogs, &mrtd_data->auth, nfc->text_store)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } + } + return consumed; +} + +void nfc_scene_passport_auth_save_name_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + void* validator_context = text_input_get_validator_callback_context(nfc->text_input); + text_input_set_validator(nfc->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + text_input_reset(nfc->text_input); +} diff --git a/applications/main/nfc/scenes/nfc_scene_passport_read.c b/applications/main/nfc/scenes/nfc_scene_passport_read.c index 23ada2a11..74f173b52 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read.c @@ -11,6 +11,7 @@ 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; + MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -40,7 +41,11 @@ void nfc_scene_passport_read_on_enter(void* context) { furi_string_cat_printf(temp_str, " %02X", data->uid[i]); } 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); + furi_string_cat_printf(temp_str, " SAK: %02X\n", data->sak); + + if(mrtd_data->auth.method != MrtdAuthMethodNone && !mrtd_data->auth_success) { + furi_string_cat_printf(temp_str, "Auth failed. Wrong params?"); + } widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); @@ -82,4 +87,4 @@ void nfc_scene_passport_read_on_exit(void* context) { // Clear view widget_reset(nfc->widget); -} \ No newline at end of file +} 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 66a4a515d..16166f644 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_read_auth.c @@ -37,7 +37,7 @@ void nfc_scene_passport_read_auth_on_enter(void* context) { 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); + furi_string_cat_printf(temp_str, "Auth.method: %s\n", mrtd_auth_method_string(mrtd_data->auth_method_used)); // TODO: indicate BAC / PACE used uint16_t lds_version = mrtd_data->files.EF_COM.lds_version; @@ -115,6 +115,8 @@ bool nfc_scene_passport_read_auth_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeLeft) { + nfc->dev->dev_data.mrtd_data.auth_success = false; + nfc->dev->dev_data.mrtd_data.auth.method = MrtdAuthMethodNone; scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); consumed = true; } else if(event.event == GuiButtonTypeCenter) { diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index c79c55d8b..23113d999 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -92,10 +92,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == NfcWorkerEventReadPassport) { notification_message(nfc->notifications, &sequence_success); + FURI_LOG_D("NFC", "Read passport, auth: %d, success: %d", + nfc->dev->dev_data.mrtd_data.auth.method, + nfc->dev->dev_data.mrtd_data.auth_success); 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); } diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 34919cbd8..bd7c9817e 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -31,6 +31,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcScenePassportAuth)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcScenePassportAuth); } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/helpers/mrtd_helpers.c similarity index 95% rename from lib/nfc/protocols/mrtd_helpers.c rename to lib/nfc/helpers/mrtd_helpers.c index b22c040f3..faab83cc0 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/helpers/mrtd_helpers.c @@ -11,6 +11,42 @@ static inline unsigned char* ucstr(const char* str) { return (unsigned char*)str; } +const char* mrtd_auth_method_string(MrtdAuthMethod method) { + switch(method) { + case MrtdAuthMethodBac: + return "BAC"; + case MrtdAuthMethodPace: + return "PACE"; + case MrtdAuthMethodNone: + return "None"; + case MrtdAuthMethodAny: + return "Any"; + default: + return "Unknown"; + } +} + +bool mrtd_auth_method_parse_string(MrtdAuthMethod* method, const char* str) { + if(!strcmp(str, "BAC")) { + *method = MrtdAuthMethodBac; + return true; + } + if(!strcmp(str, "PACE")) { + *method = MrtdAuthMethodPace; + return true; + } + if(!strcmp(str, "None")) { + *method = MrtdAuthMethodNone; + return true; + } + if(!strcmp(str, "Any")) { + *method = MrtdAuthMethodAny; + return true; + } + return false; +} + + 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}; diff --git a/lib/nfc/protocols/mrtd_helpers.h b/lib/nfc/helpers/mrtd_helpers.h similarity index 93% rename from lib/nfc/protocols/mrtd_helpers.h rename to lib/nfc/helpers/mrtd_helpers.h index e1bca39e9..994e66974 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/helpers/mrtd_helpers.h @@ -129,6 +129,22 @@ typedef struct { MrtdDate expiry_date; } EF_DG1_contents; +typedef struct { + MrtdAuthData auth; + bool auth_success; + MrtdAuthMethod auth_method_used; + + struct { + EF_DIR_contents EF_DIR; + EF_COM_contents EF_COM; + EF_DG1_contents DG1; + } files; +} MrtdData; + +const char* mrtd_auth_method_string(MrtdAuthMethod method); + +bool mrtd_auth_method_parse_string(MrtdAuthMethod* method, const char* str); + uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length); //TODO: swap order, all other functions have output last diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 148632bf8..1df92324c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -297,8 +297,8 @@ 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 = mrtd_alloc_init(tx_rx); MrtdData* mrtd_data = &nfc_worker->dev_data->mrtd_data; + MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx, mrtd_data); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); @@ -309,13 +309,24 @@ 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; - //TODO: if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; - - 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! + if(!mrtd_select_app(mrtd_app, AID.eMRTDApplication)) break; // Passport app not selected - //TODO: read general informatie - //TODO: after auth scene, do auth (BAC / PACE) + if(mrtd_data->auth.method == MrtdAuthMethodNone) { + // Selected the passport app, but auth. not selected + // Successfully read what we could + read_success = true; + break; + } + + if(!mrtd_authenticate(mrtd_app)) { + // At least we're reading an MRTD and should the app switch to the NFC scenes + read_success = true; + break; // Authentication failed + } + + mrtd_read_parse_file(mrtd_app, EF.COM); + mrtd_read_parse_file(mrtd_app, EF.DG1); read_success = true; } while(false); @@ -362,7 +373,6 @@ 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; @@ -1069,4 +1079,4 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { reader_analyzer_stop(nfc_worker->reader_analyzer); nfca_signal_free(nfca_signal); -} \ No newline at end of file +} diff --git a/lib/nfc/protocols/mrtd.c b/lib/nfc/protocols/mrtd.c index 612b03127..7e4036bb9 100644 --- a/lib/nfc/protocols/mrtd.c +++ b/lib/nfc/protocols/mrtd.c @@ -1,4 +1,7 @@ #include +#include +#include +#include #include "../helpers/iso7816.h" @@ -19,6 +22,9 @@ #define num_elements(A) (sizeof(A) / sizeof(A[0])) +static const char* mrtd_auth_file_header = "Flipper MRTD params"; +static const uint32_t mrtd_auth_file_version = 1; + static void hexdump(FuriLogLevel level, char* prefix, void* data, size_t length) { if(furi_log_get_level() >= level) { printf("%s ", prefix); @@ -425,7 +431,7 @@ bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) { return true; } -bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file) { +bool mrtd_read_parse_file(MrtdApplication* app, EFFile file) { uint8_t buffer[100]; size_t buf_len; @@ -450,13 +456,13 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file bool result = false; if(file.file_id == EF.COM.file_id) { - result = parse_ef_com(&mrtd_data->files.EF_COM, buffer, buf_len); + result = parse_ef_com(&app->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); + result = parse_ef_dir(&app->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); + result = parse_ef_dg1(&app->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); } @@ -464,52 +470,11 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file return result; } -//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_select_app(app, AID.eMRTDApplication); - - 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_data->auth_success = mrtd_bac(app, &mrtd_data->auth); - break; - case MrtdAuthMethodPace: - FURI_LOG_E(TAG, "Auth method PACE not implemented"); - break; - case MrtdAuthMethodNone: - default: - break; - } - - if(!mrtd_data->auth_success) { - return; - } - - 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.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) { +MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data) { MrtdApplication* app = malloc(sizeof(MrtdApplication)); app->tx_rx = tx_rx; + app->mrtd_data = mrtd_data; return app; } @@ -612,3 +577,168 @@ bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth) { return true; } + +bool mrtd_authenticate(MrtdApplication* app) { + MrtdAuthMethod method = app->mrtd_data->auth.method; + app->mrtd_data->auth_success = false; + app->mrtd_data->auth_method_used = MrtdAuthMethodNone; + FURI_LOG_D(TAG, "Auth method: %d", method); + switch(method) { + case MrtdAuthMethodAny: + //TODO: try PACE, then BAC. For now, fall through to just BAC + case MrtdAuthMethodBac: + app->mrtd_data->auth_success = mrtd_bac(app, &app->mrtd_data->auth); + app->mrtd_data->auth_method_used = MrtdAuthMethodBac; + break; + case MrtdAuthMethodPace: + FURI_LOG_E(TAG, "Auth method PACE not implemented"); + break; + case MrtdAuthMethodNone: + default: + break; + } + + if(!app->mrtd_data->auth_success) { + return false; + } + + return true; +} + +bool mrtd_auth_params_save(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name) { + return mrtd_auth_params_save_file(storage, dialogs, auth_data, file_name, MRTD_APP_FOLDER, MRTD_APP_EXTENSION); +} + +void mrtd_date_prepare_format_string(MrtdDate date, FuriString* format_string) { + furi_string_printf( + format_string, "%02u%02u%02u", + date.year, + date.month, + date.day); +} + +bool mrtd_date_parse_format_string(MrtdDate* date, FuriString* format_string) { + int year; + int month; + int day; + + int ret = sscanf(furi_string_get_cstr(format_string), "%02d%02d%02d", &year, &month, &day); + if(ret != 3) { + return false; + } + + date->year = year; + date->month = month; + date->day = day; + return true; +} + +bool mrtd_auth_params_save_file(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name, const char* folder, const char* extension) { + furi_assert(auth_data); + + bool saved = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Create mrtd directory if necessary + if(!storage_simply_mkdir(storage, MRTD_APP_FOLDER)) break; + + furi_string_printf(temp_str, "%s/%s%s", folder, file_name, extension); + + // Open file + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + // Write header + if(!flipper_format_write_header_cstr(file, mrtd_auth_file_header, mrtd_auth_file_version)) break; + + // Write auth method + furi_string_set(temp_str, mrtd_auth_method_string(auth_data->method)); + if(!flipper_format_write_string(file, "Method", temp_str)) break; + + // Write birth date + mrtd_date_prepare_format_string(auth_data->birth_date, temp_str); + if(!flipper_format_write_string(file, "BirthDate", temp_str)) break; + + // Write expiry date + mrtd_date_prepare_format_string(auth_data->expiry_date, temp_str); + if(!flipper_format_write_string(file, "ExpiryDate", temp_str)) break; + + // Write docnr + furi_string_set(temp_str, auth_data->doc_number); + if(!flipper_format_write_string(file, "DocNr", temp_str)) break; + + saved = true; + } while(false); + + if(!saved) { + dialog_message_show_storage_error(dialogs, "Can not save\nparams file"); + } + furi_string_free(temp_str); + flipper_format_free(file); + return saved; +} + +bool mrtd_auth_params_load(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_path, bool show_dialog) { + furi_assert(storage); + furi_assert(dialogs); + furi_assert(auth_data); + furi_assert(file_path); + + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + bool deprecated_version = false; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + + MrtdAuthData copy; + + FURI_LOG_D(TAG, "Load auth params"); + + do { + if(!flipper_format_file_open_existing(file, file_path)) break; + + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + FURI_LOG_D(TAG, "Version: %s", furi_string_get_cstr(temp_str)); + if(furi_string_cmp_str(temp_str, mrtd_auth_file_header) || (version != mrtd_auth_file_version)) { + deprecated_version = true; + break; + } + + if(!flipper_format_read_string(file, "Method", temp_str)) break; + FURI_LOG_D(TAG, "Method: %s", furi_string_get_cstr(temp_str)); + if(!mrtd_auth_method_parse_string(©.method, furi_string_get_cstr(temp_str))) break; + + if(!flipper_format_read_string(file, "BirthDate", temp_str)) break; + FURI_LOG_D(TAG, "BirthDate: %s", furi_string_get_cstr(temp_str)); + if(!mrtd_date_parse_format_string(©.birth_date, temp_str)) break; + + if(!flipper_format_read_string(file, "ExpiryDate", temp_str)) break; + FURI_LOG_D(TAG, "ExpiryDate: %s", furi_string_get_cstr(temp_str)); + if(!mrtd_date_parse_format_string(©.expiry_date, temp_str)) break; + + if(!flipper_format_read_string(file, "DocNr", temp_str)) break; + FURI_LOG_D(TAG, "DocNr: %s", furi_string_get_cstr(temp_str)); + strlcpy(copy.doc_number, furi_string_get_cstr(temp_str), MRTD_DOCNR_MAX_LENGTH); + + // Everything went fine. Save copy to pointed auth data + *auth_data = copy; + parsed = true; + } while(false); + + FURI_LOG_D(TAG, "Load done, success: %d", parsed); + + if(!parsed && show_dialog) { + if(deprecated_version) { + dialog_message_show_storage_error(dialogs, "File format deprecated"); + } else { + dialog_message_show_storage_error(dialogs, "Can not parse\nfile"); + } + } + + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} diff --git a/lib/nfc/protocols/mrtd.h b/lib/nfc/protocols/mrtd.h index c51f4f660..180186f6e 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -1,11 +1,14 @@ #pragma once #include +#include -#include "mrtd_helpers.h" +#define MRTD_APP_FOLDER NFC_APP_FOLDER "/mrtd" +#define MRTD_APP_EXTENSION ".mrtd" typedef struct { FuriHalNfcTxRxContext* tx_rx; + MrtdData* mrtd_data; uint16_t file_offset; uint8_t ksenc[16]; uint8_t ksmac[16]; @@ -14,20 +17,13 @@ typedef struct { bool secure_messaging; } MrtdApplication; -typedef struct { - MrtdAuthData auth; - bool auth_success; //TODO: register (and display) method used BAC/PACE - - struct { - EF_DIR_contents EF_DIR; - EF_COM_contents EF_COM; - EF_DG1_contents DG1; - } files; -} MrtdData; - //TODO: description -MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx); +MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx, MrtdData* mrtd_data); bool mrtd_select_app(MrtdApplication* app, AIDValue aid); -bool mrtd_select_file(MrtdApplication* app, EFFile file); -void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data); -bool mrtd_bac(MrtdApplication* app, MrtdAuthData* auth); +bool mrtd_authenticate(MrtdApplication* app); +bool mrtd_read_parse_file(MrtdApplication* app, EFFile file); + +bool mrtd_auth_params_save(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name); +bool mrtd_auth_params_save_file(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_name, const char* folder, const char* extension); + +bool mrtd_auth_params_load(Storage* storage, DialogsApp* dialogs, MrtdAuthData* auth_data, const char* file_path, bool show_dialog);