mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 12:42:30 +04:00
merge ofw pr 4271
also thanks WillyJL link to PR https://github.com/flipperdevices/flipperzero-firmware/pull/4271/files
This commit is contained in:
@@ -18,11 +18,9 @@ void mf_ultralight_auth_free(MfUltralightAuth* instance) {
|
|||||||
void mf_ultralight_auth_reset(MfUltralightAuth* instance) {
|
void mf_ultralight_auth_reset(MfUltralightAuth* instance) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
|
|
||||||
uint32_t default_password = MF_ULTRALIGHT_DEFAULT_PASSWORD;
|
|
||||||
|
|
||||||
instance->type = MfUltralightAuthTypeNone;
|
instance->type = MfUltralightAuthTypeNone;
|
||||||
memcpy(&instance->password, &default_password, sizeof(MfUltralightAuthPassword));
|
memset(&instance->password, 0, sizeof(MfUltralightAuthPassword));
|
||||||
memcpy(&instance->tdes_key, MF_ULTRALIGHT_C_DEFAULT_KEY, sizeof(MfUltralightC3DesAuthKey));
|
memset(&instance->tdes_key, 0, sizeof(MfUltralightC3DesAuthKey));
|
||||||
memset(&instance->pack, 0, sizeof(MfUltralightAuthPack));
|
memset(&instance->pack, 0, sizeof(MfUltralightAuthPack));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ enum {
|
|||||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||||
SubmenuIndexUnlockByReader,
|
SubmenuIndexUnlockByReader,
|
||||||
SubmenuIndexUnlockByPassword,
|
SubmenuIndexUnlockByPassword,
|
||||||
|
SubmenuIndexDictAttack
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -149,7 +150,15 @@ static NfcCommand
|
|||||||
}
|
}
|
||||||
if(!mf_ultralight_event->data->auth_context.skip_auth) {
|
if(!mf_ultralight_event->data->auth_context.skip_auth) {
|
||||||
mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password;
|
mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password;
|
||||||
mf_ultralight_event->data->auth_context.tdes_key = instance->mf_ul_auth->tdes_key;
|
|
||||||
|
// Only set tdes_key for Manual/Reader auth types, not for dictionary attacks
|
||||||
|
if(instance->mf_ul_auth->type == MfUltralightAuthTypeManual ||
|
||||||
|
instance->mf_ul_auth->type == MfUltralightAuthTypeReader) {
|
||||||
|
mf_ultralight_event->data->key_request_data.key = instance->mf_ul_auth->tdes_key;
|
||||||
|
mf_ultralight_event->data->key_request_data.key_provided = true;
|
||||||
|
} else {
|
||||||
|
mf_ultralight_event->data->key_request_data.key_provided = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) {
|
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) {
|
||||||
instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack;
|
instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack;
|
||||||
@@ -165,15 +174,31 @@ static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
|
|||||||
|
|
||||||
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == NfcCustomEventCardDetected) {
|
if(event.event == NfcCustomEventPollerSuccess) {
|
||||||
nfc_unlock_helper_card_detected_handler(instance);
|
notification_message(instance->notifications, &sequence_success);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
|
return true;
|
||||||
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
||||||
|
const MfUltralightData* data =
|
||||||
|
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
||||||
|
if(data->type == MfUltralightTypeMfulC &&
|
||||||
|
instance->mf_ul_auth->type == MfUltralightAuthTypeNone) {
|
||||||
|
// Start dict attack for MFUL C cards only if no specific auth was attempted
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack);
|
||||||
|
} else {
|
||||||
|
if(data->pages_read == data->pages_total) {
|
||||||
|
notification_message(instance->notifications, &sequence_success);
|
||||||
|
} else {
|
||||||
notification_message(instance->notifications, &sequence_semi_success);
|
notification_message(instance->notifications, &sequence_semi_success);
|
||||||
|
}
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) {
|
static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) {
|
||||||
@@ -197,6 +222,14 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
|
|||||||
SubmenuIndexUnlock,
|
SubmenuIndexUnlock,
|
||||||
nfc_protocol_support_common_submenu_callback,
|
nfc_protocol_support_common_submenu_callback,
|
||||||
instance);
|
instance);
|
||||||
|
if(data->type == MfUltralightTypeMfulC) {
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Unlock with Dictionary",
|
||||||
|
SubmenuIndexDictAttack,
|
||||||
|
nfc_protocol_support_common_submenu_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +282,12 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
|
|||||||
NfcSceneMfUltralightUnlockMenu;
|
NfcSceneMfUltralightUnlockMenu;
|
||||||
scene_manager_next_scene(instance->scene_manager, next_scene);
|
scene_manager_next_scene(instance->scene_manager, next_scene);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexDictAttack) {
|
||||||
|
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
instance->scene_manager, NfcSceneMfUltralightCDictAttack)) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCDictAttack);
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|||||||
@@ -680,6 +680,7 @@ static void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) {
|
|||||||
furi_string_replace(prefix, " Plus", "+"); // NTAG I2C+
|
furi_string_replace(prefix, " Plus", "+"); // NTAG I2C+
|
||||||
furi_string_replace(prefix, " (Unknown)", "");
|
furi_string_replace(prefix, " (Unknown)", "");
|
||||||
furi_string_replace_all(prefix, " ", "_");
|
furi_string_replace_all(prefix, " ", "_");
|
||||||
|
furi_string_replace_all(prefix, "/", "_");
|
||||||
name_generator_make_auto(
|
name_generator_make_auto(
|
||||||
instance->text_store, NFC_TEXT_STORE_SIZE, furi_string_get_cstr(prefix));
|
instance->text_store, NFC_TEXT_STORE_SIZE, furi_string_get_cstr(prefix));
|
||||||
furi_string_free(prefix);
|
furi_string_free(prefix);
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
#include <lib/nfc/nfc.h>
|
#include <lib/nfc/nfc.h>
|
||||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
|
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>
|
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>
|
||||||
|
#include <lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
|
||||||
#include <lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h>
|
#include <lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h>
|
||||||
|
|
||||||
#include <nfc/nfc_poller.h>
|
#include <nfc/nfc_poller.h>
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
|
|
||||||
#define NFC_NAME_SIZE 22
|
#define NFC_NAME_SIZE 22
|
||||||
#define NFC_TEXT_STORE_SIZE 128
|
#define NFC_TEXT_STORE_SIZE 128
|
||||||
#define NFC_BYTE_INPUT_STORE_SIZE 10
|
#define NFC_BYTE_INPUT_STORE_SIZE 16
|
||||||
#define NFC_LOG_SIZE_MAX (1024)
|
#define NFC_LOG_SIZE_MAX (1024)
|
||||||
#define NFC_APP_FOLDER EXT_PATH("nfc")
|
#define NFC_APP_FOLDER EXT_PATH("nfc")
|
||||||
#define NFC_APP_EXTENSION ".nfc"
|
#define NFC_APP_EXTENSION ".nfc"
|
||||||
@@ -83,6 +84,10 @@
|
|||||||
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc")
|
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc")
|
||||||
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \
|
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \
|
||||||
(NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc")
|
(NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc")
|
||||||
|
#define NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH \
|
||||||
|
(NFC_APP_FOLDER "/assets/mf_ultralight_c_dict_user.nfc")
|
||||||
|
#define NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH \
|
||||||
|
(NFC_APP_FOLDER "/assets/mf_ultralight_c_dict.nfc")
|
||||||
|
|
||||||
#define NFC_MFKEY32_APP_PATH (EXT_PATH("apps/NFC/mfkey.fap"))
|
#define NFC_MFKEY32_APP_PATH (EXT_PATH("apps/NFC/mfkey.fap"))
|
||||||
|
|
||||||
@@ -110,6 +115,14 @@ typedef struct {
|
|||||||
bool enhanced_dict;
|
bool enhanced_dict;
|
||||||
} NfcMfClassicDictAttackContext;
|
} NfcMfClassicDictAttackContext;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
KeysDict* dict;
|
||||||
|
bool auth_success;
|
||||||
|
bool is_card_present;
|
||||||
|
size_t dict_keys_total;
|
||||||
|
size_t dict_keys_current;
|
||||||
|
} NfcMfUltralightCDictContext;
|
||||||
|
|
||||||
struct NfcApp {
|
struct NfcApp {
|
||||||
DialogsApp* dialogs;
|
DialogsApp* dialogs;
|
||||||
Storage* storage;
|
Storage* storage;
|
||||||
@@ -148,6 +161,7 @@ struct NfcApp {
|
|||||||
MfUltralightAuth* mf_ul_auth;
|
MfUltralightAuth* mf_ul_auth;
|
||||||
SlixUnlock* slix_unlock;
|
SlixUnlock* slix_unlock;
|
||||||
NfcMfClassicDictAttackContext nfc_dict_context;
|
NfcMfClassicDictAttackContext nfc_dict_context;
|
||||||
|
NfcMfUltralightCDictContext mf_ultralight_c_dict_context;
|
||||||
Mfkey32Logger* mfkey32_logger;
|
Mfkey32Logger* mfkey32_logger;
|
||||||
MfUserDict* mf_user_dict;
|
MfUserDict* mf_user_dict;
|
||||||
MfClassicKeyCache* mfc_key_cache;
|
MfClassicKeyCache* mfc_key_cache;
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Sample Key (BREAKMEIFYOUCAN!)
|
||||||
|
425245414B4D454946594F5543414E21
|
||||||
|
# Hexadecimal-Reversed Sample Key
|
||||||
|
12E4143455F495649454D4B414542524
|
||||||
|
# Byte-Reversed Sample Key (!NACUOYFIEMKAERB)
|
||||||
|
214E4143554F594649454D4B41455242
|
||||||
|
# Semnox Key (IEMKAERB!NACUOY )
|
||||||
|
49454D4B41455242214E4143554F5900
|
||||||
|
# Modified Semnox Key (IEMKAERB!NACUOYF)
|
||||||
|
49454D4B41455242214E4143554F5946
|
||||||
|
|
||||||
|
# Mix of Proxmark and ChameleonMiniLiveDebugger
|
||||||
|
00000000000000000000000000000000
|
||||||
|
000102030405060708090A0B0C0D0E0F
|
||||||
|
01010101010101010101010101010101
|
||||||
|
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
|
||||||
|
00112233445566778899AABBCCDDEEFF
|
||||||
|
47454D5850524553534F53414D504C45
|
||||||
|
79702553797025537970255379702553
|
||||||
|
4E617468616E2E4C6920546564647920
|
||||||
|
43464F494D48504E4C4359454E528841
|
||||||
|
6AC292FAA1315B4D858AB3A3D7D5933A
|
||||||
|
404142434445464748494A4B4C4D4E4F
|
||||||
|
2B7E151628AED2A6ABF7158809CF4F3C
|
||||||
|
FBEED618357133667C85E08F7236A8DE
|
||||||
|
F7DDAC306AE266CCF90BC11EE46D513B
|
||||||
|
54686973206973206D79206B65792020
|
||||||
|
A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7
|
||||||
|
B0B1B2B3B4B5B6B7B0B1B2B3B4B5B6B7
|
||||||
|
B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF
|
||||||
|
D3F7D3F7D3F7D3F7D3F7D3F7D3F7D3F7
|
||||||
|
11111111111111111111111111111111
|
||||||
|
22222222222222222222222222222222
|
||||||
|
33333333333333333333333333333333
|
||||||
|
44444444444444444444444444444444
|
||||||
|
55555555555555555555555555555555
|
||||||
|
66666666666666666666666666666666
|
||||||
|
77777777777777777777777777777777
|
||||||
|
88888888888888888888888888888888
|
||||||
|
99999999999999999999999999999999
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
||||||
|
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
||||||
|
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
|
||||||
|
0102030405060708090A0B0C0D0E0F10
|
||||||
|
00010203040506070809101112131415
|
||||||
|
01020304050607080910111213141516
|
||||||
|
16151413121110090807060504030201
|
||||||
|
15141312111009080706050403020100
|
||||||
|
0F0E0D0C0B0A09080706050403020100
|
||||||
|
100F0E0D0C0B0A090807060504030201
|
||||||
|
303132333435363738393A3B3C3D3E3F
|
||||||
|
9CABF398358405AE2F0E2B3D31C99A8A
|
||||||
|
605F5E5D5C5B5A59605F5E5D5C5B5A59
|
||||||
@@ -26,6 +26,7 @@ ADD_SCENE(nfc, retry_confirm, RetryConfirm)
|
|||||||
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
|
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
|
||||||
ADD_SCENE(nfc, save_confirm, SaveConfirm)
|
ADD_SCENE(nfc, save_confirm, SaveConfirm)
|
||||||
|
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_c_dict_attack, MfUltralightCDictAttack)
|
||||||
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
||||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||||
@@ -52,6 +53,12 @@ ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
|
|||||||
ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
|
ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
|
||||||
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
|
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
|
||||||
|
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_c_keys, MfUltralightCKeys)
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_c_keys_list, MfUltralightCKeysList)
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_c_keys_delete, MfUltralightCKeysDelete)
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_c_keys_add, MfUltralightCKeysAdd)
|
||||||
|
ADD_SCENE(nfc, mf_ultralight_c_keys_warn_duplicate, MfUltralightCKeysWarnDuplicate)
|
||||||
|
|
||||||
ADD_SCENE(nfc, set_type, SetType)
|
ADD_SCENE(nfc, set_type, SetType)
|
||||||
ADD_SCENE(nfc, set_sak, SetSak)
|
ADD_SCENE(nfc, set_sak, SetSak)
|
||||||
ADD_SCENE(nfc, set_atqa, SetAtqa)
|
ADD_SCENE(nfc, set_atqa, SetAtqa)
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||||
|
} else if(scene_manager_has_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneMfUltralightCKeys)) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneMfUltralightCKeys);
|
||||||
} else {
|
} else {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
nfc->scene_manager, NfcSceneFileSelect);
|
nfc->scene_manager, NfcSceneFileSelect);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexReadCardType,
|
SubmenuIndexReadCardType,
|
||||||
SubmenuIndexMfClassicKeys,
|
SubmenuIndexMfClassicKeys,
|
||||||
|
SubmenuIndexMfUltralightCKeys,
|
||||||
SubmenuIndexMfUltralightUnlock,
|
SubmenuIndexMfUltralightUnlock,
|
||||||
SubmenuIndexSlixUnlock,
|
SubmenuIndexSlixUnlock,
|
||||||
};
|
};
|
||||||
@@ -29,6 +30,12 @@ void nfc_scene_extra_actions_on_enter(void* context) {
|
|||||||
SubmenuIndexMfClassicKeys,
|
SubmenuIndexMfClassicKeys,
|
||||||
nfc_scene_extra_actions_submenu_callback,
|
nfc_scene_extra_actions_submenu_callback,
|
||||||
instance);
|
instance);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"MIFARE Ultralight C Keys",
|
||||||
|
SubmenuIndexMfUltralightCKeys,
|
||||||
|
nfc_scene_extra_actions_submenu_callback,
|
||||||
|
instance);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu,
|
submenu,
|
||||||
"Unlock NTAG/Ultralight",
|
"Unlock NTAG/Ultralight",
|
||||||
@@ -54,6 +61,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(event.event == SubmenuIndexMfClassicKeys) {
|
if(event.event == SubmenuIndexMfClassicKeys) {
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexMfUltralightCKeys) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeys);
|
||||||
|
consumed = true;
|
||||||
} else if(event.event == SubmenuIndexMfUltralightUnlock) {
|
} else if(event.event == SubmenuIndexMfUltralightUnlock) {
|
||||||
mf_ultralight_auth_reset(instance->mf_ul_auth);
|
mf_ultralight_auth_reset(instance->mf_ul_auth);
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "../nfc_app_i.h"
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
#define NFC_SCENE_MF_CLASSIC_KEYS_MAX (100)
|
|
||||||
|
|
||||||
void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) {
|
void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
NfcApp* instance = context;
|
NfcApp* instance = context;
|
||||||
if(type == InputTypeShort) {
|
if(type == InputTypeShort) {
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
#define TAG "NfcMfUlCDictAttack"
|
||||||
|
|
||||||
|
// TODO: Support card_detected properly
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DictAttackStateUserDictInProgress,
|
||||||
|
DictAttackStateSystemDictInProgress,
|
||||||
|
};
|
||||||
|
|
||||||
|
NfcCommand nfc_mf_ultralight_c_dict_attack_worker_callback(NfcGenericEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
furi_assert(event.event_data);
|
||||||
|
furi_assert(event.protocol == NfcProtocolMfUltralight);
|
||||||
|
NfcCommand command = NfcCommandContinue;
|
||||||
|
NfcApp* instance = context;
|
||||||
|
MfUltralightPollerEvent* poller_event = event.event_data;
|
||||||
|
|
||||||
|
if(poller_event->type == MfUltralightPollerEventTypeRequestMode) {
|
||||||
|
poller_event->data->poller_mode = MfUltralightPollerModeDictAttack;
|
||||||
|
command = NfcCommandContinue;
|
||||||
|
} else if(poller_event->type == MfUltralightPollerEventTypeRequestKey) {
|
||||||
|
MfUltralightC3DesAuthKey key = {};
|
||||||
|
if(keys_dict_get_next_key(
|
||||||
|
instance->mf_ultralight_c_dict_context.dict,
|
||||||
|
key.data,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey))) {
|
||||||
|
poller_event->data->key_request_data.key = key;
|
||||||
|
poller_event->data->key_request_data.key_provided = true;
|
||||||
|
instance->mf_ultralight_c_dict_context.dict_keys_current++;
|
||||||
|
|
||||||
|
if(instance->mf_ultralight_c_dict_context.dict_keys_current % 10 == 0) {
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
poller_event->data->key_request_data.key_provided = false;
|
||||||
|
}
|
||||||
|
} else if(poller_event->type == MfUltralightPollerEventTypeReadSuccess) {
|
||||||
|
nfc_device_set_data(
|
||||||
|
instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller));
|
||||||
|
// Check if this is a successful authentication by looking at the poller's auth context
|
||||||
|
const MfUltralightData* data = nfc_poller_get_data(instance->poller);
|
||||||
|
|
||||||
|
// Update page information
|
||||||
|
dict_attack_set_pages_read(instance->dict_attack, data->pages_read);
|
||||||
|
dict_attack_set_pages_total(instance->dict_attack, data->pages_total);
|
||||||
|
|
||||||
|
if(data->pages_read == data->pages_total) {
|
||||||
|
// Full read indicates successful authentication in dict attack mode
|
||||||
|
instance->mf_ultralight_c_dict_context.auth_success = true;
|
||||||
|
dict_attack_set_key_found(instance->dict_attack, true);
|
||||||
|
}
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
instance->view_dispatcher, NfcCustomEventDictAttackComplete);
|
||||||
|
command = NfcCommandStop;
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_dict_attack_dict_attack_result_callback(
|
||||||
|
DictAttackEvent event,
|
||||||
|
void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
NfcApp* instance = context;
|
||||||
|
if(event == DictAttackEventSkipPressed) {
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_dict_attack_prepare_view(NfcApp* instance) {
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack);
|
||||||
|
|
||||||
|
// Set attack type to Ultralight C
|
||||||
|
dict_attack_set_type(instance->dict_attack, DictAttackTypeMfUltralightC);
|
||||||
|
|
||||||
|
if(state == DictAttackStateUserDictInProgress) {
|
||||||
|
do {
|
||||||
|
if(!keys_dict_check_presence(NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH)) {
|
||||||
|
state = DictAttackStateSystemDictInProgress;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||||
|
KeysDictModeOpenAlways,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
if(keys_dict_get_total_keys(instance->mf_ultralight_c_dict_context.dict) == 0) {
|
||||||
|
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||||
|
state = DictAttackStateSystemDictInProgress;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dict_attack_set_header(instance->dict_attack, "MFUL C User Dictionary");
|
||||||
|
} while(false);
|
||||||
|
}
|
||||||
|
if(state == DictAttackStateSystemDictInProgress) {
|
||||||
|
instance->mf_ultralight_c_dict_context.dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,
|
||||||
|
KeysDictModeOpenExisting,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
dict_attack_set_header(instance->dict_attack, "MFUL C System Dictionary");
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->mf_ultralight_c_dict_context.dict_keys_total =
|
||||||
|
keys_dict_get_total_keys(instance->mf_ultralight_c_dict_context.dict);
|
||||||
|
dict_attack_set_total_dict_keys(
|
||||||
|
instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_total);
|
||||||
|
instance->mf_ultralight_c_dict_context.dict_keys_current = 0;
|
||||||
|
dict_attack_set_current_dict_key(
|
||||||
|
instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current);
|
||||||
|
|
||||||
|
// Set initial Ultralight C specific values
|
||||||
|
dict_attack_set_key_found(instance->dict_attack, false);
|
||||||
|
dict_attack_set_pages_total(instance->dict_attack, 48); // Ultralight C page count
|
||||||
|
dict_attack_set_pages_read(instance->dict_attack, 0);
|
||||||
|
|
||||||
|
dict_attack_set_callback(
|
||||||
|
instance->dict_attack,
|
||||||
|
nfc_scene_mf_ultralight_c_dict_attack_dict_attack_result_callback,
|
||||||
|
instance);
|
||||||
|
scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_dict_attack_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager,
|
||||||
|
NfcSceneMfUltralightCDictAttack,
|
||||||
|
DictAttackStateUserDictInProgress);
|
||||||
|
nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance);
|
||||||
|
|
||||||
|
// Setup and start worker
|
||||||
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
|
||||||
|
nfc_poller_start(instance->poller, nfc_mf_ultralight_c_dict_attack_worker_callback, instance);
|
||||||
|
|
||||||
|
dict_attack_set_card_state(instance->dict_attack, true);
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack);
|
||||||
|
nfc_blink_read_start(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ul_c_dict_attack_update_view(NfcApp* instance) {
|
||||||
|
dict_attack_set_card_state(
|
||||||
|
instance->dict_attack, instance->mf_ultralight_c_dict_context.is_card_present);
|
||||||
|
dict_attack_set_current_dict_key(
|
||||||
|
instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_c_dict_attack_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCDictAttack);
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventDictAttackComplete) {
|
||||||
|
if(state == DictAttackStateUserDictInProgress) {
|
||||||
|
if(instance->mf_ultralight_c_dict_context.auth_success) {
|
||||||
|
notification_message(instance->notifications, &sequence_success);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
|
consumed = true;
|
||||||
|
} else {
|
||||||
|
nfc_poller_stop(instance->poller);
|
||||||
|
nfc_poller_free(instance->poller);
|
||||||
|
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager,
|
||||||
|
NfcSceneMfUltralightCDictAttack,
|
||||||
|
DictAttackStateSystemDictInProgress);
|
||||||
|
nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance);
|
||||||
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
|
||||||
|
nfc_poller_start(
|
||||||
|
instance->poller,
|
||||||
|
nfc_mf_ultralight_c_dict_attack_worker_callback,
|
||||||
|
instance);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Could check if card is fully read here like MFC dict attack, but found key means fully read
|
||||||
|
if(instance->mf_ultralight_c_dict_context.auth_success) {
|
||||||
|
notification_message(instance->notifications, &sequence_success);
|
||||||
|
} else {
|
||||||
|
notification_message(instance->notifications, &sequence_semi_success);
|
||||||
|
}
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
} else if(event.event == NfcCustomEventDictAttackDataUpdate) {
|
||||||
|
dict_attack_set_current_dict_key(
|
||||||
|
instance->dict_attack, instance->mf_ultralight_c_dict_context.dict_keys_current);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventDictAttackSkip) {
|
||||||
|
if(state == DictAttackStateUserDictInProgress) {
|
||||||
|
nfc_poller_stop(instance->poller);
|
||||||
|
nfc_poller_free(instance->poller);
|
||||||
|
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager,
|
||||||
|
NfcSceneMfUltralightCDictAttack,
|
||||||
|
DictAttackStateSystemDictInProgress);
|
||||||
|
nfc_scene_mf_ultralight_c_dict_attack_prepare_view(instance);
|
||||||
|
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight);
|
||||||
|
nfc_poller_start(
|
||||||
|
instance->poller, nfc_mf_ultralight_c_dict_attack_worker_callback, instance);
|
||||||
|
} else {
|
||||||
|
notification_message(instance->notifications, &sequence_semi_success);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_dict_attack_on_exit(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
nfc_poller_stop(instance->poller);
|
||||||
|
nfc_poller_free(instance->poller);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager,
|
||||||
|
NfcSceneMfUltralightCDictAttack,
|
||||||
|
DictAttackStateUserDictInProgress);
|
||||||
|
keys_dict_free(instance->mf_ultralight_c_dict_context.dict);
|
||||||
|
instance->mf_ultralight_c_dict_context.dict_keys_total = 0;
|
||||||
|
instance->mf_ultralight_c_dict_context.dict_keys_current = 0;
|
||||||
|
instance->mf_ultralight_c_dict_context.auth_success = false;
|
||||||
|
instance->mf_ultralight_c_dict_context.is_card_present = false;
|
||||||
|
nfc_blink_stop(instance);
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_widget_callback(
|
||||||
|
GuiButtonType result,
|
||||||
|
InputType type,
|
||||||
|
void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
if(type == InputTypeShort) {
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
// Load flipper dict keys total
|
||||||
|
uint32_t flipper_dict_keys_total = 0;
|
||||||
|
KeysDict* dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_SYSTEM_PATH,
|
||||||
|
KeysDictModeOpenExisting,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
flipper_dict_keys_total = keys_dict_get_total_keys(dict);
|
||||||
|
keys_dict_free(dict);
|
||||||
|
|
||||||
|
// Load user dict keys total
|
||||||
|
uint32_t user_dict_keys_total = 0;
|
||||||
|
dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||||
|
KeysDictModeOpenAlways,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
user_dict_keys_total = keys_dict_get_total_keys(dict);
|
||||||
|
keys_dict_free(dict);
|
||||||
|
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
widget_add_string_element(
|
||||||
|
instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Ultralight C Keys");
|
||||||
|
furi_string_printf(temp_str, "System dict: %lu", flipper_dict_keys_total);
|
||||||
|
widget_add_string_element(
|
||||||
|
instance->widget,
|
||||||
|
0,
|
||||||
|
20,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
FontSecondary,
|
||||||
|
furi_string_get_cstr(temp_str));
|
||||||
|
furi_string_printf(temp_str, "User dict: %lu", user_dict_keys_total);
|
||||||
|
widget_add_string_element(
|
||||||
|
instance->widget,
|
||||||
|
0,
|
||||||
|
32,
|
||||||
|
AlignLeft,
|
||||||
|
AlignTop,
|
||||||
|
FontSecondary,
|
||||||
|
furi_string_get_cstr(temp_str));
|
||||||
|
widget_add_icon_element(instance->widget, 87, 13, &I_Keychain_39x36);
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeCenter,
|
||||||
|
"Add",
|
||||||
|
nfc_scene_mf_ultralight_c_keys_widget_callback,
|
||||||
|
instance);
|
||||||
|
if(user_dict_keys_total > 0) {
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeRight,
|
||||||
|
"List",
|
||||||
|
nfc_scene_mf_ultralight_c_keys_widget_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_c_keys_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == GuiButtonTypeCenter) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysAdd);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == GuiButtonTypeRight) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysList);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_on_exit(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
widget_reset(instance->widget);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_add_byte_input_callback(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_add_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
ByteInput* byte_input = instance->byte_input;
|
||||||
|
byte_input_set_header_text(byte_input, "Enter the key in hex");
|
||||||
|
byte_input_set_result_callback(
|
||||||
|
byte_input,
|
||||||
|
nfc_scene_mf_ultralight_c_keys_add_byte_input_callback,
|
||||||
|
NULL,
|
||||||
|
instance,
|
||||||
|
instance->byte_input_store,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_c_keys_add_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventByteInputDone) {
|
||||||
|
// Add key to dict
|
||||||
|
KeysDict* dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||||
|
KeysDictModeOpenAlways,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
|
||||||
|
MfUltralightC3DesAuthKey key = {};
|
||||||
|
memcpy(key.data, instance->byte_input_store, sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
if(keys_dict_is_key_present(dict, key.data, sizeof(MfUltralightC3DesAuthKey))) {
|
||||||
|
scene_manager_next_scene(
|
||||||
|
instance->scene_manager, NfcSceneMfUltralightCKeysWarnDuplicate);
|
||||||
|
} else if(keys_dict_add_key(dict, key.data, sizeof(MfUltralightC3DesAuthKey))) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
|
||||||
|
dolphin_deed(DolphinDeedNfcMfcAdd);
|
||||||
|
} else {
|
||||||
|
scene_manager_previous_scene(instance->scene_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
keys_dict_free(dict);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_add_on_exit(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||||
|
byte_input_set_header_text(instance->byte_input, "");
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_delete_widget_callback(
|
||||||
|
GuiButtonType result,
|
||||||
|
InputType type,
|
||||||
|
void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
if(type == InputTypeShort) {
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_delete_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
uint32_t key_index =
|
||||||
|
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightCKeysDelete);
|
||||||
|
FuriString* key_str = furi_string_alloc();
|
||||||
|
|
||||||
|
widget_add_string_element(
|
||||||
|
instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?");
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeLeft,
|
||||||
|
"Cancel",
|
||||||
|
nfc_scene_mf_ultralight_c_keys_delete_widget_callback,
|
||||||
|
instance);
|
||||||
|
widget_add_button_element(
|
||||||
|
instance->widget,
|
||||||
|
GuiButtonTypeRight,
|
||||||
|
"Delete",
|
||||||
|
nfc_scene_mf_ultralight_c_keys_delete_widget_callback,
|
||||||
|
instance);
|
||||||
|
|
||||||
|
KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||||
|
KeysDictModeOpenAlways,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict);
|
||||||
|
furi_assert(key_index < dict_keys_num);
|
||||||
|
MfUltralightC3DesAuthKey stack_key;
|
||||||
|
for(size_t i = 0; i < (key_index + 1); i++) {
|
||||||
|
bool key_loaded = keys_dict_get_next_key(
|
||||||
|
mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
furi_assert(key_loaded);
|
||||||
|
}
|
||||||
|
furi_string_reset(key_str);
|
||||||
|
for(size_t i = 0; i < sizeof(MfUltralightC3DesAuthKey); i++) {
|
||||||
|
furi_string_cat_printf(key_str, "%02X", stack_key.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
widget_add_string_element(
|
||||||
|
instance->widget,
|
||||||
|
64,
|
||||||
|
32,
|
||||||
|
AlignCenter,
|
||||||
|
AlignCenter,
|
||||||
|
FontSecondary,
|
||||||
|
furi_string_get_cstr(key_str));
|
||||||
|
|
||||||
|
keys_dict_free(mf_ultralight_c_user_dict);
|
||||||
|
furi_string_free(key_str);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_c_keys_delete_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == GuiButtonTypeRight) {
|
||||||
|
uint32_t key_index = scene_manager_get_scene_state(
|
||||||
|
instance->scene_manager, NfcSceneMfUltralightCKeysDelete);
|
||||||
|
KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||||
|
KeysDictModeOpenAlways,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict);
|
||||||
|
furi_assert(key_index < dict_keys_num);
|
||||||
|
MfUltralightC3DesAuthKey stack_key;
|
||||||
|
for(size_t i = 0; i < (key_index + 1); i++) {
|
||||||
|
bool key_loaded = keys_dict_get_next_key(
|
||||||
|
mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
furi_assert(key_loaded);
|
||||||
|
}
|
||||||
|
bool key_delete_success = keys_dict_delete_key(
|
||||||
|
mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
keys_dict_free(mf_ultralight_c_user_dict);
|
||||||
|
if(key_delete_success) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneDeleteSuccess);
|
||||||
|
} else {
|
||||||
|
scene_manager_previous_scene(instance->scene_manager);
|
||||||
|
}
|
||||||
|
} else if(event.event == GuiButtonTypeLeft) {
|
||||||
|
scene_manager_previous_scene(instance->scene_manager);
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_delete_on_exit(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
widget_reset(instance->widget);
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
#define NFC_SCENE_MF_ULTRALIGHT_C_KEYS_LIST_MAX (100)
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_list_submenu_callback(void* context, uint32_t index) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_list_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
KeysDict* mf_ultralight_c_user_dict = keys_dict_alloc(
|
||||||
|
NFC_APP_MF_ULTRALIGHT_C_DICT_USER_PATH,
|
||||||
|
KeysDictModeOpenAlways,
|
||||||
|
sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
|
||||||
|
submenu_set_header(instance->submenu, "Select key to delete:");
|
||||||
|
FuriString* temp_str = furi_string_alloc();
|
||||||
|
|
||||||
|
size_t dict_keys_num = keys_dict_get_total_keys(mf_ultralight_c_user_dict);
|
||||||
|
size_t keys_num = MIN((size_t)NFC_SCENE_MF_ULTRALIGHT_C_KEYS_LIST_MAX, dict_keys_num);
|
||||||
|
MfUltralightC3DesAuthKey stack_key;
|
||||||
|
|
||||||
|
if(keys_num > 0) {
|
||||||
|
for(size_t i = 0; i < keys_num; i++) {
|
||||||
|
bool key_loaded = keys_dict_get_next_key(
|
||||||
|
mf_ultralight_c_user_dict, stack_key.data, sizeof(MfUltralightC3DesAuthKey));
|
||||||
|
furi_assert(key_loaded);
|
||||||
|
furi_string_reset(temp_str);
|
||||||
|
for(size_t i = 0; i < sizeof(MfUltralightC3DesAuthKey); i++) {
|
||||||
|
furi_string_cat_printf(temp_str, "%02X", stack_key.data[i]);
|
||||||
|
}
|
||||||
|
submenu_add_item(
|
||||||
|
instance->submenu,
|
||||||
|
furi_string_get_cstr(temp_str),
|
||||||
|
i,
|
||||||
|
nfc_scene_mf_ultralight_c_keys_list_submenu_callback,
|
||||||
|
instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys_dict_free(mf_ultralight_c_user_dict);
|
||||||
|
furi_string_free(temp_str);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_c_keys_list_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
bool consumed = false;
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
instance->scene_manager, NfcSceneMfUltralightCKeysDelete, event.event);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeysDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_list_on_exit(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
submenu_reset(instance->submenu);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_warn_duplicate_popup_callback(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_enter(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
Popup* popup = instance->popup;
|
||||||
|
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||||
|
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, 1500);
|
||||||
|
popup_set_context(popup, instance);
|
||||||
|
popup_set_callback(popup, nfc_scene_mf_ultralight_c_keys_warn_duplicate_popup_callback);
|
||||||
|
popup_enable_timeout(popup);
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventViewExit) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
instance->scene_manager, NfcSceneMfUltralightCKeysAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_mf_ultralight_c_keys_warn_duplicate_on_exit(void* context) {
|
||||||
|
NfcApp* instance = context;
|
||||||
|
|
||||||
|
popup_reset(instance->popup);
|
||||||
|
}
|
||||||
@@ -28,6 +28,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
|||||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
||||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||||
|
} else if(scene_manager_has_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneMfUltralightCKeys)) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
nfc->scene_manager, NfcSceneMfUltralightCKeys);
|
||||||
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) {
|
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSaveConfirm)) {
|
||||||
NfcSceneSaveConfirmState scene_state =
|
NfcSceneSaveConfirmState scene_state =
|
||||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm);
|
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm);
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ struct DictAttack {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
FuriString* header;
|
FuriString* header;
|
||||||
bool card_detected;
|
bool card_detected;
|
||||||
|
DictAttackType attack_type;
|
||||||
|
|
||||||
|
// MIFARE Classic specific
|
||||||
uint8_t sectors_total;
|
uint8_t sectors_total;
|
||||||
uint8_t sectors_read;
|
uint8_t sectors_read;
|
||||||
uint8_t current_sector;
|
uint8_t current_sector;
|
||||||
uint8_t keys_found;
|
uint8_t keys_found;
|
||||||
size_t dict_keys_total;
|
|
||||||
size_t dict_keys_current;
|
|
||||||
bool is_key_attack;
|
bool is_key_attack;
|
||||||
uint8_t key_attack_current_sector;
|
uint8_t key_attack_current_sector;
|
||||||
MfClassicNestedPhase nested_phase;
|
MfClassicNestedPhase nested_phase;
|
||||||
@@ -26,17 +27,18 @@ typedef struct {
|
|||||||
MfClassicBackdoor backdoor;
|
MfClassicBackdoor backdoor;
|
||||||
uint16_t nested_target_key;
|
uint16_t nested_target_key;
|
||||||
uint16_t msb_count;
|
uint16_t msb_count;
|
||||||
|
|
||||||
|
// Ultralight C specific
|
||||||
|
uint8_t pages_total;
|
||||||
|
uint8_t pages_read;
|
||||||
|
bool key_found;
|
||||||
|
|
||||||
|
// Common
|
||||||
|
size_t dict_keys_total;
|
||||||
|
size_t dict_keys_current;
|
||||||
} DictAttackViewModel;
|
} DictAttackViewModel;
|
||||||
|
|
||||||
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
static void dict_attack_draw_mf_classic(Canvas* canvas, DictAttackViewModel* m) {
|
||||||
DictAttackViewModel* m = model;
|
|
||||||
if(!m->card_detected) {
|
|
||||||
canvas_set_font(canvas, FontPrimary);
|
|
||||||
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!");
|
|
||||||
canvas_set_font(canvas, FontSecondary);
|
|
||||||
elements_multiline_text_aligned(
|
|
||||||
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
|
||||||
} else {
|
|
||||||
char draw_str[32] = {};
|
char draw_str[32] = {};
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
@@ -72,8 +74,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
||||||
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
|
||||||
if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
||||||
uint8_t nonce_sector =
|
uint8_t nonce_sector =
|
||||||
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);
|
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);
|
||||||
@@ -122,11 +123,7 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||||||
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
|
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
|
||||||
} else {
|
} else {
|
||||||
snprintf(
|
snprintf(
|
||||||
draw_str,
|
draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total);
|
||||||
sizeof(draw_str),
|
|
||||||
"%zu/%zu",
|
|
||||||
m->dict_keys_current,
|
|
||||||
m->dict_keys_total);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(dict_progress > 1.0f) {
|
if(dict_progress > 1.0f) {
|
||||||
@@ -141,9 +138,53 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||||||
m->keys_found,
|
m->keys_found,
|
||||||
m->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR);
|
m->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR);
|
||||||
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
||||||
snprintf(
|
snprintf(draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total);
|
||||||
draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total);
|
|
||||||
canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
|
canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dict_attack_draw_mf_ultralight_c(Canvas* canvas, DictAttackViewModel* m) {
|
||||||
|
char draw_str[32] = {};
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
||||||
|
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Trying keys");
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||||
|
|
||||||
|
float dict_progress =
|
||||||
|
m->dict_keys_total == 0 ? 0 : (float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
||||||
|
if(m->dict_keys_current == 0) {
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
|
||||||
|
} else {
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total);
|
||||||
|
}
|
||||||
|
if(dict_progress > 1.0f) {
|
||||||
|
dict_progress = 1.0f;
|
||||||
|
}
|
||||||
|
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Key found: %s", m->key_found ? "Yes" : "No");
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
||||||
|
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Pages read: %d/%d", m->pages_read, m->pages_total);
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
||||||
|
DictAttackViewModel* m = model;
|
||||||
|
if(!m->card_detected) {
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!");
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_multiline_text_aligned(
|
||||||
|
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
||||||
|
} else {
|
||||||
|
if(m->attack_type == DictAttackTypeMfClassic) {
|
||||||
|
dict_attack_draw_mf_classic(canvas, m);
|
||||||
|
} else if(m->attack_type == DictAttackTypeMfUltralightC) {
|
||||||
|
dict_attack_draw_mf_ultralight_c(canvas, m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elements_button_center(canvas, "Skip");
|
elements_button_center(canvas, "Skip");
|
||||||
}
|
}
|
||||||
@@ -195,18 +236,28 @@ void dict_attack_reset(DictAttack* instance) {
|
|||||||
instance->view,
|
instance->view,
|
||||||
DictAttackViewModel * model,
|
DictAttackViewModel * model,
|
||||||
{
|
{
|
||||||
|
model->attack_type = DictAttackTypeMfClassic;
|
||||||
|
|
||||||
|
// MIFARE Classic fields
|
||||||
model->sectors_total = 0;
|
model->sectors_total = 0;
|
||||||
model->sectors_read = 0;
|
model->sectors_read = 0;
|
||||||
model->current_sector = 0;
|
model->current_sector = 0;
|
||||||
model->keys_found = 0;
|
model->keys_found = 0;
|
||||||
model->dict_keys_total = 0;
|
|
||||||
model->dict_keys_current = 0;
|
|
||||||
model->is_key_attack = false;
|
model->is_key_attack = false;
|
||||||
model->nested_phase = MfClassicNestedPhaseNone;
|
model->nested_phase = MfClassicNestedPhaseNone;
|
||||||
model->prng_type = MfClassicPrngTypeUnknown;
|
model->prng_type = MfClassicPrngTypeUnknown;
|
||||||
model->backdoor = MfClassicBackdoorUnknown;
|
model->backdoor = MfClassicBackdoorUnknown;
|
||||||
model->nested_target_key = 0;
|
model->nested_target_key = 0;
|
||||||
model->msb_count = 0;
|
model->msb_count = 0;
|
||||||
|
|
||||||
|
// Ultralight C fields
|
||||||
|
model->pages_total = 0;
|
||||||
|
model->pages_read = 0;
|
||||||
|
model->key_found = false;
|
||||||
|
|
||||||
|
// Common fields
|
||||||
|
model->dict_keys_total = 0;
|
||||||
|
model->dict_keys_current = 0;
|
||||||
furi_string_reset(model->header);
|
furi_string_reset(model->header);
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
@@ -355,3 +406,31 @@ void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count) {
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
instance->view, DictAttackViewModel * model, { model->msb_count = msb_count; }, true);
|
instance->view, DictAttackViewModel * model, { model->msb_count = msb_count; }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_type(DictAttack* instance, DictAttackType type) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, DictAttackViewModel * model, { model->attack_type = type; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_pages_total(DictAttack* instance, uint8_t pages_total) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, DictAttackViewModel * model, { model->pages_total = pages_total; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_pages_read(DictAttack* instance, uint8_t pages_read) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, DictAttackViewModel * model, { model->pages_read = pages_read; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_key_found(DictAttack* instance, bool key_found) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, DictAttackViewModel * model, { model->key_found = key_found; }, true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DictAttackTypeMfClassic,
|
||||||
|
DictAttackTypeMfUltralightC,
|
||||||
|
} DictAttackType;
|
||||||
|
|
||||||
typedef struct DictAttack DictAttack;
|
typedef struct DictAttack DictAttack;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -56,6 +61,14 @@ void dict_attack_set_nested_target_key(DictAttack* instance, uint16_t target_key
|
|||||||
|
|
||||||
void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count);
|
void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count);
|
||||||
|
|
||||||
|
void dict_attack_set_type(DictAttack* instance, DictAttackType type);
|
||||||
|
|
||||||
|
void dict_attack_set_pages_total(DictAttack* instance, uint8_t pages_total);
|
||||||
|
|
||||||
|
void dict_attack_set_pages_read(DictAttack* instance, uint8_t pages_read);
|
||||||
|
|
||||||
|
void dict_attack_set_key_found(DictAttack* instance, bool key_found);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ extern "C" {
|
|||||||
#define MF_ULTRALIGHT_TEARING_FLAG_NUM (3)
|
#define MF_ULTRALIGHT_TEARING_FLAG_NUM (3)
|
||||||
#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4)
|
#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4)
|
||||||
#define MF_ULTRALIGHT_AUTH_PACK_SIZE (2)
|
#define MF_ULTRALIGHT_AUTH_PACK_SIZE (2)
|
||||||
#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL)
|
|
||||||
|
|
||||||
#define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE (9)
|
#define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE (9)
|
||||||
#define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE (16)
|
#define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE (16)
|
||||||
@@ -48,11 +47,6 @@ extern "C" {
|
|||||||
#define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0)
|
#define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0)
|
||||||
#define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8)
|
#define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8)
|
||||||
#define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1)
|
#define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1)
|
||||||
#define MF_ULTRALIGHT_C_DEFAULT_KEY \
|
|
||||||
(uint8_t[]) { \
|
|
||||||
0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, \
|
|
||||||
0x46 \
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MfUltralightErrorNone,
|
MfUltralightErrorNone,
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller*
|
|||||||
instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version);
|
instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version);
|
||||||
instance->state = MfUltralightPollerStateGetFeatureSet;
|
instance->state = MfUltralightPollerStateGetFeatureSet;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_D(TAG, "Didn't response. Check Ultralight C");
|
FURI_LOG_D(TAG, "Didn't respond. Check Ultralight C");
|
||||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||||
instance->state = MfUltralightPollerStateDetectMfulC;
|
instance->state = MfUltralightPollerStateDetectMfulC;
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,7 @@ static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPo
|
|||||||
instance->data->type = MfUltralightTypeMfulC;
|
instance->data->type = MfUltralightTypeMfulC;
|
||||||
instance->state = MfUltralightPollerStateGetFeatureSet;
|
instance->state = MfUltralightPollerStateGetFeatureSet;
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_D(TAG, "Didn't response. Check NTAG 203");
|
FURI_LOG_D(TAG, "Didn't respond. Check NTAG 203");
|
||||||
instance->state = MfUltralightPollerStateDetectNtag203;
|
instance->state = MfUltralightPollerStateDetectNtag203;
|
||||||
}
|
}
|
||||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||||
@@ -445,8 +445,6 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance
|
|||||||
static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) {
|
static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) {
|
||||||
NfcCommand command = NfcCommandContinue;
|
NfcCommand command = NfcCommandContinue;
|
||||||
FURI_LOG_D(TAG, "MfulC auth");
|
FURI_LOG_D(TAG, "MfulC auth");
|
||||||
|
|
||||||
do {
|
|
||||||
if(mf_ultralight_support_feature(
|
if(mf_ultralight_support_feature(
|
||||||
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
||||||
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
|
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
|
||||||
@@ -454,27 +452,98 @@ static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPol
|
|||||||
command = instance->callback(instance->general_event, instance->context);
|
command = instance->callback(instance->general_event, instance->context);
|
||||||
if(!instance->mfu_event.data->auth_context.skip_auth) {
|
if(!instance->mfu_event.data->auth_context.skip_auth) {
|
||||||
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
|
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
|
||||||
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
|
// Only use the key if it was actually provided
|
||||||
instance->error =
|
if(instance->mfu_event.data->key_request_data.key_provided) {
|
||||||
mf_ultralight_poller_auth_tdes(instance, &instance->auth_context);
|
instance->auth_context.tdes_key = instance->mfu_event.data->key_request_data.key;
|
||||||
|
} else if(instance->mode == MfUltralightPollerModeDictAttack) {
|
||||||
if(instance->error == MfUltralightErrorNone &&
|
// TODO: Can logic be rearranged to request this key before reaching mf_ultralight_poller_handler_auth_ultralight_c in poller?
|
||||||
instance->auth_context.auth_success) {
|
FURI_LOG_D(TAG, "No initial key provided, requesting key from dictionary");
|
||||||
FURI_LOG_D(TAG, "Auth success");
|
// Trigger dictionary key request
|
||||||
} else {
|
instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey;
|
||||||
FURI_LOG_D(TAG, "Auth failed");
|
command = instance->callback(instance->general_event, instance->context);
|
||||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
if(!instance->mfu_event.data->key_request_data.key_provided) {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We assume here that it is card read without explicitly provided key
|
|
||||||
// So we try to auth with default one
|
|
||||||
instance->state = MfUltralightPollerStateTryDefaultMfulCKey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instance->state = MfUltralightPollerStateReadPages;
|
instance->state = MfUltralightPollerStateReadPages;
|
||||||
} while(false);
|
return command;
|
||||||
|
} else {
|
||||||
|
instance->auth_context.tdes_key =
|
||||||
|
instance->mfu_event.data->key_request_data.key;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "No key provided, skipping auth");
|
||||||
|
instance->state = MfUltralightPollerStateReadPages;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
instance->auth_context.auth_success = false;
|
||||||
|
// For debugging
|
||||||
|
FURI_LOG_D(
|
||||||
|
"TAG",
|
||||||
|
"Key data: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||||
|
instance->auth_context.tdes_key.data[0],
|
||||||
|
instance->auth_context.tdes_key.data[1],
|
||||||
|
instance->auth_context.tdes_key.data[2],
|
||||||
|
instance->auth_context.tdes_key.data[3],
|
||||||
|
instance->auth_context.tdes_key.data[4],
|
||||||
|
instance->auth_context.tdes_key.data[5],
|
||||||
|
instance->auth_context.tdes_key.data[6],
|
||||||
|
instance->auth_context.tdes_key.data[7],
|
||||||
|
instance->auth_context.tdes_key.data[8],
|
||||||
|
instance->auth_context.tdes_key.data[9],
|
||||||
|
instance->auth_context.tdes_key.data[10],
|
||||||
|
instance->auth_context.tdes_key.data[11],
|
||||||
|
instance->auth_context.tdes_key.data[12],
|
||||||
|
instance->auth_context.tdes_key.data[13],
|
||||||
|
instance->auth_context.tdes_key.data[14],
|
||||||
|
instance->auth_context.tdes_key.data[15]);
|
||||||
|
do {
|
||||||
|
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
|
||||||
|
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||||
|
furi_hal_random_fill_buf(RndA, sizeof(RndA));
|
||||||
|
instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output);
|
||||||
|
if(instance->error != MfUltralightErrorNone) break;
|
||||||
|
|
||||||
|
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||||
|
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||||
|
instance->error = mf_ultralight_poller_authenticate_end(
|
||||||
|
instance, RndB, output, decoded_shifted_RndA);
|
||||||
|
if(instance->error != MfUltralightErrorNone) break;
|
||||||
|
|
||||||
|
mf_ultralight_3des_shift_data(RndA);
|
||||||
|
instance->auth_context.auth_success =
|
||||||
|
(memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
|
||||||
|
if(instance->auth_context.auth_success) {
|
||||||
|
FURI_LOG_E(TAG, "Auth success");
|
||||||
|
if(instance->mode == MfUltralightPollerModeDictAttack) {
|
||||||
|
memcpy(
|
||||||
|
&instance->data->page[44],
|
||||||
|
instance->auth_context.tdes_key.data,
|
||||||
|
MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);
|
||||||
|
// Continue to read pages after successful authentication
|
||||||
|
instance->state = MfUltralightPollerStateReadPages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {
|
||||||
|
FURI_LOG_E(TAG, "Auth failed");
|
||||||
|
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||||
|
if(instance->mode == MfUltralightPollerModeDictAttack) {
|
||||||
|
// Not needed? We already do a callback earlier?
|
||||||
|
instance->mfu_event.type = MfUltralightPollerEventTypeRequestKey;
|
||||||
|
command = instance->callback(instance->general_event, instance->context);
|
||||||
|
if(!instance->mfu_event.data->key_request_data.key_provided) {
|
||||||
|
instance->state = MfUltralightPollerStateReadPages;
|
||||||
|
} else {
|
||||||
|
instance->auth_context.tdes_key =
|
||||||
|
instance->mfu_event.data->key_request_data.key;
|
||||||
|
instance->state = MfUltralightPollerStateAuthMfulC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Regression review
|
||||||
|
if(instance->mode != MfUltralightPollerModeDictAttack) {
|
||||||
|
instance->state = MfUltralightPollerStateReadPages;
|
||||||
|
}
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,13 +566,17 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
|||||||
instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);
|
instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression review
|
||||||
|
const uint8_t read_cnt = instance->data->type == MfUltralightTypeMfulC ? 1 : 4;
|
||||||
if(instance->error == MfUltralightErrorNone) {
|
if(instance->error == MfUltralightErrorNone) {
|
||||||
if(start_page < instance->pages_total) {
|
for(size_t i = 0; i < read_cnt; i++) {
|
||||||
FURI_LOG_D(TAG, "Read page %d success", start_page);
|
if(start_page + i < instance->pages_total) {
|
||||||
instance->data->page[start_page] = data.page[0];
|
FURI_LOG_D(TAG, "Read page %d success", start_page + i);
|
||||||
|
instance->data->page[start_page + i] = data.page[i];
|
||||||
instance->pages_read++;
|
instance->pages_read++;
|
||||||
instance->data->pages_read = instance->pages_read;
|
instance->data->pages_read = instance->pages_read;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(instance->pages_read == instance->pages_total) {
|
if(instance->pages_read == instance->pages_total) {
|
||||||
instance->state = MfUltralightPollerStateReadCounters;
|
instance->state = MfUltralightPollerStateReadCounters;
|
||||||
@@ -570,40 +643,6 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll
|
|||||||
return NfcCommandContinue;
|
return NfcCommandContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NfcCommand
|
|
||||||
mf_ultralight_poller_handler_try_default_ultralight_c_key(MfUltralightPoller* instance) {
|
|
||||||
do {
|
|
||||||
if(!mf_ultralight_support_feature(
|
|
||||||
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(instance->auth_context.auth_success) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
FURI_LOG_D(TAG, "Trying authentication with default 3DES key");
|
|
||||||
|
|
||||||
memcpy(
|
|
||||||
&instance->auth_context.tdes_key.data,
|
|
||||||
MF_ULTRALIGHT_C_DEFAULT_KEY,
|
|
||||||
MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);
|
|
||||||
|
|
||||||
instance->error = mf_ultralight_poller_auth_tdes(instance, &instance->auth_context);
|
|
||||||
|
|
||||||
if(instance->error == MfUltralightErrorNone && instance->auth_context.auth_success) {
|
|
||||||
FURI_LOG_D(TAG, "Default 3DES key detected");
|
|
||||||
} else {
|
|
||||||
FURI_LOG_D(TAG, "Authentication attempt with default 3DES key failed");
|
|
||||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while(false);
|
|
||||||
|
|
||||||
instance->state = MfUltralightPollerStateReadPages;
|
|
||||||
return NfcCommandContinue;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NfcCommand
|
static NfcCommand
|
||||||
mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) {
|
mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) {
|
||||||
instance->state = MfUltralightPollerStateReadSuccess;
|
instance->state = MfUltralightPollerStateReadSuccess;
|
||||||
@@ -768,8 +807,6 @@ static const MfUltralightPollerReadHandler
|
|||||||
mf_ultralight_poller_handler_read_tearing_flags,
|
mf_ultralight_poller_handler_read_tearing_flags,
|
||||||
[MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,
|
[MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,
|
||||||
[MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,
|
[MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,
|
||||||
[MfUltralightPollerStateTryDefaultMfulCKey] =
|
|
||||||
mf_ultralight_poller_handler_try_default_ultralight_c_key,
|
|
||||||
[MfUltralightPollerStateCheckMfulCAuthStatus] =
|
[MfUltralightPollerStateCheckMfulCAuthStatus] =
|
||||||
mf_ultralight_poller_handler_check_mfuc_auth_status,
|
mf_ultralight_poller_handler_check_mfuc_auth_status,
|
||||||
[MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c,
|
[MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c,
|
||||||
@@ -781,7 +818,6 @@ static const MfUltralightPollerReadHandler
|
|||||||
[MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages,
|
[MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages,
|
||||||
[MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail,
|
[MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail,
|
||||||
[MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success,
|
[MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) {
|
static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ typedef enum {
|
|||||||
MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */
|
MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */
|
||||||
MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */
|
MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */
|
||||||
MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */
|
MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */
|
||||||
|
MfUltralightPollerEventTypeRequestKey, /**< Poller requests key for dict attack. */
|
||||||
} MfUltralightPollerEventType;
|
} MfUltralightPollerEventType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,6 +36,7 @@ typedef enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */
|
MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */
|
||||||
MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */
|
MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */
|
||||||
|
MfUltralightPollerModeDictAttack, /**< Poller will perform dictionary attack against card. */
|
||||||
} MfUltralightPollerMode;
|
} MfUltralightPollerMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,20 +44,29 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MfUltralightAuthPassword password; /**< Password to be used for authentication. */
|
MfUltralightAuthPassword password; /**< Password to be used for authentication. */
|
||||||
MfUltralightC3DesAuthKey tdes_key;
|
MfUltralightC3DesAuthKey tdes_key; /**< 3DES key to be used for authentication. */
|
||||||
MfUltralightAuthPack pack; /**< Pack received on successfull authentication. */
|
MfUltralightAuthPack pack; /**< Pack received on successful authentication. */
|
||||||
bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */
|
bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */
|
||||||
bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */
|
bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */
|
||||||
} MfUltralightPollerAuthContext;
|
} MfUltralightPollerAuthContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MfUltralight poller key request data.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
MfUltralightC3DesAuthKey key; /**< Key to try. */
|
||||||
|
bool key_provided; /**< Set to true if key was provided, false to stop attack. */
|
||||||
|
} MfUltralightPollerKeyRequestData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief MfUltralight poller event data.
|
* @brief MfUltralight poller event data.
|
||||||
*/
|
*/
|
||||||
typedef union {
|
typedef union {
|
||||||
MfUltralightPollerAuthContext auth_context; /**< Authentication context. */
|
MfUltralightPollerAuthContext auth_context; /**< Authentication context. */
|
||||||
MfUltralightError error; /**< Error code indicating reading fail reason. */
|
MfUltralightError error; /**< Error code indicating reading fail reason. */
|
||||||
const MfUltralightData* write_data;
|
const MfUltralightData* write_data; /**< Data to be written to card. */
|
||||||
MfUltralightPollerMode poller_mode;
|
MfUltralightPollerMode poller_mode; /**< Mode to operate in. */
|
||||||
|
MfUltralightPollerKeyRequestData key_request_data; /**< Key request data. */
|
||||||
} MfUltralightPollerEventData;
|
} MfUltralightPollerEventData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,7 +75,7 @@ typedef union {
|
|||||||
* Upon emission of an event, an instance of this struct will be passed to the callback.
|
* Upon emission of an event, an instance of this struct will be passed to the callback.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MfUltralightPollerEventType type; /**< Type of emmitted event. */
|
MfUltralightPollerEventType type; /**< Type of emitted event. */
|
||||||
MfUltralightPollerEventData* data; /**< Pointer to event specific data. */
|
MfUltralightPollerEventData* data; /**< Pointer to event specific data. */
|
||||||
} MfUltralightPollerEvent;
|
} MfUltralightPollerEvent;
|
||||||
|
|
||||||
@@ -81,19 +92,6 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
|
|||||||
MfUltralightPoller* instance,
|
MfUltralightPoller* instance,
|
||||||
MfUltralightPollerAuthContext* data);
|
MfUltralightPollerAuthContext* data);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Perform 3DES authentication with key.
|
|
||||||
*
|
|
||||||
* Must ONLY be used inside the callback function.
|
|
||||||
*
|
|
||||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
|
||||||
* @param[in, out] data pointer to the authentication context.
|
|
||||||
* @return MfUltralightErrorNone on success, an error code on failure.
|
|
||||||
*/
|
|
||||||
MfUltralightError mf_ultralight_poller_auth_tdes(
|
|
||||||
MfUltralightPoller* instance,
|
|
||||||
MfUltralightPollerAuthContext* data);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start authentication procedure.
|
* @brief Start authentication procedure.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "mf_ultralight_poller_i.h"
|
#include "mf_ultralight_poller_i.h"
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
|
||||||
|
|
||||||
#define TAG "MfUltralightPoller"
|
#define TAG "MfUltralightPoller"
|
||||||
|
|
||||||
@@ -63,38 +62,6 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
MfUltralightError mf_ultralight_poller_auth_tdes(
|
|
||||||
MfUltralightPoller* instance,
|
|
||||||
MfUltralightPollerAuthContext* data) {
|
|
||||||
furi_check(instance);
|
|
||||||
furi_check(data);
|
|
||||||
|
|
||||||
MfUltralightError ret = MfUltralightErrorNone;
|
|
||||||
|
|
||||||
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
|
|
||||||
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
|
||||||
furi_hal_random_fill_buf(RndA, sizeof(RndA));
|
|
||||||
|
|
||||||
ret = mf_ultralight_poller_authenticate_start(instance, RndA, output);
|
|
||||||
|
|
||||||
if(ret != MfUltralightErrorNone) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
|
||||||
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
|
||||||
ret = mf_ultralight_poller_authenticate_end(instance, RndB, output, decoded_shifted_RndA);
|
|
||||||
|
|
||||||
if(ret != MfUltralightErrorNone) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
mf_ultralight_3des_shift_data(RndA);
|
|
||||||
data->auth_success = (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static MfUltralightError mf_ultralight_poller_send_authenticate_cmd(
|
static MfUltralightError mf_ultralight_poller_send_authenticate_cmd(
|
||||||
MfUltralightPoller* instance,
|
MfUltralightPoller* instance,
|
||||||
const uint8_t* cmd,
|
const uint8_t* cmd,
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ extern "C" {
|
|||||||
#define MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC (60000)
|
#define MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC (60000)
|
||||||
#define MF_ULTRALIGHT_MAX_BUFF_SIZE (64)
|
#define MF_ULTRALIGHT_MAX_BUFF_SIZE (64)
|
||||||
|
|
||||||
|
#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL)
|
||||||
|
|
||||||
#define MF_ULTRALIGHT_IS_NTAG_I2C(type) \
|
#define MF_ULTRALIGHT_IS_NTAG_I2C(type) \
|
||||||
(((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \
|
(((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \
|
||||||
((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K))
|
((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K))
|
||||||
@@ -59,7 +61,6 @@ typedef enum {
|
|||||||
MfUltralightPollerStateAuthMfulC,
|
MfUltralightPollerStateAuthMfulC,
|
||||||
MfUltralightPollerStateReadPages,
|
MfUltralightPollerStateReadPages,
|
||||||
MfUltralightPollerStateTryDefaultPass,
|
MfUltralightPollerStateTryDefaultPass,
|
||||||
MfUltralightPollerStateTryDefaultMfulCKey,
|
|
||||||
MfUltralightPollerStateCheckMfulCAuthStatus,
|
MfUltralightPollerStateCheckMfulCAuthStatus,
|
||||||
MfUltralightPollerStateReadFailed,
|
MfUltralightPollerStateReadFailed,
|
||||||
MfUltralightPollerStateReadSuccess,
|
MfUltralightPollerStateReadSuccess,
|
||||||
|
|||||||
@@ -134,22 +134,21 @@ static void keys_dict_int_to_str(KeysDict* instance, const uint8_t* key_int, Fur
|
|||||||
furi_string_cat_printf(key_str, "%02X", key_int[i]);
|
furi_string_cat_printf(key_str, "%02X", key_int[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint64_t* key_int) {
|
static void keys_dict_str_to_int(KeysDict* instance, FuriString* key_str, uint8_t* key_out) {
|
||||||
furi_assert(instance);
|
furi_assert(instance);
|
||||||
furi_assert(key_str);
|
furi_assert(key_str);
|
||||||
furi_assert(key_int);
|
furi_assert(key_out);
|
||||||
|
|
||||||
uint8_t key_byte_tmp;
|
uint8_t key_byte_tmp;
|
||||||
char h, l;
|
char h, l;
|
||||||
|
|
||||||
*key_int = 0ULL;
|
// Process two hex characters at a time to create each byte
|
||||||
|
|
||||||
for(size_t i = 0; i < instance->key_size_symbols - 1; i += 2) {
|
for(size_t i = 0; i < instance->key_size_symbols - 1; i += 2) {
|
||||||
h = furi_string_get_char(key_str, i);
|
h = furi_string_get_char(key_str, i);
|
||||||
l = furi_string_get_char(key_str, i + 1);
|
l = furi_string_get_char(key_str, i + 1);
|
||||||
|
|
||||||
args_char_to_hex(h, l, &key_byte_tmp);
|
args_char_to_hex(h, l, &key_byte_tmp);
|
||||||
*key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2));
|
key_out[i / 2] = key_byte_tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,15 +192,7 @@ bool keys_dict_get_next_key(KeysDict* instance, uint8_t* key, size_t key_size) {
|
|||||||
bool key_read = keys_dict_get_next_key_str(instance, temp_key);
|
bool key_read = keys_dict_get_next_key_str(instance, temp_key);
|
||||||
|
|
||||||
if(key_read) {
|
if(key_read) {
|
||||||
size_t tmp_len = key_size;
|
keys_dict_str_to_int(instance, temp_key, key);
|
||||||
uint64_t key_int = 0;
|
|
||||||
|
|
||||||
keys_dict_str_to_int(instance, temp_key, &key_int);
|
|
||||||
|
|
||||||
while(tmp_len--) {
|
|
||||||
key[tmp_len] = (uint8_t)key_int;
|
|
||||||
key_int >>= 8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
furi_string_free(temp_key);
|
furi_string_free(temp_key);
|
||||||
|
|||||||
@@ -2765,7 +2765,6 @@ Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltral
|
|||||||
Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t"
|
Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t"
|
||||||
Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t"
|
Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t"
|
||||||
Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
|
Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
|
||||||
Function,+,mf_ultralight_poller_auth_tdes,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
|
|
||||||
Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*"
|
Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*"
|
||||||
Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*"
|
Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*"
|
||||||
Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*"
|
Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user