mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-11 06:09:08 -07:00
Merge branch 'dev' into astra/3746-mfp-detect
This commit is contained in:
1308
applications/main/nfc/api/mosgortrans/mosgortrans_util.c
Normal file
1308
applications/main/nfc/api/mosgortrans/mosgortrans_util.c
Normal file
File diff suppressed because it is too large
Load Diff
17
applications/main/nfc/api/mosgortrans/mosgortrans_util.h
Normal file
17
applications/main/nfc/api/mosgortrans/mosgortrans_util.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <bit_lib.h>
|
||||
#include <datetime.h>
|
||||
#include <furi/core/string.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -18,8 +18,8 @@ constexpr HashtableApiInterface nfc_application_hashtable_api_interface{
|
||||
.resolver_callback = &elf_resolve_from_hashtable,
|
||||
},
|
||||
/* pointers to application's API table boundaries */
|
||||
.table_cbegin = nfc_app_api_table.cbegin(),
|
||||
.table_cend = nfc_app_api_table.cend(),
|
||||
nfc_app_api_table.cbegin(),
|
||||
nfc_app_api_table.cend(),
|
||||
};
|
||||
|
||||
/* Casting to generic resolver to use in Composite API resolver */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "gallagher/gallagher_util.h"
|
||||
#include "mosgortrans/mosgortrans_util.h"
|
||||
|
||||
/*
|
||||
* A list of app's private functions and objects to expose for plugins.
|
||||
@@ -10,4 +11,8 @@ static constexpr auto nfc_app_api_table = sort(create_array_t<sym_entry>(
|
||||
gallagher_deobfuscate_and_parse_credential,
|
||||
void,
|
||||
(GallagherCredential * credential, const uint8_t* cardholder_data_obfuscated)),
|
||||
API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*)));
|
||||
API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*),
|
||||
API_METHOD(
|
||||
mosgortrans_parse_transport_block,
|
||||
bool,
|
||||
(const MfClassicBlock* block, FuriString* result))));
|
||||
|
||||
@@ -128,6 +128,15 @@ App(
|
||||
sources=["plugins/supported_cards/aime.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="bip_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="bip_plugin_ep",
|
||||
targets=["f7"],
|
||||
requires=["nfc"],
|
||||
sources=["plugins/supported_cards/bip.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="umarsh_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
|
||||
21
applications/main/nfc/helpers/felica_auth.c
Normal file
21
applications/main/nfc/helpers/felica_auth.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "felica_auth.h"
|
||||
|
||||
FelicaAuthenticationContext* felica_auth_alloc() {
|
||||
FelicaAuthenticationContext* instance = malloc(sizeof(FelicaAuthenticationContext));
|
||||
memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);
|
||||
instance->skip_auth = true;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void felica_auth_free(FelicaAuthenticationContext* instance) {
|
||||
furi_assert(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void felica_auth_reset(FelicaAuthenticationContext* instance) {
|
||||
furi_assert(instance);
|
||||
memset(instance->card_key.data, 0, FELICA_DATA_BLOCK_SIZE);
|
||||
instance->skip_auth = true;
|
||||
instance->auth_status.external = 0;
|
||||
instance->auth_status.internal = 0;
|
||||
}
|
||||
17
applications/main/nfc/helpers/felica_auth.h
Normal file
17
applications/main/nfc/helpers/felica_auth.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/felica/felica.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
FelicaAuthenticationContext* felica_auth_alloc();
|
||||
|
||||
void felica_auth_free(FelicaAuthenticationContext* instance);
|
||||
|
||||
void felica_auth_reset(FelicaAuthenticationContext* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -23,7 +23,7 @@ static void nfc_get_key_cache_file_path(const uint8_t* uid, size_t uid_len, Furi
|
||||
furi_string_cat_printf(path, "%s", NFC_APP_KEYS_EXTENSION);
|
||||
}
|
||||
|
||||
MfClassicKeyCache* mf_classic_key_cache_alloc() {
|
||||
MfClassicKeyCache* mf_classic_key_cache_alloc(void) {
|
||||
MfClassicKeyCache* instance = malloc(sizeof(MfClassicKeyCache));
|
||||
|
||||
return instance;
|
||||
|
||||
@@ -8,7 +8,7 @@ extern "C" {
|
||||
|
||||
typedef struct MfClassicKeyCache MfClassicKeyCache;
|
||||
|
||||
MfClassicKeyCache* mf_classic_key_cache_alloc();
|
||||
MfClassicKeyCache* mf_classic_key_cache_alloc(void);
|
||||
|
||||
void mf_classic_key_cache_free(MfClassicKeyCache* instance);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <furi.h>
|
||||
#include <mbedtls/sha1.h>
|
||||
|
||||
MfUltralightAuth* mf_ultralight_auth_alloc() {
|
||||
MfUltralightAuth* mf_ultralight_auth_alloc(void) {
|
||||
MfUltralightAuth* instance = malloc(sizeof(MfUltralightAuth));
|
||||
|
||||
return instance;
|
||||
|
||||
@@ -20,7 +20,7 @@ typedef struct {
|
||||
MfUltralightAuthPack pack;
|
||||
} MfUltralightAuth;
|
||||
|
||||
MfUltralightAuth* mf_ultralight_auth_alloc();
|
||||
MfUltralightAuth* mf_ultralight_auth_alloc(void);
|
||||
|
||||
void mf_ultralight_auth_free(MfUltralightAuth* instance);
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ struct NfcSupportedCards {
|
||||
NfcSupportedCardsLoadContext* load_context;
|
||||
};
|
||||
|
||||
NfcSupportedCards* nfc_supported_cards_alloc() {
|
||||
NfcSupportedCards* nfc_supported_cards_alloc(void) {
|
||||
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
|
||||
|
||||
instance->api_resolver = composite_api_resolver_alloc();
|
||||
@@ -81,7 +81,7 @@ void nfc_supported_cards_free(NfcSupportedCards* instance) {
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc() {
|
||||
static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc(void) {
|
||||
NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext));
|
||||
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct NfcSupportedCards NfcSupportedCards;
|
||||
*
|
||||
* @return pointer to allocated NfcSupportedCards instance.
|
||||
*/
|
||||
NfcSupportedCards* nfc_supported_cards_alloc();
|
||||
NfcSupportedCards* nfc_supported_cards_alloc(void);
|
||||
|
||||
/**
|
||||
* @brief Delete an NfcSupportedCards instance
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
#include "../nfc_protocol_support_common.h"
|
||||
#include "../nfc_protocol_support_gui_common.h"
|
||||
#include "../nfc_protocol_support_unlock_helper.h"
|
||||
|
||||
enum {
|
||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||
};
|
||||
|
||||
static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
@@ -18,6 +23,35 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(temp_str));
|
||||
|
||||
widget_add_button_element(
|
||||
instance->widget,
|
||||
GuiButtonTypeRight,
|
||||
"More",
|
||||
nfc_protocol_support_common_widget_callback,
|
||||
instance);
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
nfc_render_felica_dump(data, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
@@ -29,29 +63,75 @@ static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, v
|
||||
|
||||
NfcApp* instance = context;
|
||||
const FelicaPollerEvent* felica_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(felica_event->type == FelicaPollerEventTypeReady) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
command = NfcCommandStop;
|
||||
} else if(
|
||||
felica_event->type == FelicaPollerEventTypeError ||
|
||||
felica_event->type == FelicaPollerEventTypeIncomplete) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, NfcCustomEventPollerIncomplete);
|
||||
command = NfcCommandStop;
|
||||
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
|
||||
FelicaAuthenticationContext* ctx = felica_event->data->auth_context;
|
||||
ctx->skip_auth = instance->felica_auth->skip_auth;
|
||||
memcpy(ctx->card_key.data, instance->felica_auth->card_key.data, FELICA_DATA_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_felica(NfcApp* instance) {
|
||||
nfc_unlock_helper_setup_from_state(instance);
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance);
|
||||
}
|
||||
|
||||
bool nfc_scene_read_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventCardDetected) {
|
||||
nfc_unlock_helper_card_detected_handler(instance);
|
||||
} else if(event.event == NfcCustomEventPollerIncomplete) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
|
||||
if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn)) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
} else {
|
||||
bool all_unlocked = data->blocks_read == data->blocks_total;
|
||||
furi_string_cat_printf(
|
||||
temp_str,
|
||||
"\e#%s\n",
|
||||
all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked");
|
||||
nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
uint8_t* ck_data = instance->felica_auth->card_key.data;
|
||||
furi_string_cat_printf(temp_str, "Key:");
|
||||
for(uint8_t i = 0; i < 7; i++) {
|
||||
furi_string_cat_printf(temp_str, " %02X", ck_data[i]);
|
||||
if(i == 6) furi_string_cat_printf(temp_str, "...");
|
||||
}
|
||||
nfc_render_felica_blocks_count(data, temp_str, false);
|
||||
}
|
||||
felica_auth_reset(instance->felica_auth);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
@@ -74,23 +154,50 @@ static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) {
|
||||
nfc_listener_start(instance->listener, NULL, NULL);
|
||||
}
|
||||
|
||||
static void nfc_scene_read_menu_on_enter_felica(NfcApp* instance) {
|
||||
const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica);
|
||||
if(data->blocks_read != data->blocks_total) {
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Unlock",
|
||||
SubmenuIndexUnlock,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
instance);
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexUnlock) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaKeyInput);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_felica = {
|
||||
.features = NfcProtocolFeatureEmulateUid,
|
||||
|
||||
.scene_info =
|
||||
{
|
||||
.on_enter = nfc_scene_info_on_enter_felica,
|
||||
.on_event = nfc_scene_info_on_event_felica,
|
||||
},
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_scene_more_info_on_enter_felica,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
.on_enter = nfc_scene_read_on_enter_felica,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
.on_event = nfc_scene_read_on_event_felica,
|
||||
},
|
||||
.scene_read_menu =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
.on_enter = nfc_scene_read_menu_on_enter_felica,
|
||||
.on_event = nfc_scene_read_menu_on_event_felica,
|
||||
},
|
||||
.scene_read_success =
|
||||
{
|
||||
|
||||
@@ -1,19 +1,107 @@
|
||||
#include "felica_render.h"
|
||||
|
||||
void nfc_render_felica_blocks_count(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
bool render_auth_notification) {
|
||||
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
|
||||
if(render_auth_notification && data->blocks_read != data->blocks_total) {
|
||||
furi_string_cat_printf(str, "\nAuth-protected blocks!");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_felica_idm(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
furi_string_cat_printf(str, (format_type == NfcProtocolFormatTypeFull) ? "IDm:\n" : "IDm:");
|
||||
|
||||
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
(format_type == NfcProtocolFormatTypeFull) ? "%02X " : " %02X",
|
||||
data->idm.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_felica_info(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
furi_string_cat_printf(str, "IDm:");
|
||||
|
||||
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
|
||||
furi_string_cat_printf(str, " %02X", data->idm.data[i]);
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
|
||||
}
|
||||
|
||||
nfc_render_felica_idm(data, format_type, str);
|
||||
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
furi_string_cat_printf(str, "\nPMm:");
|
||||
furi_string_cat_printf(str, "\nPMm:\n");
|
||||
for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) {
|
||||
furi_string_cat_printf(str, " %02X", data->pmm.data[i]);
|
||||
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
|
||||
}
|
||||
}
|
||||
nfc_render_felica_blocks_count(data, str, true);
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block_name(
|
||||
const char* name,
|
||||
FuriString* str,
|
||||
uint8_t prefix_separator_cnt,
|
||||
uint8_t suffix_separator_cnt) {
|
||||
for(uint8_t i = 0; i < prefix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
furi_string_cat_printf(str, "[ %s ]", name);
|
||||
for(uint8_t i = 0; i < suffix_separator_cnt; i++) {
|
||||
furi_string_cat_printf(str, ":");
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
|
||||
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
|
||||
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
|
||||
furi_string_cat_printf(str, "%02X ", block->data[j]);
|
||||
}
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
static void nfc_render_felica_block(
|
||||
const FelicaBlock* block,
|
||||
FuriString* str,
|
||||
const char* name,
|
||||
uint8_t prefix_separator_cnt,
|
||||
uint8_t suffix_separator_cnt) {
|
||||
nfc_render_felica_block_name(name, str, prefix_separator_cnt, suffix_separator_cnt);
|
||||
nfc_render_felica_block_data(block, str);
|
||||
}
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
|
||||
FuriString* name = furi_string_alloc();
|
||||
for(size_t i = 0; i < 14; i++) {
|
||||
furi_string_printf(name, "S_PAD%d", i);
|
||||
uint8_t suf_cnt = 18;
|
||||
if(i == 1) {
|
||||
suf_cnt = 19;
|
||||
} else if((i == 10) || (i == 12) || (i == 13)) {
|
||||
suf_cnt = 16;
|
||||
}
|
||||
nfc_render_felica_block(
|
||||
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
|
||||
}
|
||||
furi_string_free(name);
|
||||
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
|
||||
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
|
||||
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
|
||||
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
|
||||
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
|
||||
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
|
||||
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
|
||||
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,19 @@
|
||||
|
||||
#include "../nfc_protocol_support_render_common.h"
|
||||
|
||||
void nfc_render_felica_blocks_count(
|
||||
const FelicaData* data,
|
||||
FuriString* str,
|
||||
bool render_auth_notification);
|
||||
|
||||
void nfc_render_felica_info(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
|
||||
|
||||
void nfc_render_felica_idm(
|
||||
const FelicaData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
@@ -34,6 +34,8 @@ static void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) {
|
||||
static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolMfDesfire);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
NfcApp* instance = context;
|
||||
const MfDesfirePollerEvent* mf_desfire_event = event.event_data;
|
||||
|
||||
@@ -41,10 +43,12 @@ static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent even
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
command = NfcCommandStop;
|
||||
} else if(mf_desfire_event->type == MfDesfirePollerEventTypeReadFailed) {
|
||||
command = NfcCommandReset;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) {
|
||||
|
||||
@@ -11,24 +11,29 @@ void nfc_render_mf_desfire_info(
|
||||
const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1);
|
||||
const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0;
|
||||
|
||||
if(data->master_key_settings.is_free_directory_list) {
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
if(app->key_settings.is_free_directory_list) {
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
} else {
|
||||
furi_string_cat_printf(str, "\nAuth required to read apps!");
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n%lu", bytes_total);
|
||||
|
||||
if(data->version.sw_storage & 1) {
|
||||
furi_string_push_back(str, '+');
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free\n", bytes_free);
|
||||
|
||||
const uint32_t app_count = simple_array_get_count(data->applications);
|
||||
uint32_t file_count = 0;
|
||||
|
||||
for(uint32_t i = 0; i < app_count; ++i) {
|
||||
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
|
||||
file_count += simple_array_get_count(app->file_ids);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%lu Application%s", app_count, app_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : "");
|
||||
furi_string_cat_printf(str, " bytes, %lu bytes free", bytes_free);
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
|
||||
@@ -101,17 +106,29 @@ void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriStri
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id);
|
||||
furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable);
|
||||
furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete);
|
||||
furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list);
|
||||
furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "changeKeyID ??\n");
|
||||
furi_string_cat_printf(str, "configChangeable ??\n");
|
||||
furi_string_cat_printf(str, "freeCreateDelete ??\n");
|
||||
furi_string_cat_printf(str, "freeDirectoryList 0\n");
|
||||
furi_string_cat_printf(str, "masterChangeable ??\n");
|
||||
}
|
||||
|
||||
if(data->flags) {
|
||||
furi_string_cat_printf(str, "flags %d\n", data->flags);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
if(data->is_free_directory_list) {
|
||||
furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "maxKeys ??\n");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_key_version(
|
||||
@@ -123,14 +140,16 @@ void nfc_render_mf_desfire_key_version(
|
||||
|
||||
void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) {
|
||||
const uint8_t* app_id = data->data;
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[0], app_id[1], app_id[2]);
|
||||
furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[2], app_id[1], app_id[0]);
|
||||
}
|
||||
|
||||
void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) {
|
||||
nfc_render_mf_desfire_key_settings(&data->key_settings, str);
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
if(data->key_settings.is_free_directory_list) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) {
|
||||
nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,13 +198,16 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "%s %s\n", type, comm);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights >> 12 & 0xF,
|
||||
settings->access_rights >> 8 & 0xF,
|
||||
settings->access_rights >> 4 & 0xF,
|
||||
settings->access_rights & 0xF);
|
||||
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"r %d w %d rw %d c %d\n",
|
||||
settings->access_rights[i] >> 12 & 0xF,
|
||||
settings->access_rights[i] >> 8 & 0xF,
|
||||
settings->access_rights[i] >> 4 & 0xF,
|
||||
settings->access_rights[i] & 0xF);
|
||||
}
|
||||
|
||||
uint32_t record_count = 1;
|
||||
uint32_t record_size = 0;
|
||||
@@ -217,6 +239,20 @@ void nfc_render_mf_desfire_file_settings_data(
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_auth_required = true;
|
||||
for(size_t i = 0; i < settings->access_rights_len; i++) {
|
||||
uint8_t read_rights = (settings->access_rights[i] >> 12) & 0x0f;
|
||||
uint8_t read_write_rights = (settings->access_rights[i] >> 4) & 0x0f;
|
||||
if((read_rights == 0x0e) || (read_write_rights == 0x0e)) {
|
||||
is_auth_required = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_auth_required) {
|
||||
furi_string_cat_printf(str, "Auth required to read file data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(simple_array_get_count(data->data) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "../nfc_protocol_support_common.h"
|
||||
#include "../nfc_protocol_support_gui_common.h"
|
||||
#include "../nfc_protocol_support_unlock_helper.h"
|
||||
|
||||
enum {
|
||||
SubmenuIndexUnlock = SubmenuIndexCommonMax,
|
||||
@@ -30,6 +31,8 @@ static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
|
||||
nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
@@ -155,48 +158,15 @@ static NfcCommand
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
enum {
|
||||
NfcSceneMfUltralightReadMenuStateCardSearch,
|
||||
NfcSceneMfUltralightReadMenuStateCardFound,
|
||||
};
|
||||
|
||||
static void nfc_scene_read_setup_view(NfcApp* instance) {
|
||||
Popup* popup = instance->popup;
|
||||
popup_reset(popup);
|
||||
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
|
||||
|
||||
if(state == NfcSceneMfUltralightReadMenuStateCardSearch) {
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
|
||||
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
bool unlocking =
|
||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn);
|
||||
|
||||
uint32_t state = unlocking ? NfcSceneMfUltralightReadMenuStateCardSearch :
|
||||
NfcSceneMfUltralightReadMenuStateCardFound;
|
||||
|
||||
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
|
||||
|
||||
nfc_scene_read_setup_view(instance);
|
||||
nfc_unlock_helper_setup_from_state(instance);
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
|
||||
}
|
||||
|
||||
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventCardDetected) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
|
||||
nfc_scene_read_setup_view(instance);
|
||||
nfc_unlock_helper_card_detected_handler(instance);
|
||||
} else if((event.event == NfcCustomEventPollerIncomplete)) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
@@ -245,6 +215,8 @@ static void nfc_scene_read_success_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
|
||||
nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,11 @@ static void nfc_render_mf_ultralight_pages_count(const MfUltralightData* data, F
|
||||
|
||||
void nfc_render_mf_ultralight_pwd_pack(const MfUltralightData* data, FuriString* str) {
|
||||
bool all_pages = mf_ultralight_is_all_data_read(data);
|
||||
furi_string_cat_printf(str, "\e#%s pages unlocked!", all_pages ? "All" : "Not all");
|
||||
if(all_pages) {
|
||||
furi_string_cat_printf(str, "\e#All Pages Are Unlocked!");
|
||||
} else {
|
||||
furi_string_cat_printf(str, "\e#Some Pages Are Locked!");
|
||||
}
|
||||
|
||||
MfUltralightConfigPages* config;
|
||||
mf_ultralight_get_config_page(data, &config);
|
||||
|
||||
@@ -600,7 +600,7 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
||||
}
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 56, 33, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false);
|
||||
widget, 50, 33, 78, 31, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "nfc_protocol_support_unlock_helper.h"
|
||||
|
||||
static void nfc_scene_read_setup_view(NfcApp* instance) {
|
||||
Popup* popup = instance->popup;
|
||||
popup_reset(popup);
|
||||
uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneRead);
|
||||
|
||||
if(state == NfcSceneReadMenuStateCardSearch) {
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup_set_header(instance->popup, "Don't move", 85, 27, AlignCenter, AlignTop);
|
||||
popup_set_icon(instance->popup, 12, 20, &A_Loading_24);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
|
||||
void nfc_unlock_helper_setup_from_state(NfcApp* instance) {
|
||||
bool unlocking =
|
||||
scene_manager_has_previous_scene(
|
||||
instance->scene_manager, NfcSceneMfUltralightUnlockWarn) ||
|
||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||
|
||||
uint32_t state = unlocking ? NfcSceneReadMenuStateCardSearch : NfcSceneReadMenuStateCardFound;
|
||||
|
||||
scene_manager_set_scene_state(instance->scene_manager, NfcSceneRead, state);
|
||||
|
||||
nfc_scene_read_setup_view(instance);
|
||||
}
|
||||
|
||||
void nfc_unlock_helper_card_detected_handler(NfcApp* instance) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneRead, NfcSceneReadMenuStateCardFound);
|
||||
nfc_scene_read_setup_view(instance);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
typedef enum {
|
||||
NfcSceneReadMenuStateCardSearch,
|
||||
NfcSceneReadMenuStateCardFound,
|
||||
} NfcSceneUnlockReadState;
|
||||
|
||||
void nfc_unlock_helper_setup_from_state(NfcApp* instance);
|
||||
void nfc_unlock_helper_card_detected_handler(NfcApp* instance);
|
||||
@@ -1,22 +1,28 @@
|
||||
#include "st25tb_render.h"
|
||||
#include <nfc/protocols/st25tb/st25tb.h>
|
||||
#include <machine/endian.h>
|
||||
|
||||
void nfc_render_st25tb_info(
|
||||
const St25tbData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
furi_string_cat_printf(str, "UID");
|
||||
furi_string_cat_printf(str, "UID:");
|
||||
|
||||
for(size_t i = 0; i < ST25TB_UID_SIZE; i++) {
|
||||
furi_string_cat_printf(str, " %02X", data->uid[i]);
|
||||
}
|
||||
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
furi_string_cat_printf(str, "\nSys. OTP: %08lX", data->system_otp_block);
|
||||
furi_string_cat_printf(str, "\nBlocks:");
|
||||
furi_string_cat_printf(
|
||||
str, "\nSys. OTP: %08lX", (uint32_t)__bswap32(data->system_otp_block));
|
||||
furi_string_cat_printf(str, "\n::::::::::::::::::::::[Blocks]::::::::::::::::::::::");
|
||||
for(size_t i = 0; i < st25tb_get_block_count(data->type); i += 2) {
|
||||
furi_string_cat_printf(
|
||||
str, "\n %02X %08lX %08lX", i, data->blocks[i], data->blocks[i + 1]);
|
||||
str,
|
||||
"\n %02X %08lX %08lX",
|
||||
i,
|
||||
(uint32_t)__bswap32(data->blocks[i]),
|
||||
(uint32_t)__bswap32(data->blocks[i + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ struct SlixUnlock {
|
||||
|
||||
static const SlixPassword tonie_box_pass_arr[] = {0x5B6EFD7F, 0x0F0F0F0F};
|
||||
|
||||
SlixUnlock* slix_unlock_alloc() {
|
||||
SlixUnlock* slix_unlock_alloc(void) {
|
||||
SlixUnlock* instance = malloc(sizeof(SlixUnlock));
|
||||
|
||||
return instance;
|
||||
|
||||
@@ -13,7 +13,7 @@ typedef enum {
|
||||
|
||||
typedef struct SlixUnlock SlixUnlock;
|
||||
|
||||
SlixUnlock* slix_unlock_alloc();
|
||||
SlixUnlock* slix_unlock_alloc(void);
|
||||
|
||||
void slix_unlock_free(SlixUnlock* instance);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ static void nfc_app_rpc_command_callback(const RpcAppSystemEvent* event, void* c
|
||||
}
|
||||
}
|
||||
|
||||
NfcApp* nfc_app_alloc() {
|
||||
NfcApp* nfc_app_alloc(void) {
|
||||
NfcApp* instance = malloc(sizeof(NfcApp));
|
||||
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
@@ -50,6 +50,7 @@ NfcApp* nfc_app_alloc() {
|
||||
|
||||
instance->nfc = nfc_alloc();
|
||||
|
||||
instance->felica_auth = felica_auth_alloc();
|
||||
instance->mf_ul_auth = mf_ultralight_auth_alloc();
|
||||
instance->slix_unlock = slix_unlock_alloc();
|
||||
instance->mfc_key_cache = mf_classic_key_cache_alloc();
|
||||
@@ -141,6 +142,7 @@ void nfc_app_free(NfcApp* instance) {
|
||||
|
||||
nfc_free(instance->nfc);
|
||||
|
||||
felica_auth_free(instance->felica_auth);
|
||||
mf_ultralight_auth_free(instance->mf_ul_auth);
|
||||
slix_unlock_free(instance->slix_unlock);
|
||||
mf_classic_key_cache_free(instance->mfc_key_cache);
|
||||
@@ -453,11 +455,11 @@ void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* strin
|
||||
furi_assert(string);
|
||||
|
||||
if(!furi_string_empty(instance->file_name)) {
|
||||
furi_string_cat_printf(string, "Name:%s\n", furi_string_get_cstr(instance->file_name));
|
||||
furi_string_cat_printf(string, "Name: %s\n", furi_string_get_cstr(instance->file_name));
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_is_hal_ready() {
|
||||
static bool nfc_is_hal_ready(void) {
|
||||
if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {
|
||||
// No connection to the chip, show an error screen
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "helpers/mfkey32_logger.h"
|
||||
#include "helpers/mf_classic_key_cache.h"
|
||||
#include "helpers/nfc_supported_cards.h"
|
||||
#include "helpers/felica_auth.h"
|
||||
#include "helpers/slix_unlock.h"
|
||||
|
||||
#include <dialogs/dialogs.h>
|
||||
@@ -129,6 +130,7 @@ struct NfcApp {
|
||||
NfcScanner* scanner;
|
||||
NfcListener* listener;
|
||||
|
||||
FelicaAuthenticationContext* felica_auth;
|
||||
MfUltralightAuth* mf_ul_auth;
|
||||
SlixUnlock* slix_unlock;
|
||||
NfcMfClassicDictAttackContext nfc_dict_context;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#define FLAG_EVENT (1 << 10)
|
||||
|
||||
static void nfc_cli_print_usage() {
|
||||
static void nfc_cli_print_usage(void) {
|
||||
printf("Usage:\r\n");
|
||||
printf("nfc <cmd>\r\n");
|
||||
printf("Cmd list:\r\n");
|
||||
@@ -63,7 +63,7 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||
furi_string_free(cmd);
|
||||
}
|
||||
|
||||
void nfc_on_system_start() {
|
||||
void nfc_on_system_start(void) {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL);
|
||||
|
||||
@@ -164,6 +164,6 @@ static const FlipperAppPluginDescriptor aime_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* aime_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* aime_plugin_ep(void) {
|
||||
return &aime_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,6 @@ static const FlipperAppPluginDescriptor all_in_one_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* all_in_one_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* all_in_one_plugin_ep(void) {
|
||||
return &all_in_one_plugin_descriptor;
|
||||
}
|
||||
|
||||
369
applications/main/nfc/plugins/supported_cards/bip.c
Normal file
369
applications/main/nfc/plugins/supported_cards/bip.c
Normal file
@@ -0,0 +1,369 @@
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <flipper_application/flipper_application.h>
|
||||
#include <nfc/nfc_device.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
|
||||
#define TAG "Bip"
|
||||
|
||||
#define SECTOR_BLOCK_OFFSET(sector, block) (((sector) * 4) + (block))
|
||||
|
||||
static const uint64_t bip_keys_a[] = {
|
||||
0x3a42f33af429,
|
||||
0x6338a371c0ed,
|
||||
0xf124c2578ad0,
|
||||
0x32ac3b90ac13,
|
||||
0x4ad1e273eaf1,
|
||||
0xe2c42591368a,
|
||||
0x2a3c347a1200,
|
||||
0x16f3d5ab1139,
|
||||
0x937a4fff3011,
|
||||
0x35c3d2caee88,
|
||||
0x693143f10368,
|
||||
0xa3f97428dd01,
|
||||
0x63f17a449af0,
|
||||
0xc4652c54261c,
|
||||
0xd49e2826664f,
|
||||
0x3df14c8000a1,
|
||||
};
|
||||
|
||||
static const uint64_t bip_keys_b[] = {
|
||||
0x1fc235ac1309,
|
||||
0x243f160918d1,
|
||||
0x9afc42372af1,
|
||||
0x682d401abb09,
|
||||
0x067db45454a9,
|
||||
0x15fc4c7613fe,
|
||||
0x68d30288910a,
|
||||
0xf59a36a2546d,
|
||||
0x64e3c10394c2,
|
||||
0xb736412614af,
|
||||
0x324f5df65310,
|
||||
0x643fb6de2217,
|
||||
0x82f435dedf01,
|
||||
0x0263de1278f3,
|
||||
0x51284c3686a6,
|
||||
0x6a470d54127c,
|
||||
};
|
||||
|
||||
bool bip_verify(Nfc* nfc) {
|
||||
bool verified = true;
|
||||
|
||||
const uint8_t verify_sector = 0;
|
||||
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_a_0 = {};
|
||||
bit_lib_num_to_bytes_be(bip_keys_a[0], COUNT_OF(key_a_0.data), key_a_0.data);
|
||||
|
||||
MfClassicAuthContext auth_ctx = {};
|
||||
MfClassicError error =
|
||||
mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);
|
||||
|
||||
if(error != MfClassicErrorNone) {
|
||||
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
|
||||
verified = false;
|
||||
}
|
||||
|
||||
return verified;
|
||||
}
|
||||
|
||||
static bool bip_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 {
|
||||
MfClassicType type = MfClassicType1k;
|
||||
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
|
||||
if(error != MfClassicErrorNone) break;
|
||||
if(type != MfClassicType1k) {
|
||||
FURI_LOG_W(TAG, "Card not MIFARE Classic 1k");
|
||||
break;
|
||||
}
|
||||
|
||||
data->type = type;
|
||||
MfClassicDeviceKeys keys = {};
|
||||
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||
bit_lib_num_to_bytes_be(bip_keys_a[i], sizeof(MfClassicKey), keys.key_a[i].data);
|
||||
FURI_BIT_SET(keys.key_a_mask, i);
|
||||
bit_lib_num_to_bytes_be(bip_keys_b[i], sizeof(MfClassicKey), keys.key_b[i].data);
|
||||
FURI_BIT_SET(keys.key_b_mask, i);
|
||||
}
|
||||
|
||||
error = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||
if(error == MfClassicErrorNotPresent) {
|
||||
FURI_LOG_W(TAG, "Failed to read data. Bad keys?");
|
||||
break;
|
||||
}
|
||||
|
||||
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
||||
|
||||
is_read = true;
|
||||
} while(false);
|
||||
|
||||
mf_classic_free(data);
|
||||
|
||||
return is_read;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
} BipTimestamp;
|
||||
|
||||
static void parse_bip_timestamp(const MfClassicBlock* block, BipTimestamp* timestamp) {
|
||||
furi_assert(block);
|
||||
furi_assert(timestamp);
|
||||
|
||||
timestamp->day = (((block->data[1] << 8) + block->data[0]) >> 6) & 0x1f;
|
||||
timestamp->month = (((block->data[1] << 8) + block->data[0]) >> 11) & 0xf;
|
||||
timestamp->year = 2000 + ((((block->data[2] << 8) + block->data[1]) >> 7) & 0x1f);
|
||||
timestamp->hour = (((block->data[3] << 8) + block->data[2]) >> 4) & 0x1f;
|
||||
timestamp->minute = (((block->data[3] << 8) + block->data[2]) >> 9) & 0x3f;
|
||||
timestamp->second = (((block->data[4] << 8) + block->data[3]) >> 7) & 0x3f;
|
||||
}
|
||||
|
||||
static int compare_bip_timestamp(const BipTimestamp* t1, const BipTimestamp* t2) {
|
||||
furi_assert(t1);
|
||||
furi_assert(t2);
|
||||
if(t1->year != t2->year) {
|
||||
return t1->year - t2->year;
|
||||
}
|
||||
if(t1->month != t2->month) {
|
||||
return t1->month - t2->month;
|
||||
}
|
||||
if(t1->day != t2->day) {
|
||||
return t1->day - t2->day;
|
||||
}
|
||||
if(t1->hour != t2->hour) {
|
||||
return t1->hour - t2->hour;
|
||||
}
|
||||
if(t1->minute != t2->minute) {
|
||||
return t1->minute - t2->minute;
|
||||
}
|
||||
if(t1->second != t2->second) {
|
||||
return t1->second - t2->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_bip_timestamp(const BipTimestamp* timestamp, FuriString* str) {
|
||||
furi_assert(timestamp);
|
||||
furi_assert(str);
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"%04u-%02u-%02u %02u:%02u:%02u",
|
||||
timestamp->year,
|
||||
timestamp->month,
|
||||
timestamp->day,
|
||||
timestamp->hour,
|
||||
timestamp->minute,
|
||||
timestamp->second);
|
||||
}
|
||||
|
||||
static bool is_bip_block_empty(const MfClassicBlock* block) {
|
||||
furi_assert(block);
|
||||
// check if all but last byte are zero (last is checksum)
|
||||
for(size_t i = 0; i < sizeof(block->data) - 1; i++) {
|
||||
if(block->data[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parse_uint16_le(const uint8_t* data, uint16_t* value) {
|
||||
furi_assert(data);
|
||||
furi_assert(value);
|
||||
|
||||
*value = (data[0]) | (data[1] << 8);
|
||||
}
|
||||
|
||||
static void parse_uint32_le(const uint8_t* data, uint32_t* value) {
|
||||
furi_assert(data);
|
||||
furi_assert(value);
|
||||
|
||||
*value = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
||||
}
|
||||
|
||||
static void parse_uint16_txn_amount(const uint8_t* data, uint16_t* value) {
|
||||
furi_assert(data);
|
||||
furi_assert(value);
|
||||
|
||||
parse_uint16_le(data, value);
|
||||
*value = *value >> 2;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BipTimestamp timestamp;
|
||||
uint16_t amount;
|
||||
} BipTransaction;
|
||||
|
||||
static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
furi_assert(device);
|
||||
furi_assert(parsed_data);
|
||||
|
||||
bool parsed = true;
|
||||
|
||||
struct {
|
||||
uint32_t card_id;
|
||||
uint16_t balance;
|
||||
uint16_t flags;
|
||||
BipTimestamp trip_time_window;
|
||||
BipTransaction top_ups[3];
|
||||
BipTransaction charges[3];
|
||||
} bip_data = {
|
||||
.card_id = 0,
|
||||
.balance = 0,
|
||||
.flags = 0,
|
||||
.trip_time_window = {0, 0, 0, 0, 0, 0},
|
||||
.top_ups =
|
||||
{
|
||||
{{0, 0, 0, 0, 0, 0}, 0},
|
||||
{{0, 0, 0, 0, 0, 0}, 0},
|
||||
{{0, 0, 0, 0, 0, 0}, 0},
|
||||
},
|
||||
.charges =
|
||||
{
|
||||
{{0, 0, 0, 0, 0, 0}, 0},
|
||||
{{0, 0, 0, 0, 0, 0}, 0},
|
||||
{{0, 0, 0, 0, 0, 0}, 0},
|
||||
},
|
||||
};
|
||||
|
||||
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
||||
|
||||
do {
|
||||
// verify first sector keys
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
|
||||
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
|
||||
if(key != bip_keys_a[0]) {
|
||||
parsed = false;
|
||||
break;
|
||||
}
|
||||
key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
|
||||
if(key != bip_keys_b[0]) {
|
||||
parsed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get Card ID, little-endian 4 bytes at sector 0 block 1, bytes 4-7
|
||||
parse_uint32_le(&data->block[SECTOR_BLOCK_OFFSET(0, 1)].data[4], &bip_data.card_id);
|
||||
|
||||
// Get balance, little-endian 2 bytes at sector 8 block 1, bytes 0-1
|
||||
parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[0], &bip_data.balance);
|
||||
|
||||
// Get balance flags (negative balance, etc.), little-endian 2 bytes at sector 8 block 1, bytes 2-3
|
||||
parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[2], &bip_data.flags);
|
||||
|
||||
// Get trip time window, proprietary format, at sector 5 block 1, bytes 0-7
|
||||
parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(5, 1)], &bip_data.trip_time_window);
|
||||
|
||||
// Last 3 top-ups: sector 10, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 9-10
|
||||
for(size_t i = 0; i < 3; i++) {
|
||||
if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(10, i)])) {
|
||||
continue;
|
||||
}
|
||||
BipTransaction* top_up = &bip_data.top_ups[i];
|
||||
parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(10, i)], &top_up->timestamp);
|
||||
parse_uint16_txn_amount(
|
||||
&data->block[SECTOR_BLOCK_OFFSET(10, i)].data[9], &top_up->amount);
|
||||
}
|
||||
|
||||
// Last 3 charges (i.e. trips), sector 11, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 10-11
|
||||
for(size_t i = 0; i < 3; i++) {
|
||||
if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(11, i)])) {
|
||||
continue;
|
||||
}
|
||||
BipTransaction* charge = &bip_data.charges[i];
|
||||
parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(11, i)], &charge->timestamp);
|
||||
parse_uint16_txn_amount(
|
||||
&data->block[SECTOR_BLOCK_OFFSET(11, i)].data[10], &charge->amount);
|
||||
}
|
||||
|
||||
// All data is now parsed and stored in bip_data, now print it
|
||||
|
||||
// Print basic info
|
||||
furi_string_printf(
|
||||
parsed_data,
|
||||
"\e#Tarjeta Bip!\n"
|
||||
"Card Number: %lu\n"
|
||||
"Balance: $%hu (flags %hu)\n"
|
||||
"Current Trip Window Ends:\n @",
|
||||
bip_data.card_id,
|
||||
bip_data.balance,
|
||||
bip_data.flags);
|
||||
|
||||
print_bip_timestamp(&bip_data.trip_time_window, parsed_data);
|
||||
|
||||
// Find newest top-up
|
||||
size_t newest_top_up = 0;
|
||||
for(size_t i = 1; i < 3; i++) {
|
||||
const BipTimestamp* newest = &bip_data.top_ups[newest_top_up].timestamp;
|
||||
const BipTimestamp* current = &bip_data.top_ups[i].timestamp;
|
||||
if(compare_bip_timestamp(current, newest) > 0) {
|
||||
newest_top_up = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Print top-ups, newest first
|
||||
furi_string_cat_printf(parsed_data, "\n\e#Last Top-ups");
|
||||
for(size_t i = 0; i < 3; i++) {
|
||||
const BipTransaction* top_up = &bip_data.top_ups[(3u + newest_top_up - i) % 3];
|
||||
furi_string_cat_printf(parsed_data, "\n+$%d\n @", top_up->amount);
|
||||
print_bip_timestamp(&top_up->timestamp, parsed_data);
|
||||
}
|
||||
|
||||
// Find newest charge
|
||||
size_t newest_charge = 0;
|
||||
for(size_t i = 1; i < 3; i++) {
|
||||
const BipTimestamp* newest = &bip_data.charges[newest_charge].timestamp;
|
||||
const BipTimestamp* current = &bip_data.charges[i].timestamp;
|
||||
if(compare_bip_timestamp(current, newest) > 0) {
|
||||
newest_charge = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Print charges
|
||||
furi_string_cat_printf(parsed_data, "\n\e#Last Charges (Trips)");
|
||||
for(size_t i = 0; i < 3; i++) {
|
||||
const BipTransaction* charge = &bip_data.charges[(3u + newest_charge - i) % 3];
|
||||
furi_string_cat_printf(parsed_data, "\n-$%d\n @", charge->amount);
|
||||
print_bip_timestamp(&charge->timestamp, parsed_data);
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const NfcSupportedCardsPlugin bip_plugin = {
|
||||
.protocol = NfcProtocolMfClassic,
|
||||
.verify = bip_verify,
|
||||
.read = bip_read,
|
||||
.parse = bip_parse,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor bip_plugin_descriptor = {
|
||||
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||
.entry_point = &bip_plugin,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* bip_plugin_ep(void) {
|
||||
return &bip_plugin_descriptor;
|
||||
}
|
||||
@@ -616,6 +616,6 @@ static const FlipperAppPluginDescriptor clipper_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* clipper_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* clipper_plugin_ep(void) {
|
||||
return &clipper_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,6 @@ static const FlipperAppPluginDescriptor gallagher_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point */
|
||||
const FlipperAppPluginDescriptor* gallagher_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* gallagher_plugin_ep(void) {
|
||||
return &gallagher_plugin_descriptor;
|
||||
}
|
||||
@@ -220,6 +220,6 @@ static const FlipperAppPluginDescriptor hi_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* hi_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* hi_plugin_ep(void) {
|
||||
return &hi_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,6 @@ static const FlipperAppPluginDescriptor hid_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* hid_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* hid_plugin_ep(void) {
|
||||
return &hid_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,6 @@ static const FlipperAppPluginDescriptor itso_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* itso_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* itso_plugin_ep(void) {
|
||||
return &itso_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -225,6 +225,6 @@ static const FlipperAppPluginDescriptor microel_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* microel_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* microel_plugin_ep(void) {
|
||||
return µel_plugin_descriptor;
|
||||
}
|
||||
@@ -251,6 +251,6 @@ static const FlipperAppPluginDescriptor mizip_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* mizip_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* mizip_plugin_ep(void) {
|
||||
return &mizip_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -85,11 +85,10 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
|
||||
bool is_blank = mykey_is_blank(data);
|
||||
furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", (uint32_t)__bswap32(data->blocks[7]));
|
||||
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no");
|
||||
furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no");
|
||||
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "Prod. date: %02X/%02X/%04X", mfg_day, mfg_month, mfg_year);
|
||||
parsed_data, "Prod. date: %02X/%02X/%04X\n", mfg_day, mfg_month, mfg_year);
|
||||
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no");
|
||||
furi_string_cat_printf(parsed_data, "LockID: %s", mykey_has_lockid(data) ? "maybe" : "no");
|
||||
|
||||
if(!is_blank) {
|
||||
furi_string_cat_printf(
|
||||
@@ -152,6 +151,6 @@ static const FlipperAppPluginDescriptor mykey_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* mykey_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* mykey_plugin_ep(void) {
|
||||
return &mykey_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ static bool myki_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
// Stored card number doesn't include check digit
|
||||
card_number += myki_calculate_luhn(card_number);
|
||||
|
||||
furi_string_set(parsed_data, "\e#myki\n");
|
||||
furi_string_set(parsed_data, "\e#myki\nNo.: ");
|
||||
|
||||
// Stylise card number according to the physical card
|
||||
char card_string[20];
|
||||
@@ -111,6 +111,6 @@ static const FlipperAppPluginDescriptor myki_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* myki_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* myki_plugin_ep(void) {
|
||||
return &myki_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ static bool opal_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
|
||||
furi_string_printf(
|
||||
parsed_data,
|
||||
"\e#Opal: $%s%ld.%02hu\n3085 22%02hhu %04hu %03hu%01hhu\n%s, %s\n",
|
||||
"\e#Opal: $%s%ld.%02hu\nNo.: 3085 22%02hhu %04hu %03hu%01hhu\n%s, %s\n",
|
||||
sign,
|
||||
balance_dollars,
|
||||
balance_cents,
|
||||
@@ -228,6 +228,6 @@ static const FlipperAppPluginDescriptor opal_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* opal_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* opal_plugin_ep(void) {
|
||||
return &opal_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,29 @@ static const MfClassicKeyPair plantain_4k_keys[] = {
|
||||
{.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085},
|
||||
};
|
||||
|
||||
static const MfClassicKeyPair plantain_4k_keys_legacy[] = {
|
||||
{.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 = 0x46d78e850a7e, .b = 0xa470f8130991},
|
||||
{.a = 0x42e9b54e51ab, .b = 0x0231b86df52e}, {.a = 0x0f01ceff2742, .b = 0x6fec74559ca7},
|
||||
{.a = 0xb81f2b0c2f66, .b = 0xa7e2d95f0003}, {.a = 0x9ea3387a63c1, .b = 0x437e59f57561},
|
||||
{.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;
|
||||
|
||||
@@ -126,6 +149,21 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
|
||||
PlantainCardConfig cfg = {};
|
||||
if(!plantain_get_card_config(&cfg, data->type)) break;
|
||||
|
||||
const uint8_t legacy_check_sec_num = 26;
|
||||
const uint8_t legacy_check_block_num =
|
||||
mf_classic_get_first_block_num_of_sector(legacy_check_sec_num);
|
||||
|
||||
MfClassicKey key = {0};
|
||||
bit_lib_num_to_bytes_be(
|
||||
plantain_4k_keys_legacy[legacy_check_sec_num].a, COUNT_OF(key.data), key.data);
|
||||
|
||||
error = mf_classic_poller_sync_auth(
|
||||
nfc, legacy_check_block_num, &key, MfClassicKeyTypeA, NULL);
|
||||
if(error == MfClassicErrorNone) {
|
||||
FURI_LOG_D(TAG, "Legacy keys detected");
|
||||
cfg.keys = plantain_4k_keys_legacy;
|
||||
}
|
||||
|
||||
MfClassicDeviceKeys keys = {};
|
||||
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||
bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
|
||||
@@ -192,7 +230,7 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
}
|
||||
|
||||
furi_string_printf(
|
||||
parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance);
|
||||
parsed_data, "\e#Plantain\nNo.: %llu?\nBalance:%lu\n", card_number, balance);
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
@@ -215,6 +253,6 @@ static const FlipperAppPluginDescriptor plantain_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* plantain_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* plantain_plugin_ep(void) {
|
||||
return &plantain_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
#include <core/check.h>
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
#include <nfc/nfc_device.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
#include "../../api/mosgortrans/mosgortrans_util.h"
|
||||
#include "furi_hal_rtc.h"
|
||||
|
||||
#define TAG "Troika"
|
||||
@@ -19,19 +21,6 @@ typedef struct {
|
||||
uint32_t data_sector;
|
||||
} TroikaCardConfig;
|
||||
|
||||
typedef enum {
|
||||
TroikaLayoutUnknown = 0x0,
|
||||
TroikaLayout2 = 0x2,
|
||||
TroikaLayoutE = 0xE,
|
||||
} TroikaLayout;
|
||||
|
||||
typedef enum {
|
||||
TroikaSublayoutUnknown = 0x0,
|
||||
TroikaSublayout3 = 0x3,
|
||||
TroikaSublayout5 = 0x5,
|
||||
TroikaSublayout6 = 0x6,
|
||||
} TroikaSubLayout;
|
||||
|
||||
static const MfClassicKeyPair troika_1k_keys[] = {
|
||||
{.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58},
|
||||
{.a = 0xa82607b01c0d, .b = 0x2910989b6880},
|
||||
@@ -52,33 +41,53 @@ static const MfClassicKeyPair troika_1k_keys[] = {
|
||||
};
|
||||
|
||||
static const MfClassicKeyPair troika_4k_keys[] = {
|
||||
{.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880},
|
||||
{.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
|
||||
{.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698},
|
||||
{.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dbb},
|
||||
{.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763},
|
||||
{.a = 0x9becdf3d9273, .b = 0xf8493407799d}, {.a = 0x08b386463229, .b = 0x5efbaecef46b},
|
||||
{.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880},
|
||||
{.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
|
||||
{.a = 0x6b02733bb6ec, .b = 0x7038cd25c408}, {.a = 0x403d706ba880, .b = 0xb39d19a280df},
|
||||
{.a = 0xc11f4597efb5, .b = 0x70d901648cb9}, {.a = 0x0db520c78c1c, .b = 0x73e5b9d9d3a4},
|
||||
{.a = 0x3ebce0925b2f, .b = 0x372cc880f216}, {.a = 0x16a27af45407, .b = 0x9868925175ba},
|
||||
{.a = 0xaba208516740, .b = 0xce26ecb95252}, {.a = 0xcd64e567abcd, .b = 0x8f79c4fd8a01},
|
||||
{.a = 0x764cd061f1e6, .b = 0xa74332f74994}, {.a = 0x1cc219e9fec1, .b = 0xb90de525ceb6},
|
||||
{.a = 0x2fe3cb83ea43, .b = 0xfba88f109b32}, {.a = 0x07894ffec1d6, .b = 0xefcb0e689db3},
|
||||
{.a = 0x04c297b91308, .b = 0xc8454c154cb5}, {.a = 0x7a38e3511a38, .b = 0xab16584c972a},
|
||||
{.a = 0x7545df809202, .b = 0xecf751084a80}, {.a = 0x5125974cd391, .b = 0xd3eafb5df46d},
|
||||
{.a = 0x7a86aa203788, .b = 0xe41242278ca2}, {.a = 0xafcef64c9913, .b = 0x9db96dca4324},
|
||||
{.a = 0x04eaa462f70b, .b = 0xac17b93e2fae}, {.a = 0xe734c210f27e, .b = 0x29ba8c3e9fda},
|
||||
{.a = 0xd5524f591eed, .b = 0x5daf42861b4d}, {.a = 0xe4821a377b75, .b = 0xe8709e486465},
|
||||
{.a = 0x518dc6eea089, .b = 0x97c64ac98ca4}, {.a = 0xbb52f8cce07f, .b = 0x6b6119752c70},
|
||||
{.a = 0xEC29806D9738, .b = 0xFBF225DC5D58}, //1
|
||||
{.a = 0xA0A1A2A3A4A5, .b = 0x7DE02A7F6025}, //2
|
||||
{.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //3
|
||||
{.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //4
|
||||
{.a = 0x73068F118C13, .b = 0x2B7F3253FAC5}, //5
|
||||
{.a = 0xFBC2793D540B, .b = 0xD3A297DC2698}, //6
|
||||
{.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //7
|
||||
{.a = 0xAE3D65A3DAD4, .b = 0x0F1C63013DBA}, //8
|
||||
{.a = 0xA73F5DC1D333, .b = 0xE35173494A81}, //9
|
||||
{.a = 0x69A32F1C2F19, .b = 0x6B8BD9860763}, //10
|
||||
{.a = 0x9BECDF3D9273, .b = 0xF8493407799D}, //11
|
||||
{.a = 0x08B386463229, .b = 0x5EFBAECEF46B}, //12
|
||||
{.a = 0xCD4C61C26E3D, .b = 0x31C7610DE3B0}, //13
|
||||
{.a = 0xA82607B01C0D, .b = 0x2910989B6880}, //14
|
||||
{.a = 0x0E8F64340BA4, .b = 0x4ACEC1205D75}, //15
|
||||
{.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //16
|
||||
{.a = 0x6B02733BB6EC, .b = 0x7038CD25C408}, //17
|
||||
{.a = 0x403D706BA880, .b = 0xB39D19A280DF}, //18
|
||||
{.a = 0xC11F4597EFB5, .b = 0x70D901648CB9}, //19
|
||||
{.a = 0x0DB520C78C1C, .b = 0x73E5B9D9D3A4}, //20
|
||||
{.a = 0x3EBCE0925B2F, .b = 0x372CC880F216}, //21
|
||||
{.a = 0x16A27AF45407, .b = 0x9868925175BA}, //22
|
||||
{.a = 0xABA208516740, .b = 0xCE26ECB95252}, //23
|
||||
{.a = 0xCD64E567ABCD, .b = 0x8F79C4FD8A01}, //24
|
||||
{.a = 0x764CD061F1E6, .b = 0xA74332F74994}, //25
|
||||
{.a = 0x1CC219E9FEC1, .b = 0xB90DE525CEB6}, //26
|
||||
{.a = 0x2FE3CB83EA43, .b = 0xFBA88F109B32}, //27
|
||||
{.a = 0x07894FFEC1D6, .b = 0xEFCB0E689DB3}, //28
|
||||
{.a = 0x04C297B91308, .b = 0xC8454C154CB5}, //29
|
||||
{.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30
|
||||
{.a = 0x7545DF809202, .b = 0xECF751084A80}, //31
|
||||
{.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32
|
||||
{.a = 0x7A86AA203788, .b = 0xE41242278CA2}, //33
|
||||
{.a = 0xAFCEF64C9913, .b = 0x9DB96DCA4324}, //34
|
||||
{.a = 0x04EAA462F70B, .b = 0xAC17B93E2FAE}, //35
|
||||
{.a = 0xE734C210F27E, .b = 0x29BA8C3E9FDA}, //36
|
||||
{.a = 0xD5524F591EED, .b = 0x5DAF42861B4D}, //37
|
||||
{.a = 0xE4821A377B75, .b = 0xE8709E486465}, //38
|
||||
{.a = 0x518DC6EEA089, .b = 0x97C64AC98CA4}, //39
|
||||
{.a = 0xBB52F8CCE07F, .b = 0x6B6119752C70}, //40
|
||||
};
|
||||
|
||||
static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {
|
||||
bool success = true;
|
||||
|
||||
if(type == MfClassicType1k) {
|
||||
config->data_sector = 8;
|
||||
config->data_sector = 11;
|
||||
config->keys = troika_1k_keys;
|
||||
} else if(type == MfClassicType4k) {
|
||||
config->data_sector = 8; // Further testing needed
|
||||
@@ -90,126 +99,6 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type)
|
||||
return success;
|
||||
}
|
||||
|
||||
static TroikaLayout troika_get_layout(const MfClassicData* data, uint8_t start_block_num) {
|
||||
furi_assert(data);
|
||||
|
||||
// Layout is stored in byte 6 of block, length 4 bits (bits 52 - 55), second nibble.
|
||||
const uint8_t* layout_ptr = &data->block[start_block_num].data[6];
|
||||
const uint8_t layout = (*layout_ptr & 0x0F);
|
||||
|
||||
TroikaLayout result = TroikaLayoutUnknown;
|
||||
switch(layout) {
|
||||
case TroikaLayout2:
|
||||
case TroikaLayoutE:
|
||||
result = layout;
|
||||
break;
|
||||
default:
|
||||
// If debug is enabled - pass the actual layout value for the debug text
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
return layout;
|
||||
} else {
|
||||
return TroikaLayoutUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static TroikaSubLayout troika_get_sub_layout(const MfClassicData* data, uint8_t start_block_num) {
|
||||
furi_assert(data);
|
||||
|
||||
// Sublayout is stored in byte 7 (bits 56 - 60) of block, length 5 bits (first nibble and one bit from second nibble)
|
||||
const uint8_t* sub_layout_ptr = &data->block[start_block_num].data[7];
|
||||
const uint8_t sub_layout = (*sub_layout_ptr & 0x3F) >> 3;
|
||||
|
||||
TroikaSubLayout result = TroikaSublayoutUnknown;
|
||||
switch(sub_layout) {
|
||||
case TroikaSublayout3:
|
||||
case TroikaSublayout5:
|
||||
case TroikaSublayout6:
|
||||
result = sub_layout;
|
||||
break;
|
||||
default:
|
||||
// If debug is enabled - pass the actual sublayout value for the debug text
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
return sub_layout;
|
||||
} else {
|
||||
return TroikaSublayoutUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool troika_has_balance(TroikaLayout layout, TroikaSubLayout sub_layout) {
|
||||
UNUSED(sub_layout);
|
||||
// Layout 0x2 has no balance
|
||||
|
||||
if(layout == TroikaLayout2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t troika_get_balance(
|
||||
const MfClassicData* data,
|
||||
uint8_t start_block_num,
|
||||
TroikaLayout layout,
|
||||
TroikaSubLayout sub_layout) {
|
||||
furi_assert(data);
|
||||
|
||||
// In layout 0x3 balance in bits 188:209 ( from sector start, length 22).
|
||||
// In layout 0x5 balance in bits 165:185 ( from sector start, length 20).
|
||||
|
||||
uint32_t balance = 0;
|
||||
uint8_t balance_data_offset = 0;
|
||||
bool supported_layout = false;
|
||||
|
||||
if(layout == TroikaLayoutE && sub_layout == TroikaSublayout3) {
|
||||
balance_data_offset = 7;
|
||||
supported_layout = true;
|
||||
} else if(layout == TroikaLayoutE && sub_layout == TroikaSublayout5) {
|
||||
balance_data_offset = 4;
|
||||
supported_layout = true;
|
||||
}
|
||||
|
||||
if(supported_layout) {
|
||||
const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[balance_data_offset];
|
||||
balance |= (temp_ptr[0] & 0x3) << 18;
|
||||
balance |= temp_ptr[1] << 10;
|
||||
balance |= temp_ptr[2] << 2;
|
||||
balance |= (temp_ptr[3] & 0xC0) >> 6;
|
||||
}
|
||||
|
||||
return balance / 100;
|
||||
}
|
||||
|
||||
static uint32_t troika_get_number(
|
||||
const MfClassicData* data,
|
||||
uint8_t start_block_num,
|
||||
TroikaLayout layout,
|
||||
TroikaSubLayout sub_layout) {
|
||||
furi_assert(data);
|
||||
UNUSED(sub_layout);
|
||||
|
||||
if(layout == TroikaLayoutE || layout == TroikaLayout2) {
|
||||
const uint8_t* temp_ptr = &data->block[start_block_num].data[2];
|
||||
|
||||
uint32_t number = 0;
|
||||
for(size_t i = 1; i < 5; i++) {
|
||||
number <<= 8;
|
||||
number |= temp_ptr[i];
|
||||
}
|
||||
number >>= 4;
|
||||
number |= (temp_ptr[0] & 0xf) << 28;
|
||||
|
||||
return number;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool troika_verify_type(Nfc* nfc, MfClassicType type) {
|
||||
bool verified = false;
|
||||
|
||||
@@ -230,7 +119,7 @@ static bool troika_verify_type(Nfc* nfc, MfClassicType type) {
|
||||
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Verify success!");
|
||||
verified = true;
|
||||
} while(false);
|
||||
|
||||
@@ -306,40 +195,34 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
|
||||
if(key != cfg.keys[cfg.data_sector].a) break;
|
||||
|
||||
// Get the block number of the block that contains the data
|
||||
const uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector);
|
||||
FuriString* metro_result = furi_string_alloc();
|
||||
FuriString* ground_result = furi_string_alloc();
|
||||
FuriString* tat_result = furi_string_alloc();
|
||||
|
||||
// Get layout, sublayout, balance and number
|
||||
TroikaLayout layout = troika_get_layout(data, start_block_num);
|
||||
TroikaSubLayout sub_layout = troika_get_sub_layout(data, start_block_num);
|
||||
bool result1 = mosgortrans_parse_transport_block(&data->block[32], metro_result);
|
||||
bool result2 = mosgortrans_parse_transport_block(&data->block[28], ground_result);
|
||||
bool result3 = mosgortrans_parse_transport_block(&data->block[16], tat_result);
|
||||
|
||||
if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
// If debug is enabled - proceed even if layout or sublayout is unknown, that will make collecting data easier
|
||||
if(layout == TroikaLayoutUnknown || sub_layout == TroikaSublayoutUnknown) break;
|
||||
}
|
||||
|
||||
uint32_t number = troika_get_number(data, start_block_num, layout, sub_layout);
|
||||
|
||||
furi_string_printf(parsed_data, "\e#Troika\nNum: %lu", number);
|
||||
|
||||
if(troika_has_balance(layout, sub_layout) ||
|
||||
furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
uint16_t balance = troika_get_balance(data, start_block_num, layout, sub_layout);
|
||||
furi_string_cat_printf(parsed_data, "\nBalance: %u RUR", balance);
|
||||
} else {
|
||||
furi_string_cat_printf(parsed_data, "\nBalance: Not available");
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
furi_string_cat_printf(parsed_data, "\e#Troyka card\n");
|
||||
if(result1) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data,
|
||||
"\nLayout: %02x\nSublayout: %02x\nData Block: %u",
|
||||
layout,
|
||||
sub_layout,
|
||||
start_block_num);
|
||||
parsed_data, "\e#Metro\n%s\n", furi_string_get_cstr(metro_result));
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
if(result2) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data, "\n\e#Ediniy\n%s\n", furi_string_get_cstr(ground_result));
|
||||
}
|
||||
|
||||
if(result3) {
|
||||
furi_string_cat_printf(parsed_data, "\n\e#TAT\n%s", furi_string_get_cstr(tat_result));
|
||||
}
|
||||
|
||||
furi_string_free(tat_result);
|
||||
furi_string_free(ground_result);
|
||||
furi_string_free(metro_result);
|
||||
|
||||
parsed = result1 || result2 || result3;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
@@ -361,6 +244,6 @@ static const FlipperAppPluginDescriptor troika_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* troika_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* troika_plugin_ep(void) {
|
||||
return &troika_plugin_descriptor;
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
|
||||
furi_string_printf(
|
||||
parsed_data,
|
||||
"\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n",
|
||||
"\e#Troika+Plantain\nPN: %llu?\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n",
|
||||
card_number,
|
||||
balance,
|
||||
troika_number,
|
||||
@@ -186,6 +186,6 @@ static const FlipperAppPluginDescriptor two_cities_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* two_cities_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* two_cities_plugin_ep(void) {
|
||||
return &two_cities_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,6 @@ static const FlipperAppPluginDescriptor umarsh_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* umarsh_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* umarsh_plugin_ep(void) {
|
||||
return &umarsh_plugin_descriptor;
|
||||
}
|
||||
|
||||
@@ -57,20 +57,20 @@ static bool washcity_verify(Nfc* nfc) {
|
||||
bool verified = false;
|
||||
|
||||
do {
|
||||
const uint8_t ticket_sector_number = 0;
|
||||
const uint8_t ticket_block_number =
|
||||
mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1;
|
||||
FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number);
|
||||
const uint8_t verify_sector_number = 1;
|
||||
const uint8_t verify_block_number =
|
||||
mf_classic_get_first_block_num_of_sector(verify_sector_number);
|
||||
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector_number);
|
||||
|
||||
MfClassicKey key = {0};
|
||||
bit_lib_num_to_bytes_be(
|
||||
washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data);
|
||||
washcity_1k_keys[verify_sector_number].a, COUNT_OF(key.data), key.data);
|
||||
|
||||
MfClassicAuthContext auth_context;
|
||||
MfClassicError error = mf_classic_poller_sync_auth(
|
||||
nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context);
|
||||
nfc, verify_block_number, &key, MfClassicKeyTypeA, &auth_context);
|
||||
if(error != MfClassicErrorNone) {
|
||||
FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error);
|
||||
FURI_LOG_D(TAG, "Failed to read block %u: %d", verify_block_number, error);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -192,6 +192,6 @@ static const FlipperAppPluginDescriptor washcity_plugin_descriptor = {
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* washcity_plugin_ep() {
|
||||
const FlipperAppPluginDescriptor* washcity_plugin_ep(void) {
|
||||
return &washcity_plugin_descriptor;
|
||||
}
|
||||
@@ -33,6 +33,8 @@ ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||
ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)
|
||||
ADD_SCENE(nfc, felica_key_input, FelicaKeyInput)
|
||||
ADD_SCENE(nfc, felica_unlock_warn, FelicaUnlockWarn)
|
||||
|
||||
ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)
|
||||
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
||||
@@ -43,10 +45,11 @@ 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_update_initial_wrong_card, MfClassicUpdateInitialWrongCard)
|
||||
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_write_initial_wrong_card, MfClassicWriteInitialWrongCard)
|
||||
|
||||
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
|
||||
ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
|
||||
|
||||
@@ -19,7 +19,7 @@ void nfc_scene_detect_on_enter(void* context) {
|
||||
popup_reset(instance->popup);
|
||||
popup_set_header(instance->popup, "Reading", 97, 15, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
|
||||
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
|
||||
|
||||
46
applications/main/nfc/scenes/nfc_scene_felica_key_input.c
Normal file
46
applications/main/nfc/scenes/nfc_scene_felica_key_input.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_felica_key_input_byte_input_callback(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_scene_felica_key_input_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Setup view
|
||||
ByteInput* byte_input = nfc->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter key in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_scene_felica_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc,
|
||||
nfc->felica_auth->card_key.data,
|
||||
FELICA_DATA_BLOCK_SIZE);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventByteInputDone) {
|
||||
nfc->felica_auth->skip_auth = false;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_key_input_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Clear view
|
||||
byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(nfc->byte_input, "");
|
||||
}
|
||||
59
applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c
Normal file
59
applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_felica_unlock_warn_dialog_callback(DialogExResult result, void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void nfc_scene_felica_unlock_warn_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
const char* message = "Risky Action!";
|
||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||
dialog_ex_set_context(dialog_ex, nfc);
|
||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_felica_unlock_warn_dialog_callback);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);
|
||||
|
||||
FuriString* str = furi_string_alloc();
|
||||
furi_string_cat_printf(str, "Unlock with key: ");
|
||||
for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++)
|
||||
furi_string_cat_printf(str, "%02X ", nfc->felica_auth->card_key.data[i]);
|
||||
furi_string_cat_printf(str, "?");
|
||||
|
||||
nfc_text_store_set(nfc, furi_string_get_cstr(str));
|
||||
furi_string_free(str);
|
||||
|
||||
dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 12, AlignLeft, AlignTop);
|
||||
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Unlock");
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == DialogExResultRight) {
|
||||
nfc->felica_auth->skip_auth = false;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
|
||||
consumed = true;
|
||||
} else if(event.event == DialogExResultLeft) {
|
||||
scene_manager_previous_scene(nfc->scene_manager);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_unlock_warn_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
dialog_ex_reset(nfc->dialog_ex);
|
||||
nfc_text_store_clear(nfc);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) {
|
||||
// Setup view
|
||||
Popup* popup = instance->popup;
|
||||
popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||
popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop);
|
||||
popup_set_header(popup, "Key Already Exists!", 64, 3, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
popup,
|
||||
"Please enter a\n"
|
||||
|
||||
@@ -14,7 +14,7 @@ void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) {
|
||||
NfcApp* instance = context;
|
||||
|
||||
widget_add_string_element(
|
||||
instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!");
|
||||
instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Completed!");
|
||||
widget_add_string_multiline_element(
|
||||
instance->widget,
|
||||
64,
|
||||
|
||||
@@ -61,7 +61,7 @@ static void nfc_scene_mf_classic_update_initial_setup_view(NfcApp* instance) {
|
||||
|
||||
if(state == NfcSceneMfClassicUpdateInitialStateCardSearch) {
|
||||
popup_set_text(
|
||||
instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
|
||||
instance->popup, "Use the source\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);
|
||||
@@ -111,14 +111,16 @@ bool nfc_scene_mf_classic_update_initial_on_event(void* context, SceneManagerEve
|
||||
nfc_scene_mf_classic_update_initial_setup_view(instance);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventWrongCard) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard);
|
||||
scene_manager_next_scene(
|
||||
instance->scene_manager, NfcSceneMfClassicUpdateInitialWrongCard);
|
||||
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);
|
||||
scene_manager_next_scene(
|
||||
instance->scene_manager, NfcSceneMfClassicUpdateInitialWrongCard);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_mf_classic_update_initial_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_update_initial_wrong_card_on_enter(void* context) {
|
||||
NfcApp* instance = context;
|
||||
Widget* widget = instance->widget;
|
||||
|
||||
notification_message(instance->notifications, &sequence_error);
|
||||
|
||||
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||
widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wrong Card!");
|
||||
widget_add_string_multiline_element(
|
||||
widget,
|
||||
4,
|
||||
17,
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Data management\nis only possible\nwith source card");
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Retry",
|
||||
nfc_scene_mf_classic_update_initial_wrong_card_widget_callback,
|
||||
instance);
|
||||
|
||||
// Setup and start worker
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_scene_mf_classic_update_initial_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_update_initial_wrong_card_on_exit(void* context) {
|
||||
NfcApp* instance = context;
|
||||
|
||||
widget_reset(instance->widget);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) {
|
||||
if(state == NfcSceneMfClassicWriteInitialStateCardSearch) {
|
||||
popup_set_header(instance->popup, "Writing", 95, 20, AlignCenter, AlignCenter);
|
||||
popup_set_text(
|
||||
instance->popup, "Apply the initial\ncard only", 95, 38, AlignCenter, AlignCenter);
|
||||
instance->popup, "Use the source\ncard only", 95, 38, AlignCenter, 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);
|
||||
@@ -115,7 +115,8 @@ bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEven
|
||||
nfc_scene_mf_classic_write_initial_setup_view(instance);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventWrongCard) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard);
|
||||
scene_manager_next_scene(
|
||||
instance->scene_manager, NfcSceneMfClassicWriteInitialWrongCard);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventPollerSuccess) {
|
||||
scene_manager_next_scene(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_mf_classic_wrong_card_widget_callback(
|
||||
void nfc_scene_mf_classic_write_initial_wrong_card_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
@@ -10,7 +10,7 @@ void nfc_scene_mf_classic_wrong_card_widget_callback(
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_wrong_card_on_enter(void* context) {
|
||||
void nfc_scene_mf_classic_write_initial_wrong_card_on_enter(void* context) {
|
||||
NfcApp* instance = context;
|
||||
Widget* widget = instance->widget;
|
||||
|
||||
@@ -18,7 +18,7 @@ void nfc_scene_mf_classic_wrong_card_on_enter(void* context) {
|
||||
|
||||
widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||
widget_add_string_element(
|
||||
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
|
||||
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Use The Source Card!");
|
||||
widget_add_string_multiline_element(
|
||||
widget,
|
||||
4,
|
||||
@@ -26,19 +26,19 @@ void nfc_scene_mf_classic_wrong_card_on_enter(void* context) {
|
||||
AlignLeft,
|
||||
AlignTop,
|
||||
FontSecondary,
|
||||
"Data management\nis only possible\nwith initial card");
|
||||
"Go to NFC Magic\napp if you want to\nwrite blanks");
|
||||
widget_add_button_element(
|
||||
widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Retry",
|
||||
nfc_scene_mf_classic_wrong_card_widget_callback,
|
||||
nfc_scene_mf_classic_write_initial_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) {
|
||||
bool nfc_scene_mf_classic_write_initial_wrong_card_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* instance = context;
|
||||
bool consumed = false;
|
||||
|
||||
@@ -50,7 +50,7 @@ bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent e
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_wrong_card_on_exit(void* context) {
|
||||
void nfc_scene_mf_classic_write_initial_wrong_card_on_exit(void* context) {
|
||||
NfcApp* instance = context;
|
||||
|
||||
widget_reset(instance->widget);
|
||||
@@ -35,7 +35,7 @@ void nfc_scene_mf_desfire_more_info_on_enter(void* context) {
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
|
||||
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
|
||||
furi_string_printf(
|
||||
label, "App %02x%02x%02x", app_id->data[0], app_id->data[1], app_id->data[2]);
|
||||
label, "App %02x%02x%02x", app_id->data[2], app_id->data[1], app_id->data[0]);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(label),
|
||||
|
||||
@@ -20,12 +20,17 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
|
||||
uint32_t state =
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||
if(nfc_device_get_protocol(nfc->nfc_device) == NfcProtocolMfUltralight) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock With Reader",
|
||||
SubmenuIndexMfUlUnlockMenuReader,
|
||||
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
const MfUltralightData* mfu_data =
|
||||
nfc_device_get_data(nfc->nfc_device, NfcProtocolMfUltralight);
|
||||
// Hide for MFU-C since it uses 3DES
|
||||
if(mfu_data->type != MfUltralightTypeMfulC) {
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Unlock With Reader",
|
||||
SubmenuIndexMfUlUnlockMenuReader,
|
||||
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
}
|
||||
}
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
|
||||
@@ -22,14 +22,14 @@ void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {
|
||||
for(size_t i = 0; i < sizeof(nfc->mf_ul_auth->password.data); i++) {
|
||||
furi_string_cat_printf(password_str, "%02X ", nfc->mf_ul_auth->password.data[i]);
|
||||
}
|
||||
furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!");
|
||||
furi_string_cat_str(password_str, "\nWarning: incorrect password\nwill block the card!");
|
||||
nfc_text_store_set(nfc, furi_string_get_cstr(password_str));
|
||||
furi_string_free(password_str);
|
||||
|
||||
const char* message = (type == MfUltralightAuthTypeReader) ? "Password captured!" :
|
||||
"Risky function!";
|
||||
const char* message = (type == MfUltralightAuthTypeReader) ? "Password Captured!" :
|
||||
"Risky Action!";
|
||||
dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 10, AlignCenter, AlignTop);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Continue");
|
||||
|
||||
@@ -37,7 +37,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {
|
||||
notification_message(nfc->notifications, &sequence_set_green_255);
|
||||
}
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop);
|
||||
dialog_ex_set_header(dialog_ex, "Risky action!", 64, 4, AlignCenter, AlignTop);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop);
|
||||
dialog_ex_set_icon(dialog_ex, 83, 22, &I_WarningDolphinFlip_45x42);
|
||||
|
||||
@@ -57,6 +57,11 @@ bool nfc_scene_select_protocol_on_event(void* context, SceneManagerEvent event)
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneSelectProtocol, event.event);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
instance->scene_manager, NfcSceneStart);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ void nfc_scene_slix_unlock_on_enter(void* context) {
|
||||
popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop);
|
||||
instance->popup, "Hold card next\nto Flipper's back", 94, 27, AlignCenter, AlignTop);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup);
|
||||
|
||||
instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolSlix);
|
||||
|
||||
@@ -86,7 +86,7 @@ static bool detect_reader_input_callback(InputEvent* event, void* context) {
|
||||
return consumed;
|
||||
}
|
||||
|
||||
DetectReader* detect_reader_alloc() {
|
||||
DetectReader* detect_reader_alloc(void) {
|
||||
DetectReader* detect_reader = malloc(sizeof(DetectReader));
|
||||
detect_reader->view = view_alloc();
|
||||
view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel));
|
||||
|
||||
@@ -14,7 +14,7 @@ typedef enum {
|
||||
|
||||
typedef void (*DetectReaderDoneCallback)(void* context);
|
||||
|
||||
DetectReader* detect_reader_alloc();
|
||||
DetectReader* detect_reader_alloc(void);
|
||||
|
||||
void detect_reader_free(DetectReader* detect_reader);
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ static bool dict_attack_input_callback(InputEvent* event, void* context) {
|
||||
return consumed;
|
||||
}
|
||||
|
||||
DictAttack* dict_attack_alloc() {
|
||||
DictAttack* dict_attack_alloc(void) {
|
||||
DictAttack* instance = malloc(sizeof(DictAttack));
|
||||
instance->view = view_alloc();
|
||||
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DictAttackViewModel));
|
||||
|
||||
@@ -15,7 +15,7 @@ typedef enum {
|
||||
|
||||
typedef void (*DictAttackCallback)(DictAttackEvent event, void* context);
|
||||
|
||||
DictAttack* dict_attack_alloc();
|
||||
DictAttack* dict_attack_alloc(void);
|
||||
|
||||
void dict_attack_free(DictAttack* instance);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user