1
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:
MX
2025-09-21 16:45:31 +03:00
parent 3efa40b079
commit 2c2228a4b9
25 changed files with 1091 additions and 263 deletions

View File

@@ -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));
} }

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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, "");
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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.
* *

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -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*"
1 entry status name type params
2765 Function + mf_ultralight_is_page_pwd_or_pack _Bool MfUltralightType, uint16_t
2766 Function + mf_ultralight_load _Bool MfUltralightData*, FlipperFormat*, uint32_t
2767 Function + mf_ultralight_poller_auth_pwd MfUltralightError MfUltralightPoller*, MfUltralightPollerAuthContext*
Function + mf_ultralight_poller_auth_tdes MfUltralightError MfUltralightPoller*, MfUltralightPollerAuthContext*
2768 Function + mf_ultralight_poller_authenticate_end MfUltralightError MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*
2769 Function + mf_ultralight_poller_authenticate_start MfUltralightError MfUltralightPoller*, const uint8_t*, uint8_t*
2770 Function + mf_ultralight_poller_read_counter MfUltralightError MfUltralightPoller*, uint8_t, MfUltralightCounter*