Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nfc: Capture and display PWD_AUTH for NTAGs #1471

Closed
wants to merge 9 commits into from
163 changes: 137 additions & 26 deletions applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,109 @@
#include "../nfc_i.h"
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <dolphin/dolphin.h>

#define NFC_MF_UL_DATA_NOT_CHANGED (0UL)
#define NFC_MF_UL_DATA_CHANGED (1UL)
#define NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX (200)

enum {
// View states
NfcSceneMfUltralightEmulateStateWidget,
NfcSceneMfUltralightEmulateStateTextBox,
NfcSceneMfUltralightEmulateStateMax = 0xFF,
// State flags
NfcSceneMfUltralightEmulateStateDataChanged = 1 << 8,
NfcSceneMfUltralightEmulateStateAuthAttempted = 1 << 9,
NfcSceneMfUltralightEmulateStateLogButtonShown = 1 << 10,
};

bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* context) {
UNUSED(event);
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);

if(event == NfcWorkerEventSuccess)
state |= NfcSceneMfUltralightEmulateStateDataChanged;
else if(event == NfcWorkerEventMfUltralightPwdAuth) {
// Don't update if we're exiting
if(nfc_worker_get_state(nfc->worker) != NfcWorkerStateStop) {
// Event data is only available for the duration of this callback, so we're updating the
// text box right here
MfUltralightAuth* auth = nfc_worker_get_event_data(nfc->worker);
if(auth != NULL &&
string_size(nfc->text_box_store) < NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX) {
string_cat(nfc->text_box_store, "PWD:");
for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) {
string_cat_printf(nfc->text_box_store, " %02X", auth->pwd.raw[i]);
}
string_push_back(nfc->text_box_store, '\n');
text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
}
state |= NfcSceneMfUltralightEmulateStateAuthAttempted;
}
}
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);

scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_CHANGED);
return true;
}

void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
void nfc_scene_mf_ultralight_emulate_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
furi_assert(context);
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate);
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}

// Setup view
Popup* popup = nfc->popup;
void nfc_scene_mf_ultralight_emulate_widget_config(Nfc* nfc, bool auth_attempted) {
Widget* widget = nfc->widget;
widget_reset(widget);
string_t info_str;
string_init(info_str);

widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61);
if(strcmp(nfc->dev->dev_name, "")) {
nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name);
string_printf(info_str, "Emulating\n%s", nfc->dev->dev_name);
} else {
nfc_text_store_set(nfc, "Emulating\nMf Ultralight", nfc->dev->dev_name);
string_printf(info_str, "Emulating\nMf Ultralight");
}

widget_add_string_multiline_element(
widget, 56, 31, AlignLeft, AlignTop, FontPrimary, string_get_cstr(info_str));
string_clear(info_str);
if(auth_attempted) {
widget_add_button_element(
widget,
GuiButtonTypeCenter,
"Log",
nfc_scene_mf_ultralight_emulate_widget_callback,
nfc);
}
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop);
}

void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
DOLPHIN_DEED(DolphinDeedNfcEmulate);

// Setup Widget
nfc_scene_mf_ultralight_emulate_widget_config(nfc, false);
state &= ~NfcSceneMfUltralightEmulateStateLogButtonShown;
// Setup TextBox
TextBox* text_box = nfc->text_box;
text_box_set_font(text_box, TextBoxFontHex);
text_box_set_focus(text_box, TextBoxFocusEnd);
string_reset(nfc->text_box_store);

// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
// Set Widget state and view
state = (state & ~NfcSceneMfUltralightEmulateStateMax) |
NfcSceneMfUltralightEmulateStateWidget;
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
// Start worker
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfUltralightEmulate,
Expand All @@ -40,28 +115,64 @@ void nfc_scene_mf_ultralight_emulate_on_enter(void* context) {

bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);
bool consumed = false;

if(event.type == SceneManagerEventTypeBack) {
// Stop worker
nfc_worker_stop(nfc->worker);
// Check if data changed and save in shadow file
if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate) ==
NFC_MF_UL_DATA_CHANGED) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED);
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventWorkerExit) {
if(state & NfcSceneMfUltralightEmulateStateAuthAttempted) {
if(!(state & NfcSceneMfUltralightEmulateStateLogButtonShown)) {
// Add log button to widget not already showing
nfc_scene_mf_ultralight_emulate_widget_config(nfc, true);
state |= NfcSceneMfUltralightEmulateStateLogButtonShown;
}
// The text box update logic is handled in the worker callback
state &= ~NfcSceneMfUltralightEmulateStateAuthAttempted;
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
consumed = true;
}
} else if(
event.event == GuiButtonTypeCenter && (state & NfcSceneMfUltralightEmulateStateMax) ==
NfcSceneMfUltralightEmulateStateWidget) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
state = (state & ~NfcSceneMfUltralightEmulateStateMax) |
NfcSceneMfUltralightEmulateStateTextBox;
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
if((state & NfcSceneMfUltralightEmulateStateMax) ==
NfcSceneMfUltralightEmulateStateTextBox) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
state = (state & ~NfcSceneMfUltralightEmulateStateMax) |
NfcSceneMfUltralightEmulateStateWidget;
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
consumed = true;
}
consumed = false;
}
return consumed;
}

void nfc_scene_mf_ultralight_emulate_on_exit(void* context) {
Nfc* nfc = context;
uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate);

// Stop worker
nfc_worker_stop(nfc->worker);
// Check if data changed and save in shadow file
if(state & NfcSceneMfUltralightEmulateStateDataChanged) {
state &= ~NfcSceneMfUltralightEmulateStateDataChanged;
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate, state);
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
}

// Clear view
popup_reset(nfc->popup);
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store);

nfc_blink_stop(nfc);
}
14 changes: 14 additions & 0 deletions lib/nfc/nfc_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ NfcWorker* nfc_worker_alloc() {

nfc_worker->callback = NULL;
nfc_worker->context = NULL;
nfc_worker->event_data = NULL;
nfc_worker->storage = furi_record_open(RECORD_STORAGE);

// Initialize rfal
Expand Down Expand Up @@ -51,6 +52,10 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
return nfc_worker->state;
}

void* nfc_worker_get_event_data(NfcWorker* nfc_worker) {
return nfc_worker->event_data;
}

void nfc_worker_start(
NfcWorker* nfc_worker,
NfcWorkerState state,
Expand Down Expand Up @@ -386,6 +391,15 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
mf_ul_prepare_emulation_response,
&emulator,
5000);
// Check if there was an auth attempt
if(emulator.auth_attempted) {
nfc_worker->event_data = &emulator.auth_attempt;
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventMfUltralightPwdAuth, nfc_worker->context);
}
emulator.auth_attempted = false;
nfc_worker->event_data = NULL;
}
// Check if data was modified
if(emulator.data_changed) {
nfc_worker->dev_data->mf_ul_data = emulator.data;
Expand Down
7 changes: 5 additions & 2 deletions lib/nfc/nfc_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ typedef enum {
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,

// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey,
// Mifare Ultralight/NTAG events
NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
} NfcWorkerEvent;

typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
Expand All @@ -64,6 +65,8 @@ NfcWorker* nfc_worker_alloc();

NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);

void* nfc_worker_get_event_data(NfcWorker* nfc_worker);

void nfc_worker_free(NfcWorker* nfc_worker);

void nfc_worker_start(
Expand Down
1 change: 1 addition & 0 deletions lib/nfc/nfc_worker_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct NfcWorker {

NfcWorkerCallback callback;
void* context;
void* event_data;

NfcWorkerState state;

Expand Down
12 changes: 10 additions & 2 deletions lib/nfc/protocols/mifare_ultralight.c
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,8 @@ static void mf_ul_emulate_write(
}

void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) {
emulator->comp_write_cmd_started = false;
emulator->sector_select_cmd_started = false;
emulator->curr_sector = 0;
emulator->ntag_i2c_plus_sector3_lockout = false;
emulator->auth_success = false;
Expand Down Expand Up @@ -1245,8 +1247,7 @@ void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* d
emulator->config = mf_ultralight_get_config_pages(&emulator->data);
emulator->page_num = emulator->data.data_size / 4;
emulator->data_changed = false;
emulator->comp_write_cmd_started = false;
emulator->sector_select_cmd_started = false;
memset(&emulator->auth_attempt, 0, sizeof(MfUltralightAuth));
mf_ul_reset_emulation(emulator, true);
}

Expand Down Expand Up @@ -1706,6 +1707,13 @@ bool mf_ul_prepare_emulation_response(
} else if(cmd == MF_UL_AUTH) {
if(emulator->supported_features & MfUltralightSupportAuth) {
if(buff_rx_len == (1 + 4) * 8) {
// Record password sent by PCD
memcpy(
emulator->auth_attempt.pwd.raw,
&buff_rx[1],
sizeof(emulator->auth_attempt.pwd.raw));
emulator->auth_attempted = true;

uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data);
if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) {
if(emulator->data.curr_authlim != UINT16_MAX) {
Expand Down
2 changes: 2 additions & 0 deletions lib/nfc/protocols/mifare_ultralight.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ typedef struct {
bool sector_select_cmd_started;
bool ntag_i2c_plus_sector3_lockout;
bool read_counter_incremented;
bool auth_attempted;
MfUltralightAuth auth_attempt;
} MfUltralightEmulator;

void mf_ul_reset(MfUltralightData* data);
Expand Down