mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge remote-tracking branch 'xero-dev/dev' into dev
This commit is contained in:
@@ -32,12 +32,12 @@ Coming from other custom firmware, you'll get:
|
||||
- *MIFARE Classic Accelerated dictionary attack*: dictionary attacks reduced to several seconds - checks ~3500 keys per second
|
||||
- *MIFARE Classic Nested attack support*: collects nested nonces to be cracked by MFKey
|
||||
- *MIFARE Classic Static encrypted backdoor support*: collects static encrypted nonces to be cracked by MFKey using NXP/Fudan backdoor
|
||||
- **MFKey 3.0**: Mfkey32, Static Nested, and Static Encrypted attacks all on your Flipper Zero
|
||||
- **MIFARE Ultralight C Dictionary attack** (coming soon!)
|
||||
- **MIFARE Ultralight C Emulation** (coming soon!)
|
||||
- **MFKey 3.1**: Mfkey32, Static Nested, and Static Encrypted attacks all on your Flipper Zero
|
||||
- **MIFARE Ultralight C Dictionary attack**: Ultralight C key management and bruteforce attacks, feature parity with MIFARE Classic
|
||||
- **MIFARE Ultralight C Key recovery** (NXP disclosure in progress!)
|
||||
- **NFC app memory improvements** (coming soon!)
|
||||
- **Minimal theme** (coming soon!)
|
||||
- *Future*: MIFARE DESFire Dictionary attack, Tesla credential enrollment
|
||||
- *Future*: Tesla credential enrollment, MIFARE Classic EV1 support (signature sector Nested attacks), fully locked Ultralight C support, faster static encrypted attacks
|
||||
|
||||
# Contributing
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ enum {
|
||||
SubmenuIndexUnlockByReader,
|
||||
SubmenuIndexUnlockByPassword,
|
||||
SubmenuIndexWrite,
|
||||
SubmenuIndexDictAttack
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -150,7 +151,15 @@ static NfcCommand
|
||||
}
|
||||
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.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) {
|
||||
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) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventCardDetected) {
|
||||
nfc_unlock_helper_card_detected_handler(instance);
|
||||
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
if(event.event == NfcCustomEventPollerSuccess) {
|
||||
notification_message(instance->notifications, &sequence_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
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) {
|
||||
@@ -190,6 +215,14 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc
|
||||
SubmenuIndexUnlock,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
instance);
|
||||
if(data->type == MfUltralightTypeMfulC) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock with Dictionary",
|
||||
SubmenuIndexDictAttack,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
instance);
|
||||
}
|
||||
} else if(
|
||||
data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 ||
|
||||
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) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||
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;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <lib/nfc/nfc.h>
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.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 <nfc/nfc_poller.h>
|
||||
@@ -64,7 +65,7 @@
|
||||
|
||||
#define NFC_NAME_SIZE 22
|
||||
#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_APP_FOLDER EXT_PATH("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_NESTED_PATH \
|
||||
(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"))
|
||||
|
||||
@@ -107,6 +112,14 @@ typedef struct {
|
||||
bool enhanced_dict;
|
||||
} 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 {
|
||||
DialogsApp* dialogs;
|
||||
Storage* storage;
|
||||
@@ -145,6 +158,7 @@ struct NfcApp {
|
||||
MfUltralightAuth* mf_ul_auth;
|
||||
SlixUnlock* slix_unlock;
|
||||
NfcMfClassicDictAttackContext nfc_dict_context;
|
||||
NfcMfUltralightCDictContext mf_ultralight_c_dict_context;
|
||||
Mfkey32Logger* mfkey32_logger;
|
||||
MfUserDict* mf_user_dict;
|
||||
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, 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_success, MfUltralightWriteSuccess)
|
||||
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_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_sak, SetSak)
|
||||
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)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
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 {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneFileSelect);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexReadCardType,
|
||||
SubmenuIndexMfClassicKeys,
|
||||
SubmenuIndexMfUltralightCKeys,
|
||||
SubmenuIndexMfUltralightUnlock,
|
||||
SubmenuIndexSlixUnlock,
|
||||
};
|
||||
@@ -29,6 +30,12 @@ void nfc_scene_extra_actions_on_enter(void* context) {
|
||||
SubmenuIndexMfClassicKeys,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"MIFARE Ultralight C Keys",
|
||||
SubmenuIndexMfUltralightCKeys,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock NTAG/Ultralight",
|
||||
@@ -54,6 +61,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.event == SubmenuIndexMfClassicKeys) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexMfUltralightCKeys) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightCKeys);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexMfUltralightUnlock) {
|
||||
mf_ultralight_auth_reset(instance->mf_ul_auth);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#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) {
|
||||
NfcApp* instance = context;
|
||||
if(type == InputTypeShort) {
|
||||
|
||||
@@ -39,7 +39,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
|
||||
instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
|
||||
} else if(keys_dict_add_key(dict, key.data, sizeof(MfClassicKey))) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess);
|
||||
dolphin_deed(DolphinDeedNfcMfcAdd);
|
||||
dolphin_deed(DolphinDeedNfcKeyAdd);
|
||||
} else {
|
||||
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
|
||||
|
||||
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)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
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)) {
|
||||
NfcSceneSaveConfirmState scene_state =
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSaveConfirm);
|
||||
|
||||
@@ -13,12 +13,13 @@ struct DictAttack {
|
||||
typedef struct {
|
||||
FuriString* header;
|
||||
bool card_detected;
|
||||
DictAttackType attack_type;
|
||||
|
||||
// MIFARE Classic specific
|
||||
uint8_t sectors_total;
|
||||
uint8_t sectors_read;
|
||||
uint8_t current_sector;
|
||||
uint8_t keys_found;
|
||||
size_t dict_keys_total;
|
||||
size_t dict_keys_current;
|
||||
bool is_key_attack;
|
||||
uint8_t key_attack_current_sector;
|
||||
MfClassicNestedPhase nested_phase;
|
||||
@@ -26,8 +27,161 @@ typedef struct {
|
||||
MfClassicBackdoor backdoor;
|
||||
uint16_t nested_target_key;
|
||||
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;
|
||||
|
||||
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) {
|
||||
DictAttackViewModel* m = model;
|
||||
if(!m->card_detected) {
|
||||
@@ -37,113 +191,11 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
||||
} else {
|
||||
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->attack_type == DictAttackTypeMfClassic) {
|
||||
dict_attack_draw_mf_classic(canvas, m);
|
||||
} else if(m->attack_type == DictAttackTypeMfUltralightC) {
|
||||
dict_attack_draw_mf_ultralight_c(canvas, m);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
@@ -195,18 +247,28 @@ void dict_attack_reset(DictAttack* instance) {
|
||||
instance->view,
|
||||
DictAttackViewModel * model,
|
||||
{
|
||||
model->attack_type = DictAttackTypeMfClassic;
|
||||
|
||||
// MIFARE Classic fields
|
||||
model->sectors_total = 0;
|
||||
model->sectors_read = 0;
|
||||
model->current_sector = 0;
|
||||
model->keys_found = 0;
|
||||
model->dict_keys_total = 0;
|
||||
model->dict_keys_current = 0;
|
||||
model->is_key_attack = false;
|
||||
model->nested_phase = MfClassicNestedPhaseNone;
|
||||
model->prng_type = MfClassicPrngTypeUnknown;
|
||||
model->backdoor = MfClassicBackdoorUnknown;
|
||||
model->nested_target_key = 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);
|
||||
},
|
||||
false);
|
||||
@@ -355,3 +417,45 @@ void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count) {
|
||||
with_view_model(
|
||||
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" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
DictAttackTypeMfClassic,
|
||||
DictAttackTypeMfUltralightC,
|
||||
} DictAttackType;
|
||||
|
||||
typedef struct DictAttack DictAttack;
|
||||
|
||||
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_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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,7 @@ static const DolphinDeedWeight dolphin_deed_weights[] = {
|
||||
{3, DolphinAppNfc}, // DolphinDeedNfcSave
|
||||
{1, DolphinAppNfc}, // DolphinDeedNfcDetectReader
|
||||
{2, DolphinAppNfc}, // DolphinDeedNfcEmulate
|
||||
{2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd
|
||||
{2, DolphinAppNfc}, // DolphinDeedNfcKeyAdd
|
||||
{1, DolphinAppNfc}, // DolphinDeedNfcAddSave
|
||||
{1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ typedef enum {
|
||||
DolphinDeedNfcSave,
|
||||
DolphinDeedNfcDetectReader,
|
||||
DolphinDeedNfcEmulate,
|
||||
DolphinDeedNfcMfcAdd,
|
||||
DolphinDeedNfcKeyAdd,
|
||||
DolphinDeedNfcAddSave,
|
||||
DolphinDeedNfcAddEmulate,
|
||||
|
||||
|
||||
@@ -861,6 +861,10 @@ void byte_input_set_result_callback(
|
||||
model->callback_context = callback_context;
|
||||
model->bytes = bytes;
|
||||
model->bytes_count = bytes_count;
|
||||
// Zero out the bytes
|
||||
for(uint8_t i = 0; i < bytes_count; i++) {
|
||||
model->bytes[i] = 0;
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ After collecting nonces using the Extract MF Keys option, press the Start button
|
||||
|
||||
## Credits
|
||||
|
||||
Developers: noproto, AG, Flipper Devices, WillyJL
|
||||
Developers: noproto, AG, Flipper Devices, WillyJL, CavallUwU, Ivisayan
|
||||
Thanks: AloneLiberty, Foxushka, bettse, Equip
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 3.1
|
||||
- Key recovery is 20% faster, new write buffering of Static Encrypted Nested key candidates performs recovery 70x faster
|
||||
## 3.0
|
||||
- Added Static Encrypted Nested key recovery, added NFC app support, dropped FlipperNested support
|
||||
## 2.7
|
||||
|
||||
@@ -15,7 +15,7 @@ App(
|
||||
fap_icon_assets="images",
|
||||
fap_weburl="https://github.com/noproto/FlipperMfkey",
|
||||
fap_description="MIFARE Classic key recovery tool",
|
||||
fap_version="3.0",
|
||||
fap_version="3.1",
|
||||
)
|
||||
|
||||
App(
|
||||
|
||||
@@ -6,251 +6,260 @@
|
||||
#include <nfc/helpers/nfc_util.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
|
||||
#define LF_POLY_ODD (0x29CE5C)
|
||||
#define LF_POLY_ODD (0x29CE5C)
|
||||
#define LF_POLY_EVEN (0x870804)
|
||||
#define BIT(x, n) ((x) >> (n) & 1)
|
||||
#define BEBIT(x, n) BIT(x, (n) ^ 24)
|
||||
#define BIT(x, n) ((x) >> (n) & 1)
|
||||
#define BEBIT(x, n) BIT(x, (n) ^ 24)
|
||||
#define SWAPENDIAN(x) \
|
||||
((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
|
||||
((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
|
||||
|
||||
static inline uint32_t prng_successor(uint32_t x, uint32_t n);
|
||||
static inline int filter(uint32_t const x);
|
||||
static inline uint8_t evenparity32(uint32_t x);
|
||||
static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2);
|
||||
void crypto1_get_lfsr(struct Crypto1State* state, MfClassicKey* lfsr);
|
||||
static inline uint32_t crypt_word(struct Crypto1State* s);
|
||||
static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x);
|
||||
static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x);
|
||||
void crypto1_get_lfsr(struct Crypto1State *state, MfClassicKey *lfsr);
|
||||
static inline uint32_t crypt_word(struct Crypto1State *s);
|
||||
static inline void crypt_word_noret(struct Crypto1State *s, uint32_t in, int x);
|
||||
static inline uint32_t crypt_word_ret(struct Crypto1State *s, uint32_t in, int x);
|
||||
static uint32_t crypt_word_par(
|
||||
struct Crypto1State* s,
|
||||
uint32_t in,
|
||||
int is_encrypted,
|
||||
uint32_t nt_plain,
|
||||
uint8_t* parity_keystream_bits);
|
||||
static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x);
|
||||
static inline uint8_t napi_lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb);
|
||||
static inline uint32_t napi_lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb);
|
||||
struct Crypto1State *s,
|
||||
uint32_t in,
|
||||
int is_encrypted,
|
||||
uint32_t nt_plain,
|
||||
uint8_t *parity_keystream_bits);
|
||||
static inline void rollback_word_noret(struct Crypto1State *s, uint32_t in, int x);
|
||||
static inline uint8_t napi_lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb);
|
||||
static inline uint32_t napi_lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb);
|
||||
|
||||
static const uint8_t lookup1[256] = {
|
||||
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16,
|
||||
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8,
|
||||
8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24};
|
||||
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16,
|
||||
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8,
|
||||
8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 0, 0, 16, 16, 0, 16, 0, 0,
|
||||
0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
|
||||
8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24};
|
||||
static const uint8_t lookup2[256] = {
|
||||
0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4,
|
||||
4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6,
|
||||
2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2,
|
||||
2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4,
|
||||
0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2,
|
||||
2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4,
|
||||
4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2,
|
||||
2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2,
|
||||
2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6};
|
||||
0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4,
|
||||
4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6,
|
||||
2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2,
|
||||
2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4,
|
||||
0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2,
|
||||
2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4,
|
||||
4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2,
|
||||
2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2,
|
||||
2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6};
|
||||
|
||||
static inline int filter(uint32_t const x) {
|
||||
uint32_t f;
|
||||
f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff];
|
||||
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
|
||||
return BIT(0xEC57E80A, f);
|
||||
static inline int filter(uint32_t const x)
|
||||
{
|
||||
uint32_t f;
|
||||
f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff];
|
||||
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
|
||||
return BIT(0xEC57E80A, f);
|
||||
}
|
||||
|
||||
#ifndef __ARM_ARCH_7EM__
|
||||
static inline uint8_t evenparity32(uint32_t x) {
|
||||
return __builtin_parity(x);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ARM_ARCH_7EM__
|
||||
static inline uint8_t evenparity32(uint32_t x) {
|
||||
uint32_t result;
|
||||
__asm__ volatile("eor r1, %[x], %[x], lsr #16 \n\t" // r1 = x ^ (x >> 16)
|
||||
"eor r1, r1, r1, lsr #8 \n\t" // r1 = r1 ^ (r1 >> 8)
|
||||
"eor r1, r1, r1, lsr #4 \n\t" // r1 = r1 ^ (r1 >> 4)
|
||||
"eor r1, r1, r1, lsr #2 \n\t" // r1 = r1 ^ (r1 >> 2)
|
||||
"eor r1, r1, r1, lsr #1 \n\t" // r1 = r1 ^ (r1 >> 1)
|
||||
"and %[result], r1, #1 \n\t" // result = r1 & 1
|
||||
: [result] "=r"(result)
|
||||
: [x] "r"(x)
|
||||
: "r1");
|
||||
return result;
|
||||
static inline uint8_t evenparity32(uint32_t x)
|
||||
{
|
||||
// fold 32 bits -> 16 -> 8 -> 4
|
||||
x ^= x >> 16;
|
||||
x ^= x >> 8;
|
||||
x ^= x >> 4;
|
||||
// magic 0x6996: bit i tells you parity of i (0 ≤ i < 16)
|
||||
return (uint8_t)((0x6996u >> (x & 0xF)) & 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2) {
|
||||
int p = data[item] >> 25;
|
||||
p = p << 1 | evenparity32(data[item] & mask1);
|
||||
p = p << 1 | evenparity32(data[item] & mask2);
|
||||
data[item] = p << 24 | (data[item] & 0xffffff);
|
||||
static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2)
|
||||
{
|
||||
int p = data[item] >> 25;
|
||||
p = p << 1 | evenparity32(data[item] & mask1);
|
||||
p = p << 1 | evenparity32(data[item] & mask2);
|
||||
data[item] = p << 24 | (data[item] & 0xffffff);
|
||||
}
|
||||
|
||||
static inline uint32_t crypt_word(struct Crypto1State* s) {
|
||||
// "in" and "x" are always 0 (last iteration)
|
||||
uint32_t res_ret = 0;
|
||||
uint32_t feedin, t;
|
||||
for(int i = 0; i <= 31; i++) {
|
||||
res_ret |= (filter(s->odd) << (24 ^ i)); //-V629
|
||||
feedin = LF_POLY_EVEN & s->even;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
}
|
||||
return res_ret;
|
||||
static inline uint32_t crypt_word(struct Crypto1State *s)
|
||||
{
|
||||
// "in" and "x" are always 0 (last iteration)
|
||||
uint32_t res_ret = 0;
|
||||
uint32_t feedin, t;
|
||||
for (int i = 0; i <= 31; i++)
|
||||
{
|
||||
res_ret |= (filter(s->odd) << (24 ^ i)); //-V629
|
||||
feedin = LF_POLY_EVEN & s->even;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
}
|
||||
return res_ret;
|
||||
}
|
||||
|
||||
static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x) {
|
||||
uint8_t ret;
|
||||
uint32_t feedin, t, next_in;
|
||||
for(int i = 0; i <= 31; i++) {
|
||||
next_in = BEBIT(in, i);
|
||||
ret = filter(s->odd);
|
||||
feedin = ret & (!!x);
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
}
|
||||
return;
|
||||
static inline void crypt_word_noret(struct Crypto1State *s, uint32_t in, int x)
|
||||
{
|
||||
uint8_t ret;
|
||||
uint32_t feedin, t, next_in;
|
||||
for (int i = 0; i <= 31; i++)
|
||||
{
|
||||
next_in = BEBIT(in, i);
|
||||
ret = filter(s->odd);
|
||||
feedin = ret & (!!x);
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static inline uint32_t crypt_word_ret(struct Crypto1State* s, uint32_t in, int x) {
|
||||
uint32_t ret = 0;
|
||||
uint32_t feedin, t, next_in;
|
||||
uint8_t next_ret;
|
||||
for(int i = 0; i <= 31; i++) {
|
||||
next_in = BEBIT(in, i);
|
||||
next_ret = filter(s->odd);
|
||||
feedin = next_ret & (!!x);
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
ret |= next_ret << (24 ^ i);
|
||||
}
|
||||
return ret;
|
||||
static inline uint32_t crypt_word_ret(struct Crypto1State *s, uint32_t in, int x)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
uint32_t feedin, t, next_in;
|
||||
uint8_t next_ret;
|
||||
for (int i = 0; i <= 31; i++)
|
||||
{
|
||||
next_in = BEBIT(in, i);
|
||||
next_ret = filter(s->odd);
|
||||
feedin = next_ret & (!!x);
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
ret |= next_ret << (24 ^ i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t get_nth_byte(uint32_t value, int n) {
|
||||
if(n < 0 || n > 3) {
|
||||
// Handle invalid input
|
||||
return 0;
|
||||
}
|
||||
return (value >> (8 * (3 - n))) & 0xFF;
|
||||
static uint8_t get_nth_byte(uint32_t value, int n)
|
||||
{
|
||||
if (n < 0 || n > 3)
|
||||
{
|
||||
// Handle invalid input
|
||||
return 0;
|
||||
}
|
||||
return (value >> (8 * (3 - n))) & 0xFF;
|
||||
}
|
||||
|
||||
static uint8_t crypt_bit(struct Crypto1State* s, uint8_t in, int is_encrypted) {
|
||||
uint32_t feedin, t;
|
||||
uint8_t ret = filter(s->odd);
|
||||
feedin = ret & !!is_encrypted;
|
||||
feedin ^= !!in;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
s->even = s->even << 1 | evenparity32(feedin);
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
return ret;
|
||||
static uint8_t crypt_bit(struct Crypto1State *s, uint8_t in, int is_encrypted)
|
||||
{
|
||||
uint32_t feedin, t;
|
||||
uint8_t ret = filter(s->odd);
|
||||
feedin = ret & !!is_encrypted;
|
||||
feedin ^= !!in;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
s->even = s->even << 1 | evenparity32(feedin);
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint32_t crypt_word_par(
|
||||
struct Crypto1State* s,
|
||||
uint32_t in,
|
||||
int is_encrypted,
|
||||
uint32_t nt_plain,
|
||||
uint8_t* parity_keystream_bits) {
|
||||
uint32_t ret = 0;
|
||||
*parity_keystream_bits = 0; // Reset parity keystream bits
|
||||
struct Crypto1State *s,
|
||||
uint32_t in,
|
||||
int is_encrypted,
|
||||
uint32_t nt_plain,
|
||||
uint8_t *parity_keystream_bits)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
*parity_keystream_bits = 0; // Reset parity keystream bits
|
||||
|
||||
for(int i = 0; i < 32; i++) {
|
||||
uint8_t bit = crypt_bit(s, BEBIT(in, i), is_encrypted);
|
||||
ret |= bit << (24 ^ i);
|
||||
// Save keystream parity bit
|
||||
if((i + 1) % 8 == 0) {
|
||||
*parity_keystream_bits |=
|
||||
(filter(s->odd) ^ nfc_util_even_parity8(get_nth_byte(nt_plain, i / 8)))
|
||||
<< (3 - (i / 8));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
uint8_t bit = crypt_bit(s, BEBIT(in, i), is_encrypted);
|
||||
ret |= bit << (24 ^ i);
|
||||
// Save keystream parity bit
|
||||
if ((i + 1) % 8 == 0)
|
||||
{
|
||||
*parity_keystream_bits |=
|
||||
(filter(s->odd) ^ nfc_util_even_parity8(get_nth_byte(nt_plain, i / 8)))
|
||||
<< (3 - (i / 8));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x) {
|
||||
uint8_t ret;
|
||||
uint32_t feedin, t, next_in;
|
||||
for(int i = 31; i >= 0; i--) {
|
||||
next_in = BEBIT(in, i);
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
ret = filter(s->odd);
|
||||
feedin = ret & (!!x);
|
||||
feedin ^= s->even & 1;
|
||||
feedin ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even |= (evenparity32(feedin)) << 23;
|
||||
}
|
||||
return;
|
||||
static inline void rollback_word_noret(struct Crypto1State *s, uint32_t in, int x)
|
||||
{
|
||||
uint8_t ret;
|
||||
uint32_t feedin, t, next_in;
|
||||
for (int i = 31; i >= 0; i--)
|
||||
{
|
||||
next_in = BEBIT(in, i);
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
ret = filter(s->odd);
|
||||
feedin = ret & (!!x);
|
||||
feedin ^= s->even & 1;
|
||||
feedin ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even |= (evenparity32(feedin)) << 23;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
/*
|
||||
uint32_t rollback_word(struct Crypto1State *s, uint32_t in, int x) {
|
||||
uint32_t res_ret = 0;
|
||||
uint8_t ret;
|
||||
uint32_t feedin, t, next_in;
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
next_in = BEBIT(in, i);
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
ret = filter(s->odd);
|
||||
feedin = ret & (!!x);
|
||||
feedin ^= s->even & 1;
|
||||
feedin ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even |= (evenparity32(feedin)) << 23;
|
||||
res_ret |= (ret << (24 ^ i));
|
||||
}
|
||||
return res_ret;
|
||||
uint32_t res_ret = 0;
|
||||
uint8_t ret;
|
||||
uint32_t feedin, t, next_in;
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
next_in = BEBIT(in, i);
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
ret = filter(s->odd);
|
||||
feedin = ret & (!!x);
|
||||
feedin ^= s->even & 1;
|
||||
feedin ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= !!next_in;
|
||||
s->even |= (evenparity32(feedin)) << 23;
|
||||
res_ret |= (ret << (24 ^ i));
|
||||
}
|
||||
return res_ret;
|
||||
}
|
||||
*/
|
||||
|
||||
uint8_t napi_lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb) {
|
||||
int out;
|
||||
uint8_t ret;
|
||||
uint32_t t;
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
uint8_t napi_lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb)
|
||||
{
|
||||
int out;
|
||||
uint8_t ret;
|
||||
uint32_t t;
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
|
||||
out = s->even & 1;
|
||||
out ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
out ^= LF_POLY_ODD & s->odd;
|
||||
out ^= !!in;
|
||||
out ^= (ret = filter(s->odd)) & !!fb;
|
||||
out = s->even & 1;
|
||||
out ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
out ^= LF_POLY_ODD & s->odd;
|
||||
out ^= !!in;
|
||||
out ^= (ret = filter(s->odd)) & !!fb;
|
||||
|
||||
s->even |= evenparity32(out) << 23;
|
||||
return ret;
|
||||
s->even |= evenparity32(out) << 23;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t napi_lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb) {
|
||||
int i;
|
||||
uint32_t ret = 0;
|
||||
for(i = 31; i >= 0; --i)
|
||||
ret |= napi_lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24);
|
||||
return ret;
|
||||
uint32_t napi_lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb)
|
||||
{
|
||||
int i;
|
||||
uint32_t ret = 0;
|
||||
for (i = 31; i >= 0; --i)
|
||||
ret |= napi_lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint32_t prng_successor(uint32_t x, uint32_t n) {
|
||||
SWAPENDIAN(x);
|
||||
while(n--)
|
||||
x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
|
||||
return SWAPENDIAN(x);
|
||||
static inline uint32_t prng_successor(uint32_t x, uint32_t n)
|
||||
{
|
||||
SWAPENDIAN(x);
|
||||
while (n--)
|
||||
x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
|
||||
return SWAPENDIAN(x);
|
||||
}
|
||||
|
||||
#endif // CRYPTO1_H
|
||||
#endif // CRYPTO1_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,100 +9,115 @@
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
|
||||
struct Crypto1State {
|
||||
uint32_t odd, even;
|
||||
struct Crypto1State
|
||||
{
|
||||
uint32_t odd, even;
|
||||
};
|
||||
struct Msb {
|
||||
int tail;
|
||||
uint32_t states[768];
|
||||
struct Msb
|
||||
{
|
||||
int tail;
|
||||
uint32_t states[768];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MissingNonces,
|
||||
ZeroNonces,
|
||||
InsufficientRAM,
|
||||
typedef enum
|
||||
{
|
||||
MissingNonces,
|
||||
ZeroNonces,
|
||||
InsufficientRAM,
|
||||
} MFKeyError;
|
||||
|
||||
typedef enum {
|
||||
Ready,
|
||||
Initializing,
|
||||
DictionaryAttack,
|
||||
MFKeyAttack,
|
||||
Complete,
|
||||
Error,
|
||||
Help,
|
||||
typedef enum
|
||||
{
|
||||
Ready,
|
||||
Initializing,
|
||||
DictionaryAttack,
|
||||
MFKeyAttack,
|
||||
Complete,
|
||||
Error,
|
||||
Help,
|
||||
} MFKeyState;
|
||||
|
||||
// TODO: Can we eliminate any of the members of this struct?
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
MFKeyError err;
|
||||
MFKeyState mfkey_state;
|
||||
int cracked;
|
||||
int unique_cracked;
|
||||
int num_completed;
|
||||
int num_candidates;
|
||||
int total;
|
||||
int dict_count;
|
||||
int search;
|
||||
int eta_timestamp;
|
||||
int eta_total;
|
||||
int eta_round;
|
||||
bool mfkey32_present;
|
||||
bool nested_present;
|
||||
bool close_thread_please;
|
||||
FuriThread* mfkeythread;
|
||||
KeysDict* cuid_dict;
|
||||
typedef struct
|
||||
{
|
||||
FuriMutex *mutex;
|
||||
MFKeyError err;
|
||||
MFKeyState mfkey_state;
|
||||
int cracked;
|
||||
int unique_cracked;
|
||||
int num_completed;
|
||||
int num_candidates;
|
||||
int total;
|
||||
int dict_count;
|
||||
int search;
|
||||
int eta_timestamp;
|
||||
int eta_total;
|
||||
int eta_round;
|
||||
bool mfkey32_present;
|
||||
bool nested_present;
|
||||
bool close_thread_please;
|
||||
FuriThread *mfkeythread;
|
||||
KeysDict *cuid_dict;
|
||||
MfClassicKey *key_buffer;
|
||||
size_t key_buffer_size;
|
||||
size_t key_buffer_count;
|
||||
} ProgramState;
|
||||
|
||||
typedef enum {
|
||||
mfkey32,
|
||||
static_nested,
|
||||
static_encrypted
|
||||
typedef enum
|
||||
{
|
||||
mfkey32,
|
||||
static_nested,
|
||||
static_encrypted
|
||||
} AttackType;
|
||||
|
||||
typedef struct {
|
||||
AttackType attack;
|
||||
MfClassicKey key; // key
|
||||
uint32_t uid; // serial number
|
||||
uint32_t nt0; // tag challenge first
|
||||
uint32_t nt1; // tag challenge second
|
||||
uint32_t uid_xor_nt0; // uid ^ nt0
|
||||
uint32_t uid_xor_nt1; // uid ^ nt1
|
||||
union {
|
||||
// Mfkey32
|
||||
struct {
|
||||
uint32_t p64; // 64th successor of nt0
|
||||
uint32_t p64b; // 64th successor of nt1
|
||||
uint32_t nr0_enc; // first encrypted reader challenge
|
||||
uint32_t ar0_enc; // first encrypted reader response
|
||||
uint32_t nr1_enc; // second encrypted reader challenge
|
||||
uint32_t ar1_enc; // second encrypted reader response
|
||||
};
|
||||
// Nested
|
||||
struct {
|
||||
uint32_t ks1_1_enc; // first encrypted keystream
|
||||
uint32_t ks1_2_enc; // second encrypted keystream
|
||||
char par_1_str[5]; // first parity bits (string representation)
|
||||
char par_2_str[5]; // second parity bits (string representation)
|
||||
uint8_t par_1; // first parity bits
|
||||
uint8_t par_2; // second parity bits
|
||||
};
|
||||
};
|
||||
typedef struct
|
||||
{
|
||||
AttackType attack;
|
||||
MfClassicKey key; // key
|
||||
uint32_t uid; // serial number
|
||||
uint32_t nt0; // tag challenge first
|
||||
uint32_t nt1; // tag challenge second
|
||||
uint32_t uid_xor_nt0; // uid ^ nt0
|
||||
uint32_t uid_xor_nt1; // uid ^ nt1
|
||||
union
|
||||
{
|
||||
// Mfkey32
|
||||
struct
|
||||
{
|
||||
uint32_t p64; // 64th successor of nt0
|
||||
uint32_t p64b; // 64th successor of nt1
|
||||
uint32_t nr0_enc; // first encrypted reader challenge
|
||||
uint32_t ar0_enc; // first encrypted reader response
|
||||
uint32_t nr1_enc; // second encrypted reader challenge
|
||||
uint32_t ar1_enc; // second encrypted reader response
|
||||
};
|
||||
// Nested
|
||||
struct
|
||||
{
|
||||
uint32_t ks1_1_enc; // first encrypted keystream
|
||||
uint32_t ks1_2_enc; // second encrypted keystream
|
||||
char par_1_str[5]; // first parity bits (string representation)
|
||||
char par_2_str[5]; // second parity bits (string representation)
|
||||
uint8_t par_1; // first parity bits
|
||||
uint8_t par_2; // second parity bits
|
||||
};
|
||||
};
|
||||
} MfClassicNonce;
|
||||
|
||||
typedef struct {
|
||||
Stream* stream;
|
||||
uint32_t total_nonces;
|
||||
MfClassicNonce* remaining_nonce_array;
|
||||
size_t remaining_nonces;
|
||||
typedef struct
|
||||
{
|
||||
Stream *stream;
|
||||
uint32_t total_nonces;
|
||||
MfClassicNonce *remaining_nonce_array;
|
||||
size_t remaining_nonces;
|
||||
} MfClassicNonceArray;
|
||||
|
||||
struct KeysDict {
|
||||
Stream* stream;
|
||||
size_t key_size;
|
||||
size_t key_size_symbols;
|
||||
size_t total_keys;
|
||||
struct KeysDict
|
||||
{
|
||||
Stream *stream;
|
||||
size_t key_size;
|
||||
size_t key_size_symbols;
|
||||
size_t total_keys;
|
||||
};
|
||||
|
||||
#endif // MFKEY_H
|
||||
#endif // MFKEY_H
|
||||
Submodule applications/system/picopass updated: 00f8a221fa...9889c71198
@@ -36,7 +36,7 @@ Version differences:
|
||||
ATQA: 00 44
|
||||
SAK: 00
|
||||
|
||||
### Description
|
||||
### Description
|
||||
|
||||
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.
|
||||
@@ -56,7 +56,7 @@ None, there are no versions yet.
|
||||
Application data: 00 12 34 FF
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
### 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->state = MfUltralightPollerStateGetFeatureSet;
|
||||
} 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);
|
||||
instance->state = MfUltralightPollerStateDetectMfulC;
|
||||
}
|
||||
@@ -266,7 +266,7 @@ static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPo
|
||||
instance->data->type = MfUltralightTypeMfulC;
|
||||
instance->state = MfUltralightPollerStateGetFeatureSet;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Didn't response. Check NTAG 203");
|
||||
FURI_LOG_D(TAG, "Didn't respond. Check NTAG 203");
|
||||
instance->state = MfUltralightPollerStateDetectNtag203;
|
||||
}
|
||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||
@@ -452,7 +452,47 @@ static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPol
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
if(!instance->mfu_event.data->auth_context.skip_auth) {
|
||||
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: 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 {
|
||||
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};
|
||||
@@ -469,20 +509,40 @@ static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPol
|
||||
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_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);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -505,12 +565,16 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
||||
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(start_page < instance->pages_total) {
|
||||
FURI_LOG_D(TAG, "Read page %d success", start_page);
|
||||
instance->data->page[start_page] = data.page[0];
|
||||
instance->pages_read++;
|
||||
instance->data->pages_read = instance->pages_read;
|
||||
for(size_t i = 0; i < read_cnt; i++) {
|
||||
if(start_page + i < instance->pages_total) {
|
||||
FURI_LOG_D(TAG, "Read page %d success", start_page + i);
|
||||
instance->data->page[start_page + i] = data.page[i];
|
||||
instance->pages_read++;
|
||||
instance->data->pages_read = instance->pages_read;
|
||||
}
|
||||
}
|
||||
|
||||
if(instance->pages_read == instance->pages_total) {
|
||||
@@ -753,7 +817,6 @@ static const MfUltralightPollerReadHandler
|
||||
[MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages,
|
||||
[MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail,
|
||||
[MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success,
|
||||
|
||||
};
|
||||
|
||||
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. */
|
||||
MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */
|
||||
MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */
|
||||
MfUltralightPollerEventTypeRequestKey, /**< Poller requests key for dict attack. */
|
||||
} MfUltralightPollerEventType;
|
||||
|
||||
/**
|
||||
@@ -35,6 +36,7 @@ typedef enum {
|
||||
typedef enum {
|
||||
MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */
|
||||
MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */
|
||||
MfUltralightPollerModeDictAttack, /**< Poller will perform dictionary attack against card. */
|
||||
} MfUltralightPollerMode;
|
||||
|
||||
/**
|
||||
@@ -42,20 +44,29 @@ typedef enum {
|
||||
*/
|
||||
typedef struct {
|
||||
MfUltralightAuthPassword password; /**< Password to be used for authentication. */
|
||||
MfUltralightC3DesAuthKey tdes_key;
|
||||
MfUltralightAuthPack pack; /**< Pack received on successfull authentication. */
|
||||
MfUltralightC3DesAuthKey tdes_key; /**< 3DES key to be used for authentication. */
|
||||
MfUltralightAuthPack pack; /**< Pack received on successful authentication. */
|
||||
bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */
|
||||
bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */
|
||||
} 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.
|
||||
*/
|
||||
typedef union {
|
||||
MfUltralightPollerAuthContext auth_context; /**< Authentication context. */
|
||||
MfUltralightError error; /**< Error code indicating reading fail reason. */
|
||||
const MfUltralightData* write_data;
|
||||
MfUltralightPollerMode poller_mode;
|
||||
const MfUltralightData* write_data; /**< Data to be written to card. */
|
||||
MfUltralightPollerMode poller_mode; /**< Mode to operate in. */
|
||||
MfUltralightPollerKeyRequestData key_request_data; /**< Key request data. */
|
||||
} MfUltralightPollerEventData;
|
||||
|
||||
/**
|
||||
@@ -64,7 +75,7 @@ typedef union {
|
||||
* Upon emission of an event, an instance of this struct will be passed to the callback.
|
||||
*/
|
||||
typedef struct {
|
||||
MfUltralightPollerEventType type; /**< Type of emmitted event. */
|
||||
MfUltralightPollerEventType type; /**< Type of emitted event. */
|
||||
MfUltralightPollerEventData* data; /**< Pointer to event specific data. */
|
||||
} MfUltralightPollerEvent;
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
|
||||
uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||
mf_ultralight_3des_decrypt(
|
||||
&instance->des_context,
|
||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
||||
instance->auth_context.tdes_key.data,
|
||||
iv,
|
||||
encRndB,
|
||||
sizeof(encRndB),
|
||||
@@ -145,7 +145,7 @@ MfUltralightError mf_ultralight_poller_authenticate_start(
|
||||
|
||||
mf_ultralight_3des_encrypt(
|
||||
&instance->des_context,
|
||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
||||
instance->auth_context.tdes_key.data,
|
||||
encRndB,
|
||||
output,
|
||||
MF_ULTRALIGHT_C_AUTH_DATA_SIZE,
|
||||
@@ -179,7 +179,7 @@ MfUltralightError mf_ultralight_poller_authenticate_end(
|
||||
|
||||
mf_ultralight_3des_decrypt(
|
||||
&instance->des_context,
|
||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
||||
instance->auth_context.tdes_key.data,
|
||||
RndB,
|
||||
bit_buffer_get_data(instance->rx_buffer) + 1,
|
||||
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]);
|
||||
}
|
||||
|
||||
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(key_str);
|
||||
furi_assert(key_int);
|
||||
furi_assert(key_out);
|
||||
|
||||
uint8_t key_byte_tmp;
|
||||
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) {
|
||||
h = furi_string_get_char(key_str, i);
|
||||
l = furi_string_get_char(key_str, i + 1);
|
||||
|
||||
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);
|
||||
|
||||
if(key_read) {
|
||||
size_t tmp_len = key_size;
|
||||
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;
|
||||
}
|
||||
keys_dict_str_to_int(instance, temp_key, key);
|
||||
}
|
||||
|
||||
furi_string_free(temp_key);
|
||||
|
||||
Reference in New Issue
Block a user