mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 12:51:22 +04:00
NFC: Ultralight C App Key Management, Dictionary Attack (#4271)
* Upstream Ultralight C dictionary attack (squashed) * linter: formatting * unit_tests: nfc: split nfc data to named var * Fix mf_ultralight_poller_sync_read_card * linter: suppressed warnings on TODOs --------- Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: hedger <hedger@nanode.su>
This commit is contained in:
@@ -268,9 +268,9 @@ static void mf_ultralight_reader_test(const char* path) {
|
|||||||
nfc_listener_stop(mfu_listener);
|
nfc_listener_stop(mfu_listener);
|
||||||
nfc_listener_free(mfu_listener);
|
nfc_listener_free(mfu_listener);
|
||||||
|
|
||||||
mu_assert(
|
MfUltralightData* mfu_other_data =
|
||||||
mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)),
|
(MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);
|
||||||
"Data not matches");
|
mu_assert(mf_ultralight_is_equal(mfu_data, mfu_other_data), "Data mismatch");
|
||||||
|
|
||||||
mf_ultralight_free(mfu_data);
|
mf_ultralight_free(mfu_data);
|
||||||
nfc_device_free(nfc_device);
|
nfc_device_free(nfc_device);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ enum {
|
|||||||
SubmenuIndexUnlockByReader,
|
SubmenuIndexUnlockByReader,
|
||||||
SubmenuIndexUnlockByPassword,
|
SubmenuIndexUnlockByPassword,
|
||||||
SubmenuIndexWrite,
|
SubmenuIndexWrite,
|
||||||
|
SubmenuIndexDictAttack
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -150,7 +151,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;
|
||||||
@@ -166,15 +175,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);
|
||||||
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
|
||||||
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;
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
|
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) {
|
||||||
@@ -190,6 +215,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);
|
||||||
|
}
|
||||||
} else if(
|
} else if(
|
||||||
data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||
|
data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||
|
||||||
data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 ||
|
data->type == MfUltralightTypeNTAG216 || data->type == MfUltralightTypeUL11 ||
|
||||||
@@ -258,6 +291,12 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
|
|||||||
} else if(event.event == SubmenuIndexCommonEdit) {
|
} else if(event.event == SubmenuIndexCommonEdit) {
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||||
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;
|
||||||
|
|||||||
@@ -47,6 +47,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>
|
||||||
@@ -64,7 +65,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"
|
||||||
@@ -80,6 +81,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"))
|
||||||
|
|
||||||
@@ -107,6 +112,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;
|
||||||
@@ -145,6 +158,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
|
||||||
@@ -25,6 +25,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_write, MfUltralightWrite)
|
ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite)
|
||||||
ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess)
|
ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess)
|
||||||
ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail)
|
ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail)
|
||||||
@@ -57,6 +58,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) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
|
|||||||
instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
|
instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
|
||||||
} else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {
|
} else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
|
||||||
dolphin_deed(DolphinDeedNfcMfcAdd);
|
dolphin_deed(DolphinDeedNfcKeyAdd);
|
||||||
} else {
|
} else {
|
||||||
scene_manager_previous_scene(instance->scene_manager);
|
scene_manager_previous_scene(instance->scene_manager);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
#include "../nfc_app_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
#define TAG "NfcMfUlCDictAttack"
|
||||||
|
|
||||||
|
// TODO: Support card_detected properly -nofl
|
||||||
|
|
||||||
|
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(DolphinDeedNfcKeyAdd);
|
||||||
|
} 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,8 +27,150 @@ 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_mf_classic(Canvas* canvas, DictAttackViewModel* m) {
|
||||||
|
char draw_str[32] = {};
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
switch(m->nested_phase) {
|
||||||
|
case MfClassicNestedPhaseAnalyzePRNG:
|
||||||
|
furi_string_set(m->header, "PRNG Analysis");
|
||||||
|
break;
|
||||||
|
case MfClassicNestedPhaseDictAttack:
|
||||||
|
case MfClassicNestedPhaseDictAttackVerify:
|
||||||
|
case MfClassicNestedPhaseDictAttackResume:
|
||||||
|
furi_string_set(m->header, "Nested Dictionary");
|
||||||
|
break;
|
||||||
|
case MfClassicNestedPhaseCalibrate:
|
||||||
|
case MfClassicNestedPhaseRecalibrate:
|
||||||
|
furi_string_set(m->header, "Calibration");
|
||||||
|
break;
|
||||||
|
case MfClassicNestedPhaseCollectNtEnc:
|
||||||
|
furi_string_set(m->header, "Nonce Collection");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m->prng_type == MfClassicPrngTypeHard) {
|
||||||
|
furi_string_cat(m->header, " (Hard)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m->backdoor != MfClassicBackdoorNone && m->backdoor != MfClassicBackdoorUnknown) {
|
||||||
|
if(m->nested_phase != MfClassicNestedPhaseNone) {
|
||||||
|
furi_string_cat(m->header, " (Backdoor)");
|
||||||
|
} else {
|
||||||
|
furi_string_set(m->header, "Backdoor Read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
||||||
|
if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
||||||
|
uint8_t nonce_sector =
|
||||||
|
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Collecting from sector: %d", nonce_sector);
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||||
|
} else if(m->is_key_attack) {
|
||||||
|
snprintf(
|
||||||
|
draw_str,
|
||||||
|
sizeof(draw_str),
|
||||||
|
"Reuse key check for sector: %d",
|
||||||
|
m->key_attack_current_sector);
|
||||||
|
} else {
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector);
|
||||||
|
}
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||||
|
float dict_progress = 0;
|
||||||
|
if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG ||
|
||||||
|
m->nested_phase == MfClassicNestedPhaseDictAttack ||
|
||||||
|
m->nested_phase == MfClassicNestedPhaseDictAttackVerify ||
|
||||||
|
m->nested_phase == MfClassicNestedPhaseDictAttackResume) {
|
||||||
|
// Phase: Nested dictionary attack
|
||||||
|
uint8_t target_sector =
|
||||||
|
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16);
|
||||||
|
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
|
||||||
|
} else if(
|
||||||
|
m->nested_phase == MfClassicNestedPhaseCalibrate ||
|
||||||
|
m->nested_phase == MfClassicNestedPhaseRecalibrate ||
|
||||||
|
m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
||||||
|
// Phase: Nonce collection
|
||||||
|
if(m->prng_type == MfClassicPrngTypeWeak) {
|
||||||
|
uint8_t target_sector = m->nested_target_key / 4;
|
||||||
|
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
|
||||||
|
} else {
|
||||||
|
uint16_t max_msb = UINT8_MAX + 1;
|
||||||
|
dict_progress = (float)(m->msb_count) / (float)(max_msb);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", m->msb_count, max_msb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dict_progress = m->dict_keys_total == 0 ?
|
||||||
|
0 :
|
||||||
|
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
||||||
|
if(m->dict_keys_current == 0) {
|
||||||
|
// Cause when people see 0 they think it's broken
|
||||||
|
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),
|
||||||
|
"Keys found: %d/%d",
|
||||||
|
m->keys_found,
|
||||||
|
m->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR);
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
||||||
|
snprintf(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
||||||
DictAttackViewModel* m = model;
|
DictAttackViewModel* m = model;
|
||||||
if(!m->card_detected) {
|
if(!m->card_detected) {
|
||||||
@@ -37,113 +180,11 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||||||
elements_multiline_text_aligned(
|
elements_multiline_text_aligned(
|
||||||
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
||||||
} else {
|
} else {
|
||||||
char draw_str[32] = {};
|
if(m->attack_type == DictAttackTypeMfClassic) {
|
||||||
canvas_set_font(canvas, FontSecondary);
|
dict_attack_draw_mf_classic(canvas, m);
|
||||||
|
} else if(m->attack_type == DictAttackTypeMfUltralightC) {
|
||||||
switch(m->nested_phase) {
|
dict_attack_draw_mf_ultralight_c(canvas, m);
|
||||||
case MfClassicNestedPhaseAnalyzePRNG:
|
|
||||||
furi_string_set(m->header, "PRNG Analysis");
|
|
||||||
break;
|
|
||||||
case MfClassicNestedPhaseDictAttack:
|
|
||||||
case MfClassicNestedPhaseDictAttackVerify:
|
|
||||||
case MfClassicNestedPhaseDictAttackResume:
|
|
||||||
furi_string_set(m->header, "Nested Dictionary");
|
|
||||||
break;
|
|
||||||
case MfClassicNestedPhaseCalibrate:
|
|
||||||
case MfClassicNestedPhaseRecalibrate:
|
|
||||||
furi_string_set(m->header, "Calibration");
|
|
||||||
break;
|
|
||||||
case MfClassicNestedPhaseCollectNtEnc:
|
|
||||||
furi_string_set(m->header, "Nonce Collection");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m->prng_type == MfClassicPrngTypeHard) {
|
|
||||||
furi_string_cat(m->header, " (Hard)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m->backdoor != MfClassicBackdoorNone && m->backdoor != MfClassicBackdoorUnknown) {
|
|
||||||
if(m->nested_phase != MfClassicNestedPhaseNone) {
|
|
||||||
furi_string_cat(m->header, " (Backdoor)");
|
|
||||||
} else {
|
|
||||||
furi_string_set(m->header, "Backdoor Read");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas_draw_str_aligned(
|
|
||||||
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
|
||||||
if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
|
||||||
uint8_t nonce_sector =
|
|
||||||
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);
|
|
||||||
snprintf(draw_str, sizeof(draw_str), "Collecting from sector: %d", nonce_sector);
|
|
||||||
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
|
||||||
} else if(m->is_key_attack) {
|
|
||||||
snprintf(
|
|
||||||
draw_str,
|
|
||||||
sizeof(draw_str),
|
|
||||||
"Reuse key check for sector: %d",
|
|
||||||
m->key_attack_current_sector);
|
|
||||||
} else {
|
|
||||||
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector);
|
|
||||||
}
|
|
||||||
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
|
||||||
float dict_progress = 0;
|
|
||||||
if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG ||
|
|
||||||
m->nested_phase == MfClassicNestedPhaseDictAttack ||
|
|
||||||
m->nested_phase == MfClassicNestedPhaseDictAttackVerify ||
|
|
||||||
m->nested_phase == MfClassicNestedPhaseDictAttackResume) {
|
|
||||||
// Phase: Nested dictionary attack
|
|
||||||
uint8_t target_sector =
|
|
||||||
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16);
|
|
||||||
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
|
|
||||||
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
|
|
||||||
} else if(
|
|
||||||
m->nested_phase == MfClassicNestedPhaseCalibrate ||
|
|
||||||
m->nested_phase == MfClassicNestedPhaseRecalibrate ||
|
|
||||||
m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
|
||||||
// Phase: Nonce collection
|
|
||||||
if(m->prng_type == MfClassicPrngTypeWeak) {
|
|
||||||
uint8_t target_sector = m->nested_target_key / 4;
|
|
||||||
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
|
|
||||||
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
|
|
||||||
} else {
|
|
||||||
uint16_t max_msb = UINT8_MAX + 1;
|
|
||||||
dict_progress = (float)(m->msb_count) / (float)(max_msb);
|
|
||||||
snprintf(draw_str, sizeof(draw_str), "%d/%d", m->msb_count, max_msb);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dict_progress = m->dict_keys_total == 0 ?
|
|
||||||
0 :
|
|
||||||
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
|
||||||
if(m->dict_keys_current == 0) {
|
|
||||||
// Cause when people see 0 they think it's broken
|
|
||||||
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),
|
|
||||||
"Keys found: %d/%d",
|
|
||||||
m->keys_found,
|
|
||||||
m->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR);
|
|
||||||
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
|
||||||
snprintf(
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
|
|||||||
{3, DolphinAppNfc}, // DolphinDeedNfcSave
|
{3, DolphinAppNfc}, // DolphinDeedNfcSave
|
||||||
{1, DolphinAppNfc}, // DolphinDeedNfcDetectReader
|
{1, DolphinAppNfc}, // DolphinDeedNfcDetectReader
|
||||||
{2, DolphinAppNfc}, // DolphinDeedNfcEmulate
|
{2, DolphinAppNfc}, // DolphinDeedNfcEmulate
|
||||||
{2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd
|
{2, DolphinAppNfc}, // DolphinDeedNfcKeyAdd
|
||||||
{1, DolphinAppNfc}, // DolphinDeedNfcAddSave
|
{1, DolphinAppNfc}, // DolphinDeedNfcAddSave
|
||||||
{1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate
|
{1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ typedef enum {
|
|||||||
DolphinDeedNfcSave,
|
DolphinDeedNfcSave,
|
||||||
DolphinDeedNfcDetectReader,
|
DolphinDeedNfcDetectReader,
|
||||||
DolphinDeedNfcEmulate,
|
DolphinDeedNfcEmulate,
|
||||||
DolphinDeedNfcMfcAdd,
|
DolphinDeedNfcKeyAdd,
|
||||||
DolphinDeedNfcAddSave,
|
DolphinDeedNfcAddSave,
|
||||||
DolphinDeedNfcAddEmulate,
|
DolphinDeedNfcAddEmulate,
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Version differences:
|
|||||||
ATQA: 00 44
|
ATQA: 00 44
|
||||||
SAK: 00
|
SAK: 00
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
This file format is used to store the UID, SAK and ATQA of an ISO14443-3A device.
|
This file format is used to store the UID, SAK and ATQA of an ISO14443-3A device.
|
||||||
UID must be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long.
|
UID must be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long.
|
||||||
@@ -56,7 +56,7 @@ None, there are no versions yet.
|
|||||||
Application data: 00 12 34 FF
|
Application data: 00 12 34 FF
|
||||||
Protocol info: 11 81 E1
|
Protocol info: 11 81 E1
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
This file format is used to store the UID, Application data and Protocol info of a ISO14443-3B device.
|
This file format is used to store the UID, Application data and Protocol info of a ISO14443-3B device.
|
||||||
UID must be 4 bytes long. Application data is 4 bytes long. Protocol info is 3 bytes long.
|
UID must be 4 bytes long. Application data is 4 bytes long. Protocol info is 3 bytes long.
|
||||||
@@ -80,7 +80,7 @@ None, there are no versions yet.
|
|||||||
# ISO14443-4A specific data
|
# ISO14443-4A specific data
|
||||||
ATS: 06 75 77 81 02 80
|
ATS: 06 75 77 81 02 80
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
This file format is used to store the UID, SAK and ATQA of a ISO14443-4A device. It also stores the Answer to Select (ATS) data of the card.
|
This file format is used to store the UID, SAK and ATQA of a ISO14443-4A device. It also stores the Answer to Select (ATS) data of the card.
|
||||||
ATS must be no less than 5 bytes long.
|
ATS must be no less than 5 bytes long.
|
||||||
@@ -303,6 +303,26 @@ None, there are no versions yet.
|
|||||||
|
|
||||||
This file contains a list of Mifare Classic keys. Each key is represented as a hex string. Lines starting with '#' are ignored as comments. Blank lines are ignored as well.
|
This file contains a list of Mifare Classic keys. Each key is represented as a hex string. Lines starting with '#' are ignored as comments. Blank lines are ignored as well.
|
||||||
|
|
||||||
|
## Mifare Ultralight C Dictionary
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
# Hexadecimal-Reversed Sample Key
|
||||||
|
12E4143455F495649454D4B414542524
|
||||||
|
# Byte-Reversed Sample Key (!NACUOYFIEMKAERB)
|
||||||
|
214E4143554F594649454D4B41455242
|
||||||
|
# Sample Key (BREAKMEIFYOUCAN!)
|
||||||
|
425245414B4D454946594F5543414E21
|
||||||
|
# Semnox Key (IEMKAERB!NACUOY )
|
||||||
|
49454D4B41455242214E4143554F5900
|
||||||
|
# Modified Semnox Key (IEMKAERB!NACUOYF)
|
||||||
|
49454D4B41455242214E4143554F5946
|
||||||
|
...
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
This file contains a list of Mifare Ultralight C keys. Each key is represented as a hex string. Lines starting with '#' are ignored as comments. Blank lines are ignored as well.
|
||||||
|
|
||||||
## EMV resources
|
## EMV resources
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -452,7 +452,48 @@ 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
|
||||||
|
if(instance->mfu_event.data->key_request_data.key_provided) {
|
||||||
|
instance->auth_context.tdes_key = instance->mfu_event.data->key_request_data.key;
|
||||||
|
} else if(instance->mode == MfUltralightPollerModeDictAttack) {
|
||||||
|
// TODO: -nofl Can logic be rearranged to request this key
|
||||||
|
// before reaching mf_ultralight_poller_handler_auth_ultralight_c in poller?
|
||||||
|
FURI_LOG_D(TAG, "No initial key provided, requesting key from dictionary");
|
||||||
|
// Trigger dictionary key request
|
||||||
|
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;
|
||||||
|
return command;
|
||||||
|
} else {
|
||||||
|
instance->auth_context.tdes_key =
|
||||||
|
instance->mfu_event.data->key_request_data.key;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: use key from auth context (for sync poller compatibility)
|
||||||
|
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
|
||||||
|
}
|
||||||
|
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 {
|
do {
|
||||||
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
|
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
|
||||||
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||||
@@ -469,20 +510,40 @@ static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPol
|
|||||||
mf_ultralight_3des_shift_data(RndA);
|
mf_ultralight_3des_shift_data(RndA);
|
||||||
instance->auth_context.auth_success =
|
instance->auth_context.auth_success =
|
||||||
(memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
|
(memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
|
||||||
|
|
||||||
if(instance->auth_context.auth_success) {
|
if(instance->auth_context.auth_success) {
|
||||||
FURI_LOG_D(TAG, "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);
|
} while(false);
|
||||||
|
|
||||||
if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {
|
if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {
|
||||||
FURI_LOG_D(TAG, "Auth failed");
|
FURI_LOG_E(TAG, "Auth failed");
|
||||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instance->state = MfUltralightPollerStateReadPages;
|
// Regression review
|
||||||
|
if(instance->mode != MfUltralightPollerModeDictAttack) {
|
||||||
|
instance->state = MfUltralightPollerStateReadPages;
|
||||||
|
}
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,12 +566,16 @@ 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->pages_read++;
|
instance->data->page[start_page + i] = data.page[i];
|
||||||
instance->data->pages_read = instance->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) {
|
||||||
@@ -753,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;
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
|
|||||||
uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||||
mf_ultralight_3des_decrypt(
|
mf_ultralight_3des_decrypt(
|
||||||
&instance->des_context,
|
&instance->des_context,
|
||||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
instance->auth_context.tdes_key.data,
|
||||||
iv,
|
iv,
|
||||||
encRndB,
|
encRndB,
|
||||||
sizeof(encRndB),
|
sizeof(encRndB),
|
||||||
@@ -145,7 +145,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
|
|||||||
|
|
||||||
mf_ultralight_3des_encrypt(
|
mf_ultralight_3des_encrypt(
|
||||||
&instance->des_context,
|
&instance->des_context,
|
||||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
instance->auth_context.tdes_key.data,
|
||||||
encRndB,
|
encRndB,
|
||||||
output,
|
output,
|
||||||
MF_ULTRALIGHT_C_AUTH_DATA_SIZE,
|
MF_ULTRALIGHT_C_AUTH_DATA_SIZE,
|
||||||
@@ -179,7 +179,7 @@ MfUltralightError mf_ultralight_poller_authenticate_end(
|
|||||||
|
|
||||||
mf_ultralight_3des_decrypt(
|
mf_ultralight_3des_decrypt(
|
||||||
&instance->des_context,
|
&instance->des_context,
|
||||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
instance->auth_context.tdes_key.data,
|
||||||
RndB,
|
RndB,
|
||||||
bit_buffer_get_data(instance->rx_buffer) + 1,
|
bit_buffer_get_data(instance->rx_buffer) + 1,
|
||||||
MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE,
|
MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user