Skip to content

Commit 843c9c6

Browse files
authored
Merge pull request #11 from flipperdevices/gornek/2245_mifare_classic_emulation
Gornek/2245 mifare classic emulation
2 parents fc39ec4 + 5b9d7b2 commit 843c9c6

16 files changed

+604
-10
lines changed

applications/nfc/nfc_worker.c

+23
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <lib/nfc_protocols/mifare_ultralight.h>
88
#include <lib/nfc_protocols/mifare_classic.h>
99
#include <lib/nfc_protocols/mifare_desfire.h>
10+
#include <lib/nfc_protocols/nfca.h>
1011

1112
#include "helpers/nfc_mf_classic_dict.h"
1213

@@ -104,6 +105,8 @@ int32_t nfc_worker_task(void* context) {
104105
nfc_worker_emulate_mifare_ul(nfc_worker);
105106
} else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) {
106107
nfc_worker_mifare_classic_dict_attack(nfc_worker);
108+
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
109+
nfc_worker_emulate_mifare_classic(nfc_worker);
107110
} else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) {
108111
nfc_worker_read_mifare_desfire(nfc_worker);
109112
}
@@ -474,6 +477,26 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) {
474477
stream_free(nfc_worker->dict_stream);
475478
}
476479

480+
void nfc_worker_emulate_mifare_classic(NfcWorker* nfc_worker) {
481+
FuriHalNfcTxRxContext tx_rx;
482+
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
483+
MfClassicEmulator emulator = {
484+
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
485+
.data = nfc_worker->dev_data->mf_classic_data,
486+
};
487+
NfcaSignal* nfca_signal = nfca_signal_alloc();
488+
tx_rx.nfca_signal = nfca_signal;
489+
490+
while(nfc_worker->state == NfcWorkerStateEmulateMifareClassic) {
491+
if(furi_hal_nfc_listen(
492+
nfc_data->uid, nfc_data->uid_len, nfc_data->atqa, nfc_data->sak, true, 4000)) {
493+
mf_classic_emulator(&emulator, &tx_rx);
494+
}
495+
}
496+
497+
nfca_signal_free(nfca_signal);
498+
}
499+
477500
void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) {
478501
ReturnCode err;
479502
uint8_t tx_buff[64] = {};

applications/nfc/nfc_worker.h

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ typedef enum {
1919
NfcWorkerStateReadMifareUltralight,
2020
NfcWorkerStateEmulateMifareUltralight,
2121
NfcWorkerStateReadMifareClassic,
22+
NfcWorkerStateEmulateMifareClassic,
2223
NfcWorkerStateReadMifareDesfire,
2324
// Transition
2425
NfcWorkerStateStop,

applications/nfc/scenes/nfc_scene_config.h

+1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ ADD_SCENE(nfc, restore_original, RestoreOriginal)
3434
ADD_SCENE(nfc, debug, Debug)
3535
ADD_SCENE(nfc, field, Field)
3636
ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic)
37+
ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic)
3738
ADD_SCENE(nfc, dict_not_found, DictNotFound)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include "../nfc_i.h"
2+
#include <dolphin/dolphin.h>
3+
4+
#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL)
5+
#define NFC_MF_CLASSIC_DATA_CHANGED (1UL)
6+
7+
void nfc_emulate_mifare_classic_worker_callback(NfcWorkerEvent event, void* context) {
8+
UNUSED(event);
9+
Nfc* nfc = context;
10+
11+
scene_manager_set_scene_state(
12+
nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_CLASSIC_DATA_CHANGED);
13+
}
14+
15+
void nfc_scene_emulate_mifare_classic_on_enter(void* context) {
16+
Nfc* nfc = context;
17+
DOLPHIN_DEED(DolphinDeedNfcEmulate);
18+
19+
// Setup view
20+
Popup* popup = nfc->popup;
21+
if(strcmp(nfc->dev->dev_name, "")) {
22+
nfc_text_store_set(nfc, "%s", nfc->dev->dev_name);
23+
}
24+
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
25+
popup_set_header(popup, "Emulating\nMf Classic", 56, 31, AlignLeft, AlignTop);
26+
27+
// Setup and start worker
28+
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
29+
nfc_worker_start(
30+
nfc->worker,
31+
NfcWorkerStateEmulateMifareClassic,
32+
&nfc->dev->dev_data,
33+
nfc_emulate_mifare_classic_worker_callback,
34+
nfc);
35+
}
36+
37+
bool nfc_scene_emulate_mifare_classic_on_event(void* context, SceneManagerEvent event) {
38+
Nfc* nfc = context;
39+
bool consumed = false;
40+
41+
if(event.type == SceneManagerEventTypeTick) {
42+
notification_message(nfc->notifications, &sequence_blink_blue_10);
43+
consumed = true;
44+
} else if(event.type == SceneManagerEventTypeBack) {
45+
// Stop worker
46+
nfc_worker_stop(nfc->worker);
47+
// Check if data changed and save in shadow file
48+
if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateMifareUl) ==
49+
NFC_MF_CLASSIC_DATA_CHANGED) {
50+
scene_manager_set_scene_state(
51+
nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_CLASSIC_DATA_NOT_CHANGED);
52+
nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name);
53+
}
54+
consumed = false;
55+
}
56+
return consumed;
57+
}
58+
59+
void nfc_scene_emulate_mifare_classic_on_exit(void* context) {
60+
Nfc* nfc = context;
61+
62+
// Clear view
63+
popup_reset(nfc->popup);
64+
}

applications/nfc/scenes/nfc_scene_saved_menu.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ void nfc_scene_saved_menu_on_enter(void* context) {
2727
SubmenuIndexEmulate,
2828
nfc_scene_saved_menu_submenu_callback,
2929
nfc);
30-
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
30+
} else if(
31+
nfc->dev->format == NfcDeviceSaveFormatMifareUl ||
32+
nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
3133
submenu_add_item(
32-
submenu,
33-
"Emulate Ultralight",
34-
SubmenuIndexEmulate,
35-
nfc_scene_saved_menu_submenu_callback,
36-
nfc);
34+
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
3735
}
3836
submenu_add_item(
3937
submenu, "Edit UID and Name", SubmenuIndexEdit, nfc_scene_saved_menu_submenu_callback, nfc);
@@ -64,6 +62,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
6462
if(event.event == SubmenuIndexEmulate) {
6563
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
6664
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl);
65+
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
66+
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareClassic);
6767
} else {
6868
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
6969
}

firmware/targets/f7/furi_hal/furi_hal_nfc.c

+87-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#include "furi_hal_nfc.h"
22
#include <st25r3916.h>
3+
#include <st25r3916_irq.h>
34
#include <rfal_rf.h>
45
#include <furi.h>
56
#include <m-string.h>
6-
#include <lib/nfc_protocols/nfca.h>
7+
8+
#include <lib/digital_signal/digital_signal.h>
9+
#include <furi_hal_delay.h>
710

811
#define TAG "FuriHalNfc"
912

@@ -394,6 +397,80 @@ ReturnCode furi_hal_nfc_data_exchange(
394397
return ret;
395398
}
396399

400+
static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
401+
furi_assert(tx_rx->nfca_signal);
402+
403+
platformDisableIrqCallback();
404+
405+
bool ret = false;
406+
407+
// Start transparent mode
408+
st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE);
409+
// Reconfigure gpio
410+
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
411+
furi_hal_gpio_init(&gpio_spi_r_sck, GpioModeInput, GpioPullUp, GpioSpeedLow);
412+
furi_hal_gpio_init(&gpio_spi_r_miso, GpioModeInput, GpioPullUp, GpioSpeedLow);
413+
furi_hal_gpio_init(&gpio_nfc_cs, GpioModeInput, GpioPullUp, GpioSpeedLow);
414+
furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
415+
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
416+
417+
// Send signal
418+
nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits / 8, tx_rx->tx_parity);
419+
digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi);
420+
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
421+
422+
// Configure gpio back to SPI and exit transparent
423+
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
424+
st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA);
425+
426+
// Manually wait for interrupt
427+
furi_hal_gpio_init(&gpio_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh);
428+
st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE);
429+
430+
uint32_t irq = 0;
431+
uint8_t rxe = 0;
432+
uint32_t start = DWT->CYCCNT;
433+
while(true) {
434+
if(furi_hal_gpio_read(&gpio_rfid_pull) == true) {
435+
st25r3916ReadRegister(ST25R3916_REG_IRQ_MAIN, &rxe);
436+
if(rxe & (1 << 4)) {
437+
irq = 1;
438+
break;
439+
}
440+
}
441+
uint32_t timeout = DWT->CYCCNT - start;
442+
if(timeout / furi_hal_delay_instructions_per_microsecond() > timeout_ms * 1000) {
443+
FURI_LOG_D(TAG, "Interrupt waiting timeout");
444+
break;
445+
}
446+
}
447+
if(irq) {
448+
uint8_t fifo_stat[2];
449+
st25r3916ReadMultipleRegisters(
450+
ST25R3916_REG_FIFO_STATUS1, fifo_stat, ST25R3916_FIFO_STATUS_LEN);
451+
uint16_t len =
452+
((((uint16_t)fifo_stat[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >>
453+
ST25R3916_REG_FIFO_STATUS2_fifo_b_shift)
454+
<< RFAL_BITS_IN_BYTE);
455+
len |= (((uint16_t)fifo_stat[0]) & 0x00FFU);
456+
uint8_t rx[100];
457+
st25r3916ReadFifo(rx, len);
458+
459+
tx_rx->rx_bits = len * 8;
460+
memcpy(tx_rx->rx_data, rx, len);
461+
462+
ret = true;
463+
} else {
464+
FURI_LOG_E(TAG, "Timeout error");
465+
ret = false;
466+
}
467+
468+
st25r3916ClearInterrupts();
469+
platformEnableIrqCallback();
470+
471+
return ret;
472+
}
473+
397474
static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) {
398475
uint32_t flags = 0;
399476

@@ -405,6 +482,9 @@ static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) {
405482
} else if(type == FuriHalNfcTxRxTypeRaw) {
406483
flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP |
407484
RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE;
485+
} else if(type == FuriHalNfcTxRxTypeRxRaw) {
486+
flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP |
487+
RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE;
408488
}
409489

410490
return flags;
@@ -470,6 +550,10 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
470550
uint8_t* temp_rx_buff = NULL;
471551
uint16_t* temp_rx_bits = NULL;
472552

553+
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTransparent) {
554+
return furi_hal_nfc_transparent_tx_rx(tx_rx, timeout_ms);
555+
}
556+
473557
// Prepare data for FIFO if necessary
474558
uint32_t flags = furi_hal_nfc_tx_rx_get_flag(tx_rx->tx_rx_type);
475559
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) {
@@ -502,7 +586,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
502586
osDelay(1);
503587
}
504588

505-
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) {
589+
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw ||
590+
tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) {
506591
tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity(
507592
temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity);
508593
} else {

firmware/targets/furi_hal_include/furi_hal_nfc.h

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <stdbool.h>
1111
#include <stdint.h>
1212

13+
#include <lib/nfc_protocols/nfca.h>
14+
1315
#ifdef __cplusplus
1416
extern "C" {
1517
#endif
@@ -39,6 +41,8 @@ typedef enum {
3941
FuriHalNfcTxRxTypeRxNoCrc,
4042
FuriHalNfcTxRxTypeRxKeepPar,
4143
FuriHalNfcTxRxTypeRaw,
44+
FuriHalNfcTxRxTypeRxRaw,
45+
FuriHalNfcTxRxTransparent,
4246
} FuriHalNfcTxRxType;
4347

4448
typedef bool (*FuriHalNfcEmulateCallback)(
@@ -80,6 +84,7 @@ typedef struct {
8084
uint8_t rx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE];
8185
uint16_t rx_bits;
8286
FuriHalNfcTxRxType tx_rx_type;
87+
NfcaSignal* nfca_signal;
8388
} FuriHalNfcTxRxContext;
8489

8590
/** Init nfc

0 commit comments

Comments
 (0)