diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index 949bb3fc292b..6f150e285ab9 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -4,6 +4,7 @@ App( entry_point="unit_tests_on_system_start", cdefines=["APP_UNIT_TESTS"], provides=["delay_test"], + requires=["nfc"], order=100, ) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 2ea4159e6080..8500d46c9b3a 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -12,6 +12,8 @@ #include #include +#include + #include #include "../minunit.h" @@ -19,18 +21,7 @@ #define TAG "NfcTest" #define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc") - -// Maximum allowed time for buffer preparation to fit 500us nt message timeout -#define NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX (150) -#define NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX (640) -#define NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX (110) -#define NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX (440) - -// Maximum allowed time for buffer preparation to fit 500us nt message timeout -#define NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX (150) -#define NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX (640) -#define NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX (110) -#define NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX (440) +#define MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc") typedef struct { Storage* storage; @@ -432,6 +423,67 @@ static void mf_classic_value_block() { nfc_free(poller); } +MU_TEST(mf_classic_dict_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) { + mu_assert( + storage_simply_remove(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH), + "Remove test dict failed"); + } + + MfDict* dict = mf_dict_alloc(MfDictTypeUnitTest); + mu_assert(dict != NULL, "mf_dict_alloc() failed"); + + size_t dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert(dict_keys_total == 0, "mf_dict_keys_total() failed"); + + const uint32_t test_key_num = 30; + MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); + for(size_t i = 0; i < test_key_num; i++) { + furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); + mu_assert(mf_dict_add_key(dict, &key_arr_ref[i]), "add key failed"); + + size_t dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert(dict_keys_total == (i + 1), "mf_dict_keys_total() failed"); + } + + mf_dict_free(dict); + + dict = mf_dict_alloc(MfDictTypeUnitTest); + mu_assert(dict != NULL, "mf_dict_alloc() failed"); + + dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert(dict_keys_total == test_key_num, "mf_dict_keys_total() failed"); + + MfClassicKey key_dut = {}; + size_t key_idx = 0; + while(mf_dict_get_next_key(dict, &key_dut)) { + mu_assert( + memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, + "Loaded key data mismatch"); + key_idx++; + } + + uint32_t delete_keys_idx[] = {1, 3, 9, 11, 19, 27}; + + for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { + MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; + mu_assert(mf_dict_is_key_present(dict, key), "mf_dict_is_key_present() failed"); + mu_assert(mf_dict_delete_key(dict, key), "mf_dict_delete_key() failed"); + } + + dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert( + dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), + "mf_dict_keys_total() failed"); + + mf_dict_free(dict); + free(key_arr_ref); + + mu_assert( + storage_simply_remove(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH), "Remove test dict failed"); +} + MU_TEST_SUITE(nfc) { nfc_test_alloc(); @@ -471,6 +523,8 @@ MU_TEST_SUITE(nfc) { MU_RUN_TEST(mf_classic_write); MU_RUN_TEST(mf_classic_value_block); + MU_RUN_TEST(mf_classic_dict_test); + nfc_test_free(); } diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 2d96f99c2799..27b11d8da06f 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -38,6 +38,20 @@ App( requires=["nfc"], ) +App( + appid="plantain_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="plantain_plugin_ep", + requires=["nfc"], +) + +App( + appid="two_cities_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="two_cities_plugin_ep", + requires=["nfc"], +) + # App( # appid="nfc_start", # apptype=FlipperAppType.STARTUP, diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c new file mode 100644 index 000000000000..ef6a0f6bcaa6 --- /dev/null +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -0,0 +1,211 @@ +#include "mf_classic_key_cache.h" + +#include +#include + +#define NFC_APP_KEYS_EXTENSION ".keys" +#define NFC_APP_KEY_CACHE_FOLDER "/ext/nfc/.cache" + +static const char* mf_classic_key_cache_file_header = "Flipper NFC keys"; +static const uint32_t mf_classic_key_cache_file_version = 1; + +struct MfClassicKeyCache { + MfClassicDeviceKeys keys; + MfClassicKeyType current_key_type; + uint8_t current_sector; +}; + +static void nfc_get_key_cache_file_path(const uint8_t* uid, size_t uid_len, FuriString* path) { + furi_string_printf(path, "%s/", NFC_APP_KEY_CACHE_FOLDER); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(path, "%02X", uid[i]); + } + furi_string_cat_printf(path, "%s", NFC_APP_KEYS_EXTENSION); +} + +MfClassicKeyCache* mf_classic_key_cache_alloc() { + MfClassicKeyCache* instance = malloc(sizeof(MfClassicKeyCache)); + + return instance; +} + +void mf_classic_key_cache_free(MfClassicKeyCache* instance) { + furi_assert(instance); + + free(instance); +} + +bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data) { + UNUSED(instance); + furi_assert(data); + + size_t uid_len = 0; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + FuriString* file_path = furi_string_alloc(); + nfc_get_key_cache_file_path(uid, uid_len, file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* temp_str = furi_string_alloc(); + bool save_success = false; + do { + if(!storage_simply_mkdir(storage, NFC_APP_KEY_CACHE_FOLDER)) break; + if(!storage_simply_remove(storage, furi_string_get_cstr(file_path))) break; + if(!flipper_format_buffered_file_open_always(ff, furi_string_get_cstr(file_path))) break; + + if(!flipper_format_write_header_cstr( + ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version)) + break; + if(!flipper_format_write_string_cstr( + ff, "Mifare Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) + break; + if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break; + + uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); + bool key_save_success = true; + for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_save_success = flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), sec_tr->key_a.data, sizeof(MfClassicKey)); + } + if(!key_save_success) break; + if(FURI_BIT(data->key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_save_success = flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), sec_tr->key_b.data, sizeof(MfClassicKey)); + } + } + save_success = key_save_success; + } while(false); + + flipper_format_free(ff); + furi_string_free(temp_str); + furi_string_free(file_path); + furi_record_close(RECORD_STORAGE); + + return save_success; +} + +bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + FuriString* file_path = furi_string_alloc(); + nfc_get_key_cache_file_path(uid, uid_len, file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* temp_str = furi_string_alloc(); + bool load_success = false; + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(file_path))) break; + + uint32_t version = 0; + if(!flipper_format_read_header(ff, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, mf_classic_key_cache_file_header)) break; + if(version != mf_classic_key_cache_file_version) break; + + if(!flipper_format_read_hex_uint64(ff, "Key A map", &instance->keys.key_a_mask, 1)) break; + if(!flipper_format_read_hex_uint64(ff, "Key B map", &instance->keys.key_b_mask, 1)) break; + + bool key_read_success = true; + for(size_t i = 0; (i < MF_CLASSIC_TOTAL_SECTORS_MAX) && (key_read_success); i++) { + if(FURI_BIT(instance->keys.key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_read_success = flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + instance->keys.key_a[i].data, + sizeof(MfClassicKey)); + } + if(!key_read_success) break; + if(FURI_BIT(instance->keys.key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_read_success = flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + instance->keys.key_b[i].data, + sizeof(MfClassicKey)); + } + } + load_success = key_read_success; + load_success = true; + } while(false); + + flipper_format_buffered_file_close(ff); + flipper_format_free(ff); + furi_string_free(temp_str); + furi_string_free(file_path); + furi_record_close(RECORD_STORAGE); + + return load_success; +} + +void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data) { + furi_assert(instance); + furi_assert(data); + + mf_classic_key_cache_reset(instance); + instance->keys.key_a_mask = data->key_a_mask; + instance->keys.key_b_mask = data->key_b_mask; + for(size_t i = 0; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + + if(FURI_BIT(data->key_a_mask, i)) { + instance->keys.key_a[i] = sec_tr->key_a; + } + if(FURI_BIT(data->key_b_mask, i)) { + instance->keys.key_b[i] = sec_tr->key_b; + } + } +} + +bool mf_classic_key_cahce_get_next_key( + MfClassicKeyCache* instance, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type) { + furi_assert(instance); + furi_assert(sector_num); + furi_assert(key); + furi_assert(key_type); + + bool next_key_found = false; + for(uint8_t i = instance->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + if(FURI_BIT(instance->keys.key_a_mask, i)) { + FURI_BIT_CLEAR(instance->keys.key_a_mask, i); + *key = instance->keys.key_a[i]; + *key_type = MfClassicKeyTypeA; + *sector_num = i; + + next_key_found = true; + break; + } + if(FURI_BIT(instance->keys.key_b_mask, i)) { + FURI_BIT_CLEAR(instance->keys.key_b_mask, i); + *key = instance->keys.key_b[i]; + *key_type = MfClassicKeyTypeB; + *sector_num = i; + + next_key_found = true; + instance->current_sector = i; + break; + } + } + + return next_key_found; +} + +void mf_classic_key_cache_reset(MfClassicKeyCache* instance) { + furi_assert(instance); + + instance->current_key_type = MfClassicKeyTypeA; + instance->current_sector = 0; + instance->keys.key_a_mask = 0; + instance->keys.key_b_mask = 0; +} diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.h b/applications/main/nfc/helpers/mf_classic_key_cache.h new file mode 100644 index 000000000000..407c6e28bd42 --- /dev/null +++ b/applications/main/nfc/helpers/mf_classic_key_cache.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfClassicKeyCache MfClassicKeyCache; + +MfClassicKeyCache* mf_classic_key_cache_alloc(); + +void mf_classic_key_cache_free(MfClassicKeyCache* instance); + +bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len); + +void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data); + +bool mf_classic_key_cahce_get_next_key( + MfClassicKeyCache* instance, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type); + +bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data); + +void mf_classic_key_cache_reset(MfClassicKeyCache* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mf_dict.c b/applications/main/nfc/helpers/mf_dict.c index 71a4a23c61a3..094dc0e27614 100644 --- a/applications/main/nfc/helpers/mf_dict.c +++ b/applications/main/nfc/helpers/mf_dict.c @@ -5,8 +5,8 @@ #include -#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") -#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_dict_user.nfc") +#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc") +#define MF_CLASSIC_DICT_SYSTEM_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") #define MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc") #define TAG "MfDict" @@ -18,18 +18,36 @@ struct MfDict { uint32_t total_keys; }; +typedef struct { + const char* path; + FS_OpenMode open_mode; +} MfDictFile; + +static const MfDictFile mf_dict_file[MfDictTypeNum] = { + [MfDictTypeUser] = + { + .path = MF_CLASSIC_DICT_USER_PATH, + .open_mode = FSOM_OPEN_ALWAYS, + }, + [MfDictTypeSystem] = + { + .path = MF_CLASSIC_DICT_SYSTEM_PATH, + .open_mode = FSOM_OPEN_EXISTING, + }, + [MfDictTypeUnitTest] = + { + .path = MF_CLASSIC_DICT_UNIT_TEST_PATH, + .open_mode = FSOM_OPEN_ALWAYS, + }, +}; + bool mf_dict_check_presence(MfDictType dict_type) { + furi_assert(dict_type < MfDictTypeNum); + Storage* storage = furi_record_open(RECORD_STORAGE); - bool dict_present = false; - if(dict_type == MfDictTypeSystem) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; - } else if(dict_type == MfDictTypeUser) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; - } else if(dict_type == MfDictTypeUnitTest) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == - FSE_OK; - } + const char* path = mf_dict_file[dict_type].path; + bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK; furi_record_close(RECORD_STORAGE); @@ -37,6 +55,8 @@ bool mf_dict_check_presence(MfDictType dict_type) { } MfDict* mf_dict_alloc(MfDictType dict_type) { + furi_assert(dict_type < MfDictTypeNum); + MfDict* dict = malloc(sizeof(MfDict)); Storage* storage = furi_record_open(RECORD_STORAGE); dict->stream = buffered_file_stream_alloc(storage); @@ -44,30 +64,13 @@ MfDict* mf_dict_alloc(MfDictType dict_type) { bool dict_loaded = false; do { - if(dict_type == MfDictTypeSystem) { - if(!buffered_file_stream_open( - dict->stream, - MF_CLASSIC_DICT_FLIPPER_PATH, - FSAM_READ_WRITE, - FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(dict->stream); - break; - } - } else if(dict_type == MfDictTypeUser) { - if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { - buffered_file_stream_close(dict->stream); - break; - } - } else if(dict_type == MfDictTypeUnitTest) { - if(!buffered_file_stream_open( - dict->stream, - MF_CLASSIC_DICT_UNIT_TEST_PATH, - FSAM_READ_WRITE, - FSOM_OPEN_ALWAYS)) { - buffered_file_stream_close(dict->stream); - break; - } + if(!buffered_file_stream_open( + dict->stream, + mf_dict_file[dict_type].path, + FSAM_READ_WRITE, + mf_dict_file[dict_type].open_mode)) { + buffered_file_stream_close(dict->stream); + break; } // Check for new line ending @@ -124,7 +127,7 @@ void mf_dict_free(MfDict* dict) { free(dict); } -static void mf_dict_int_to_str(uint8_t* key_int, FuriString* key_str) { +static void mf_dict_int_to_str(const uint8_t* key_int, FuriString* key_str) { furi_string_reset(key_str); for(size_t i = 0; i < 6; i++) { furi_string_cat_printf(key_str, "%02X", key_int[i]); @@ -155,7 +158,7 @@ bool mf_dict_rewind(MfDict* dict) { return stream_rewind(dict->stream); } -bool mf_dict_get_next_key_str(MfDict* dict, FuriString* key) { +static bool mf_dict_get_next_key_str(MfDict* dict, FuriString* key) { furi_assert(dict); furi_assert(dict->stream); @@ -188,7 +191,7 @@ bool mf_dict_get_next_key(MfDict* dict, MfClassicKey* key) { return key_read; } -bool mf_dict_is_key_present_str(MfDict* dict, FuriString* key) { +static bool mf_dict_is_key_present_str(MfDict* dict, FuriString* key) { furi_assert(dict); furi_assert(dict->stream); @@ -210,17 +213,17 @@ bool mf_dict_is_key_present_str(MfDict* dict, FuriString* key) { return key_found; } -bool mf_dict_is_key_present(MfDict* dict, uint8_t* key) { +bool mf_dict_is_key_present(MfDict* dict, const MfClassicKey* key) { FuriString* temp_key; temp_key = furi_string_alloc(); - mf_dict_int_to_str(key, temp_key); + mf_dict_int_to_str(key->data, temp_key); bool key_found = mf_dict_is_key_present_str(dict, temp_key); furi_string_free(temp_key); return key_found; } -bool mf_dict_add_key_str(MfDict* dict, FuriString* key) { +static bool mf_dict_add_key_str(MfDict* dict, FuriString* key) { furi_assert(dict); furi_assert(dict->stream); @@ -238,113 +241,37 @@ bool mf_dict_add_key_str(MfDict* dict, FuriString* key) { return key_added; } -bool mf_dict_add_key(MfDict* dict, uint8_t* key) { +bool mf_dict_add_key(MfDict* dict, const MfClassicKey* key) { furi_assert(dict); furi_assert(dict->stream); FuriString* temp_key; temp_key = furi_string_alloc(); - mf_dict_int_to_str(key, temp_key); + mf_dict_int_to_str(key->data, temp_key); bool key_added = mf_dict_add_key_str(dict, temp_key); furi_string_free(temp_key); return key_added; } -bool mf_dict_get_key_at_index_str(MfDict* dict, FuriString* key, uint32_t target) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* next_line; - uint32_t index = 0; - next_line = furi_string_alloc(); - furi_string_reset(key); - - bool key_found = false; - while(!key_found) { - if(!stream_read_line(dict->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - if(index++ != target) continue; - furi_string_set_n(key, next_line, 0, 12); - key_found = true; - } - - furi_string_free(next_line); - return key_found; -} - -bool mf_dict_get_key_at_index(MfDict* dict, uint64_t* key, uint32_t target) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* temp_key; - temp_key = furi_string_alloc(); - bool key_found = mf_dict_get_key_at_index_str(dict, temp_key, target); - if(key_found) { - mf_dict_str_to_int(temp_key, key); - } - furi_string_free(temp_key); - return key_found; -} - -bool mf_dict_find_index_str(MfDict* dict, FuriString* key, uint32_t* target) { +bool mf_dict_delete_key(MfDict* dict, const MfClassicKey* key) { furi_assert(dict); furi_assert(dict->stream); - FuriString* next_line; - next_line = furi_string_alloc(); - - bool key_found = false; - uint32_t index = 0; - stream_rewind(dict->stream); - while(!key_found) { //-V654 - if(!stream_read_line(dict->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - furi_string_left(next_line, 12); - if(!furi_string_equal(key, next_line)) continue; - key_found = true; - *target = index; - } - - furi_string_free(next_line); - return key_found; -} - -bool mf_dict_find_index(MfDict* dict, uint8_t* key, uint32_t* target) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* temp_key; - temp_key = furi_string_alloc(); - mf_dict_int_to_str(key, temp_key); - bool key_found = mf_dict_find_index_str(dict, temp_key, target); - - furi_string_free(temp_key); - return key_found; -} - -bool mf_dict_delete_index(MfDict* dict, uint32_t target) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* next_line; - next_line = furi_string_alloc(); - uint32_t index = 0; - bool key_removed = false; + MfClassicKey temp_key = {}; + + mf_dict_rewind(dict); while(!key_removed) { - if(!stream_read_line(dict->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - if(index++ != target) continue; - stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent); - if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break; - dict->total_keys--; - key_removed = true; + if(!mf_dict_get_next_key(dict, &temp_key)) break; + if(memcmp(temp_key.data, key->data, sizeof(MfClassicKey)) == 0) { + stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent); + if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break; + dict->total_keys--; + key_removed = true; + } } + mf_dict_rewind(dict); - furi_string_free(next_line); return key_removed; } diff --git a/applications/main/nfc/helpers/mf_dict.h b/applications/main/nfc/helpers/mf_dict.h index b1725f549262..33c6f32e7420 100644 --- a/applications/main/nfc/helpers/mf_dict.h +++ b/applications/main/nfc/helpers/mf_dict.h @@ -15,6 +15,8 @@ typedef enum { MfDictTypeUser, MfDictTypeSystem, MfDictTypeUnitTest, + + MfDictTypeNum, } MfDictType; typedef struct MfDict MfDict; @@ -51,57 +53,13 @@ uint32_t mf_dict_get_total_keys(MfDict* dict); */ bool mf_dict_rewind(MfDict* dict); -bool mf_dict_is_key_present(MfDict* dict, uint8_t* key); - -bool mf_dict_is_key_present_str(MfDict* dict, FuriString* key); +bool mf_dict_is_key_present(MfDict* dict, const MfClassicKey* key); bool mf_dict_get_next_key(MfDict* dict, MfClassicKey* key); -bool mf_dict_get_next_key_str(MfDict* dict, FuriString* key); - -/** Get key at target offset as uint64_t - * - * @param dict MfDict instance - * @param[out] key Pointer to the uint64_t key - * @param[in] target Target offset from current position - * - * @return true on success - */ -bool mf_dict_get_key_at_index(MfDict* dict, uint64_t* key, uint32_t target); - -/** Get key at target offset as string_t - * - * @param dict MfDict instance - * @param[out] key Found key destination buffer - * @param[in] target Target offset from current position - * - * @return true on success - */ -bool mf_dict_get_key_at_index_str(MfDict* dict, FuriString* key, uint32_t target); - -bool mf_dict_add_key(MfDict* dict, uint8_t* key); +bool mf_dict_add_key(MfDict* dict, const MfClassicKey* key); -/** Add string representation of the key - * - * @param dict MfDict instance - * @param[in] key String representation of the key - * - * @return true on success - */ -bool mf_dict_add_key_str(MfDict* dict, FuriString* key); - -bool mf_dict_find_index(MfDict* dict, uint8_t* key, uint32_t* target); - -bool mf_dict_find_index_str(MfDict* dict, FuriString* key, uint32_t* target); - -/** Delete key at target offset - * - * @param dict MfDict instance - * @param[in] target Target offset from current position - * - * @return true on success - */ -bool mf_dict_delete_index(MfDict* dict, uint32_t target); +bool mf_dict_delete_key(MfDict* dict, const MfClassicKey* key); #ifdef __cplusplus } diff --git a/applications/main/nfc/helpers/mf_user_dict.c b/applications/main/nfc/helpers/mf_user_dict.c new file mode 100644 index 000000000000..3fefe9274624 --- /dev/null +++ b/applications/main/nfc/helpers/mf_user_dict.c @@ -0,0 +1,74 @@ +#include "mf_user_dict.h" + +#include + +struct MfUserDict { + size_t keys_num; + MfClassicKey* keys_arr; +}; + +MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { + MfUserDict* instance = malloc(sizeof(MfUserDict)); + + MfDict* dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + + size_t dict_keys_num = mf_dict_get_total_keys(dict); + instance->keys_num = MIN(max_keys_to_load, dict_keys_num); + + if(instance->keys_num > 0) { + instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey)); + for(size_t i = 0; i < instance->keys_num; i++) { + bool key_loaded = mf_dict_get_next_key(dict, &instance->keys_arr[i]); + furi_assert(key_loaded); + } + } + mf_dict_free(dict); + + return instance; +} + +void mf_user_dict_free(MfUserDict* instance) { + furi_assert(instance); + + if(instance->keys_num > 0) { + free(instance->keys_arr); + } + free(instance); +} + +size_t mf_user_dict_get_keys_cnt(MfUserDict* instance) { + furi_assert(instance); + + return instance->keys_num; +} + +void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str) { + furi_assert(instance); + furi_assert(str); + furi_assert(index < instance->keys_num); + furi_assert(instance->keys_arr); + + furi_string_reset(str); + for(size_t i = 0; i < sizeof(MfClassicKey); i++) { + furi_string_cat_printf(str, "%02X", instance->keys_arr[index].data[i]); + } +} + +bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) { + furi_assert(instance); + furi_assert(index < instance->keys_num); + furi_assert(instance->keys_arr); + + MfDict* dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + + bool key_delete_success = mf_dict_delete_key(dict, &instance->keys_arr[index]); + mf_dict_free(dict); + + if(key_delete_success) { + instance->keys_num--; + } + + return key_delete_success; +} diff --git a/applications/main/nfc/helpers/mf_user_dict.h b/applications/main/nfc/helpers/mf_user_dict.h new file mode 100644 index 000000000000..74a9a40ff899 --- /dev/null +++ b/applications/main/nfc/helpers/mf_user_dict.h @@ -0,0 +1,23 @@ +#pragma once + +#include "mf_dict.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfUserDict MfUserDict; + +MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load); + +void mf_user_dict_free(MfUserDict* instance); + +size_t mf_user_dict_get_keys_cnt(MfUserDict* instance); + +void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str); + +bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index fb94cb334d69..93c39d4b51cd 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -7,23 +7,20 @@ typedef enum { // Mf classic dict attack events NfcCustomEventDictAttackComplete, NfcCustomEventDictAttackSkip, - NfcCustomEventDictAttackCardDetected, - NfcCustomEventDictAttackCardNotDetected, - NfcCustomEventDictAttackFoundKeyA, - NfcCustomEventDictAttackFoundKeyB, - NfcCustomEventDictAttackNewSector, - NfcCustomEventDictAttackNewKeyBatch, - NfcCustomEventDictAttackKeyAttackStart, - NfcCustomEventDictAttackKeyAttackStop, - NfcCustomEventDictAttackKeyAttackNextSector, + NfcCustomEventDictAttackDataUpdate, + + NfcCustomEventCardDetected, + NfcCustomEventCardLost, NfcCustomEventViewExit, NfcCustomEventWorkerExit, NfcCustomEventWorkerUpdate, + NfcCustomEventWrongCard, NfcCustomEventTimerExpired, NfcCustomEventByteInputDone, NfcCustomEventTextInputDone, NfcCustomEventDictAttackDone, + NfcCustomEventRpcLoad, NfcCustomEventRpcSessionClose, diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index f25432f13fa1..feef09e5340c 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -7,6 +7,8 @@ #include "../nfc_protocol_support_gui_common.h" +#define TAG "MfClassicApp" + enum { SubmenuIndexDetectReader = SubmenuIndexCommonMax, SubmenuIndexWrite, @@ -39,27 +41,70 @@ static NfcCommand nfc_scene_read_poller_callback_mf_classic(NfcGenericEvent even furi_assert(event.protocol == NfcProtocolMfClassic); NfcApp* instance = context; - const MfClassicPollerEvent* mf_classic_event = event.data; - - // TODO: Implement read mf_classic using key cache - if(mf_classic_event->type == MfClassicPollerEventTypeReadComplete) { - const MfClassicData* mf_classic_data = nfc_poller_get_data(instance->poller); - nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mf_classic_data); - - const NfcCustomEvent custom_event = mf_classic_is_card_read(mf_classic_data) ? - NfcCustomEventPollerSuccess : - NfcCustomEventPollerIncomplete; + const MfClassicPollerEvent* mfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller)); + size_t uid_len = 0; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) { + FURI_LOG_I(TAG, "Key cache found"); + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else { + FURI_LOG_I(TAG, "Key cache not found"); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventPollerIncomplete); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + uint8_t sector_num = 0; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + if(mf_classic_key_cahce_get_next_key( + instance->mfc_key_cache, §or_num, &key, &key_type)) { + mfc_event->data->read_sector_request_data.sector_num = sector_num; + mfc_event->data->read_sector_request_data.key = key; + mfc_event->data->read_sector_request_data.key_type = key_type; + mfc_event->data->read_sector_request_data.key_provided = true; + } else { + mfc_event->data->read_sector_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller)); + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + NfcCustomEvent custom_event = mf_classic_is_card_read(mfc_data) ? + NfcCustomEventPollerSuccess : + NfcCustomEventPollerIncomplete; view_dispatcher_send_custom_event(instance->view_dispatcher, custom_event); - return NfcCommandStop; + command = NfcCommandStop; } - return NfcCommandContinue; + return command; } static void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) { + mf_classic_key_cache_reset(instance->mfc_key_cache); nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance); } +static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, uint32_t event) { + if(event == NfcCustomEventPollerIncomplete) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + if(mf_classic_is_card_read(mfc_data)) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventPollerSuccess); + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + } + } + + return true; +} + static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { Submenu* submenu = instance->submenu; const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); @@ -144,13 +189,13 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, uint32_t bool consumed = false; if(event == SubmenuIndexDetectReader) { - scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); consumed = true; } else if(event == SubmenuIndexWrite) { - scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); consumed = true; } else if(event == SubmenuIndexUpdate) { - scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); consumed = true; } @@ -168,7 +213,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { .scene_read = { .on_enter = nfc_scene_read_on_enter_mf_classic, - .on_event = NULL, + .on_event = nfc_scene_read_on_event_mf_classic, }, .scene_read_menu = { @@ -185,7 +230,9 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { .on_enter = nfc_scene_saved_menu_on_enter_mf_classic, .on_event = nfc_scene_saved_menu_on_event_mf_classic, }, - .scene_emulate = { - .on_enter = nfc_scene_emulate_on_enter_mf_classic, - .on_event = NULL, - }}; + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_classic, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index 2175404074e1..866e7d2ef586 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -127,11 +127,20 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcCustomEventPollerIncomplete) { - nfc_supported_cards_read(instance->nfc_device, instance->nfc); - - view_dispatcher_send_custom_event( - instance->view_dispatcher, NfcCustomEventPollerSuccess); - consumed = true; + bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc); + if(card_read) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else { + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_idx]; + if(nfc_protocol_support[protocol]->scene_read.on_event) { + consumed = + nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + } + } } else if(event.event == NfcCustomEventPollerFailure) { if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) { scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h index 5b5d74aff122..f152b99f49b0 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -26,4 +26,5 @@ typedef struct { NfcProtocolSupportSceneBase scene_read_success; NfcProtocolSupportSceneBase scene_saved_menu; NfcProtocolSupportSceneBase scene_emulate; + // TODO Add scene save to save mf classic key cache } NfcProtocolSupportBase; diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 4240e61f277e..f37d42a64820 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -48,6 +48,7 @@ NfcApp* nfc_app_alloc() { instance->nfc = nfc_alloc(); instance->mf_ul_auth = mf_ultralight_auth_alloc(); + instance->mfc_key_cache = mf_classic_key_cache_alloc(); // Nfc device instance->nfc_device = nfc_device_alloc(); @@ -107,6 +108,7 @@ NfcApp* nfc_app_alloc() { instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget)); // Dict attack + instance->dict_attack = dict_attack_alloc(); view_dispatcher_add_view( instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack)); @@ -148,6 +150,7 @@ void nfc_app_free(NfcApp* instance) { nfc_free(instance->nfc); mf_ultralight_auth_free(instance->mf_ul_auth); + mf_classic_key_cache_free(instance->mfc_key_cache); // Nfc device nfc_device_free(instance->nfc_device); @@ -244,7 +247,7 @@ void nfc_blink_stop(NfcApp* nfc) { notification_message(nfc->notifications, &sequence_blink_stop); } -void nfc_make_app_folder(NfcApp* instance) { +void nfc_make_app_folders(NfcApp* instance) { furi_assert(instance); if(!storage_simply_mkdir(instance->storage, NFC_APP_FOLDER)) { @@ -262,6 +265,13 @@ bool nfc_save_file(NfcApp* instance, FuriString* path) { dialog_message_show_storage_error(instance->dialogs, "Cannot save\nkey file"); } + // TODO move this to protocol support save scene + if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) { + mf_classic_key_cache_save( + instance->mfc_key_cache, + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); + } + return result; } @@ -313,9 +323,10 @@ static bool nfc_save_internal(NfcApp* instance, const char* extension) { bool result = false; - nfc_make_app_folder(instance); + nfc_make_app_folders(instance); - if(furi_string_end_with(instance->file_path, NFC_APP_EXTENSION)) { + if(furi_string_end_with(instance->file_path, NFC_APP_EXTENSION) || + (furi_string_end_with(instance->file_path, NFC_APP_SHADOW_EXTENSION))) { size_t filename_start = furi_string_search_rchar(instance->file_path, '/'); furi_string_left(instance->file_path, filename_start); } diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index 2a7002e60630..90666f317161 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -23,12 +23,15 @@ #include #include "views/dict_attack.h" #include "views/detect_reader.h" +#include "views/dict_attack.h" #include #include "helpers/nfc_custom_event.h" #include "helpers/mf_ultralight_auth.h" #include "helpers/mf_dict.h" #include "helpers/mfkey32_logger.h" +#include "helpers/mf_user_dict.h" +#include "helpers/mf_classic_key_cache.h" #include #include @@ -61,7 +64,7 @@ #define NFC_APP_SHADOW_EXTENSION ".shd" #define NFC_APP_MFKEY32_LOGS_FILE_NAME ".mfkey32.log" -#define NFC_APP_MFKEY32_LOGS_FILE_PATH NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME +#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME) typedef enum { NfcRpcStateIdle, @@ -71,10 +74,14 @@ typedef enum { typedef struct { MfDict* dict; - uint32_t total_keys; - uint32_t current_key; + uint8_t sectors_total; + uint8_t sectors_read; uint8_t current_sector; - MfClassicType type; + uint8_t keys_found; + size_t dict_keys_total; + size_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; } NfcMfClassicDictAttackContext; struct NfcApp { @@ -105,8 +112,8 @@ struct NfcApp { ByteInput* byte_input; TextBox* text_box; Widget* widget; - DictAttack* dict_attack; DetectReader* detect_reader; + DictAttack* dict_attack; Nfc* nfc; NfcPoller* poller; @@ -116,6 +123,8 @@ struct NfcApp { MfUltralightAuth* mf_ul_auth; NfcMfClassicDictAttackContext mf_dict_context; Mfkey32Logger* mfkey32_logger; + MfUserDict* mf_user_dict; + MfClassicKeyCache* mfc_key_cache; NfcDevice* nfc_device; Iso14443_3aData* iso14443_3a_edit_data; diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c new file mode 100644 index 000000000000..25f53e2ece23 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -0,0 +1,216 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Plantain" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + const MfClassicKeyPair* keys; + uint32_t data_sector; +} PlantainCardConfig; + +static const MfClassicKeyPair plantain_1k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, + {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, + {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, + {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, +}; + +static const MfClassicKeyPair plantain_4k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a}, + {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3}, + {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056}, + {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf}, + {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406}, + {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA}, + {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3}, + {.a = 0x0eb23cc8110b, .b = 0x04dc35277635}, {.a = 0xbc4580b7f20b, .b = 0xd0a4131fb290}, + {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9}, + {.a = 0xfd8705e721b0, .b = 0x296fc317a513}, {.a = 0x22052b480d11, .b = 0xe19504c39461}, + {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a}, + {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085}, +}; + +static bool plantain_get_card_config(PlantainCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->data_sector = 8; + config->keys = plantain_1k_keys; + } else if(type == MfClassicType4k) { + config->data_sector = 8; + config->keys = plantain_4k_keys; + } else { + success = false; + } + + return success; +} + +static bool plantain_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.data_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool plantain_verify(Nfc* nfc) { + return plantain_verify_type(nfc, MfClassicType1k) || + plantain_verify_type(nfc, MfClassicType4k); +} + +static bool plantain_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + if(!mf_classic_detect_protocol(data->iso14443_3a_data, &data->type)) break; + + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, data->type)) break; + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + } + keys.key_a_mask = 0xFFFFFFFFFFFFFFFFU; + keys.key_b_mask = 0xFFFFFFFFFFFFFFFFU; + + MfClassicError error = mf_classic_poller_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, data->type)) break; + + // Verify key + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != cfg.keys[cfg.data_sector].a) break; + + // Point to block 0 of sector 4, value 0 + const uint8_t* temp_ptr = data->block[16].data; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = data->block[0].data; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + + furi_string_printf( + parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin plantain_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = plantain_verify, + .read = plantain_read, + .parse = plantain_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor plantain_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &plantain_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* plantain_plugin_ep() { + return &plantain_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index 3b7cd60b0f95..a1134ff6225c 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -80,7 +80,7 @@ static bool troika_verify_type(Nfc* nfc, MfClassicType type) { bool verified = false; do { - TroikaCardConfig cfg; + TroikaCardConfig cfg = {}; if(!troika_get_card_config(&cfg, type)) break; const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); @@ -119,34 +119,23 @@ static bool troika_read(Nfc* nfc, NfcDevice* device) { do { if(!mf_classic_detect_protocol(data->iso14443_3a_data, &data->type)) break; - TroikaCardConfig cfg; + TroikaCardConfig cfg = {}; if(!troika_get_card_config(&cfg, data->type)) break; - MfClassicKey key = {0}; - nfc_util_num2bytes(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data); - - const uint8_t block_num_start = mf_classic_get_first_block_num_of_sector(cfg.data_sector); - const uint8_t block_num_end = block_num_start + 2; - - uint8_t block_num; - for(block_num = block_num_start; block_num < block_num_end; ++block_num) { - MfClassicBlock block; - MfClassicError error; - - error = mf_classic_poller_read_block(nfc, block_num, &key, MfClassicKeyTypeA, &block); - - if(error != MfClassicErrorNone) { - FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); - break; - } - - mf_classic_set_block_read(data, block_num, &block); + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); } + keys.key_a_mask = 0xFFFFFFFFFFFFFFFFU; + keys.key_b_mask = 0xFFFFFFFFFFFFFFFFU; - if(block_num != block_num_end) break; + MfClassicError error = mf_classic_poller_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } - mf_classic_set_key_found( - data, cfg.data_sector, MfClassicKeyTypeA, cfg.keys[cfg.data_sector].a); nfc_device_set_data(device, NfcProtocolMfClassic, data); is_read = true; @@ -166,7 +155,7 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) { do { // Verify card type - TroikaCardConfig cfg; + TroikaCardConfig cfg = {}; if(!troika_get_card_config(&cfg, data->type)) break; // Verify key diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c new file mode 100644 index 000000000000..530d62ad9570 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -0,0 +1,185 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "TwoCities" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static const MfClassicKeyPair two_cities_4k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a}, + {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3}, + {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056}, + {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf}, + {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406}, + {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA}, + {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a}, + {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085}, +}; + +bool two_cities_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 4; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + nfc_util_num2bytes(two_cities_4k_keys[verify_sector].a, COUNT_OF(key.data), key.data); + + MfClassicError error = + mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool two_cities_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + if(!mf_classic_detect_protocol(data->iso14443_3a_data, &data->type)) break; + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(two_cities_4k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + nfc_util_num2bytes(two_cities_4k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + } + keys.key_a_mask = 0xFFFFFFFFFFFFFFFFU; + keys.key_b_mask = 0xFFFFFFFFFFFFFFFFU; + + MfClassicError error = mf_classic_poller_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + if(key != two_cities_4k_keys[4].a) return false; + + // ===== + // PLANTAIN + // ===== + + // Point to block 0 of sector 4, value 0 + const uint8_t* temp_ptr = data->block[16].data; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = data->block[0].data; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + + // ===== + // --PLANTAIN-- + // ===== + // TROIKA + // ===== + + const uint8_t* troika_temp_ptr = &data->block[33].data[5]; + uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25; + troika_temp_ptr = &data->block[32].data[2]; + uint32_t troika_number = 0; + for(size_t i = 0; i < 4; i++) { + troika_number <<= 8; + troika_number |= troika_temp_ptr[i]; + } + troika_number >>= 4; + + furi_string_printf( + parsed_data, + "\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n", + card_number, + balance, + troika_number, + troika_balance); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin two_cities_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = two_cities_verify, + .read = two_cities_read, + .parse = two_cities_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor two_cities_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &two_cities_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* two_cities_plugin_ep() { + return &two_cities_plugin_descriptor; +} diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 5dde7ce1e068..2d6b4975b53d 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -35,6 +35,18 @@ ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) ADD_SCENE(nfc, mf_classic_detect_reader, MfClassicDetectReader) ADD_SCENE(nfc, mf_classic_mfkey_nonces_info, MfClassicMfkeyNoncesInfo) ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete) +ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) +ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) +ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial) +ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess) +ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) + +ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) +ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) +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, set_type, SetType) ADD_SCENE(nfc, set_sak, SetSak) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 934e53bf1637..f0c22eec4d58 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -25,13 +25,13 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - // if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { - // consumed = scene_manager_search_and_switch_to_previous_scene( - // nfc->scene_manager, NfcSceneMfClassicKeys); - // } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneFileSelect); - // } + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 455afb06300a..d9b49749c804 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -7,62 +7,66 @@ enum SubmenuIndex { }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { - NfcApp* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); + view_dispatcher_send_custom_event(instance->view_dispatcher, index); } void nfc_scene_extra_actions_on_enter(void* context) { - NfcApp* nfc = context; - Submenu* submenu = nfc->submenu; + NfcApp* instance = context; + Submenu* submenu = instance->submenu; submenu_add_item( submenu, "Read Specific Card Type", SubmenuIndexReadCardType, nfc_scene_extra_actions_submenu_callback, - nfc); + instance); submenu_add_item( submenu, "Mifare Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, - nfc); + instance); submenu_add_item( submenu, "Unlock NTAG/Ultralight", SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, - nfc); + instance); submenu_set_selected_item( - submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + submenu, scene_manager_get_scene_state(instance->scene_manager, NfcSceneExtraActions)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { - NfcApp* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexMfClassicKeys) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented); + if(mf_dict_check_presence(MfDictTypeSystem)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys); + } else { + scene_manager_previous_scene(instance->scene_manager); + } consumed = true; } else if(event.event == SubmenuIndexMfUltralightUnlock) { - mf_ultralight_auth_reset(nfc->mf_ul_auth); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + mf_ultralight_auth_reset(instance->mf_ul_auth); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); consumed = true; } else if(event.event == SubmenuIndexReadCardType) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSelectProtocol); + scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state(instance->scene_manager, NfcSceneExtraActions, event.event); } return consumed; } void nfc_scene_extra_actions_on_exit(void* context) { - NfcApp* nfc = context; + NfcApp* instance = context; - submenu_reset(nfc->submenu); + submenu_reset(instance->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 24a29a95cd08..839b674c03a3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -6,9 +6,8 @@ #define TAG "NfcMfClassicDictAttack" typedef enum { - DictAttackStateIdle, DictAttackStateUserDictInProgress, - DictAttackStateFlipperDictInProgress, + DictAttackStateSystemDictInProgress, } DictAttackState; NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) { @@ -20,194 +19,241 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) NfcCommand command = NfcCommandContinue; MfClassicPollerEvent* mfc_event = event.data; - NfcApp* nfc_app = context; - if(mfc_event->type == MfClassicPollerEventTypeStart) { - nfc_app->mf_dict_context.type = mfc_event->data->start_data.type; + NfcApp* instance = context; + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttack; + mfc_event->data->poller_mode.data = mfc_data; + instance->mf_dict_context.sectors_total = mf_classic_get_total_sectors_num(mfc_data->type); + mf_classic_get_read_sectors_and_keys( + mfc_data, + &instance->mf_dict_context.sectors_read, + &instance->mf_dict_context.keys_found); view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackCardDetected); - } else if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { - view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackCardDetected); - } else if(mfc_event->type == MfClassicPollerEventTypeCardNotDetected) { - view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackCardNotDetected); + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) { MfClassicKey key = {}; - if(mf_dict_get_next_key(nfc_app->mf_dict_context.dict, &key)) { + if(mf_dict_get_next_key(instance->mf_dict_context.dict, &key)) { mfc_event->data->key_request_data.key = key; mfc_event->data->key_request_data.key_provided = true; - nfc_app->mf_dict_context.current_key++; - if(nfc_app->mf_dict_context.current_key % 10 == 0) { + instance->mf_dict_context.dict_keys_current++; + if(instance->mf_dict_context.dict_keys_current % 10 == 0) { view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackNewKeyBatch); + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } } else { mfc_event->data->key_request_data.key_provided = false; } - } else if(mfc_event->type == MfClassicPollerEventTypeNewSector) { + } else if(mfc_event->type == MfClassicPollerEventTypeDataUpdate) { + MfClassicPollerEventDataUpdate* data_update = &mfc_event->data->data_update; + instance->mf_dict_context.sectors_read = data_update->sectors_read; + instance->mf_dict_context.keys_found = data_update->keys_found; + instance->mf_dict_context.current_sector = data_update->current_sector; view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackNewSector); - mf_dict_rewind(nfc_app->mf_dict_context.dict); - nfc_app->mf_dict_context.current_key = 0; + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { + mf_dict_rewind(instance->mf_dict_context.dict); + instance->mf_dict_context.dict_keys_current = 0; + instance->mf_dict_context.current_sector = + mfc_event->data->next_sector_data.current_sector; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyA) { view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackFoundKeyA); + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyB) { view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackFoundKeyB); + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStart) { - nfc_app->mf_dict_context.current_sector = mfc_event->data->key_attack_data.start_sector; + instance->mf_dict_context.key_attack_current_sector = + mfc_event->data->key_attack_data.current_sector; + instance->mf_dict_context.is_key_attack = true; view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackKeyAttackStart); + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) { - mf_dict_rewind(nfc_app->mf_dict_context.dict); - nfc_app->mf_dict_context.current_key = 0; - view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackKeyAttackStop); - } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackNextSector) { + mf_dict_rewind(instance->mf_dict_context.dict); + instance->mf_dict_context.is_key_attack = false; + instance->mf_dict_context.dict_keys_current = 0; view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackKeyAttackNextSector); - } else if(mfc_event->type == MfClassicPollerEventTypeReadComplete) { + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); view_dispatcher_send_custom_event( - nfc_app->view_dispatcher, NfcCustomEventDictAttackComplete); + instance->view_dispatcher, NfcCustomEventDictAttackComplete); command = NfcCommandStop; } return command; } -void nfc_dict_attack_dict_attack_result_callback(void* context) { +void nfc_dict_attack_dict_attack_result_callback(DictAttackEvent event, void* context) { furi_assert(context); - NfcApp* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackSkip); -} - -static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* nfc) { - const MfClassicData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfClassic); - uint8_t sectors_read = 0; - uint8_t keys_found = 0; + NfcApp* instance = context; - // Calculate found keys and read sectors - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); - dict_attack_set_keys_found(nfc->dict_attack, keys_found); - dict_attack_set_sector_read(nfc->dict_attack, sectors_read); + if(event == DictAttackEventSkipPressed) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip); + } } -static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* nfc, DictAttackState state) { - const MfClassicData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfClassic); +static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) { + NfcMfClassicDictAttackContext* mfc_dict = &instance->mf_dict_context; - // Identify scene state - if(state == DictAttackStateIdle) { - if(mf_dict_check_presence(MfDictTypeUser)) { - state = DictAttackStateUserDictInProgress; - nfc->mf_dict_context.dict = mf_dict_alloc(MfDictTypeUser); - } else { - state = DictAttackStateFlipperDictInProgress; - nfc->mf_dict_context.dict = mf_dict_alloc(MfDictTypeSystem); - } - nfc->mf_dict_context.total_keys = mf_dict_get_total_keys(nfc->mf_dict_context.dict); - nfc->mf_dict_context.current_key = 0; - } else if(state == DictAttackStateUserDictInProgress) { - state = DictAttackStateFlipperDictInProgress; + if(mfc_dict->is_key_attack) { + dict_attack_set_key_attack(instance->dict_attack, mfc_dict->key_attack_current_sector); + } else { + dict_attack_reset_key_attack(instance->dict_attack); + dict_attack_set_sectors_total(instance->dict_attack, mfc_dict->sectors_total); + dict_attack_set_sectors_read(instance->dict_attack, mfc_dict->sectors_read); + dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found); + dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current); + dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector); } +} - // Setup view +static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); if(state == DictAttackStateUserDictInProgress) { - dict_attack_set_header(nfc->dict_attack, "MF Classic User Dictionary"); + do { + if(!mf_dict_check_presence(MfDictTypeUser)) { + state = DictAttackStateSystemDictInProgress; + break; + } + + instance->mf_dict_context.dict = mf_dict_alloc(MfDictTypeUser); + if(mf_dict_get_total_keys(instance->mf_dict_context.dict) == 0) { + mf_dict_free(instance->mf_dict_context.dict); + state = DictAttackStateSystemDictInProgress; + break; + } + + dict_attack_set_header(instance->dict_attack, "MF Classic User Dictionary"); + } while(false); } - if(state == DictAttackStateFlipperDictInProgress) { - dict_attack_set_header(nfc->dict_attack, "MF Classic System Dictionary"); + if(state == DictAttackStateSystemDictInProgress) { + instance->mf_dict_context.dict = mf_dict_alloc(MfDictTypeSystem); + dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); - dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); - dict_attack_set_current_sector(nfc->dict_attack, 0); - dict_attack_set_card_detected(nfc->dict_attack, data->type); - dict_attack_set_total_dict_keys(nfc->dict_attack, nfc->mf_dict_context.total_keys); - nfc_scene_mf_classic_dict_attack_update_view(nfc); + instance->mf_dict_context.dict_keys_total = + mf_dict_get_total_keys(instance->mf_dict_context.dict); + dict_attack_set_total_dict_keys( + instance->dict_attack, instance->mf_dict_context.dict_keys_total); + instance->mf_dict_context.dict_keys_current = 0; + + dict_attack_set_callback( + instance->dict_attack, nfc_dict_attack_dict_attack_result_callback, instance); + nfc_scene_mf_classic_dict_attack_update_view(instance); + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack, state); } void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { - NfcApp* nfc = context; - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, DictAttackStateIdle); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); - nfc_blink_read_start(nfc); - notification_message(nfc->notifications, &sequence_display_backlight_enforce_on); - - nfc->poller = nfc_poller_alloc(nfc->nfc, NfcProtocolMfClassic); - nfc_poller_start(nfc->poller, nfc_dict_attack_worker_callback, nfc); + NfcApp* instance = context; + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + dict_attack_set_card_state(instance->dict_attack, true); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack); + nfc_blink_read_start(instance); + notification_message(instance->notifications, &sequence_display_backlight_enforce_on); + + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); } bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { - NfcApp* nfc = context; + NfcApp* instance = context; bool consumed = false; uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack); + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventDictAttackComplete) { if(state == DictAttackStateUserDictInProgress) { - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + mf_dict_free(instance->mf_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); consumed = true; } else { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadSuccess); + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } - } else if(event.event == NfcCustomEventDictAttackCardDetected) { - dict_attack_set_card_detected(nfc->dict_attack, nfc->mf_dict_context.type); - consumed = true; - } else if(event.event == NfcCustomEventDictAttackCardNotDetected) { - dict_attack_set_card_removed(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcCustomEventDictAttackFoundKeyA) { - dict_attack_inc_keys_found(nfc->dict_attack); + } else if(event.event == NfcCustomEventCardDetected) { + dict_attack_set_card_state(instance->dict_attack, true); consumed = true; - } else if(event.event == NfcCustomEventDictAttackFoundKeyB) { - dict_attack_inc_keys_found(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcCustomEventDictAttackNewSector) { - nfc_device_set_data( - nfc->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(nfc->poller)); - nfc_scene_mf_classic_dict_attack_update_view(nfc); - dict_attack_inc_current_sector(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcCustomEventDictAttackNewKeyBatch) { - nfc_device_set_data( - nfc->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(nfc->poller)); - nfc_scene_mf_classic_dict_attack_update_view(nfc); - dict_attack_inc_current_dict_key(nfc->dict_attack, 10); + } else if(event.event == NfcCustomEventCardLost) { + dict_attack_set_card_state(instance->dict_attack, false); consumed = true; + } else if(event.event == NfcCustomEventDictAttackDataUpdate) { + nfc_scene_mf_classic_dict_attack_update_view(instance); } else if(event.event == NfcCustomEventDictAttackSkip) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); if(state == DictAttackStateUserDictInProgress) { - nfc_poller_stop(nfc->poller); + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + mf_dict_free(instance->mf_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); consumed = true; - } else if(state == DictAttackStateFlipperDictInProgress) { - nfc_poller_stop(nfc->poller); + } else if(state == DictAttackStateSystemDictInProgress) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } - } else if(event.event == NfcCustomEventDictAttackKeyAttackStart) { - dict_attack_set_key_attack( - nfc->dict_attack, true, nfc->mf_dict_context.current_sector); - } else if(event.event == NfcCustomEventDictAttackKeyAttackStop) { - dict_attack_set_key_attack(nfc->dict_attack, false, 0); - } else if(event.event == NfcCustomEventDictAttackKeyAttackNextSector) { - dict_attack_inc_key_attack_current_sector(nfc->dict_attack); } } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); consumed = true; } return consumed; } void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { - NfcApp* nfc = context; + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + dict_attack_reset(instance->dict_attack); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); + + mf_dict_free(instance->mf_dict_context.dict); - nfc_poller_stop(nfc->poller); - nfc_poller_free(nfc->poller); + instance->mf_dict_context.current_sector = 0; + instance->mf_dict_context.sectors_total = 0; + instance->mf_dict_context.sectors_read = 0; + instance->mf_dict_context.current_sector = 0; + instance->mf_dict_context.keys_found = 0; + instance->mf_dict_context.dict_keys_total = 0; + instance->mf_dict_context.dict_keys_current = 0; + instance->mf_dict_context.is_key_attack = false; + instance->mf_dict_context.key_attack_current_sector = 0; - nfc_blink_stop(nfc); - notification_message(nfc->notifications, &sequence_display_backlight_enforce_auto); + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_display_backlight_enforce_auto); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c new file mode 100644 index 000000000000..08491e0bb252 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -0,0 +1,90 @@ +#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) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_on_enter(void* context) { + NfcApp* instance = context; + + // Load flipper dict keys total + MfDict* dict = mf_dict_alloc(MfDictTypeSystem); + furi_assert(dict); + uint32_t flipper_dict_keys_total = mf_dict_get_total_keys(dict); + mf_dict_free(dict); + + // Load user dict keys total + uint32_t user_dict_keys_total = 0; + dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + user_dict_keys_total = mf_dict_get_total_keys(dict); + mf_dict_free(dict); + + FuriString* temp_str = furi_string_alloc(); + widget_add_string_element( + instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic 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_classic_keys_widget_callback, + instance); + if(user_dict_keys_total > 0) { + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "List", + nfc_scene_mf_classic_keys_widget_callback, + instance); + } + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_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, NfcSceneMfClassicKeysAdd); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysList); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c new file mode 100644 index 000000000000..77c9f88097a8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -0,0 +1,61 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_classic_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_classic_keys_add_byte_input_callback, + NULL, + instance, + instance->byte_input_store, + sizeof(MfClassicKey)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_classic_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 + MfDict* dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + + MfClassicKey key = {}; + memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); + if(mf_dict_is_key_present(dict, &key)) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); + } else if(mf_dict_add_key(dict, &key)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); + dolphin_deed(DolphinDeedNfcMfcAdd); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + + mf_dict_free(dict); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_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, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c new file mode 100644 index 000000000000..b245a2a552d6 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c @@ -0,0 +1,77 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_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_classic_keys_delete_on_enter(void* context) { + NfcApp* instance = context; + + uint32_t key_index = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicKeysDelete); + 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_classic_keys_delete_widget_callback, + instance); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Delete", + nfc_scene_mf_classic_keys_delete_widget_callback, + instance); + + mf_user_dict_get_key_str(instance->mf_user_dict, key_index, key_str); + widget_add_string_element( + instance->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(key_str)); + + furi_string_free(key_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_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, NfcSceneMfClassicKeysDelete); + if(mf_user_dict_delete_key(instance->mf_user_dict, key_index)) { + 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_classic_keys_delete_on_exit(void* context) { + NfcApp* instance = context; + + mf_user_dict_free(instance->mf_user_dict); + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c new file mode 100644 index 000000000000..7370c06840e9 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c @@ -0,0 +1,51 @@ +#include "../nfc_app_i.h" + +#define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100) + +void nfc_scene_mf_classic_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_classic_keys_list_on_enter(void* context) { + NfcApp* instance = context; + + instance->mf_user_dict = mf_user_dict_alloc(NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX); + + submenu_set_header(instance->submenu, "Select key to delete:"); + FuriString* temp_str = furi_string_alloc(); + for(size_t i = 0; i < mf_user_dict_get_keys_cnt(instance->mf_user_dict); i++) { + mf_user_dict_get_key_str(instance->mf_user_dict, i, temp_str); + submenu_add_item( + instance->submenu, + furi_string_get_cstr(temp_str), + i, + nfc_scene_mf_classic_keys_list_submenu_callback, + instance); + } + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_classic_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, NfcSceneMfClassicKeysDelete, event.event); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysDelete); + } else if(event.type == SceneManagerEventTypeBack) { + mf_user_dict_free(instance->mf_user_dict); + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_list_on_exit(void* context) { + NfcApp* instance = context; + + submenu_reset(instance->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c new file mode 100644 index 000000000000..991c956c1c00 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -0,0 +1,49 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Please enter a\n" + "different key.", + 4, + 24, + AlignLeft, + AlignTop); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_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, NfcSceneMfClassicKeysAdd); + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) { + NfcApp* instance = context; + + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c new file mode 100644 index 000000000000..89aa13bcca3a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c @@ -0,0 +1,144 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfClassicUpdateInitialStateCardSearch, + NfcSceneMfClassicUpdateInitialStateCardFound, +}; + +NfcCommand nfc_mf_classic_update_initial_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + const MfClassicPollerEvent* mfc_event = event.data; + NfcApp* instance = context; + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* updated_data = nfc_poller_get_data(instance->poller); + const MfClassicData* old_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + if(iso14443_3a_is_equal(updated_data->iso14443_3a_data, old_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + uint8_t sector_num = 0; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + if(mf_classic_key_cahce_get_next_key( + instance->mfc_key_cache, §or_num, &key, &key_type)) { + mfc_event->data->read_sector_request_data.sector_num = sector_num; + mfc_event->data->read_sector_request_data.key = key; + mfc_event->data->read_sector_request_data.key_type = key_type; + mfc_event->data->read_sector_request_data.key_provided = true; + } else { + mfc_event->data->read_sector_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + const MfClassicData* updated_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, updated_data); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_mf_classic_update_initial_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + + if(state == NfcSceneMfClassicUpdateInitialStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_initial_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + mf_classic_key_cache_load_from_data(instance->mfc_key_cache, mfc_data); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + nfc_scene_mf_classic_update_initial_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_mf_classic_update_initial_worker_callback, instance); + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_classic_update_initial_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardFound); + nfc_scene_mf_classic_update_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + nfc_scene_mf_classic_update_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventWorkerExit) { + if(nfc_save_shadow_file(instance)) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicUpdateInitialSuccess); + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_mf_classic_update_initial_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, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c new file mode 100644 index 000000000000..02e307b01baa --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_update_initial_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_initial_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_initial_success_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, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_initial_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c new file mode 100644 index 000000000000..4ff65b834dd2 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -0,0 +1,146 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfClassicWriteInitialStateCardSearch, + NfcSceneMfClassicWriteInitialStateCardFound, +}; + +NfcCommand + nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + MfClassicPollerEvent* mfc_event = event.data; + const MfClassicData* write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); + if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; + } else { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { + uint8_t sector = mfc_event->data->sec_tr_data.sector_num; + uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); + if(mf_classic_is_block_read(write_data, sec_tr)) { + mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; + mfc_event->data->sec_tr_data.sector_trailer_provided = true; + } else { + mfc_event->data->sec_tr_data.sector_trailer_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { + uint8_t block_num = mfc_event->data->write_block_data.block_num; + if(mf_classic_is_block_read(write_data, block_num)) { + mfc_event->data->write_block_data.write_block = write_data->block[block_num]; + mfc_event->data->write_block_data.write_block_provided = true; + } else { + mfc_event->data->write_block_data.write_block_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeFail) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } + return command; +} + +static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); + + if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_initial_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + nfc_scene_mf_classic_write_initial_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start( + instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance); + + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardFound); + nfc_scene_mf_classic_write_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + nfc_scene_mf_classic_write_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_write_initial_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, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c new file mode 100644 index 000000000000..f85e5a80c3f8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c @@ -0,0 +1,62 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_write_initial_fail_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_classic_write_initial_fail_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Finish", + nfc_scene_mf_classic_write_initial_fail_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c new file mode 100644 index 000000000000..acb75cd2e9f8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_initial_success_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, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_initial_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 000000000000..50025048af43 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,57 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_wrong_card_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_classic_wrong_card_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_classic_wrong_card_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(instance->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 106bcf94ffd7..0cb26c0d45af 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -25,16 +25,13 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - // if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { - // consumed = scene_manager_search_and_switch_to_previous_scene( - // nfc->scene_manager, NfcSceneMfClassicKeys); - // } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { - // consumed = scene_manager_search_and_switch_to_previous_scene( - // nfc->scene_manager, NfcSceneSavedMenu); - // } else { - consumed = scene_manager_search_and_switch_to_another_scene( - nfc->scene_manager, NfcSceneFileSelect); - // } + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); + } } } return consumed; diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index 8f4bd063e8b8..b4a21bc57e13 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -2,42 +2,34 @@ #include -typedef enum { - DictAttackStateRead, - DictAttackStateCardRemoved, -} DictAttackState; - struct DictAttack { View* view; DictAttackCallback callback; void* context; - bool card_present; }; typedef struct { - DictAttackState state; - MfClassicType type; FuriString* header; + bool card_detected; uint8_t sectors_total; uint8_t sectors_read; - uint8_t sector_current; - uint8_t keys_total; + uint8_t current_sector; uint8_t keys_found; - uint16_t dict_keys_total; - uint16_t dict_keys_current; + size_t dict_keys_total; + size_t dict_keys_current; bool is_key_attack; uint8_t key_attack_current_sector; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { DictAttackViewModel* m = model; - if(m->state == DictAttackStateCardRemoved) { + if(!m->card_detected) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); - } else if(m->state == DictAttackStateRead) { + } else { char draw_str[32] = {}; canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( @@ -49,14 +41,14 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { "Reuse key check for sector: %d", m->key_attack_current_sector); } else { - snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); + 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 = m->dict_keys_total == 0 ? 0 : (float)(m->dict_keys_current) / (float)(m->dict_keys_total); float progress = m->sectors_total == 0 ? 0 : - ((float)(m->sector_current) + dict_progress) / + ((float)(m->current_sector) + dict_progress) / (float)(m->sectors_total); if(progress > 1.0) { progress = 1.0; @@ -70,7 +62,8 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { } 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->keys_total); + snprintf( + draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->sectors_total * 2); 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); @@ -80,55 +73,56 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { } static bool dict_attack_input_callback(InputEvent* event, void* context) { - DictAttack* dict_attack = context; + DictAttack* instance = context; bool consumed = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { - if(dict_attack->callback) { - dict_attack->callback(dict_attack->context); + if(instance->callback) { + instance->callback(DictAttackEventSkipPressed, instance->context); } consumed = true; } + return consumed; } DictAttack* dict_attack_alloc() { - DictAttack* dict_attack = malloc(sizeof(DictAttack)); - dict_attack->view = view_alloc(); - view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); - view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); - view_set_input_callback(dict_attack->view, dict_attack_input_callback); - view_set_context(dict_attack->view, dict_attack); + DictAttack* instance = malloc(sizeof(DictAttack)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); + view_set_draw_callback(instance->view, dict_attack_draw_callback); + view_set_input_callback(instance->view, dict_attack_input_callback); + view_set_context(instance->view, instance); with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { model->header = furi_string_alloc(); }, false); - return dict_attack; + + return instance; } -void dict_attack_free(DictAttack* dict_attack) { - furi_assert(dict_attack); +void dict_attack_free(DictAttack* instance) { + furi_assert(instance); + with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { furi_string_free(model->header); }, - false); - view_free(dict_attack->view); - free(dict_attack); + instance->view, DictAttackViewModel * model, { furi_string_free(model->header); }, false); + + view_free(instance->view); + free(instance); } -void dict_attack_reset(DictAttack* dict_attack) { - furi_assert(dict_attack); +void dict_attack_reset(DictAttack* instance) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { - model->state = DictAttackStateRead; - model->type = MfClassicType1k; + model->card_detected = false; model->sectors_total = 0; model->sectors_read = 0; - model->sector_current = 0; - model->keys_total = 0; + model->current_sector = 0; model->keys_found = 0; model->dict_keys_total = 0; model->dict_keys_current = 0; @@ -138,152 +132,108 @@ void dict_attack_reset(DictAttack* dict_attack) { false); } -View* dict_attack_get_view(DictAttack* dict_attack) { - furi_assert(dict_attack); - return dict_attack->view; +View* dict_attack_get_view(DictAttack* instance) { + furi_assert(instance); + + return instance->view; } -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { - furi_assert(dict_attack); +void dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context) { + furi_assert(instance); furi_assert(callback); - dict_attack->callback = callback; - dict_attack->context = context; + + instance->callback = callback; + instance->context = context; } -void dict_attack_set_header(DictAttack* dict_attack, const char* header) { - furi_assert(dict_attack); +void dict_attack_set_header(DictAttack* instance, const char* header) { + furi_assert(instance); furi_assert(header); with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { furi_string_set(model->header, header); }, true); } -void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { - furi_assert(dict_attack); - dict_attack->card_present = true; +void dict_attack_set_card_state(DictAttack* instance, bool detected) { + furi_assert(instance); + with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->state = DictAttackStateRead; - model->sectors_total = mf_classic_get_total_sectors_num(type); - model->keys_total = model->sectors_total * 2; - }, - true); + instance->view, DictAttackViewModel * model, { model->card_detected = detected; }, true); } -void dict_attack_set_card_removed(DictAttack* dict_attack) { - furi_assert(dict_attack); - dict_attack->card_present = false; +void dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, - { model->state = DictAttackStateCardRemoved; }, + { model->sectors_total = sectors_total; }, true); } -bool dict_attack_get_card_state(DictAttack* dict_attack) { - furi_assert(dict_attack); - return dict_attack->card_present; -} +void dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read) { + furi_assert(instance); -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { - furi_assert(dict_attack); with_view_model( - dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); + instance->view, DictAttackViewModel * model, { model->sectors_read = sectors_read; }, true); } -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); -} +void dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found) { + furi_assert(instance); -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { - furi_assert(dict_attack); with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->sector_current = curr_sec; - model->dict_keys_current = 0; - }, - true); + instance->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); } -void dict_attack_inc_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->sector_current < model->sectors_total) { - model->sector_current++; - model->dict_keys_current = 0; - } - }, - true); -} +void dict_attack_set_current_sector(DictAttack* instance, uint8_t current_sector) { + furi_assert(instance); -void dict_attack_inc_keys_found(DictAttack* dict_attack) { - furi_assert(dict_attack); with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, - { - if(model->keys_found < model->keys_total) { - model->keys_found++; - } - }, + { model->current_sector = current_sector; }, true); } -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { - furi_assert(dict_attack); +void dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { model->dict_keys_total = dict_keys_total; }, true); } -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { - furi_assert(dict_attack); +void dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, - { - if(model->dict_keys_current + keys_tried < model->dict_keys_total) { - model->dict_keys_current += keys_tried; - } - }, + { model->dict_keys_current = cur_key_num; }, true); } -void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { - furi_assert(dict_attack); +void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { - model->is_key_attack = is_key_attack; + model->is_key_attack = true; model->key_attack_current_sector = sector; }, true); } -void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); +void dict_attack_reset_key_attack(DictAttack* instance) { + furi_assert(instance); + with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->key_attack_current_sector < model->sectors_total) { - model->key_attack_current_sector++; - } - }, - true); + instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true); } diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index 940fb74e9685..54a0220fe590 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -1,46 +1,50 @@ #pragma once + #include #include -#include -#include +#ifdef __cplusplus +extern "C" { +#endif typedef struct DictAttack DictAttack; -typedef void (*DictAttackCallback)(void* context); - -DictAttack* dict_attack_alloc(); +typedef enum { + DictAttackEventSkipPressed, +} DictAttackEvent; -void dict_attack_free(DictAttack* dict_attack); +typedef void (*DictAttackCallback)(DictAttackEvent event, void* context); -void dict_attack_reset(DictAttack* dict_attack); +DictAttack* dict_attack_alloc(); -View* dict_attack_get_view(DictAttack* dict_attack); +void dict_attack_free(DictAttack* instance); -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); +void dict_attack_reset(DictAttack* instance); -void dict_attack_set_header(DictAttack* dict_attack, const char* header); +View* dict_attack_get_view(DictAttack* instance); -void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); +void dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context); -void dict_attack_set_card_removed(DictAttack* dict_attack); +void dict_attack_set_header(DictAttack* instance, const char* header); -bool dict_attack_get_card_state(DictAttack* dict_attack); +void dict_attack_set_card_state(DictAttack* instance, bool detected); -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); +void dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total); -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); +void dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read); -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); +void dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found); -void dict_attack_inc_current_sector(DictAttack* dict_attack); +void dict_attack_set_current_sector(DictAttack* instance, uint8_t curr_sec); -void dict_attack_inc_keys_found(DictAttack* dict_attack); +void dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total); -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); +void dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num); -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); +void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector); -void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); +void dict_attack_reset_key_attack(DictAttack* instance); -void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); \ No newline at end of file +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2486c0b72cdd..62bf6b0b8376 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,36.0,, +Version,+,35.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2078,6 +2078,7 @@ Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType Function,+,mf_classic_get_total_sectors_num,uint8_t,MfClassicType Function,+,mf_classic_get_uid,const uint8_t*,"const MfClassicData*, size_t*" Function,+,mf_classic_is_allowed_access,_Bool,"MfClassicData*, uint8_t, MfClassicKeyType, MfClassicAction" +Function,+,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicSectorTrailer*, uint8_t, MfClassicKeyType, MfClassicAction" Function,+,mf_classic_is_block_read,_Bool,"const MfClassicData*, uint8_t" Function,+,mf_classic_is_card_read,_Bool,const MfClassicData* Function,+,mf_classic_is_equal,_Bool,"const MfClassicData*, const MfClassicData*" @@ -2088,6 +2089,7 @@ Function,-,mf_classic_is_value_block,_Bool,"MfClassicData*, uint8_t" Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t" Function,+,mf_classic_poller_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" Function,+,mf_classic_poller_change_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t, int32_t*" +Function,+,mf_classic_poller_read,MfClassicError,"Nfc*, const MfClassicDeviceKeys*, MfClassicData*" Function,+,mf_classic_poller_read_block,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicBlock*" Function,+,mf_classic_poller_read_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t*" Function,+,mf_classic_poller_write_block,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicBlock*" diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index 71d3e5147d02..ad9a8551f470 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -370,32 +370,29 @@ bool mf_classic_detect_protocol(Iso14443_3aData* data, MfClassicType* type) { uint8_t atqa1 = data->atqa[1]; uint8_t sak = data->sak; bool mf_classic_detected = false; + MfClassicType tmp_type = MfClassicTypeMini; - if((atqa0 = 0x44) || (atqa0 = 0x44)) { - if((sak == 0x08) || (sak = 0x88)) { - if(type) { - *type = MfClassicType1k; - } + if((atqa0 == 0x44) || (atqa0 == 0x04)) { + if((sak == 0x08) || (sak == 0x88)) { + tmp_type = MfClassicType1k; mf_classic_detected = true; } else if(sak == 0x09) { - if(type) { - *type = MfClassicTypeMini; - } + tmp_type = MfClassicTypeMini; mf_classic_detected = true; } } else if((atqa0 == 0x01) && (atqa1 == 0x0f) && (sak == 0x01)) { // Skylender support - if(type) { - *type = MfClassicType1k; - } + tmp_type = MfClassicType1k; mf_classic_detected = true; } else if(((atqa0 == 0x42) || (atqa0 == 0x02)) && (sak == 0x18)) { - if(*type) { - *type = MfClassicType4k; - } + tmp_type = MfClassicType4k; mf_classic_detected = true; } + if(type) { + *type = tmp_type; + } + return mf_classic_detected; } @@ -673,13 +670,13 @@ static bool mf_classic_is_allowed_access_sector_trailer( return true; } -static bool mf_classic_is_allowed_access_data_block( - MfClassicData* data, +bool mf_classic_is_allowed_access_data_block( + MfClassicSectorTrailer* sec_tr, uint8_t block_num, MfClassicKeyType key_type, MfClassicAction action) { - uint8_t sector_num = mf_classic_get_sector_by_block(block_num); - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + furi_assert(sec_tr); + uint8_t* access_bits_arr = sec_tr->access_bits.data; if(block_num == 0 && action == MfClassicActionDataWrite) { @@ -755,8 +752,10 @@ bool mf_classic_is_allowed_access( access_allowed = mf_classic_is_allowed_access_sector_trailer(data, block_num, key_type, action); } else { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); access_allowed = - mf_classic_is_allowed_access_data_block(data, block_num, key_type, action); + mf_classic_is_allowed_access_data_block(sec_tr, block_num, key_type, action); } return access_allowed; @@ -765,11 +764,14 @@ bool mf_classic_is_allowed_access( bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) { furi_assert(data); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + // Check if key A can write, if it can, it's transport configuration, not data block return !mf_classic_is_allowed_access_data_block( - data, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) && + sec_tr, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) && (mf_classic_is_allowed_access_data_block( - data, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) || + sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) || mf_classic_is_allowed_access_data_block( - data, block_num, MfClassicKeyTypeB, MfClassicActionDataDec)); + sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataDec)); } diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h index 312cb7ed3618..47a8649af661 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.h +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -120,10 +120,10 @@ typedef struct { } MfClassicSectorTrailer; typedef struct { - MfClassicKey key_a[MF_CLASSIC_TOTAL_SECTORS_MAX]; - MfClassicKey key_b[MF_CLASSIC_TOTAL_SECTORS_MAX]; uint64_t key_a_mask; + MfClassicKey key_a[MF_CLASSIC_TOTAL_SECTORS_MAX]; uint64_t key_b_mask; + MfClassicKey key_b[MF_CLASSIC_TOTAL_SECTORS_MAX]; } MfClassicDeviceKeys; typedef struct { @@ -217,6 +217,12 @@ void mf_classic_get_read_sectors_and_keys( bool mf_classic_is_card_read(const MfClassicData* data); +bool mf_classic_is_allowed_access_data_block( + MfClassicSectorTrailer* sec_tr, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action); + bool mf_classic_is_allowed_access( MfClassicData* data, uint8_t block_num, diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index 0d11dc2574a2..b8def569b54f 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -50,82 +50,315 @@ void mf_classic_poller_free(MfClassicPoller* instance) { free(instance); } -NfcCommand mf_classic_poller_handler_idle(MfClassicPoller* instance) { +static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance) { + MfClassicPollerEventDataUpdate* data_update = &instance->mfc_event_data.data_update; + + mf_classic_get_read_sectors_and_keys( + instance->data, &data_update->sectors_read, &data_update->keys_found); + data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector; + instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate; + return instance->callback(instance->general_event, instance->context); +} + +NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + iso14443_3a_copy( instance->data->iso14443_3a_data, iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + mf_classic_detect_protocol(instance->data->iso14443_3a_data, &instance->data->type); + instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type); + + memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext)); + + instance->mfc_event.type = MfClassicPollerEventTypeRequestMode; + command = instance->callback(instance->general_event, instance->context); + + if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttack) { + mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data); + instance->state = MfClassicPollerStateRequestKey; + } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) { + instance->state = MfClassicPollerStateRequestReadSector; + } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) { + instance->state = MfClassicPollerStateRequestSectorTrailer; + } else { + furi_crash("Invalid mode selected"); + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_sector_trailer(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; - if(mf_classic_detect_protocol(instance->data->iso14443_3a_data, &instance->data->type)) { - if(instance->card_state == MfClassicCardStateNotDetected) { - instance->card_state = MfClassicCardStateDetected; - instance->mfc_event.type = MfClassicPollerEventTypeCardDetected; - command = instance->callback(instance->general_event, instance->context); + if(write_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateSuccess; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeRequestSectorTrailer; + instance->mfc_event_data.sec_tr_data.sector_num = write_ctx->current_sector; + command = instance->callback(instance->general_event, instance->context); + if(instance->mfc_event_data.sec_tr_data.sector_trailer_provided) { + instance->state = MfClassicPollerStateCheckWriteConditions; + memcpy( + &write_ctx->sec_tr, + &instance->mfc_event_data.sec_tr_data.sector_trailer, + sizeof(MfClassicSectorTrailer)); + write_ctx->current_block = + MAX(1, mf_classic_get_first_block_num_of_sector(write_ctx->current_sector)); + + } else { + write_ctx->current_sector++; } - instance->state = instance->prev_state; } return command; } -NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) { +NfcCommand mf_classic_handler_check_write_conditions(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; - instance->sectors_read = 0; - instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type); + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + MfClassicSectorTrailer* sec_tr = &write_ctx->sec_tr; - instance->mfc_event_data.start_data.type = instance->data->type; - command = instance->callback(instance->general_event, instance->context); + do { + // Check last block in sector to write + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(write_ctx->current_sector); + if(write_ctx->current_block == sec_tr_block_num) { + write_ctx->current_sector++; + instance->state = MfClassicPollerStateRequestSectorTrailer; + break; + } + + // Check write and read access + if(mf_classic_is_allowed_access_data_block( + sec_tr, write_ctx->current_block, MfClassicKeyTypeA, MfClassicActionDataWrite)) { + write_ctx->key_type_write = MfClassicKeyTypeA; + } else if(mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + MfClassicKeyTypeB, + MfClassicActionDataWrite)) { + write_ctx->key_type_write = MfClassicKeyTypeB; + } else { + FURI_LOG_D(TAG, "Not allowed to write block %d", write_ctx->current_block); + write_ctx->current_block++; + break; + } + + if(mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + write_ctx->key_type_write, + MfClassicActionDataRead)) { + write_ctx->key_type_read = write_ctx->key_type_write; + } else { + write_ctx->key_type_read = write_ctx->key_type_write == MfClassicKeyTypeA ? + MfClassicKeyTypeB : + MfClassicKeyTypeA; + if(!mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + write_ctx->key_type_read, + MfClassicActionDataRead)) { + FURI_LOG_D(TAG, "Not allowed to read block %d", write_ctx->current_block); + write_ctx->current_block++; + break; + } + } - instance->prev_state = MfClassicPollerStateStart; - instance->state = MfClassicPollerStateNewSector; + write_ctx->need_halt_before_write = + (write_ctx->key_type_read != write_ctx->key_type_write); + instance->state = MfClassicPollerStateReadBlock; + } while(false); return command; } -NfcCommand mf_classic_poller_handler_new_sector(MfClassicPoller* instance) { +NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + + MfClassicKey* auth_key = write_ctx->key_type_read == MfClassicKeyTypeA ? + &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + MfClassicError error = MfClassicErrorNone; + + do { + // Authenticate to sector + error = mf_classic_async_auth( + instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } - if(instance->read_mode == MfClassicReadModeKeyReuse) { - instance->key_reuse_sector++; - if(instance->key_reuse_sector == instance->sectors_total) { - instance->read_mode = MfClassicReadModeDictAttack; - instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop; - command = instance->callback(instance->general_event, instance->context); - } else if(mf_classic_is_sector_read(instance->data, instance->sectors_read)) { - instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackNextSector; - command = instance->callback(instance->general_event, instance->context); - } else { - instance->state = MfClassicPollerStateAuthKeyA; - instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackNextSector; - command = instance->callback(instance->general_event, instance->context); + // Read block from tag + error = + mf_classic_async_read_block(instance, write_ctx->current_block, &write_ctx->tag_block); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + if(write_ctx->need_halt_before_write) { + mf_classic_async_halt(instance); + } + instance->state = MfClassicPollerStateWriteBlock; + } while(false); + + return command; +} + +NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + MfClassicKey* auth_key = write_ctx->key_type_write == MfClassicKeyTypeA ? + &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + MfClassicError error = MfClassicErrorNone; + + do { + // Request block to write + instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock; + instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block; + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfc_event_data.write_block_data.write_block_provided) break; + + // Compare tag and saved block + if(memcmp( + write_ctx->tag_block.data, + instance->mfc_event_data.write_block_data.write_block.data, + sizeof(MfClassicBlock)) == 0) { + FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block); + break; + } + + // Reauth if necessary + if(write_ctx->need_halt_before_write) { + error = mf_classic_async_auth( + instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D( + TAG, "Failed to auth to block %d for writing", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + } + + // Write block + error = mf_classic_async_write_block( + instance, + write_ctx->current_block, + &instance->mfc_event_data.write_block_data.write_block); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to write block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; } + + } while(false); + + mf_classic_async_halt(instance); + write_ctx->current_block++; + instance->state = MfClassicPollerStateCheckWriteConditions; + + return command; +} + +NfcCommand mf_classic_poller_handler_request_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx; + MfClassicPollerEventDataReadSectorRequest* sec_read = + &instance->mfc_event_data.read_sector_request_data; + instance->mfc_event.type = MfClassicPollerEventTypeRequestReadSector; + command = instance->callback(instance->general_event, instance->context); + + if(!sec_read->key_provided) { + instance->state = MfClassicPollerStateSuccess; } else { - if(instance->sectors_read == instance->sectors_total) { - instance->state = MfClassicPollerStateReadComplete; - } else if(mf_classic_is_sector_read(instance->data, instance->sectors_read)) { - instance->sectors_read++; - instance->mfc_event.type = MfClassicPollerEventTypeNewSector; - command = instance->callback(instance->general_event, instance->context); + sec_read_ctx->current_sector = sec_read->sector_num; + sec_read_ctx->key = sec_read->key; + sec_read_ctx->key_type = sec_read->key_type; + sec_read_ctx->current_block = + mf_classic_get_first_block_num_of_sector(sec_read->sector_num); + sec_read_ctx->auth_passed = false; + instance->state = MfClassicPollerStateReadSectorBlocks; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx; + + do { + MfClassicError error = MfClassicErrorNone; + + if(!sec_read_ctx->auth_passed) { + uint64_t key = nfc_util_bytes2num(sec_read_ctx->key.data, sizeof(MfClassicKey)); + FURI_LOG_D( + TAG, + "Auth to block %d with key %c: %06llx", + sec_read_ctx->current_block, + sec_read_ctx->key_type == MfClassicKeyTypeA ? 'A' : 'B', + key); + error = mf_classic_async_auth( + instance, + sec_read_ctx->current_block, + &sec_read_ctx->key, + sec_read_ctx->key_type, + NULL); + if(error != MfClassicErrorNone) break; + + sec_read_ctx->auth_passed = true; + if(!mf_classic_is_key_found( + instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type)) { + mf_classic_set_key_found( + instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type, key); + } + } + if(mf_classic_is_block_read(instance->data, sec_read_ctx->current_block)) break; + + FURI_LOG_D(TAG, "Reading block %d", sec_read_ctx->current_block); + MfClassicBlock read_block = {}; + error = mf_classic_async_read_block(instance, sec_read_ctx->current_block, &read_block); + if(error == MfClassicErrorNone) { + mf_classic_set_block_read(instance->data, sec_read_ctx->current_block, &read_block); } else { - instance->state = MfClassicPollerStateRequestKey; + mf_classic_async_halt(instance); + sec_read_ctx->auth_passed = false; } + } while(false); + + uint8_t sec_tr_num = mf_classic_get_sector_trailer_num_by_sector(sec_read_ctx->current_sector); + sec_read_ctx->current_block++; + if(sec_read_ctx->current_block > sec_tr_num) { + mf_classic_async_halt(instance); + instance->state = MfClassicPollerStateRequestReadSector; } - instance->prev_state = instance->state; return command; } NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; - instance->mfc_event_data.key_request_data.sector_num = instance->sectors_read; + instance->mfc_event.type = MfClassicPollerEventTypeRequestKey; command = instance->callback(instance->general_event, instance->context); if(instance->mfc_event_data.key_request_data.key_provided) { - instance->current_key = instance->mfc_event_data.key_request_data.key; + dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key; instance->state = MfClassicPollerStateAuthKeyA; } else { - instance->state = MfClassicPollerStateNewSector; + instance->state = MfClassicPollerStateNextSector; } return command; @@ -133,47 +366,31 @@ NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) { NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; - uint8_t sector = 0; - if(instance->read_mode == MfClassicReadModeKeyReuse) { - sector = instance->key_reuse_sector; - } else { - sector = instance->sectors_read; - } - - if(mf_classic_is_key_found(instance->data, sector, MfClassicKeyTypeA)) { - instance->prev_state = instance->state; + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) { instance->state = MfClassicPollerStateAuthKeyB; } else { - uint8_t block = mf_classic_get_first_block_num_of_sector(sector); - uint64_t key = nfc_util_bytes2num(instance->current_key.data, 6); + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key); MfClassicError error = mf_classic_async_auth( - instance, block, &instance->current_key, MfClassicKeyTypeA, NULL); + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL); if(error == MfClassicErrorNone) { FURI_LOG_I(TAG, "Key A found"); - mf_classic_set_key_found(instance->data, sector, MfClassicKeyTypeA, key); - if(instance->read_mode != MfClassicReadModeKeyReuse) { - if(sector < instance->sectors_total - 1) { - instance->read_mode = MfClassicReadModeKeyReuse; - instance->key_reuse_sector = sector; - instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; - instance->mfc_event_data.key_attack_data.start_sector = - instance->key_reuse_sector; - command = instance->callback(instance->general_event, instance->context); - } - } - instance->mfc_event.type = MfClassicPollerEventTypeFoundKeyA; - command = instance->callback(instance->general_event, instance->context); - instance->prev_state = instance->state; + mf_classic_set_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + dict_attack_ctx->current_block = block; + dict_attack_ctx->auth_passed = true; instance->state = MfClassicPollerStateReadSector; } else { - if(instance->read_mode == MfClassicReadModeKeyReuse) { - instance->state = MfClassicPollerStateAuthKeyB; - } else { - instance->state = MfClassicPollerStateRequestKey; - } + mf_classic_async_halt(instance); + instance->state = MfClassicPollerStateAuthKeyB; } } @@ -182,100 +399,308 @@ NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) { NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; - uint8_t sector = 0; - if(instance->read_mode == MfClassicReadModeKeyReuse) { - sector = instance->key_reuse_sector; - } else { - sector = instance->sectors_read; - } - - if(mf_classic_is_key_found(instance->data, sector, MfClassicKeyTypeB)) { - instance->state = MfClassicPollerStateNewSector; + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) { + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateNextSector; + } else { + instance->state = MfClassicPollerStateRequestKey; + } } else { - uint8_t block = mf_classic_get_first_block_num_of_sector(sector); - uint64_t key = nfc_util_bytes2num(instance->current_key.data, 6); + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key); MfClassicError error = mf_classic_async_auth( - instance, block, &instance->current_key, MfClassicKeyTypeB, NULL); + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL); if(error == MfClassicErrorNone) { - FURI_LOG_D(TAG, "Key B found"); - instance->mfc_event.type = MfClassicPollerEventTypeFoundKeyB; - mf_classic_set_key_found(instance->data, sector, MfClassicKeyTypeB, key); - if(instance->read_mode != MfClassicReadModeKeyReuse) { - if(sector < instance->sectors_total - 1) { - instance->read_mode = MfClassicReadModeKeyReuse; - instance->key_reuse_sector = sector; - - instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; - instance->mfc_event_data.key_attack_data.start_sector = - instance->key_reuse_sector; - command = instance->callback(instance->general_event, instance->context); - } - } - command = instance->callback(instance->general_event, instance->context); + FURI_LOG_I(TAG, "Key B found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + dict_attack_ctx->current_block = block; + + dict_attack_ctx->auth_passed = true; instance->state = MfClassicPollerStateReadSector; } else { - if(instance->read_mode == MfClassicReadModeKeyReuse) { - instance->state = MfClassicPollerStateNewSector; - } else { - instance->state = MfClassicPollerStateRequestKey; - } + mf_classic_async_halt(instance); + instance->state = MfClassicPollerStateRequestKey; } } - instance->prev_state = instance->state; + + return command; +} + +NfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + dict_attack_ctx->current_sector++; + if(dict_attack_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateSuccess; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeNextSector; + instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateRequestKey; + } return command; } NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; - uint8_t block_start = mf_classic_get_first_block_num_of_sector(instance->sectors_read); - uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(instance->sectors_read); + MfClassicError error = MfClassicErrorNone; + uint8_t block_num = dict_attack_ctx->current_block; MfClassicBlock block = {}; - for(size_t i = block_start; i < block_start + total_blocks; i++) { - FURI_LOG_D(TAG, "Reding block %d", i); - if(mf_classic_is_block_read(instance->data, i)) continue; - MfClassicError error = mf_classic_async_read_block(instance, i, &block); + do { + if(mf_classic_is_block_read(instance->data, block_num)) break; + + if(!dict_attack_ctx->auth_passed) { + error = mf_classic_async_auth( + instance, + block_num, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + NULL); + if(error != MfClassicErrorNone) { + instance->state = MfClassicPollerStateNextSector; + FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector"); + break; + } + } + + FURI_LOG_D(TAG, "Reading block %d", block_num); + error = mf_classic_async_read_block(instance, block_num, &block); + + if(error != MfClassicErrorNone) { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + FURI_LOG_D(TAG, "Failed to read block %d", block_num); + } else { + mf_classic_set_block_read(instance->data, block_num, &block); + } + } while(false); + + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->current_sector); + dict_attack_ctx->current_block++; + if(dict_attack_ctx->current_block > sec_tr_block_num) { + mf_classic_poller_handle_data_update(instance); + + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + + if(dict_attack_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateNextSector; + } else { + dict_attack_ctx->reuse_key_sector = dict_attack_ctx->current_sector; + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; + instance->mfc_event_data.key_attack_data.current_sector = + dict_attack_ctx->reuse_key_sector; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_start(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) { + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + instance->state = MfClassicPollerStateKeyReuseAuthKeyB; + } else { + dict_attack_ctx->reuse_key_sector++; + if(dict_attack_ctx->reuse_key_sector > instance->sectors_total) { + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateRequestKey; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; + instance->mfc_event_data.key_attack_data.current_sector = + dict_attack_ctx->reuse_key_sector; + command = instance->callback(instance->general_event, instance->context); + + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + instance->state = MfClassicPollerStateKeyReuseAuthKeyA; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateKeyReuseStart; + } else { + uint8_t block = + mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key); + + MfClassicError error = mf_classic_async_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL); if(error == MfClassicErrorNone) { - mf_classic_set_block_read(instance->data, i, &block); + FURI_LOG_I(TAG, "Key A found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + dict_attack_ctx->current_block = block; + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateKeyReuseReadSector; } else { - FURI_LOG_D(TAG, "Failed to read %d block", i); - break; + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + instance->state = MfClassicPollerStateKeyReuseStart; } } - instance->prev_state = instance->state; - if(instance->prev_state == MfClassicPollerStateAuthKeyA) { - instance->state = MfClassicPollerStateAuthKeyB; + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB)) { + instance->state = MfClassicPollerStateKeyReuseStart; } else { - instance->state = MfClassicPollerStateNewSector; + uint8_t block = + mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key); + + MfClassicError error = mf_classic_async_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key B found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + dict_attack_ctx->current_block = block; + + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateKeyReuseReadSector; + } else { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + instance->state = MfClassicPollerStateKeyReuseStart; + } } return command; } -NfcCommand mf_classic_poller_handler_read_complete(MfClassicPoller* instance) { +NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* instance) { NfcCommand command = NfcCommandContinue; - instance->mfc_event.type = MfClassicPollerEventTypeReadComplete; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + MfClassicError error = MfClassicErrorNone; + uint8_t block_num = dict_attack_ctx->current_block; + MfClassicBlock block = {}; + + do { + if(mf_classic_is_block_read(instance->data, block_num)) break; + + if(!dict_attack_ctx->auth_passed) { + error = mf_classic_async_auth( + instance, + block_num, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + NULL); + if(error != MfClassicErrorNone) { + instance->state = MfClassicPollerStateKeyReuseStart; + break; + } + } + + FURI_LOG_D(TAG, "Reading block %d", block_num); + error = mf_classic_async_read_block(instance, block_num, &block); + + if(error != MfClassicErrorNone) { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + FURI_LOG_D(TAG, "Failed to read block %d", block_num); + } else { + mf_classic_set_block_read(instance->data, block_num, &block); + } + } while(false); + + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->reuse_key_sector); + dict_attack_ctx->current_block++; + if(dict_attack_ctx->current_block > sec_tr_block_num) { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + + mf_classic_poller_handle_data_update(instance); + instance->state = MfClassicPollerStateKeyReuseStart; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_success(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->mfc_event.type = MfClassicPollerEventTypeSuccess; + command = instance->callback(instance->general_event, instance->context); + + return command; +} + +NfcCommand mf_classic_poller_handler_fail(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->mfc_event.type = MfClassicPollerEventTypeFail; command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateStart; return command; } static const MfClassicPollerReadHandler mf_classic_poller_dict_attack_handler[MfClassicPollerStateNum] = { - [MfClassicPollerStateIdle] = mf_classic_poller_handler_idle, [MfClassicPollerStateStart] = mf_classic_poller_handler_start, - [MfClassicPollerStateNewSector] = mf_classic_poller_handler_new_sector, + [MfClassicPollerStateRequestSectorTrailer] = + mf_classic_poller_handler_request_sector_trailer, + [MfClassicPollerStateCheckWriteConditions] = mf_classic_handler_check_write_conditions, + [MfClassicPollerStateReadBlock] = mf_classic_poller_handler_read_block, + [MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block, + [MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector, [MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key, + [MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector, + [MfClassicPollerStateReadSectorBlocks] = + mf_classic_poller_handler_request_read_sector_blocks, [MfClassicPollerStateAuthKeyA] = mf_classic_poller_handler_auth_a, [MfClassicPollerStateAuthKeyB] = mf_classic_poller_handler_auth_b, [MfClassicPollerStateReadSector] = mf_classic_poller_handler_read_sector, - [MfClassicPollerStateReadComplete] = mf_classic_poller_handler_read_complete, + [MfClassicPollerStateKeyReuseStart] = mf_classic_poller_handler_key_reuse_start, + [MfClassicPollerStateKeyReuseAuthKeyA] = mf_classic_poller_handler_key_reuse_auth_key_a, + [MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b, + [MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector, + [MfClassicPollerStateSuccess] = mf_classic_poller_handler_success, + [MfClassicPollerStateFail] = mf_classic_poller_handler_fail, }; NfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) { @@ -287,25 +712,19 @@ NfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) { Iso14443_3aPollerEvent* iso14443_3a_event = event.data; NfcCommand command = NfcCommandContinue; - UNUSED(mf_classic_poller_dict_attack_handler); if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { - // TODO: Temporary measure - iso14443_3a_copy( - instance->data->iso14443_3a_data, - iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); - instance->mfc_event.type = MfClassicPollerEventTypeReadComplete; - command = instance->callback(instance->general_event, instance->context); - - // command = mf_classic_poller_dict_attack_handler[instance->state](instance); - // } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { - // if(iso14443_3a_event->data->error == Iso14443_3aErrorNotPresent) { - // if(instance->card_state == MfClassicCardStateDetected) { - // instance->card_state = MfClassicCardStateNotDetected; - // instance->mfc_event.type = MfClassicPollerEventTypeCardNotDetected; - // command = instance->callback(instance->general_event, instance->context); - // instance->state = MfClassicPollerStateIdle; - // } - // } + if(instance->card_state == MfClassicCardStateLost) { + instance->card_state = MfClassicCardStateDetected; + instance->mfc_event.type = MfClassicPollerEventTypeCardDetected; + instance->callback(instance->general_event, instance->context); + } + command = mf_classic_poller_dict_attack_handler[instance->state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + if(instance->card_state == MfClassicCardStateDetected) { + instance->card_state = MfClassicCardStateLost; + instance->mfc_event.type = MfClassicPollerEventTypeCardLost; + command = instance->callback(instance->general_event, instance->context); + } } return command; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.h b/lib/nfc/protocols/mf_classic/mf_classic_poller.h index 4e2ae68ed633..da1f3c3dce54 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.h @@ -10,38 +10,93 @@ extern "C" { typedef struct MfClassicPoller MfClassicPoller; typedef enum { - MfClassicPollerEventTypeStart, + // Start event + MfClassicPollerEventTypeRequestMode, + + // Read with key cache events + MfClassicPollerEventTypeRequestReadSector, + + // Write events + MfClassicPollerEventTypeRequestSectorTrailer, + MfClassicPollerEventTypeRequestWriteBlock, + + // Dictionary attack events MfClassicPollerEventTypeRequestKey, - MfClassicPollerEventTypeNewSector, + MfClassicPollerEventTypeNextSector, + MfClassicPollerEventTypeDataUpdate, MfClassicPollerEventTypeFoundKeyA, MfClassicPollerEventTypeFoundKeyB, - MfClassicPollerEventTypeCardDetected, MfClassicPollerEventTypeCardNotDetected, MfClassicPollerEventTypeKeyAttackStart, MfClassicPollerEventTypeKeyAttackStop, MfClassicPollerEventTypeKeyAttackNextSector, - MfClassicPollerEventTypeReadComplete, + + // Common events + MfClassicPollerEventTypeCardDetected, + MfClassicPollerEventTypeCardLost, + MfClassicPollerEventTypeSuccess, + MfClassicPollerEventTypeFail, } MfClassicPollerEventType; +typedef enum { + MfClassicPollerModeRead, + MfClassicPollerModeWrite, + MfClassicPollerModeDictAttack, +} MfClassicPollerMode; + typedef struct { - MfClassicType type; -} MfClassicPollerEventDataStart; + MfClassicPollerMode mode; + const MfClassicData* data; +} MfClassicPollerEventDataRequestMode; + +typedef struct { + uint8_t current_sector; +} MfClassicPollerEventDataDictAttackNextSector; + +typedef struct { + uint8_t sectors_read; + uint8_t keys_found; + uint8_t current_sector; +} MfClassicPollerEventDataUpdate; typedef struct { - uint8_t sector_num; MfClassicKey key; bool key_provided; } MfClassicPollerEventDataKeyRequest; typedef struct { - uint8_t start_sector; + uint8_t sector_num; + MfClassicKey key; + MfClassicKeyType key_type; + bool key_provided; +} MfClassicPollerEventDataReadSectorRequest; + +typedef struct { + uint8_t sector_num; + MfClassicBlock sector_trailer; + bool sector_trailer_provided; +} MfClassicPollerEventDataSectorTrailerRequest; + +typedef struct { + uint8_t block_num; + MfClassicBlock write_block; + bool write_block_provided; +} MfClassicPollerEventDataWriteBlockRequest; + +typedef struct { + uint8_t current_sector; } MfClassicPollerEventKeyAttackData; typedef union { MfClassicError error; - MfClassicPollerEventDataStart start_data; + MfClassicPollerEventDataRequestMode poller_mode; + MfClassicPollerEventDataDictAttackNextSector next_sector_data; MfClassicPollerEventDataKeyRequest key_request_data; + MfClassicPollerEventDataUpdate data_update; + MfClassicPollerEventDataReadSectorRequest read_sector_request_data; MfClassicPollerEventKeyAttackData key_attack_data; + MfClassicPollerEventDataSectorTrailerRequest sec_tr_data; + MfClassicPollerEventDataWriteBlockRequest write_block_data; } MfClassicPollerEventData; typedef struct { diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c index 958565f6146d..58e8358f9dac 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c @@ -112,7 +112,7 @@ MfClassicError mf_classic_async_auth( return ret; } -MfClassicError mf_classic_aync_halt(MfClassicPoller* instance) { +MfClassicError mf_classic_async_halt(MfClassicPoller* instance) { MfClassicError ret = MfClassicErrorNone; Iso14443_3aError error = Iso14443_3aErrorNone; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h index 812865675386..a8d5a056d53b 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -15,41 +15,83 @@ typedef enum { } MfClassicAuthState; typedef enum { - MfClassicCardStateNotDetected, MfClassicCardStateDetected, + MfClassicCardStateLost, } MfClassicCardState; -typedef enum { - MfClassicReadModeDictAttack, - MfClassicReadModeKeyReuse, -} MfClassicReadMode; - typedef enum { MfClassicPollerStateStart, - MfClassicPollerStateIdle, - MfClassicPollerStateNewSector, + + // Write states + MfClassicPollerStateRequestSectorTrailer, + MfClassicPollerStateCheckWriteConditions, + MfClassicPollerStateReadBlock, + MfClassicPollerStateWriteBlock, + + // Read states + MfClassicPollerStateRequestReadSector, + MfClassicPollerStateReadSectorBlocks, + + // Dict attack states + MfClassicPollerStateNextSector, MfClassicPollerStateRequestKey, + MfClassicPollerStateReadSector, MfClassicPollerStateAuthKeyA, MfClassicPollerStateAuthKeyB, - MfClassicPollerStateReadSector, - MfClassicPollerStateReadComplete, + MfClassicPollerStateKeyReuseStart, + MfClassicPollerStateKeyReuseAuthKeyA, + MfClassicPollerStateKeyReuseAuthKeyB, + MfClassicPollerStateKeyReuseReadSector, + MfClassicPollerStateSuccess, + MfClassicPollerStateFail, MfClassicPollerStateNum, } MfClassicPollerState; +typedef struct { + uint8_t current_sector; + MfClassicSectorTrailer sec_tr; + uint8_t current_block; + MfClassicKeyType key_type_read; + MfClassicKeyType key_type_write; + bool need_halt_before_write; + MfClassicBlock tag_block; +} MfClassicPollerWriteContext; + +typedef struct { + uint8_t current_sector; + MfClassicKey current_key; + MfClassicKeyType current_key_type; + bool auth_passed; + uint8_t current_block; + uint8_t reuse_key_sector; +} MfClassicPollerDictAttackContext; + +typedef struct { + uint8_t current_sector; + uint16_t current_block; + MfClassicKeyType key_type; + MfClassicKey key; + bool auth_passed; +} MfClassicPollerReadContext; + +typedef union { + MfClassicPollerWriteContext write_ctx; + MfClassicPollerDictAttackContext dict_attack_ctx; + MfClassicPollerReadContext read_ctx; + +} MfClassicPollerModeContext; + struct MfClassicPoller { Iso14443_3aPoller* iso14443_3a_poller; MfClassicPollerState state; - MfClassicPollerState prev_state; MfClassicAuthState auth_state; MfClassicCardState card_state; - MfClassicReadMode read_mode; - MfClassicKey current_key; - uint8_t sectors_read; - uint8_t key_reuse_sector; uint8_t sectors_total; + MfClassicPollerModeContext mode_ctx; + Crypto1* crypto; BitBuffer* tx_plain_buffer; BitBuffer* tx_encrypted_buffer; @@ -94,12 +136,18 @@ typedef struct { int32_t new_value; } MfClassicChangeValueContext; +typedef struct { + MfClassicDeviceKeys keys; + uint8_t current_sector; +} MfClassicReadContext; + typedef union { MfClassicAuthContext auth_context; MfClassicReadBlockContext read_block_context; MfClassicWriteBlockContext write_block_context; MfClassicReadValueContext read_value_context; MfClassicChangeValueContext change_value_context; + MfClassicReadContext read_context; } MfClassicPollerContextData; MfClassicError mf_classic_process_error(Iso14443_3aError error); @@ -115,7 +163,7 @@ MfClassicError mf_classic_async_auth( MfClassicKeyType key_type, MfClassicAuthContext* data); -MfClassicError mf_classic_aync_halt(MfClassicPoller* instance); +MfClassicError mf_classic_async_halt(MfClassicPoller* instance); MfClassicError mf_classic_async_read_block(MfClassicPoller* instance, uint8_t block_num, MfClassicBlock* data); diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c index a787a43258b7..21db8cd6ae9b 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c @@ -56,7 +56,7 @@ static MfClassicError mf_classic_poller_read_block_handler( poller, data->read_block_context.block_num, &data->read_block_context.block); if(error != MfClassicErrorNone) break; - error = mf_classic_aync_halt(poller); + error = mf_classic_async_halt(poller); if(error != MfClassicErrorNone) break; } while(false); @@ -82,7 +82,7 @@ static MfClassicError mf_classic_poller_write_block_handler( poller, data->write_block_context.block_num, &data->write_block_context.block); if(error != MfClassicErrorNone) break; - error = mf_classic_aync_halt(poller); + error = mf_classic_async_halt(poller); if(error != MfClassicErrorNone) break; } while(false); @@ -113,7 +113,7 @@ static MfClassicError mf_classic_poller_read_value_handler( break; } - error = mf_classic_aync_halt(poller); + error = mf_classic_async_halt(poller); if(error != MfClassicErrorNone) break; } while(false); @@ -149,7 +149,7 @@ static MfClassicError mf_classic_poller_change_value_handler( error = mf_classic_async_read_block(poller, data->change_value_context.block_num, &block); if(error != MfClassicErrorNone) break; - error = mf_classic_aync_halt(poller); + error = mf_classic_async_halt(poller); if(error != MfClassicErrorNone) break; if(!mf_classic_block_to_value(&block, &data->change_value_context.new_value, NULL)) { @@ -350,3 +350,113 @@ MfClassicError mf_classic_poller_change_value( return error; } + +static bool mf_classic_poller_read_get_next_key( + MfClassicReadContext* read_ctx, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type) { + bool next_key_found = false; + + for(uint8_t i = read_ctx->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + if(FURI_BIT(read_ctx->keys.key_a_mask, i)) { + FURI_BIT_CLEAR(read_ctx->keys.key_a_mask, i); + *key = read_ctx->keys.key_a[i]; + *key_type = MfClassicKeyTypeA; + *sector_num = i; + + next_key_found = true; + break; + } + if(FURI_BIT(read_ctx->keys.key_b_mask, i)) { + FURI_BIT_CLEAR(read_ctx->keys.key_b_mask, i); + *key = read_ctx->keys.key_b[i]; + *key_type = MfClassicKeyTypeB; + *sector_num = i; + + next_key_found = true; + read_ctx->current_sector = i; + break; + } + } + + return next_key_found; +} + +NfcCommand mf_classic_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + MfClassicPollerContext* poller_context = context; + MfClassicPollerEvent* mfc_event = event.data; + + if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + poller_context->error = MfClassicErrorNotPresent; + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + MfClassicPollerEventDataReadSectorRequest* req_data = + &mfc_event->data->read_sector_request_data; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + uint8_t sector_num = 0; + if(mf_classic_poller_read_get_next_key( + &poller_context->data.read_context, §or_num, &key, &key_type)) { + req_data->sector_num = sector_num; + req_data->key = key; + req_data->key_type = key_type; + req_data->key_provided = true; + } else { + req_data->key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + command = NfcCommandStop; + } + + if(command == NfcCommandStop) { + furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT); + } + + return command; +} + +MfClassicError + mf_classic_poller_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data) { + furi_assert(nfc); + furi_assert(keys); + furi_assert(data); + + MfClassicError error = MfClassicErrorNone; + MfClassicPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + poller_context.data.read_context.keys = *keys; + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic); + nfc_poller_start(poller, mf_classic_poller_read_callback, &poller_context); + furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + + if(poller_context.error != MfClassicErrorNone) { + error = poller_context.error; + } else { + const MfClassicData* mfc_data = nfc_poller_get_data(poller); + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + + mf_classic_get_read_sectors_and_keys(mfc_data, §ors_read, &keys_found); + if((sectors_read > 0) || (keys_found > 0)) { + mf_classic_copy(data, mfc_data); + } else { + error = MfClassicErrorNotPresent; + } + } + + nfc_poller_free(poller); + + return error; +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h index bf0ef05f7d2f..58c3b96f852a 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h @@ -43,6 +43,9 @@ MfClassicError mf_classic_poller_change_value( int32_t data, int32_t* new_value); +MfClassicError + mf_classic_poller_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data); + #ifdef __cplusplus } #endif