From 496c059e39f206176a51f1a7d5de764efa908ce7 Mon Sep 17 00:00:00 2001 From: Chris van Marle Date: Thu, 27 Oct 2022 18:39:46 +0200 Subject: [PATCH] MRTD save/load BAC params --- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_passport_auth.c | 58 +++++++ .../nfc_scene_passport_auth_save_name.c | 81 ++++++++++ .../main/nfc/scenes/nfc_scene_save_success.c | 3 + lib/nfc/protocols/mrtd.c | 144 ++++++++++++++++++ lib/nfc/protocols/mrtd.h | 8 + lib/nfc/protocols/mrtd_helpers.c | 36 +++++ lib/nfc/protocols/mrtd_helpers.h | 19 +-- 8 files changed, 335 insertions(+), 15 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_passport_auth_save_name.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index cb28a07f9..20d8b19f3 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -42,6 +42,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 1af80cd62..7376bdf39 100644 --- a/applications/main/nfc/scenes/nfc_scene_passport_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_passport_auth.c @@ -11,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) { @@ -25,6 +27,47 @@ void nfc_scene_passport_auth_method_changed(VariableItem* item) { variable_item_set_current_value_text(item, mrtd_auth_method_string(index)); } +void nfc_scene_passport_load_select_changed(VariableItem* item) { + Nfc* nfc = variable_item_get_context(item); + UNUSED(nfc); //TODO: remove either this or previous line + //TODO: iterate through params +} + +bool nfc_scene_passport_auth_load(Nfc* nfc) { + const DialogsFileBrowserOptions browser_options = { + .extension = MRTD_APP_EXTENSION, + .skip_assets = true, + .icon = &I_Nfc_10px, //TODO: custom icon? + .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); + + //TODO: make sure this call is ok: + nfc_scene_passport_auth_on_enter(nfc); + } + + return res; +} + void nfc_scene_passport_auth_on_enter(void* context) { Nfc* nfc = context; MrtdData* mrtd_data = &nfc->dev->dev_data.mrtd_data; @@ -36,12 +79,16 @@ 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; const size_t temp_str_size = 15; char temp_str[temp_str_size]; + + uint8_t num_params_saved = 0; + snprintf(temp_str, temp_str_size, "%02u%02u%02u", mrtd_data->auth.birth_date.year, mrtd_data->auth.birth_date.month, @@ -85,6 +132,9 @@ void nfc_scene_passport_auth_on_enter(void* context) { 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", num_params_saved, nfc_scene_passport_load_select_changed, nfc); + 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); @@ -97,6 +147,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); @@ -123,6 +177,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_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index dcd2519f1..99dacba8f 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -33,6 +33,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.c b/lib/nfc/protocols/mrtd.c index 102dd4a12..f06959ead 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); @@ -555,3 +561,141 @@ bool mrtd_authenticate(MrtdApplication* app, MrtdData* mrtd_data) { 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 57825994c..0a9e64801 100644 --- a/lib/nfc/protocols/mrtd.h +++ b/lib/nfc/protocols/mrtd.h @@ -4,6 +4,9 @@ #include "mrtd_helpers.h" +#define MRTD_APP_FOLDER NFC_APP_FOLDER "/mrtd" +#define MRTD_APP_EXTENSION ".mrtd" + typedef struct { FuriHalNfcTxRxContext* tx_rx; uint16_t file_offset; @@ -32,3 +35,8 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data); //TODO: remove bool mrtd_select_app(MrtdApplication* app, AIDValue aid); bool mrtd_authenticate(MrtdApplication* app, MrtdData* mrtd_data); bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, 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); diff --git a/lib/nfc/protocols/mrtd_helpers.c b/lib/nfc/protocols/mrtd_helpers.c index 7456830d1..70d740309 100644 --- a/lib/nfc/protocols/mrtd_helpers.c +++ b/lib/nfc/protocols/mrtd_helpers.c @@ -9,6 +9,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/protocols/mrtd_helpers.h index d3310b0eb..d538a7079 100644 --- a/lib/nfc/protocols/mrtd_helpers.h +++ b/lib/nfc/protocols/mrtd_helpers.h @@ -25,21 +25,6 @@ typedef enum { MrtdAuthMethodPace, } MrtdAuthMethod; -inline 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"; - } -} - typedef enum { MrtdTypeUnknown, MrtdTypeTD1, @@ -144,6 +129,10 @@ typedef struct { MrtdDate expiry_date; } EF_DG1_contents; +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