1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

[FL-3772] Felica poller (#3570)

* New types for felica poller
* New functions for felica data transmissions
* Felica memory map extended with new fields
* Init/deinit of mbedtls context added for felica encryption
* Functions for session key and mac calculations added
* Raw felica_poller implementation added
* Removed MAC type parameter from check_mac function
* Replaced all data fields needed for auth with context structure
* Clean up felica_poller.c
* Now RC block is filled with random numbers
* New parameter for counting well-read blocks
* Some cleanups
* Felica file save and load logic added
* Now we use card key from context for session key calculation
* Copying card key to card block from auth context when both authentications succeeded, otherwise decrement blocks count by 1
* New felica poller event added
* Moved some data structions to public namespace
* FelicaAuthenticationContext struct moved to felica.h
* Field type and name changed for better ones
* Helper functions for felica_auth added to the app
* New scene for felica card key input added
* Logic for felica key input added
* Auth context request processing added
* Added block index definitions and replaced all index numbers with them
* More macro defines
* Replace nesting with do while block
* New function for write operations mac calculation added
* Replace nesting with do while block
* Make functions static for now because they are used internally
* Wrote some comments
* Raw felica render implementation
* New felica scenes
* Adjusted felica dump rendering according design requirements
* New felica scene added
* Helper for switching scene during unlock added
* Added warning scene and transfer to it
* Moved unlock scene logic to separate files
* Magic number changed
* New felica render logic
* Felica scenes adjusted according to design requirements
* Felica poller cleanups
* Some asserts added and some fixed
* Replcaed asserts to checks in public api
* Fixed pvs warnings in felica_poller
* New event for felica_poller added for incomplete read actions
* Handling of new poller event added
* Update SConscript with felica files
* Update api_symbols.csv with felica functions
* Sync API versions

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
RebornedBrain
2024-04-10 12:51:36 +03:00
committed by GitHub
parent db14ca964b
commit fb9728d570
22 changed files with 1186 additions and 85 deletions

View 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;
}

View 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

View File

@@ -7,6 +7,11 @@
#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_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) { static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device; 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)); temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str); 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( widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); 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; NfcApp* instance = context;
const FelicaPollerEvent* felica_event = event.event_data; const FelicaPollerEvent* felica_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(felica_event->type == FelicaPollerEventTypeReady) { if(felica_event->type == FelicaPollerEventTypeReady) {
nfc_device_set_data( nfc_device_set_data(
instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller)); instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); 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) { 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); 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) { static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device; const NfcDevice* device = instance->nfc_device;
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
FuriString* temp_str = furi_string_alloc(); FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn)) {
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str); 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( widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); 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); 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 = { const NfcProtocolSupportBase nfc_protocol_support_felica = {
.features = NfcProtocolFeatureEmulateUid, .features = NfcProtocolFeatureEmulateUid,
.scene_info = .scene_info =
{ {
.on_enter = nfc_scene_info_on_enter_felica, .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, .on_event = nfc_protocol_support_common_on_event_empty,
}, },
.scene_read = .scene_read =
{ {
.on_enter = nfc_scene_read_on_enter_felica, .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 = .scene_read_menu =
{ {
.on_enter = nfc_protocol_support_common_on_enter_empty, .on_enter = nfc_scene_read_menu_on_enter_felica,
.on_event = nfc_protocol_support_common_on_event_empty, .on_event = nfc_scene_read_menu_on_event_felica,
}, },
.scene_read_success = .scene_read_success =
{ {

View File

@@ -1,19 +1,107 @@
#include "felica_render.h" #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( void nfc_render_felica_info(
const FelicaData* data, const FelicaData* data,
NfcProtocolFormatType format_type, NfcProtocolFormatType format_type,
FuriString* str) { FuriString* str) {
furi_string_cat_printf(str, "IDm:"); if(format_type == NfcProtocolFormatTypeFull) {
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
for(size_t i = 0; i < FELICA_IDM_SIZE; i++) {
furi_string_cat_printf(str, " %02X", data->idm.data[i]);
} }
nfc_render_felica_idm(data, format_type, str);
if(format_type == NfcProtocolFormatTypeFull) { 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) { 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);
} }

View File

@@ -4,7 +4,19 @@
#include "../nfc_protocol_support_render_common.h" #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( void nfc_render_felica_info(
const FelicaData* data, const FelicaData* data,
NfcProtocolFormatType format_type, NfcProtocolFormatType format_type,
FuriString* str); 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);

View File

@@ -8,6 +8,7 @@
#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h" #include "../nfc_protocol_support_gui_common.h"
#include "../nfc_protocol_support_unlock_helper.h"
enum { enum {
SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlock = SubmenuIndexCommonMax,
@@ -157,48 +158,15 @@ static NfcCommand
return NfcCommandContinue; 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, "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);
}
static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
bool unlocking = nfc_unlock_helper_setup_from_state(instance);
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_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, 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) { bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventCardDetected) { if(event.event == NfcCustomEventCardDetected) {
scene_manager_set_scene_state( nfc_unlock_helper_card_detected_handler(instance);
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
nfc_scene_read_setup_view(instance);
} else if((event.event == NfcCustomEventPollerIncomplete)) { } else if((event.event == NfcCustomEventPollerIncomplete)) {
notification_message(instance->notifications, &sequence_semi_success); notification_message(instance->notifications, &sequence_semi_success);
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);

View File

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

View File

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

View File

@@ -50,6 +50,7 @@ NfcApp* nfc_app_alloc(void) {
instance->nfc = nfc_alloc(); instance->nfc = nfc_alloc();
instance->felica_auth = felica_auth_alloc();
instance->mf_ul_auth = mf_ultralight_auth_alloc(); instance->mf_ul_auth = mf_ultralight_auth_alloc();
instance->slix_unlock = slix_unlock_alloc(); instance->slix_unlock = slix_unlock_alloc();
instance->mfc_key_cache = mf_classic_key_cache_alloc(); instance->mfc_key_cache = mf_classic_key_cache_alloc();
@@ -141,6 +142,7 @@ void nfc_app_free(NfcApp* instance) {
nfc_free(instance->nfc); nfc_free(instance->nfc);
felica_auth_free(instance->felica_auth);
mf_ultralight_auth_free(instance->mf_ul_auth); mf_ultralight_auth_free(instance->mf_ul_auth);
slix_unlock_free(instance->slix_unlock); slix_unlock_free(instance->slix_unlock);
mf_classic_key_cache_free(instance->mfc_key_cache); mf_classic_key_cache_free(instance->mfc_key_cache);

View File

@@ -32,6 +32,7 @@
#include "helpers/mfkey32_logger.h" #include "helpers/mfkey32_logger.h"
#include "helpers/mf_classic_key_cache.h" #include "helpers/mf_classic_key_cache.h"
#include "helpers/nfc_supported_cards.h" #include "helpers/nfc_supported_cards.h"
#include "helpers/felica_auth.h"
#include "helpers/slix_unlock.h" #include "helpers/slix_unlock.h"
#include <dialogs/dialogs.h> #include <dialogs/dialogs.h>
@@ -129,6 +130,7 @@ struct NfcApp {
NfcScanner* scanner; NfcScanner* scanner;
NfcListener* listener; NfcListener* listener;
FelicaAuthenticationContext* felica_auth;
MfUltralightAuth* mf_ul_auth; MfUltralightAuth* mf_ul_auth;
SlixUnlock* slix_unlock; SlixUnlock* slix_unlock;
NfcMfClassicDictAttackContext nfc_dict_context; NfcMfClassicDictAttackContext nfc_dict_context;

View File

@@ -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_unlock_warn, MfUltralightUnlockWarn)
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass) 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_more_info, MfDesfireMoreInfo)
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)

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

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

View File

@@ -24,6 +24,7 @@ env.Append(
File("protocols/mf_desfire/mf_desfire.h"), File("protocols/mf_desfire/mf_desfire.h"),
File("protocols/slix/slix.h"), File("protocols/slix/slix.h"),
File("protocols/st25tb/st25tb.h"), File("protocols/st25tb/st25tb.h"),
File("protocols/felica/felica.h"),
# Pollers # Pollers
File("protocols/iso14443_3a/iso14443_3a_poller.h"), File("protocols/iso14443_3a/iso14443_3a_poller.h"),
File("protocols/iso14443_3b/iso14443_3b_poller.h"), File("protocols/iso14443_3b/iso14443_3b_poller.h"),
@@ -33,6 +34,7 @@ env.Append(
File("protocols/mf_classic/mf_classic_poller.h"), File("protocols/mf_classic/mf_classic_poller.h"),
File("protocols/mf_desfire/mf_desfire_poller.h"), File("protocols/mf_desfire/mf_desfire_poller.h"),
File("protocols/st25tb/st25tb_poller.h"), File("protocols/st25tb/st25tb_poller.h"),
File("protocols/felica/felica_poller.h"),
# Listeners # Listeners
File("protocols/iso14443_3a/iso14443_3a_listener.h"), File("protocols/iso14443_3a/iso14443_3a_listener.h"),
File("protocols/iso14443_4a/iso14443_4a_listener.h"), File("protocols/iso14443_4a/iso14443_4a_listener.h"),

View File

@@ -13,6 +13,14 @@
static const uint32_t felica_data_format_version = 1; static const uint32_t felica_data_format_version = 1;
/** @brief This is used in felica_prepare_first_block to define which
* type of block needs to be prepared.
*/
typedef enum {
FelicaMACTypeRead,
FelicaMACTypeWrite,
} FelicaMACType;
const NfcDeviceBase nfc_device_felica = { const NfcDeviceBase nfc_device_felica = {
.protocol_name = FELICA_PROTOCOL_NAME, .protocol_name = FELICA_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)felica_alloc, .alloc = (NfcDeviceAlloc)felica_alloc,
@@ -35,18 +43,18 @@ FelicaData* felica_alloc(void) {
} }
void felica_free(FelicaData* data) { void felica_free(FelicaData* data) {
furi_assert(data); furi_check(data);
free(data); free(data);
} }
void felica_reset(FelicaData* data) { void felica_reset(FelicaData* data) {
furi_check(data);
memset(data, 0, sizeof(FelicaData)); memset(data, 0, sizeof(FelicaData));
} }
void felica_copy(FelicaData* data, const FelicaData* other) { void felica_copy(FelicaData* data, const FelicaData* other) {
furi_assert(data); furi_check(data);
furi_assert(other); furi_check(other);
*data = *other; *data = *other;
} }
@@ -59,7 +67,7 @@ bool felica_verify(FelicaData* data, const FuriString* device_type) {
} }
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) { bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data); furi_check(data);
bool parsed = false; bool parsed = false;
@@ -77,13 +85,32 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
break; break;
parsed = true; parsed = true;
uint32_t blocks_total = 0;
uint32_t blocks_read = 0;
if(!flipper_format_read_uint32(ff, "Blocks total", &blocks_total, 1)) break;
if(!flipper_format_read_uint32(ff, "Blocks read", &blocks_read, 1)) break;
data->blocks_total = (uint8_t)blocks_total;
data->blocks_read = (uint8_t)blocks_read;
FuriString* temp_str = furi_string_alloc();
for(uint8_t i = 0; i < data->blocks_total; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_hex(
ff,
furi_string_get_cstr(temp_str),
(&data->data.dump[i * sizeof(FelicaBlock)]),
sizeof(FelicaBlock))) {
parsed = false;
break;
}
}
} while(false); } while(false);
return parsed; return parsed;
} }
bool felica_save(const FelicaData* data, FlipperFormat* ff) { bool felica_save(const FelicaData* data, FlipperFormat* ff) {
furi_assert(data); furi_check(data);
bool saved = false; bool saved = false;
@@ -98,15 +125,33 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break; break;
uint32_t blocks_total = data->blocks_total;
uint32_t blocks_read = data->blocks_read;
if(!flipper_format_write_uint32(ff, "Blocks total", &blocks_total, 1)) break;
if(!flipper_format_write_uint32(ff, "Blocks read", &blocks_read, 1)) break;
saved = true; saved = true;
FuriString* temp_str = furi_string_alloc();
for(uint8_t i = 0; i < blocks_total; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_write_hex(
ff,
furi_string_get_cstr(temp_str),
(&data->data.dump[i * sizeof(FelicaBlock)]),
sizeof(FelicaBlock))) {
saved = false;
break;
}
}
furi_string_free(temp_str);
} while(false); } while(false);
return saved; return saved;
} }
bool felica_is_equal(const FelicaData* data, const FelicaData* other) { bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
furi_assert(data); furi_check(data);
furi_assert(other); furi_check(other);
return memcmp(data, other, sizeof(FelicaData)) == 0; return memcmp(data, other, sizeof(FelicaData)) == 0;
} }
@@ -119,7 +164,7 @@ const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType nam
} }
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) { const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
furi_assert(data); furi_check(data);
// Consider Manufacturer ID as UID // Consider Manufacturer ID as UID
if(uid_len) { if(uid_len) {
@@ -130,7 +175,7 @@ const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
} }
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) { bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data); furi_check(data);
// Consider Manufacturer ID as UID // Consider Manufacturer ID as UID
const bool uid_valid = uid_len == FELICA_IDM_SIZE; const bool uid_valid = uid_len == FELICA_IDM_SIZE;
@@ -145,3 +190,149 @@ FelicaData* felica_get_base_data(const FelicaData* data) {
UNUSED(data); UNUSED(data);
furi_crash("No base data"); furi_crash("No base data");
} }
static void felica_reverse_copy_block(const uint8_t* array, uint8_t* reverse_array) {
furi_assert(array);
furi_assert(reverse_array);
for(int i = 0; i < 8; i++) {
reverse_array[i] = array[7 - i];
}
}
void felica_calculate_session_key(
mbedtls_des3_context* ctx,
const uint8_t* ck,
const uint8_t* rc,
uint8_t* out) {
furi_check(ctx);
furi_check(ck);
furi_check(rc);
furi_check(out);
uint8_t iv[8];
memset(iv, 0, 8);
uint8_t ck_reversed[16];
felica_reverse_copy_block(ck, ck_reversed);
felica_reverse_copy_block(ck + 8, ck_reversed + 8);
uint8_t rc_reversed[16];
felica_reverse_copy_block(rc, rc_reversed);
felica_reverse_copy_block(rc + 8, rc_reversed + 8);
mbedtls_des3_set2key_enc(ctx, ck_reversed);
mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, FELICA_DATA_BLOCK_SIZE, iv, rc_reversed, out);
}
static bool felica_calculate_mac(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* first_block,
const uint8_t* data,
const size_t length,
uint8_t* mac) {
furi_check((length % 8) == 0);
uint8_t reverse_data[8];
uint8_t iv[8];
uint8_t out[8];
mbedtls_des3_set2key_enc(ctx, session_key);
felica_reverse_copy_block(rc, iv);
felica_reverse_copy_block(first_block, reverse_data);
uint8_t i = 0;
bool error = false;
do {
if(mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 8, iv, reverse_data, out) == 0) {
memcpy(iv, out, sizeof(iv));
felica_reverse_copy_block(data + i, reverse_data);
i += 8;
} else {
error = true;
break;
}
} while(i <= length);
if(!error) {
felica_reverse_copy_block(out, mac);
}
return !error;
}
static void felica_prepare_first_block(
FelicaMACType operation_type,
const uint8_t* blocks,
const uint8_t block_count,
uint8_t* out) {
furi_check(blocks);
furi_check(out);
if(operation_type == FelicaMACTypeRead) {
memset(out, 0xFF, 8);
for(uint8_t i = 0, j = 0; i < block_count; i++, j += 2) {
out[j] = blocks[i];
out[j + 1] = 0;
}
} else {
furi_check(block_count == 4);
memset(out, 0, 8);
out[0] = blocks[0];
out[1] = blocks[1];
out[2] = blocks[2];
out[4] = blocks[3];
out[6] = FELICA_BLOCK_INDEX_MAC_A;
}
}
bool felica_check_mac(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* blocks,
const uint8_t block_count,
uint8_t* data) {
furi_check(ctx);
furi_check(session_key);
furi_check(rc);
furi_check(blocks);
furi_check(data);
uint8_t first_block[8];
uint8_t mac[8];
felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);
uint8_t data_size_without_mac = FELICA_DATA_BLOCK_SIZE * (block_count - 1);
felica_calculate_mac(ctx, session_key, rc, first_block, data, data_size_without_mac, mac);
uint8_t* mac_ptr = data + data_size_without_mac;
return !memcmp(mac, mac_ptr, 8);
}
void felica_calculate_mac_write(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* wcnt,
const uint8_t* data,
uint8_t* mac) {
furi_check(ctx);
furi_check(session_key);
furi_check(rc);
furi_check(wcnt);
furi_check(data);
furi_check(mac);
const uint8_t WCNT_length = 3;
uint8_t block_data[WCNT_length + 1];
uint8_t first_block[8];
memcpy(block_data, wcnt, WCNT_length);
block_data[3] = FELICA_BLOCK_INDEX_STATE;
felica_prepare_first_block(FelicaMACTypeWrite, block_data, WCNT_length + 1, first_block);
uint8_t session_swapped[FELICA_DATA_BLOCK_SIZE];
memcpy(session_swapped, session_key + 8, 8);
memcpy(session_swapped + 8, session_key, 8);
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
}

View File

@@ -2,6 +2,7 @@
#include <toolbox/bit_buffer.h> #include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h> #include <nfc/protocols/nfc_device_base_i.h>
#include <mbedtls/include/mbedtls/des.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -9,6 +10,23 @@ extern "C" {
#define FELICA_IDM_SIZE (8U) #define FELICA_IDM_SIZE (8U)
#define FELICA_PMM_SIZE (8U) #define FELICA_PMM_SIZE (8U)
#define FELICA_DATA_BLOCK_SIZE (16U)
#define FELICA_BLOCKS_TOTAL_COUNT (27U)
#define FELICA_BLOCK_INDEX_REG (0x0EU)
#define FELICA_BLOCK_INDEX_RC (0x80U)
#define FELICA_BLOCK_INDEX_MAC (0x81U)
#define FELICA_BLOCK_INDEX_ID (0x82U)
#define FELICA_BLOCK_INDEX_D_ID (0x83U)
#define FELICA_BLOCK_INDEX_SER_C (0x84U)
#define FELICA_BLOCK_INDEX_SYS_C (0x85U)
#define FELICA_BLOCK_INDEX_CKV (0x86U)
#define FELICA_BLOCK_INDEX_CK (0x87U)
#define FELICA_BLOCK_INDEX_MC (0x88U)
#define FELICA_BLOCK_INDEX_WCNT (0x90U)
#define FELICA_BLOCK_INDEX_MAC_A (0x91U)
#define FELICA_BLOCK_INDEX_STATE (0x92U)
#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)
#define FELICA_GUARD_TIME_US (20000U) #define FELICA_GUARD_TIME_US (20000U)
#define FELICA_FDT_POLL_FC (10000U) #define FELICA_FDT_POLL_FC (10000U)
@@ -23,6 +41,7 @@ extern "C" {
#define FELICA_TIME_SLOT_8 (0x07U) #define FELICA_TIME_SLOT_8 (0x07U)
#define FELICA_TIME_SLOT_16 (0x0FU) #define FELICA_TIME_SLOT_16 (0x0FU)
/** @brief Type of possible Felica errors */
typedef enum { typedef enum {
FelicaErrorNone, FelicaErrorNone,
FelicaErrorNotPresent, FelicaErrorNotPresent,
@@ -35,17 +54,78 @@ typedef enum {
FelicaErrorTimeout, FelicaErrorTimeout,
} FelicaError; } FelicaError;
/** @brief Separate type for card key block. Used in authentication process */
typedef struct {
uint8_t data[FELICA_DATA_BLOCK_SIZE];
} FelicaCardKey;
/** @brief In Felica there two types of auth. Internal is the first one, after
* which external became possible. Here are two flags representing which one
* was passed */
typedef struct {
bool internal : 1;
bool external : 1;
} FelicaAuthenticationStatus;
/** @brief Struct which controls the process of authentication and can be passed as
* a parameter to the application level. In order to force user to fill card key block data. */
typedef struct {
bool skip_auth; /**< By default it is true, so auth is skipped. By setting this to false several auth steps will be performed in order to pass auth*/
FelicaCardKey
card_key; /**< User must fill this field with known card key in order to pass auth*/
FelicaAuthenticationStatus auth_status; /**< Authentication status*/
} FelicaAuthenticationContext;
/** @brief Felica ID block */
typedef struct { typedef struct {
uint8_t data[FELICA_IDM_SIZE]; uint8_t data[FELICA_IDM_SIZE];
} FelicaIDm; } FelicaIDm;
/** @brief Felica PMm block */
typedef struct { typedef struct {
uint8_t data[FELICA_PMM_SIZE]; uint8_t data[FELICA_PMM_SIZE];
} FelicaPMm; } FelicaPMm;
/** @brief Felica block with status flags indicating last operation with it.
* See Felica manual for more details on status codes. */
typedef struct {
uint8_t SF1; /**< Status flag 1, equals to 0 when success*/
uint8_t SF2; /**< Status flag 2, equals to 0 when success*/
uint8_t data[FELICA_DATA_BLOCK_SIZE]; /**< Block data */
} FelicaBlock;
/** @brief Felica filesystem structure */
typedef struct {
FelicaBlock spad[14];
FelicaBlock reg;
FelicaBlock rc;
FelicaBlock mac;
FelicaBlock id;
FelicaBlock d_id;
FelicaBlock ser_c;
FelicaBlock sys_c;
FelicaBlock ckv;
FelicaBlock ck;
FelicaBlock mc;
FelicaBlock wcnt;
FelicaBlock mac_a;
FelicaBlock state;
FelicaBlock crc_check;
} FelicaFileSystem;
/** @brief Union which represents filesystem in junction with plain data dump */
typedef union {
FelicaFileSystem fs;
uint8_t dump[sizeof(FelicaFileSystem)];
} FelicaFSUnion;
/** @brief Structure used to store Felica data and additional values about reading */
typedef struct { typedef struct {
FelicaIDm idm; FelicaIDm idm;
FelicaPMm pmm; FelicaPMm pmm;
uint8_t blocks_total;
uint8_t blocks_read;
FelicaFSUnion data;
} FelicaData; } FelicaData;
extern const NfcDeviceBase nfc_device_felica; extern const NfcDeviceBase nfc_device_felica;
@@ -74,6 +154,27 @@ bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);
FelicaData* felica_get_base_data(const FelicaData* data); FelicaData* felica_get_base_data(const FelicaData* data);
void felica_calculate_session_key(
mbedtls_des3_context* ctx,
const uint8_t* ck,
const uint8_t* rc,
uint8_t* out);
bool felica_check_mac(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* blocks,
const uint8_t block_count,
uint8_t* data);
void felica_calculate_mac_write(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* wcnt,
const uint8_t* data,
uint8_t* mac);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -3,6 +3,11 @@
#include <nfc/protocols/nfc_poller_base.h> #include <nfc/protocols/nfc_poller_base.h>
#include <furi.h> #include <furi.h>
#include <furi_hal.h>
#define TAG "FelicaPoller"
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
const FelicaData* felica_poller_get_data(FelicaPoller* instance) { const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
furi_assert(instance); furi_assert(instance);
@@ -23,6 +28,8 @@ static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US); nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC); nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US); nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
mbedtls_des3_init(&instance->auth.des_context);
instance->data = felica_alloc(); instance->data = felica_alloc();
instance->felica_event.data = &instance->felica_event_data; instance->felica_event.data = &instance->felica_event_data;
@@ -40,6 +47,7 @@ static void felica_poller_free(FelicaPoller* instance) {
furi_assert(instance->rx_buffer); furi_assert(instance->rx_buffer);
furi_assert(instance->data); furi_assert(instance->data);
mbedtls_des3_free(&instance->auth.des_context);
bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer); bit_buffer_free(instance->rx_buffer);
felica_free(instance->data); felica_free(instance->data);
@@ -55,6 +63,212 @@ static void
instance->context = context; instance->context = context;
} }
NfcCommand felica_poller_state_handler_idle(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Idle");
felica_reset(instance->data);
instance->state = FelicaPollerStateActivated;
return NfcCommandContinue;
}
NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Activate");
NfcCommand command = NfcCommandContinue;
FelicaError error = felica_poller_activate(instance, instance->data);
if(error == FelicaErrorNone) {
furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE);
instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext;
instance->felica_event_data.auth_context = &instance->auth.context;
instance->callback(instance->general_event, instance->context);
bool skip_auth = instance->auth.context.skip_auth;
instance->state = skip_auth ? FelicaPollerStateReadBlocks :
FelicaPollerStateAuthenticateInternal;
} else if(error != FelicaErrorTimeout) {
instance->felica_event.type = FelicaPollerEventTypeError;
instance->felica_event_data.error = error;
instance->state = FelicaPollerStateReadFailed;
}
return command;
}
NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Auth Internal");
felica_calculate_session_key(
&instance->auth.des_context,
instance->auth.context.card_key.data,
instance->data->data.fs.rc.data,
instance->auth.session_key.data);
instance->state = FelicaPollerStateReadBlocks;
uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0};
FelicaPollerWriteCommandResponse* tx_resp;
do {
FelicaError error = felica_poller_write_blocks(
instance, 1, blocks, instance->data->data.fs.rc.data, &tx_resp);
if((error != FelicaErrorNone) || (tx_resp->SF1 != 0) || (tx_resp->SF2 != 0)) break;
blocks[0] = FELICA_BLOCK_INDEX_ID;
blocks[1] = FELICA_BLOCK_INDEX_WCNT;
blocks[2] = FELICA_BLOCK_INDEX_MAC_A;
FelicaPollerReadCommandResponse* rx_resp;
error = felica_poller_read_blocks(instance, sizeof(blocks), blocks, &rx_resp);
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
if(felica_check_mac(
&instance->auth.des_context,
instance->auth.session_key.data,
instance->data->data.fs.rc.data,
blocks,
rx_resp->block_count,
rx_resp->data)) {
instance->auth.context.auth_status.internal = true;
instance->data->data.fs.wcnt.SF1 = 0;
instance->data->data.fs.wcnt.SF2 = 0;
memcpy(
instance->data->data.fs.wcnt.data,
rx_resp->data + FELICA_DATA_BLOCK_SIZE,
FELICA_DATA_BLOCK_SIZE);
instance->state = FelicaPollerStateAuthenticateExternal;
}
} while(false);
return NfcCommandContinue;
}
NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Auth External");
instance->state = FelicaPollerStateReadBlocks;
uint8_t blocks[2];
instance->data->data.fs.state.data[0] = 1;
FelicaAuthentication* auth = &instance->auth;
felica_calculate_mac_write(
&auth->des_context,
auth->session_key.data,
instance->data->data.fs.rc.data,
instance->data->data.fs.wcnt.data,
instance->data->data.fs.state.data,
instance->data->data.fs.mac_a.data);
memcpy(instance->data->data.fs.mac_a.data + 8, instance->data->data.fs.wcnt.data, 3); //-V1086
uint8_t tx_data[FELICA_DATA_BLOCK_SIZE * 2];
memcpy(tx_data, instance->data->data.fs.state.data, FELICA_DATA_BLOCK_SIZE);
memcpy(
tx_data + FELICA_DATA_BLOCK_SIZE,
instance->data->data.fs.mac_a.data,
FELICA_DATA_BLOCK_SIZE);
do {
blocks[0] = FELICA_BLOCK_INDEX_STATE;
blocks[1] = FELICA_BLOCK_INDEX_MAC_A;
FelicaPollerWriteCommandResponse* tx_resp;
FelicaError error = felica_poller_write_blocks(instance, 2, blocks, tx_data, &tx_resp);
if(error != FelicaErrorNone || tx_resp->SF1 != 0 || tx_resp->SF2 != 0) break;
FelicaPollerReadCommandResponse* rx_resp;
error = felica_poller_read_blocks(instance, 1, blocks, &rx_resp);
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
instance->data->data.fs.state.SF1 = 0;
instance->data->data.fs.state.SF2 = 0;
memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE);
instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];
} while(false);
instance->state = FelicaPollerStateReadBlocks;
return NfcCommandContinue;
}
NfcCommand felica_poller_state_handler_read_blocks(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Read Blocks");
uint8_t block_count = 1;
uint8_t block_list[4] = {0, 0, 0, 0};
block_list[0] = instance->block_index;
instance->block_index++;
if(instance->block_index == FELICA_BLOCK_INDEX_REG + 1) {
instance->block_index = FELICA_BLOCK_INDEX_RC;
} else if(instance->block_index == FELICA_BLOCK_INDEX_MC + 1) {
instance->block_index = FELICA_BLOCK_INDEX_WCNT;
} else if(instance->block_index == FELICA_BLOCK_INDEX_STATE + 1) {
instance->block_index = FELICA_BLOCK_INDEX_CRC_CHECK;
}
FelicaPollerReadCommandResponse* response;
FelicaError error = felica_poller_read_blocks(instance, block_count, block_list, &response);
if(error == FelicaErrorNone) {
block_count = (response->SF1 == 0) ? response->block_count : block_count;
uint8_t* data_ptr =
instance->data->data.dump + instance->data->blocks_total * sizeof(FelicaBlock);
*data_ptr++ = response->SF1;
*data_ptr++ = response->SF2;
if(response->SF1 == 0) {
uint8_t* response_data_ptr = response->data;
instance->data->blocks_read++;
memcpy(data_ptr, response_data_ptr, FELICA_DATA_BLOCK_SIZE);
} else {
memset(data_ptr, 0, FELICA_DATA_BLOCK_SIZE);
}
instance->data->blocks_total++;
if(instance->data->blocks_total == FELICA_BLOCKS_TOTAL_COUNT) {
instance->state = FelicaPollerStateReadSuccess;
}
} else {
instance->felica_event.type = FelicaPollerEventTypeError;
instance->felica_event_data.error = error;
instance->state = FelicaPollerStateReadFailed;
}
return NfcCommandContinue;
}
NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Read Success");
if(!instance->auth.context.auth_status.internal ||
!instance->auth.context.auth_status.external) {
instance->data->blocks_read--;
instance->felica_event.type = FelicaPollerEventTypeIncomplete;
} else {
memcpy(
instance->data->data.fs.ck.data,
instance->auth.context.card_key.data,
FELICA_DATA_BLOCK_SIZE);
instance->felica_event.type = FelicaPollerEventTypeReady;
}
instance->felica_event_data.error = FelicaErrorNone;
return instance->callback(instance->general_event, instance->context);
}
NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {
FURI_LOG_D(TAG, "Read Fail");
instance->callback(instance->general_event, instance->context);
return NfcCommandStop;
}
static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = {
[FelicaPollerStateIdle] = felica_poller_state_handler_idle,
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
[FelicaPollerStateReadBlocks] = felica_poller_state_handler_read_blocks,
[FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success,
[FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,
};
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) { static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid); furi_assert(event.protocol == NfcProtocolInvalid);
@@ -65,24 +279,7 @@ static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
NfcCommand command = NfcCommandContinue; NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) { if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != FelicaPollerStateActivated) { command = felica_poller_handler[instance->state](instance);
FelicaError error = felica_poller_activate(instance, instance->data);
if(error == FelicaErrorNone) {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->felica_event.type = FelicaPollerEventTypeError;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = FelicaErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
} }
return command; return command;

View File

@@ -19,14 +19,33 @@ typedef struct FelicaPoller FelicaPoller;
*/ */
typedef enum { typedef enum {
FelicaPollerEventTypeError, /**< An error occured during activation procedure. */ FelicaPollerEventTypeError, /**< An error occured during activation procedure. */
FelicaPollerEventTypeReady, /**< The card was activated by the poller. */ FelicaPollerEventTypeReady, /**< The card was activated and fully read by the poller. */
FelicaPollerEventTypeIncomplete, /**< The card was activated and partly read by the poller. */
FelicaPollerEventTypeRequestAuthContext, /**< Authentication context was requested by poller. */
} FelicaPollerEventType; } FelicaPollerEventType;
/**
* @brief Stucture for holding Felica session key which is calculated from rc and ck.
*/
typedef struct {
uint8_t data[FELICA_DATA_BLOCK_SIZE];
} FelicaSessionKey;
/**
* @brief Structure used to hold authentication related fields.
*/
typedef struct {
mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */
FelicaSessionKey session_key; /**< Calculated session key. */
FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */
} FelicaAuthentication;
/** /**
* @brief Felica poller event data. * @brief Felica poller event data.
*/ */
typedef union { typedef union {
FelicaError error; /**< Error code indicating card activation fail reason. */ FelicaError error; /**< Error code indicating card activation fail reason. */
FelicaAuthenticationContext* auth_context; /**< Authentication context to be filled by user. */
} FelicaPollerEventData; } FelicaPollerEventData;
/** /**

View File

@@ -3,6 +3,11 @@
#include <nfc/helpers/felica_crc.h> #include <nfc/helpers/felica_crc.h>
#define TAG "FelicaPoller" #define TAG "FelicaPoller"
#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06U)
#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U)
#define FELICA_SERVICE_RW_ACCESS (0x0009U)
#define FELICA_SERVICE_RO_ACCESS (0x000BU)
static FelicaError felica_poller_process_error(NfcError error) { static FelicaError felica_poller_process_error(NfcError error) {
switch(error) { switch(error) {
@@ -15,8 +20,8 @@ static FelicaError felica_poller_process_error(NfcError error) {
} }
} }
static FelicaError felica_poller_frame_exchange( FelicaError felica_poller_frame_exchange(
FelicaPoller* instance, const FelicaPoller* instance,
const BitBuffer* tx_buffer, const BitBuffer* tx_buffer,
BitBuffer* rx_buffer, BitBuffer* rx_buffer,
uint32_t fwt) { uint32_t fwt) {
@@ -93,11 +98,105 @@ FelicaError felica_poller_polling(
return error; return error;
} }
static void felica_poller_prepare_tx_buffer(
const FelicaPoller* instance,
const uint8_t command,
const uint16_t service_code,
const uint8_t block_count,
const uint8_t* const blocks,
const uint8_t data_block_count,
const uint8_t* data) {
FelicaCommandHeader cmd = {
.code = command,
.idm = instance->data->idm,
.service_num = 1,
.service_code = service_code,
.block_count = block_count,
};
FelicaBlockListElement block_list[4] = {{0}, {0}, {0}, {0}};
for(uint8_t i = 0; i < block_count; i++) {
block_list[i].length = 1;
block_list[i].block_number = blocks[i];
}
uint8_t block_list_count = block_count;
uint8_t block_list_size = block_list_count * sizeof(FelicaBlockListElement);
uint8_t total_size = sizeof(FelicaCommandHeader) + 1 + block_list_size +
data_block_count * FELICA_DATA_BLOCK_SIZE;
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, total_size);
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeader));
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block_list, block_list_size);
if(data_block_count != 0) {
bit_buffer_append_bytes(
instance->tx_buffer, data, data_block_count * FELICA_DATA_BLOCK_SIZE);
}
}
FelicaError felica_poller_read_blocks(
FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
FelicaPollerReadCommandResponse** const response_ptr) {
furi_assert(instance);
furi_assert(block_count <= 4);
furi_assert(block_numbers);
furi_assert(response_ptr);
felica_poller_prepare_tx_buffer(
instance,
FELICA_CMD_READ_WITHOUT_ENCRYPTION,
FELICA_SERVICE_RO_ACCESS,
block_count,
block_numbers,
0,
NULL);
bit_buffer_reset(instance->rx_buffer);
FelicaError error = felica_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
if(error == FelicaErrorNone) {
*response_ptr = (FelicaPollerReadCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
}
return error;
}
FelicaError felica_poller_write_blocks(
const FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
const uint8_t* data,
FelicaPollerWriteCommandResponse** const response_ptr) {
furi_assert(instance);
furi_assert(block_count <= 2);
furi_assert(block_numbers);
furi_assert(data);
furi_assert(response_ptr);
felica_poller_prepare_tx_buffer(
instance,
FELICA_CMD_WRITE_WITHOUT_ENCRYPTION,
FELICA_SERVICE_RW_ACCESS,
block_count,
block_numbers,
block_count,
data);
bit_buffer_reset(instance->rx_buffer);
FelicaError error = felica_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
if(error == FelicaErrorNone) {
*response_ptr =
(FelicaPollerWriteCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
}
return error;
}
FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) { FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {
furi_assert(instance); furi_assert(instance);
felica_reset(data);
FelicaError ret; FelicaError ret;
do { do {

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "felica_poller.h" #include "felica_poller.h"
#include <toolbox/bit_buffer.h> #include <toolbox/bit_buffer.h>
#ifdef __cplusplus #ifdef __cplusplus
@@ -18,11 +17,20 @@ extern "C" {
typedef enum { typedef enum {
FelicaPollerStateIdle, FelicaPollerStateIdle,
FelicaPollerStateActivated, FelicaPollerStateActivated,
FelicaPollerStateAuthenticateInternal,
FelicaPollerStateAuthenticateExternal,
FelicaPollerStateReadBlocks,
FelicaPollerStateReadSuccess,
FelicaPollerStateReadFailed,
FelicaPollerStateNum
} FelicaPollerState; } FelicaPollerState;
struct FelicaPoller { struct FelicaPoller {
Nfc* nfc; Nfc* nfc;
FelicaPollerState state; FelicaPollerState state;
FelicaAuthentication auth;
FelicaData* data; FelicaData* data;
BitBuffer* tx_buffer; BitBuffer* tx_buffer;
BitBuffer* rx_buffer; BitBuffer* rx_buffer;
@@ -31,6 +39,7 @@ struct FelicaPoller {
FelicaPollerEvent felica_event; FelicaPollerEvent felica_event;
FelicaPollerEventData felica_event_data; FelicaPollerEventData felica_event_data;
NfcGenericCallback callback; NfcGenericCallback callback;
uint8_t block_index;
void* context; void* context;
}; };
@@ -46,13 +55,106 @@ typedef struct {
uint8_t request_data[2]; uint8_t request_data[2];
} FelicaPollerPollingResponse; } FelicaPollerPollingResponse;
typedef struct {
uint8_t service_code : 4;
uint8_t access_mode : 3;
uint8_t length : 1;
uint8_t block_number;
} FelicaBlockListElement;
#pragma pack(push, 1)
typedef struct {
uint8_t code;
FelicaIDm idm;
uint8_t service_num;
uint16_t service_code;
uint8_t block_count;
} FelicaCommandHeader;
#pragma pack(pop)
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
uint8_t SF1;
uint8_t SF2;
uint8_t block_count;
uint8_t data[];
} FelicaPollerReadCommandResponse;
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
uint8_t SF1;
uint8_t SF2;
} FelicaPollerWriteCommandResponse;
const FelicaData* felica_poller_get_data(FelicaPoller* instance); const FelicaData* felica_poller_get_data(FelicaPoller* instance);
/**
* @brief Performs felica polling operation as part of the activation process
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] cmd Pointer to polling command structure
* @param[out] resp Pointer to the response structure
* @return FelicaErrorNone on success, an error code on failure
*/
FelicaError felica_poller_polling( FelicaError felica_poller_polling(
FelicaPoller* instance, FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd, const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp); FelicaPollerPollingResponse* resp);
/**
* @brief Performs felica read operation for blocks provided as parameters
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] block_count Amount of blocks involved in reading procedure
* @param[in] block_numbers Array with block indexes according to felica docs
* @param[out] response_ptr Pointer to the response structure
* @return FelicaErrorNone on success, an error code on failure.
*/
FelicaError felica_poller_read_blocks(
FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
FelicaPollerReadCommandResponse** const response_ptr);
/**
* @brief Performs felica write operation with data provided as parameters
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] block_count Amount of blocks involved in writing procedure
* @param[in] block_numbers Array with block indexes according to felica docs
* @param[in] data Data of blocks provided in block_numbers
* @param[out] response_ptr Pointer to the response structure
* @return FelicaErrorNone on success, an error code on failure.
*/
FelicaError felica_poller_write_blocks(
const FelicaPoller* instance,
const uint8_t block_count,
const uint8_t* const block_numbers,
const uint8_t* data,
FelicaPollerWriteCommandResponse** const response_ptr);
/**
* @brief Perform frame exchange procedure.
*
* Prepares data for sending by adding crc, after that performs
* low level calls to send package data to the card
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer with data to be transmitted
* @param[out] rx_buffer pointer to the buffer with received data from card
* @param[in] fwt timeout window
* @return FelicaErrorNone on success, an error code on failure.
*/
FelicaError felica_poller_frame_exchange(
const FelicaPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,60.6,, Version,+,60.7,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,, Header,+,applications/services/cli/cli_vcp.h,,
1 entry status name type params
2 Version + 60.6 60.7
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,60.6,, Version,+,60.7,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
@@ -133,6 +133,8 @@ Header,+,lib/nfc/nfc_device.h,,
Header,+,lib/nfc/nfc_listener.h,, Header,+,lib/nfc/nfc_listener.h,,
Header,+,lib/nfc/nfc_poller.h,, Header,+,lib/nfc/nfc_poller.h,,
Header,+,lib/nfc/nfc_scanner.h,, Header,+,lib/nfc/nfc_scanner.h,,
Header,+,lib/nfc/protocols/felica/felica.h,,
Header,+,lib/nfc/protocols/felica/felica_poller.h,,
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,,
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,,
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,, Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,,
@@ -987,6 +989,22 @@ Function,-,fdim,double,"double, double"
Function,-,fdimf,float,"float, float" Function,-,fdimf,float,"float, float"
Function,-,fdiml,long double,"long double, long double" Function,-,fdiml,long double,"long double, long double"
Function,-,fdopen,FILE*,"int, const char*" Function,-,fdopen,FILE*,"int, const char*"
Function,+,felica_alloc,FelicaData*,
Function,+,felica_calculate_mac_write,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*"
Function,+,felica_calculate_session_key,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, uint8_t*"
Function,+,felica_check_mac,_Bool,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*"
Function,+,felica_copy,void,"FelicaData*, const FelicaData*"
Function,+,felica_free,void,FelicaData*
Function,+,felica_get_base_data,FelicaData*,const FelicaData*
Function,+,felica_get_device_name,const char*,"const FelicaData*, NfcDeviceNameType"
Function,+,felica_get_uid,const uint8_t*,"const FelicaData*, size_t*"
Function,+,felica_is_equal,_Bool,"const FelicaData*, const FelicaData*"
Function,+,felica_load,_Bool,"FelicaData*, FlipperFormat*, uint32_t"
Function,+,felica_poller_activate,FelicaError,"FelicaPoller*, FelicaData*"
Function,+,felica_reset,void,FelicaData*
Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*"
Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t"
Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*"
Function,-,feof,int,FILE* Function,-,feof,int,FILE*
Function,-,feof_unlocked,int,FILE* Function,-,feof_unlocked,int,FILE*
Function,-,ferror,int,FILE* Function,-,ferror,int,FILE*
@@ -3739,6 +3757,7 @@ Variable,+,message_red_255,const NotificationMessage,
Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage,
Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage,
Variable,+,message_vibro_on,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage,
Variable,-,nfc_device_felica,const NfcDeviceBase,
Variable,-,nfc_device_mf_classic,const NfcDeviceBase, Variable,-,nfc_device_mf_classic,const NfcDeviceBase,
Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, Variable,-,nfc_device_mf_desfire,const NfcDeviceBase,
Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase,
1 entry status name type params
2 Version + 60.6 60.7
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/cli/cli.h
133 Header + lib/nfc/nfc_listener.h
134 Header + lib/nfc/nfc_poller.h
135 Header + lib/nfc/nfc_scanner.h
136 Header + lib/nfc/protocols/felica/felica.h
137 Header + lib/nfc/protocols/felica/felica_poller.h
138 Header + lib/nfc/protocols/iso14443_3a/iso14443_3a.h
139 Header + lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h
140 Header + lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h
989 Function - fdimf float float, float
990 Function - fdiml long double long double, long double
991 Function - fdopen FILE* int, const char*
992 Function + felica_alloc FelicaData*
993 Function + felica_calculate_mac_write void mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*
994 Function + felica_calculate_session_key void mbedtls_des3_context*, const uint8_t*, const uint8_t*, uint8_t*
995 Function + felica_check_mac _Bool mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*
996 Function + felica_copy void FelicaData*, const FelicaData*
997 Function + felica_free void FelicaData*
998 Function + felica_get_base_data FelicaData* const FelicaData*
999 Function + felica_get_device_name const char* const FelicaData*, NfcDeviceNameType
1000 Function + felica_get_uid const uint8_t* const FelicaData*, size_t*
1001 Function + felica_is_equal _Bool const FelicaData*, const FelicaData*
1002 Function + felica_load _Bool FelicaData*, FlipperFormat*, uint32_t
1003 Function + felica_poller_activate FelicaError FelicaPoller*, FelicaData*
1004 Function + felica_reset void FelicaData*
1005 Function + felica_save _Bool const FelicaData*, FlipperFormat*
1006 Function + felica_set_uid _Bool FelicaData*, const uint8_t*, size_t
1007 Function + felica_verify _Bool FelicaData*, const FuriString*
1008 Function - feof int FILE*
1009 Function - feof_unlocked int FILE*
1010 Function - ferror int FILE*
3757 Variable + message_sound_off const NotificationMessage
3758 Variable + message_vibro_off const NotificationMessage
3759 Variable + message_vibro_on const NotificationMessage
3760 Variable - nfc_device_felica const NfcDeviceBase
3761 Variable - nfc_device_mf_classic const NfcDeviceBase
3762 Variable - nfc_device_mf_desfire const NfcDeviceBase
3763 Variable - nfc_device_mf_ultralight const NfcDeviceBase