diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index d2917a90e..d84707e07 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -22,4 +22,4 @@ "SConstruct": "python", "*.fam": "python", } -} \ No newline at end of file +} diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 2b63175da..66caf8df7 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -16,7 +16,7 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { LfRfidReadViewModel* model = _model; canvas_set_color(canvas, ColorBlack); - canvas_draw_icon(canvas, 0, 8, &I_NFC_manual); + canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50); canvas_set_font(canvas, FontPrimary); diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index 60e1c1997..15ea5348f 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -37,6 +37,10 @@ #include "rpc/rpc_app.h" +#include + +ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST); + #define NFC_TEXT_STORE_SIZE 128 typedef enum { @@ -60,6 +64,7 @@ struct Nfc { char text_store[NFC_TEXT_STORE_SIZE + 1]; string_t text_box_store; uint8_t byte_input_store[6]; + MfClassicUserKeys_t mfc_key_strs; // Used in MFC key listing void* rpc_ctx; NfcRpcState rpc_state; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 540fe1098..a25850c84 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -32,6 +32,9 @@ ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) +ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) +ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) +ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 713b99ebf..1664a9e5b 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -25,8 +25,13 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneFileSelect); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_device_info.c b/applications/main/nfc/scenes/nfc_scene_device_info.c index 8228c7ea3..245aea5c5 100644 --- a/applications/main/nfc/scenes/nfc_scene_device_info.c +++ b/applications/main/nfc/scenes/nfc_scene_device_info.c @@ -47,7 +47,9 @@ void nfc_scene_device_info_on_enter(void* context) { } string_clear(country_name); } - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + } else if( + dev_data->protocol == NfcDeviceProtocolMifareClassic || + dev_data->protocol == NfcDeviceProtocolMifareUl) { string_set(temp_str, nfc->dev->dev_data.parsed_data); } diff --git a/applications/main/nfc/scenes/nfc_scene_dict_not_found.c b/applications/main/nfc/scenes/nfc_scene_dict_not_found.c index dc21b08b1..781c5a932 100644 --- a/applications/main/nfc/scenes/nfc_scene_dict_not_found.c +++ b/applications/main/nfc/scenes/nfc_scene_dict_not_found.c @@ -31,7 +31,10 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneExtraActions); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 43e49e5a0..e888e9d35 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -17,7 +17,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { submenu_add_item( submenu, - "Mf Classic Keys", + "Mifare Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index fcb8bc189..a2e6ae745 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -26,15 +26,25 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { } widget_add_string_element( - nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys"); + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys"); char temp_str[32]; - snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "Flipper list: %ld", flipper_dict_keys_total); widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); - snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "User list: %ld", user_dict_keys_total); widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain); + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc); + widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); + if(user_dict_keys_total > 0) { + widget_add_button_element( + nfc->widget, + GuiButtonTypeRight, + "List", + nfc_scene_mf_classic_keys_widget_callback, + nfc); + } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } @@ -47,6 +57,12 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) if(event.event == GuiButtonTypeCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); consumed = true; + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(nfc->scene_manager); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); + consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 9f56b0f45..2921d21c9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -29,15 +29,16 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { // Add key to dict - bool key_added = false; MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); if(dict) { - if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { - key_added = true; + if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) { + scene_manager_next_scene( + nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); + } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } - } - if(key_added) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c new file mode 100644 index 000000000..16a189da6 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c @@ -0,0 +1,77 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_delete_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_mf_classic_keys_delete_on_enter(void* context) { + Nfc* nfc = context; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + uint32_t key_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + // Setup Custom Widget view + string_t key_str; + string_init(key_str); + + widget_add_string_element( + nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); + widget_add_button_element( + nfc->widget, + GuiButtonTypeLeft, + "Cancel", + nfc_scene_mf_classic_keys_delete_widget_callback, + nfc); + widget_add_button_element( + nfc->widget, + GuiButtonTypeRight, + "Delete", + nfc_scene_mf_classic_keys_delete_widget_callback, + nfc); + + mf_classic_dict_get_key_at_index_str(dict, key_str, key_index); + widget_add_string_element( + nfc->widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(key_str)); + + string_clear(key_str); + mf_classic_dict_free(dict); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + uint32_t key_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(event.event == GuiButtonTypeRight) { + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(mf_classic_dict_delete_index(dict, key_index)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } + mf_classic_dict_free(dict); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_delete_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c new file mode 100644 index 000000000..36f01897e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_classic_keys_list_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + uint32_t index = 0; + string_t temp_key; + MfClassicUserKeys_init(nfc->mfc_key_strs); + string_init(temp_key); + if(dict) { + mf_classic_dict_rewind(dict); + while(mf_classic_dict_get_next_key_str(dict, temp_key)) { + char* current_key = (char*)malloc(sizeof(char) * 13); + strncpy(current_key, string_get_cstr(temp_key), 12); + MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key); + FURI_LOG_D("ListKeys", "Key %d: %s", index, current_key); + submenu_add_item( + submenu, + current_key, + index++, + nfc_scene_mf_classic_keys_list_submenu_callback, + nfc); + } + } + submenu_set_header(submenu, "Select key to delete:"); + mf_classic_dict_free(dict); + string_clear(temp_key); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + consumed = true; + } + return consumed; +} + +void nfc_scene_mf_classic_keys_list_on_exit(void* context) { + Nfc* nfc = context; + + MfClassicUserKeys_it_t it; + for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it); + MfClassicUserKeys_next(it)) { + free(*MfClassicUserKeys_ref(it)); + } + MfClassicUserKeys_clear(nfc->mfc_key_strs); + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c new file mode 100644 index 000000000..ab41989b2 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Please enter a\n" + "different key.", + 4, + 24, + AlignLeft, + AlignTop); + popup_set_timeout(popup, 5000); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_keys_warn_duplicate_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, NfcSceneMfClassicKeysAdd); + } + } + return consumed; +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) { + Nfc* nfc = context; + + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 1a106bdb4..25008004b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -27,7 +27,7 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState popup_reset(nfc->popup); popup_set_text( nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); } else if(state == NfcSceneMfUlReadStateReading) { popup_reset(nfc->popup); popup_set_header( diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c index 56fa20578..f6dc5984e 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -34,15 +34,19 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { nfc); string_t temp_str; - string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); - 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, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); - if(mf_ul_data->data_read != mf_ul_data->data_size) { - string_cat_printf(temp_str, "\nPassword-protected pages!"); + if(string_size(nfc->dev->dev_data.parsed_data)) { + string_init_set(temp_str, nfc->dev->dev_data.parsed_data); + } else { + string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); + 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, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + if(mf_ul_data->data_read != mf_ul_data->data_size) { + string_cat_printf(temp_str, "\nPassword-protected pages!"); + } } widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); string_clear(temp_str); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index da21b9f3d..e6df476f0 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -26,7 +26,7 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { popup_reset(nfc->popup); popup_set_text( nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); } else if(state == NfcSceneReadStateReading) { popup_reset(nfc->popup); popup_set_header( diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 2c12749df..730dd41e8 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring); + dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index a3b17451f..dcd2519f1 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -27,7 +27,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } 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 { diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index c7aec5d87..c1043b3a7 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -91,7 +91,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { bool application_info_present = false; if(dev_data->protocol == NfcDeviceProtocolEMV) { application_info_present = true; - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + } else if( + dev_data->protocol == NfcDeviceProtocolMifareClassic || + dev_data->protocol == NfcDeviceProtocolMifareUl) { application_info_present = nfc_supported_card_verify_and_parse(dev_data); } diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c b/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c index 1088e2959..3617dc0f1 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c +++ b/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c @@ -109,7 +109,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, }, { - {.width = 1, .icon = &I_Pin_arrow_up7x9, .value = HID_KEYBOARD_L_SHIFT}, + {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, diff --git a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c b/applications/plugins/bt_hid_app/views/bt_hid_mouse.c index f9d84f9fb..395cb52c9 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c +++ b/applications/plugins/bt_hid_app/views/bt_hid_mouse.c @@ -53,7 +53,7 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9); + canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9); canvas_set_color(canvas, ColorBlack); // Down diff --git a/applications/services/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c index 5502d5f6d..bf05f06b9 100644 --- a/applications/services/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -117,7 +117,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9); break; case InputKeyUp: - canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9); + canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9); break; case InputKeyLeft: canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7); diff --git a/assets/icons/NFC/Keychain.png b/assets/icons/NFC/Keychain.png deleted file mode 100644 index 7ba1b11da..000000000 Binary files a/assets/icons/NFC/Keychain.png and /dev/null differ diff --git a/assets/icons/NFC/Keychain_39x36.png b/assets/icons/NFC/Keychain_39x36.png new file mode 100644 index 000000000..d15850b5b Binary files /dev/null and b/assets/icons/NFC/Keychain_39x36.png differ diff --git a/assets/icons/NFC/NFC_manual.png b/assets/icons/NFC/NFC_manual_60x50.png similarity index 100% rename from assets/icons/NFC/NFC_manual.png rename to assets/icons/NFC/NFC_manual_60x50.png diff --git a/assets/icons/NFC/Reader_detect_43x40.png b/assets/icons/NFC/Reader_detect_43x40.png new file mode 100644 index 000000000..d833a5277 Binary files /dev/null and b/assets/icons/NFC/Reader_detect_43x40.png differ diff --git a/assets/icons/NFC/Restoring.png b/assets/icons/NFC/Restoring_38x32.png similarity index 100% rename from assets/icons/NFC/Restoring.png rename to assets/icons/NFC/Restoring_38x32.png diff --git a/assets/icons/PIN/Pin_arrow_up7x9.png b/assets/icons/PIN/Pin_arrow_up_7x9.png similarity index 100% rename from assets/icons/PIN/Pin_arrow_up7x9.png rename to assets/icons/PIN/Pin_arrow_up_7x9.png diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index f016188ea..2356d8d1b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -4162,7 +4162,7 @@ Variable,+,I_KeyBackspaceSelected_16x9,const Icon, Variable,+,I_KeyBackspace_16x9,const Icon, Variable,+,I_KeySaveSelected_24x11,const Icon, Variable,+,I_KeySave_24x11,const Icon, -Variable,+,I_Keychain,const Icon, +Variable,+,I_Keychain_39x36,const Icon, Variable,+,I_Left_mouse_icon_9x9,const Icon, Variable,+,I_Lock_7x8,const Icon, Variable,+,I_Lock_8x8,const Icon, @@ -4172,7 +4172,7 @@ Variable,+,I_Mode_25x27,const Icon, Variable,+,I_Mode_hvr_25x27,const Icon, Variable,+,I_Mute_25x27,const Icon, Variable,+,I_Mute_hvr_25x27,const Icon, -Variable,+,I_NFC_manual,const Icon, +Variable,+,I_NFC_manual_60x50,const Icon, Variable,+,I_Nfc_10px,const Icon, Variable,+,I_Ok_btn_9x9,const Icon, Variable,+,I_Ok_btn_pressed_13x13,const Icon, @@ -4180,7 +4180,7 @@ Variable,+,I_Percent_10x14,const Icon, Variable,+,I_Pin_arrow_down_7x9,const Icon, Variable,+,I_Pin_arrow_left_9x7,const Icon, Variable,+,I_Pin_arrow_right_9x7,const Icon, -Variable,+,I_Pin_arrow_up7x9,const Icon, +Variable,+,I_Pin_arrow_up_7x9,const Icon, Variable,+,I_Pin_attention_dpad_29x29,const Icon, Variable,+,I_Pin_back_arrow_10x8,const Icon, Variable,+,I_Pin_back_full_40x8,const Icon, diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c index 410ddbd8b..a615e7899 100644 --- a/lib/nfc/helpers/mf_classic_dict.c +++ b/lib/nfc/helpers/mf_classic_dict.c @@ -86,38 +86,30 @@ void mf_classic_dict_free(MfClassicDict* dict) { free(dict); } +static void mf_classic_dict_int_to_str(uint8_t* key_int, string_t key_str) { + string_reset(key_str); + for(size_t i = 0; i < 6; i++) { + string_cat_printf(key_str, "%02X", key_int[i]); + } +} + +static void mf_classic_dict_str_to_int(string_t key_str, uint64_t* key_int) { + uint8_t key_byte_tmp; + + *key_int = 0ULL; + for(uint8_t i = 0; i < 12; i += 2) { + args_char_to_hex( + string_get_char(key_str, i), string_get_char(key_str, i + 1), &key_byte_tmp); + *key_int |= (uint8_t)key_byte_tmp << 8 * (5 - i / 2); + } +} + uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) { furi_assert(dict); return dict->total_keys; } -bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - uint8_t key_byte_tmp = 0; - string_t next_line; - string_init(next_line); - - bool key_read = false; - *key = 0ULL; - while(!key_read) { - if(!stream_read_line(dict->stream, next_line)) break; - if(string_get_char(next_line, 0) == '#') continue; - if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - for(uint8_t i = 0; i < 12; i += 2) { - args_char_to_hex( - string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); - *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); - } - key_read = true; - } - - string_clear(next_line); - return key_read; -} - bool mf_classic_dict_rewind(MfClassicDict* dict) { furi_assert(dict); furi_assert(dict->stream); @@ -125,24 +117,194 @@ bool mf_classic_dict_rewind(MfClassicDict* dict) { return stream_rewind(dict->stream); } -bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { +bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key) { furi_assert(dict); furi_assert(dict->stream); - string_t key_str; - string_init(key_str); - for(size_t i = 0; i < 6; i++) { - string_cat_printf(key_str, "%02X", key[i]); + bool key_read = false; + string_reset(key); + while(!key_read) { + if(!stream_read_line(dict->stream, key)) break; + if(string_get_char(key, 0) == '#') continue; + if(string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue; + string_left(key, 12); + key_read = true; } - string_cat_printf(key_str, "\n"); + + return key_read; +} + +bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + bool key_read = mf_classic_dict_get_next_key_str(dict, temp_key); + if(key_read) { + mf_classic_dict_str_to_int(temp_key, key); + } + string_clear(temp_key); + return key_read; +} + +bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + string_init(next_line); + + bool key_found = false; + stream_rewind(dict->stream); + while(!key_found) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + string_left(next_line, 12); + if(!string_equal_p(key, next_line)) continue; + key_found = true; + } + + string_clear(next_line); + return key_found; +} + +bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) { + string_t temp_key; + + string_init(temp_key); + mf_classic_dict_int_to_str(key, temp_key); + bool key_found = mf_classic_dict_is_key_present_str(dict, temp_key); + string_clear(temp_key); + return key_found; +} + +bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_cat_printf(key, "\n"); bool key_added = false; do { if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; - if(!stream_insert_string(dict->stream, key_str)) break; + if(!stream_insert_string(dict->stream, key)) break; + dict->total_keys++; key_added = true; } while(false); - string_clear(key_str); + string_left(key, 12); return key_added; } + +bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + mf_classic_dict_int_to_str(key, temp_key); + bool key_added = mf_classic_dict_add_key_str(dict, temp_key); + + string_clear(temp_key); + return key_added; +} + +bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + uint32_t index = 0; + string_init(next_line); + string_reset(key); + + bool key_found = false; + while(!key_found) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + if(index++ != target) continue; + string_set_n(key, next_line, 0, 12); + key_found = true; + } + + string_clear(next_line); + return key_found; +} + +bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + bool key_found = mf_classic_dict_get_key_at_index_str(dict, temp_key, target); + if(key_found) { + mf_classic_dict_str_to_int(temp_key, key); + } + string_clear(temp_key); + return key_found; +} + +bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + string_init(next_line); + + bool key_found = false; + uint32_t index = 0; + stream_rewind(dict->stream); + while(!key_found) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + string_left(next_line, 12); + if(!string_equal_p(key, next_line)) continue; + key_found = true; + *target = index; + } + + string_clear(next_line); + return key_found; +} + +bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + mf_classic_dict_int_to_str(key, temp_key); + bool key_found = mf_classic_dict_find_index_str(dict, temp_key, target); + + string_clear(temp_key); + return key_found; +} + +bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + string_init(next_line); + uint32_t index = 0; + + bool key_removed = false; + while(!key_removed) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + if(index++ != target) continue; + stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent); + if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break; + dict->total_keys--; + key_removed = true; + } + + string_clear(next_line); + return key_removed; +} diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/helpers/mf_classic_dict.h index 2654e668c..aaea3c400 100644 --- a/lib/nfc/helpers/mf_classic_dict.h +++ b/lib/nfc/helpers/mf_classic_dict.h @@ -21,8 +21,26 @@ void mf_classic_dict_free(MfClassicDict* dict); uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict); -bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); - bool mf_classic_dict_rewind(MfClassicDict* dict); +bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key); + +bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key); + +bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); + +bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key); + +bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target); + +bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target); + bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key); + +bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key); + +bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target); + +bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target); + +bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 6355f8d1e..2feae443f 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -123,7 +123,25 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC } do { - // Read card + // Try to read supported card + FURI_LOG_I(TAG, "Trying to read a supported card ..."); + for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { + if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareUl) { + if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { + if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { + read_success = true; + nfc_supported_card[i].parse(nfc_worker->dev_data); + break; + } + } else { + furi_hal_nfc_sleep(); + } + } + } + if(read_success) break; + furi_hal_nfc_sleep(); + + // Otherwise, try to read as usual if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; if(!mf_ul_read_card(tx_rx, &reader, &data)) break; // Copy data @@ -149,14 +167,17 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont do { // Try to read supported card - FURI_LOG_I(TAG, "Try read supported card ..."); + FURI_LOG_I(TAG, "Trying to read a supported card ..."); for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { read_success = true; nfc_supported_card[i].parse(nfc_worker->dev_data); + break; } + } else { + furi_hal_nfc_sleep(); } } } diff --git a/lib/nfc/parsers/all_in_one.c b/lib/nfc/parsers/all_in_one.c new file mode 100644 index 000000000..b49a32f7c --- /dev/null +++ b/lib/nfc/parsers/all_in_one.c @@ -0,0 +1,113 @@ +#include "nfc_supported_card.h" +#include "all_in_one.h" + +#include +#include + +#include "furi_hal.h" + +#define ALL_IN_ONE_LAYOUT_UNKNOWN 0 +#define ALL_IN_ONE_LAYOUT_A 1 +#define ALL_IN_ONE_LAYOUT_D 2 +#define ALL_IN_ONE_LAYOUT_E2 3 +#define ALL_IN_ONE_LAYOUT_E3 4 +#define ALL_IN_ONE_LAYOUT_E5 5 +#define ALL_IN_ONE_LAYOUT_2 6 + +uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) { + // I absolutely hate what's about to happen here. + + // Switch on the second half of the third byte of page 5 + FURI_LOG_I("all_in_one", "Layout byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 2]); + FURI_LOG_I( + "all_in_one", "Layout half-byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 3] & 0x0F); + switch(dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F) { + // If it is A, the layout type is a type A layout + case 0x0A: + return ALL_IN_ONE_LAYOUT_A; + case 0x0D: + return ALL_IN_ONE_LAYOUT_D; + case 0x02: + return ALL_IN_ONE_LAYOUT_2; + default: + FURI_LOG_I( + "all_in_one", + "Unknown layout type: %d", + dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F); + return ALL_IN_ONE_LAYOUT_UNKNOWN; + } +} + +bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + UNUSED(nfc_worker); + // If this is a all_in_one pass, first 2 bytes of page 4 are 0x45 0xD9 + MfUltralightReader reader = {}; + MfUltralightData data = {}; + + if(!mf_ul_read_card(tx_rx, &reader, &data)) { + return false; + } else { + if(data.data[4 * 4] == 0x45 && data.data[4 * 4 + 1] == 0xD9) { + FURI_LOG_I("all_in_one", "Pass verified"); + return true; + } + } + return false; +} + +bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + MfUltralightReader reader = {}; + MfUltralightData data = {}; + if(!mf_ul_read_card(tx_rx, &reader, &data)) { + return false; + } else { + memcpy(&nfc_worker->dev_data->mf_ul_data, &data, sizeof(data)); + FURI_LOG_I("all_in_one", "Card read"); + return true; + } +} + +bool all_in_one_parser_parse(NfcDeviceData* dev_data) { + if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) { + FURI_LOG_I("all_in_one", "Pass not verified"); + return false; + } + + // If the layout is a then the ride count is stored in the first byte of page 8 + uint8_t ride_count = 0; + uint32_t serial = 0; + if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) { + ride_count = dev_data->mf_ul_data.data[4 * 8]; + } else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) { + // If the layout is D, the ride count is stored in the second byte of page 9 + ride_count = dev_data->mf_ul_data.data[4 * 9 + 1]; + // I hate this with a burning passion. + + // The number starts at the second half of the third byte on page 4, and is 32 bits long + // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte + // B8 17 A2 A4 BD becomes 81 7A 2A 4B + serial = (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | + dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | + dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | + dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | + (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); + } else { + FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data)); + ride_count = 137; + } + + // I hate this with a burning passion. + + // The number starts at the second half of the third byte on page 4, and is 32 bits long + // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte + // B8 17 A2 A4 BD becomes 81 7A 2A 4B + serial = + (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | + dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | + dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); + + // Format string for rides count + string_printf( + dev_data->parsed_data, "\e#All-In-One\nNumber: %u\nRides left: %u", serial, ride_count); + return true; +} \ No newline at end of file diff --git a/lib/nfc/parsers/all_in_one.h b/lib/nfc/parsers/all_in_one.h new file mode 100644 index 000000000..9b646d4dc --- /dev/null +++ b/lib/nfc/parsers/all_in_one.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool all_in_one_parser_parse(NfcDeviceData* dev_data); \ No newline at end of file diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/parsers/nfc_supported_card.c index 480c970e7..fc2dc34e0 100644 --- a/lib/nfc/parsers/nfc_supported_card.c +++ b/lib/nfc/parsers/nfc_supported_card.c @@ -1,14 +1,54 @@ #include "nfc_supported_card.h" -#include "troyka_parser.h" +#include "plantain_parser.h" +#include "troika_parser.h" +#include "plantain_4k_parser.h" +#include "troika_4k_parser.h" +#include "two_cities.h" +#include "all_in_one.h" NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { - [NfcSupportedCardTypeTroyka] = + [NfcSupportedCardTypePlantain] = { .protocol = NfcDeviceProtocolMifareClassic, - .verify = troyka_parser_verify, - .read = troyka_parser_read, - .parse = troyka_parser_parse, + .verify = plantain_parser_verify, + .read = plantain_parser_read, + .parse = plantain_parser_parse, + }, + [NfcSupportedCardTypeTroika] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troika_parser_verify, + .read = troika_parser_read, + .parse = troika_parser_parse, + }, + [NfcSupportedCardTypePlantain4K] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = plantain_4k_parser_verify, + .read = plantain_4k_parser_read, + .parse = plantain_4k_parser_parse, + }, + [NfcSupportedCardTypeTroika4K] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troika_4k_parser_verify, + .read = troika_4k_parser_read, + .parse = troika_4k_parser_parse, + }, + [NfcSupportedCardTypeTwoCities] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = two_cities_parser_verify, + .read = two_cities_parser_read, + .parse = two_cities_parser_parse, + }, + [NfcSupportedCardTypeAllInOne] = + { + .protocol = NfcDeviceProtocolMifareUl, + .verify = all_in_one_parser_verify, + .read = all_in_one_parser_read, + .parse = all_in_one_parser_parse, }, }; diff --git a/lib/nfc/parsers/nfc_supported_card.h b/lib/nfc/parsers/nfc_supported_card.h index 9b5d1c053..d34b5794a 100644 --- a/lib/nfc/parsers/nfc_supported_card.h +++ b/lib/nfc/parsers/nfc_supported_card.h @@ -7,7 +7,12 @@ #include typedef enum { - NfcSupportedCardTypeTroyka, + NfcSupportedCardTypePlantain, + NfcSupportedCardTypeTroika, + NfcSupportedCardTypePlantain4K, + NfcSupportedCardTypeTroika4K, + NfcSupportedCardTypeTwoCities, + NfcSupportedCardTypeAllInOne, NfcSupportedCardTypeEnd, } NfcSupportedCardType; diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/parsers/plantain_4k_parser.c new file mode 100644 index 000000000..77387707b --- /dev/null +++ b/lib/nfc/parsers/plantain_4k_parser.c @@ -0,0 +1,153 @@ +#include "nfc_supported_card.h" +#include "plantain_parser.h" // For luhn and string_push_uint64 + +#include +#include + +#include "furi_hal.h" + +static const MfClassicAuthContext plantain_keys_4k[] = { + {.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF}, + {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, + {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, + {.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, + {.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, + {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, + {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, + {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, + {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf}, + {.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a}, + {.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b}, + {.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3}, + {.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22}, + {.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056}, + {.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510}, + {.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf}, + {.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439}, + {.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406}, + {.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3}, + {.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA}, + {.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f}, + {.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3}, + {.sector = 30, .key_a = 0x0eb23cc8110b, .key_b = 0x04dc35277635}, + {.sector = 31, .key_a = 0xbc4580b7f20b, .key_b = 0xd0a4131fb290}, + {.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023}, + {.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9}, + {.sector = 34, .key_a = 0xfd8705e721b0, .key_b = 0x296fc317a513}, + {.sector = 35, .key_a = 0x22052b480d11, .key_b = 0xe19504c39461}, + {.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7}, + {.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a}, + {.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7}, + {.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085}, +}; + +bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { + return false; + } + + uint8_t sector = 8; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Plant4K", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { + FURI_LOG_D("Plant4K", "Sector %d verified", sector); + return true; + } + return false; +} + +bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(plantain_keys_4k); i++) { + mf_classic_reader_add_sector( + &reader, + plantain_keys_4k[i].sector, + plantain_keys_4k[i].key_a, + plantain_keys_4k[i].key_b); + FURI_LOG_T("plant4k", "Added sector %d", plantain_keys_4k[i].sector); + } + for(int i = 0; i < 5; i++) { + if(mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40) { + return true; + } + } + return false; +} + +bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != plantain_keys_4k[8].key_a) return false; + + // Point to block 0 of sector 4, value 0 + uint8_t* temp_ptr = &data->block[4 * 4].value[0]; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = &data->block[0 * 4].value[0]; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + // Convert card number to string + string_t card_number_str; + string_init(card_number_str); + // Should look like "361301047292848684" + // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead + string_push_uint64(card_number, card_number_str); + // Add suffix with luhn checksum (1 digit) to the card number string + string_t card_number_suffix; + string_init(card_number_suffix); + + // The number to calculate the checksum on doesn't fit into uint64_t, idk + //uint8_t luhn_checksum = plantain_calculate_luhn(card_number); + + // // Convert luhn checksum to string + // string_t luhn_checksum_str; + // string_init(luhn_checksum_str); + // string_push_uint64(luhn_checksum, luhn_checksum_str); + + string_cat_printf(card_number_suffix, "-"); + // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum); + string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); + // Free all not needed strings + string_clear(card_number_suffix); + // string_clear(luhn_checksum_str); + + string_printf( + dev_data->parsed_data, + "\e#Plantain\nN:%s\nBalance:%d\n", + string_get_cstr(card_number_str), + balance); + string_clear(card_number_str); + + return true; +} diff --git a/lib/nfc/parsers/plantain_4k_parser.h b/lib/nfc/parsers/plantain_4k_parser.h new file mode 100644 index 000000000..29998af15 --- /dev/null +++ b/lib/nfc/parsers/plantain_4k_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_4k_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/parsers/plantain_parser.c new file mode 100644 index 000000000..ff81b8e9b --- /dev/null +++ b/lib/nfc/parsers/plantain_parser.c @@ -0,0 +1,147 @@ +#include "nfc_supported_card.h" + +#include +#include + +#include "furi_hal.h" + +static const MfClassicAuthContext plantain_keys[] = { + {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, + {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, + {.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, + {.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, + {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, + {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, + {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, + {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, +}; + +bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) { + return false; + } + + uint8_t sector = 8; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Plant", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { + FURI_LOG_D("Plant", "Sector %d verified", sector); + return true; + } + return false; +} + +bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(plantain_keys); i++) { + mf_classic_reader_add_sector( + &reader, plantain_keys[i].sector, plantain_keys[i].key_a, plantain_keys[i].key_b); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; +} + +void string_push_uint64(uint64_t input, string_t output) { + const uint8_t base = 10; + + do { + char c = input % base; + input /= base; + + if(c < 10) + c += '0'; + else + c += 'A' - 10; + string_push_back(output, c); + } while(input); + + // reverse string + for(uint8_t i = 0; i < string_size(output) / 2; i++) { + char c = string_get_char(output, i); + string_set_char(output, i, string_get_char(output, string_size(output) - i - 1)); + string_set_char(output, string_size(output) - i - 1, c); + } +} + +uint8_t plantain_calculate_luhn(uint64_t number) { + // No. + UNUSED(number); + return 0; +} + +bool plantain_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != plantain_keys[8].key_a) return false; + + // Point to block 0 of sector 4, value 0 + uint8_t* temp_ptr = &data->block[4 * 4].value[0]; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = &data->block[0 * 4].value[0]; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + // Convert card number to string + string_t card_number_str; + string_init(card_number_str); + // Should look like "361301047292848684" + // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead + string_push_uint64(card_number, card_number_str); + // Add suffix with luhn checksum (1 digit) to the card number string + string_t card_number_suffix; + string_init(card_number_suffix); + + // The number to calculate the checksum on doesn't fit into uint64_t, idk + //uint8_t luhn_checksum = plantain_calculate_luhn(card_number); + + // // Convert luhn checksum to string + // string_t luhn_checksum_str; + // string_init(luhn_checksum_str); + // string_push_uint64(luhn_checksum, luhn_checksum_str); + + string_cat_printf(card_number_suffix, "-"); + // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum); + string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); + // Free all not needed strings + string_clear(card_number_suffix); + // string_clear(luhn_checksum_str); + + string_printf( + dev_data->parsed_data, + "\e#Plantain\nN:%s\nBalance:%d\n", + string_get_cstr(card_number_str), + balance); + string_clear(card_number_str); + + return true; +} diff --git a/lib/nfc/parsers/plantain_parser.h b/lib/nfc/parsers/plantain_parser.h new file mode 100644 index 000000000..d37f0dd48 --- /dev/null +++ b/lib/nfc/parsers/plantain_parser.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_parser_parse(NfcDeviceData* dev_data); + +void string_push_uint64(uint64_t input, string_t output); + +uint8_t plantain_calculate_luhn(uint64_t number); diff --git a/lib/nfc/parsers/troika_4k_parser.c b/lib/nfc/parsers/troika_4k_parser.c new file mode 100644 index 000000000..8c32381f2 --- /dev/null +++ b/lib/nfc/parsers/troika_4k_parser.c @@ -0,0 +1,104 @@ +#include "nfc_supported_card.h" + +#include +#include + +static const MfClassicAuthContext troika_4k_keys[] = { + {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, + {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, + {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5}, + {.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698}, + {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb}, + {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, + {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, + {.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d}, + {.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b}, + {.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0}, + {.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, + {.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75}, + {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408}, + {.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df}, + {.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9}, + {.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4}, + {.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216}, + {.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba}, + {.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252}, + {.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01}, + {.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994}, + {.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6}, + {.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32}, + {.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3}, + {.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5}, + {.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a}, + {.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80}, + {.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d}, + {.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2}, + {.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324}, + {.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae}, + {.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda}, + {.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d}, + {.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465}, + {.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4}, + {.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70}, +}; + +bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { + return false; + } + + uint8_t sector = 11; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Troika", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { + FURI_LOG_D("Troika", "Sector %d verified", sector); + return true; + } + return false; +} + +bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) { + mf_classic_reader_add_sector( + &reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; +} + +bool troika_4k_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != troika_4k_keys[4].key_a) return false; + + // Verify card type + if(data->type != MfClassicType4k) return false; + + uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; + uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; + temp_ptr = &data->block[8 * 4].value[3]; + uint32_t number = 0; + for(size_t i = 0; i < 4; i++) { + number <<= 8; + number |= temp_ptr[i]; + } + number >>= 4; + + string_printf(dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); + + return true; +} diff --git a/lib/nfc/parsers/troika_4k_parser.h b/lib/nfc/parsers/troika_4k_parser.h new file mode 100644 index 000000000..c1d6f01d3 --- /dev/null +++ b/lib/nfc/parsers/troika_4k_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_4k_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/troyka_parser.c b/lib/nfc/parsers/troika_parser.c similarity index 68% rename from lib/nfc/parsers/troyka_parser.c rename to lib/nfc/parsers/troika_parser.c index 51ffa42e1..f396b1680 100644 --- a/lib/nfc/parsers/troyka_parser.c +++ b/lib/nfc/parsers/troika_parser.c @@ -3,7 +3,7 @@ #include #include -static const MfClassicAuthContext troyka_keys[] = { +static const MfClassicAuthContext troika_keys[] = { {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, @@ -22,42 +22,50 @@ static const MfClassicAuthContext troyka_keys[] = { {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, }; -bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { furi_assert(nfc_worker); UNUSED(nfc_worker); + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) { + return false; + } - MfClassicAuthContext auth_ctx = { - .key_a = MF_CLASSIC_NO_KEY, - .key_b = MF_CLASSIC_NO_KEY, - .sector = 8, - }; - return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333); + uint8_t sector = 11; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Troika", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { + FURI_LOG_D("Troika", "Sector %d verified", sector); + return true; + } + return false; } -bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { furi_assert(nfc_worker); MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); - for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { + for(size_t i = 0; i < COUNT_OF(troika_keys); i++) { mf_classic_reader_add_sector( - &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); + &reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b); } return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; } -bool troyka_parser_parse(NfcDeviceData* dev_data) { +bool troika_parser_parse(NfcDeviceData* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; - bool troyka_parsed = false; + bool troika_parsed = false; do { // Verify key MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(key != troyka_keys[8].key_a) break; + if(key != troika_keys[8].key_a) break; + + // Verify card type + if(data->type != MfClassicType1k) break; // Parse data uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; @@ -71,9 +79,9 @@ bool troyka_parser_parse(NfcDeviceData* dev_data) { number >>= 4; string_printf( - dev_data->parsed_data, "\e#Troyka\nNum: %ld\nBalance: %d rur.", number, balance); - troyka_parsed = true; + dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); + troika_parsed = true; } while(false); - return troyka_parsed; + return troika_parsed; } diff --git a/lib/nfc/parsers/troika_parser.h b/lib/nfc/parsers/troika_parser.h new file mode 100644 index 000000000..2aae48d29 --- /dev/null +++ b/lib/nfc/parsers/troika_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/troyka_parser.h b/lib/nfc/parsers/troyka_parser.h deleted file mode 100644 index 445fe40e5..000000000 --- a/lib/nfc/parsers/troyka_parser.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "nfc_supported_card.h" - -bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -bool troyka_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/parsers/two_cities.c new file mode 100644 index 000000000..d052dff19 --- /dev/null +++ b/lib/nfc/parsers/two_cities.c @@ -0,0 +1,171 @@ +#include "nfc_supported_card.h" +#include "plantain_parser.h" // For plantain-specific stuff + +#include +#include + +#include "furi_hal.h" + +static const MfClassicAuthContext two_cities_keys_4k[] = { + {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, + {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, + {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, + {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, + {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, + {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, + {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, + {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf}, + {.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a}, + {.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b}, + {.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3}, + {.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22}, + {.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056}, + {.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510}, + {.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf}, + {.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439}, + {.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406}, + {.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3}, + {.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA}, + {.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f}, + {.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3}, + {.sector = 30, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, + {.sector = 31, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, + {.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023}, + {.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9}, + {.sector = 34, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 35, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7}, + {.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a}, + {.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7}, + {.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085}, +}; + +bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { + return false; + } + + uint8_t sector = 4; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("2cities", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) { + FURI_LOG_D("2cities", "Sector %d verified", sector); + return true; + } + return false; +} + +bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(two_cities_keys_4k); i++) { + mf_classic_reader_add_sector( + &reader, + two_cities_keys_4k[i].sector, + two_cities_keys_4k[i].key_a, + two_cities_keys_4k[i].key_b); + FURI_LOG_T("2cities", "Added sector %d", two_cities_keys_4k[i].sector); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; +} + +bool two_cities_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != two_cities_keys_4k[4].key_a) return false; + + // ===== + // PLANTAIN + // ===== + + // Point to block 0 of sector 4, value 0 + uint8_t* temp_ptr = &data->block[4 * 4].value[0]; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = &data->block[0 * 4].value[0]; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + // Convert card number to string + string_t card_number_str; + string_init(card_number_str); + // Should look like "361301047292848684" + // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead + string_push_uint64(card_number, card_number_str); + // Add suffix with luhn checksum (1 digit) to the card number string + string_t card_number_suffix; + string_init(card_number_suffix); + + // The number to calculate the checksum on doesn't fit into uint64_t, idk + //uint8_t luhn_checksum = two_cities_calculate_luhn(card_number); + + // // Convert luhn checksum to string + // string_t luhn_checksum_str; + // string_init(luhn_checksum_str); + // string_push_uint64(luhn_checksum, luhn_checksum_str); + + string_cat_printf(card_number_suffix, "-"); + // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum); + string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); + // Free all not needed strings + string_clear(card_number_suffix); + // string_clear(luhn_checksum_str); + + // ===== + // --PLANTAIN-- + // ===== + // TROIKA + // ===== + + uint8_t* troika_temp_ptr = &data->block[8 * 4 + 1].value[5]; + uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25; + troika_temp_ptr = &data->block[8 * 4].value[3]; + uint32_t troika_number = 0; + for(size_t i = 0; i < 4; i++) { + troika_number <<= 8; + troika_number |= troika_temp_ptr[i]; + } + troika_number >>= 4; + + string_printf( + dev_data->parsed_data, + "\e#Troika+Plantain\nPN: %s\nPB: %d rur.\nTN: %d\nTB: %d rur.\n", + string_get_cstr(card_number_str), + balance, + troika_number, + troika_balance); + string_clear(card_number_str); + + return true; +} diff --git a/lib/nfc/parsers/two_cities.h b/lib/nfc/parsers/two_cities.h new file mode 100644 index 000000000..e735bea8e --- /dev/null +++ b/lib/nfc/parsers/two_cities.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool two_cities_parser_parse(NfcDeviceData* dev_data);