NFC: Add support for Gen4 "ultimate card" in Magic app (#2238)

* NFC: gen4 gtu detect in magic app
* NFC: more support for GTU card
* NFC: Fix Gen1 in Magic
* Allow double UIDs for MFClassic on GTU cards
* NFC: Small magic app tweaks
* nfc magic: notify card event on wiping
* nfc magic: fix power consumption
* nfc magic: disable i2c writing and fix wipe loop
* NfcMagic: correct formatting in printf
* NfcMagic: correct formatting in printf, proper version
* nfc_magic: rework card found notification and gen4 wiping

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Avery 2023-05-26 03:01:02 +10:00 committed by GitHub
parent 77bb997b0b
commit 490447bbd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1345 additions and 150 deletions

View File

@ -0,0 +1,175 @@
#include "classic_gen1.h"
#include <furi_hal_nfc.h>
#define TAG "Magic"
#define MAGIC_CMD_WUPA (0x40)
#define MAGIC_CMD_WIPE (0x41)
#define MAGIC_CMD_ACCESS (0x43)
#define MAGIC_MIFARE_READ_CMD (0x30)
#define MAGIC_MIFARE_WRITE_CMD (0xA0)
#define MAGIC_ACK (0x0A)
#define MAGIC_BUFFER_SIZE (32)
bool magic_gen1_wupa() {
bool magic_activated = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
// Start communication
tx_data[0] = MAGIC_CMD_WUPA;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
7,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
magic_activated = true;
} while(false);
return magic_activated;
}
bool magic_gen1_data_access_cmd() {
bool write_cmd_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_CMD_ACCESS;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
write_cmd_success = true;
} while(false);
return write_cmd_success;
}
bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data) {
furi_assert(data);
bool read_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_MIFARE_READ_CMD;
tx_data[1] = block_num;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
2 * 8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnOk) break;
if(rx_len != 16 * 8) break;
memcpy(data->value, rx_data, sizeof(data->value));
read_success = true;
} while(false);
return read_success;
}
bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) {
furi_assert(data);
bool write_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_MIFARE_WRITE_CMD;
tx_data[1] = block_num;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
2 * 8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
memcpy(tx_data, data->value, sizeof(data->value));
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
16 * 8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
write_success = true;
} while(false);
return write_success;
}
bool magic_gen1_wipe() {
bool wipe_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
tx_data[0] = MAGIC_CMD_WIPE;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
8,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON |
FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP,
furi_hal_nfc_ll_ms2fc(2000));
if(ret != FuriHalNfcReturnIncompleteByte) break;
if(rx_len != 4) break;
if(rx_data[0] != MAGIC_ACK) break;
wipe_success = true;
} while(false);
return wipe_success;
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <lib/nfc/protocols/mifare_classic.h>
bool magic_gen1_wupa();
bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data);
bool magic_gen1_data_access_cmd();
bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data);
bool magic_gen1_wipe();

View File

@ -0,0 +1,33 @@
#include "common.h"
#include <furi_hal_nfc.h>
#define REQA (0x26)
#define CL1_PREFIX (0x93)
#define SELECT (0x70)
#define MAGIC_BUFFER_SIZE (32)
bool magic_activate() {
FuriHalNfcReturn ret = 0;
// Setup nfc poller
furi_hal_nfc_exit_sleep();
furi_hal_nfc_ll_txrx_on();
furi_hal_nfc_ll_poll();
ret = furi_hal_nfc_ll_set_mode(
FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106);
if(ret != FuriHalNfcReturnOk) return false;
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER);
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER);
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA);
return true;
}
void magic_deactivate() {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_sleep();
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef enum {
MagicTypeClassicGen1,
MagicTypeClassicDirectWrite,
MagicTypeClassicAPDU,
MagicTypeUltralightGen1,
MagicTypeUltralightDirectWrite,
MagicTypeUltralightC_Gen1,
MagicTypeUltralightC_DirectWrite,
MagicTypeGen4,
} MagicType;
bool magic_activate();
void magic_deactivate();

View File

@ -0,0 +1,199 @@
#include "gen4.h"
#include <furi_hal_nfc.h>
#include <stdlib.h>
#define TAG "Magic"
#define MAGIC_CMD_PREFIX (0xCF)
#define MAGIC_CMD_GET_CFG (0xC6)
#define MAGIC_CMD_WRITE (0xCD)
#define MAGIC_CMD_READ (0xCE)
#define MAGIC_CMD_SET_CFG (0xF0)
#define MAGIC_CMD_FUSE_CFG (0xF1)
#define MAGIC_CMD_SET_PWD (0xFE)
#define MAGIC_BUFFER_SIZE (40)
const uint8_t MAGIC_DEFAULT_CONFIG[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x78, 0x00, 0x91, 0x02, 0xDA, 0xBC, 0x19, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x00, 0x08, 0x00
};
const uint8_t MAGIC_DEFAULT_BLOCK0[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const uint8_t MAGIC_EMPTY_BLOCK[16] = { 0 };
const uint8_t MAGIC_DEFAULT_SECTOR_TRAILER[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static bool magic_gen4_is_block_num_trailer(uint8_t n) {
n++;
if (n < 32 * 4) {
return (n % 4 == 0);
}
return (n % 16 == 0);
}
bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config) {
bool is_valid_config_len = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
// Start communication
tx_data[0] = MAGIC_CMD_PREFIX;
tx_data[1] = (uint8_t)(pwd >> 24);
tx_data[2] = (uint8_t)(pwd >> 16);
tx_data[3] = (uint8_t)(pwd >> 8);
tx_data[4] = (uint8_t)pwd;
tx_data[5] = MAGIC_CMD_GET_CFG;
ret = furi_hal_nfc_ll_txrx(
tx_data,
6,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_TXRX_DEFAULT,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnOk) break;
if(rx_len != 30 && rx_len != 32) break;
memcpy(config, rx_data, rx_len);
is_valid_config_len = true;
} while(false);
return is_valid_config_len;
}
bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse) {
bool write_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
// Start communication
tx_data[0] = MAGIC_CMD_PREFIX;
tx_data[1] = (uint8_t)(pwd >> 24);
tx_data[2] = (uint8_t)(pwd >> 16);
tx_data[3] = (uint8_t)(pwd >> 8);
tx_data[4] = (uint8_t)pwd;
tx_data[5] = fuse ? MAGIC_CMD_FUSE_CFG : MAGIC_CMD_SET_CFG;
memcpy(tx_data + 6, config, config_length);
ret = furi_hal_nfc_ll_txrx(
tx_data,
6 + config_length,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_TXRX_DEFAULT,
furi_hal_nfc_ll_ms2fc(20));
if(ret != FuriHalNfcReturnOk) break;
if(rx_len != 2) break;
write_success = true;
} while(false);
return write_success;
}
bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd) {
bool change_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
// Start communication
tx_data[0] = MAGIC_CMD_PREFIX;
tx_data[1] = (uint8_t)(old_pwd >> 24);
tx_data[2] = (uint8_t)(old_pwd >> 16);
tx_data[3] = (uint8_t)(old_pwd >> 8);
tx_data[4] = (uint8_t)old_pwd;
tx_data[5] = MAGIC_CMD_SET_PWD;
tx_data[6] = (uint8_t)(new_pwd >> 24);
tx_data[7] = (uint8_t)(new_pwd >> 16);
tx_data[8] = (uint8_t)(new_pwd >> 8);
tx_data[9] = (uint8_t)new_pwd;
ret = furi_hal_nfc_ll_txrx(
tx_data,
10,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_TXRX_DEFAULT,
furi_hal_nfc_ll_ms2fc(20));
FURI_LOG_I(TAG, "ret %d, len %d", ret, rx_len);
if(ret != FuriHalNfcReturnOk) break;
if(rx_len != 2) break;
change_success = true;
} while(false);
return change_success;
}
bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data) {
bool write_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
uint16_t rx_len = 0;
FuriHalNfcReturn ret = 0;
do {
// Start communication
tx_data[0] = MAGIC_CMD_PREFIX;
tx_data[1] = (uint8_t)(pwd >> 24);
tx_data[2] = (uint8_t)(pwd >> 16);
tx_data[3] = (uint8_t)(pwd >> 8);
tx_data[4] = (uint8_t)pwd;
tx_data[5] = MAGIC_CMD_WRITE;
tx_data[6] = block_num;
memcpy(tx_data + 7, data, 16);
ret = furi_hal_nfc_ll_txrx(
tx_data,
23,
rx_data,
sizeof(rx_data),
&rx_len,
FURI_HAL_NFC_TXRX_DEFAULT,
furi_hal_nfc_ll_ms2fc(200));
if(ret != FuriHalNfcReturnOk) break;
if(rx_len != 2) break;
write_success = true;
} while(false);
return write_success;
}
bool magic_gen4_wipe(uint32_t pwd) {
if(!magic_gen4_set_cfg(pwd, MAGIC_DEFAULT_CONFIG, sizeof(MAGIC_DEFAULT_CONFIG), false)) {
FURI_LOG_E(TAG, "Set config failed");
return false;
}
if(!magic_gen4_write_blk(pwd, 0, MAGIC_DEFAULT_BLOCK0)) {
FURI_LOG_E(TAG, "Block 0 write failed");
return false;
}
for(size_t i = 1; i < 64; i++) {
const uint8_t* block = magic_gen4_is_block_num_trailer(i) ? MAGIC_DEFAULT_SECTOR_TRAILER : MAGIC_EMPTY_BLOCK;
if(!magic_gen4_write_blk(pwd, i, block)) {
FURI_LOG_E(TAG, "Block %d write failed", i);
return false;
}
}
for(size_t i = 65; i < 256; i++) {
if(!magic_gen4_write_blk(pwd, i, MAGIC_EMPTY_BLOCK)) {
FURI_LOG_E(TAG, "Block %d write failed", i);
return false;
}
}
return true;
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <lib/nfc/protocols/mifare_classic.h>
#define MAGIC_GEN4_DEFAULT_PWD 0x00000000
#define MAGIC_GEN4_CONFIG_LEN 32
#define NFCID1_SINGLE_SIZE 4
#define NFCID1_DOUBLE_SIZE 7
#define NFCID1_TRIPLE_SIZE 10
typedef enum {
MagicGen4UIDLengthSingle = 0x00,
MagicGen4UIDLengthDouble = 0x01,
MagicGen4UIDLengthTriple = 0x02
} MagicGen4UIDLength;
typedef enum {
MagicGen4UltralightModeUL_EV1 = 0x00,
MagicGen4UltralightModeNTAG = 0x01,
MagicGen4UltralightModeUL_C = 0x02,
MagicGen4UltralightModeUL = 0x03
} MagicGen4UltralightMode;
typedef enum {
// for writing original (shadow) data
MagicGen4ShadowModePreWrite = 0x00,
// written data can be read once before restored to original
MagicGen4ShadowModeRestore = 0x01,
// written data is discarded
MagicGen4ShadowModeIgnore = 0x02,
// apparently for UL?
MagicGen4ShadowModeHighSpeedIgnore = 0x03
} MagicGen4ShadowMode;
bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config);
bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse);
bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd);
bool magic_gen4_read_blk(uint32_t pwd, uint8_t block_num, uint8_t* data);
bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data);
bool magic_gen4_wipe(uint32_t pwd);
void magic_gen4_deactivate();

View File

@ -1,4 +1,4 @@
#include "magic.h"
#include "classic_gen1.h"
#include <furi_hal_nfc.h>
@ -15,7 +15,7 @@
#define MAGIC_BUFFER_SIZE (32)
bool magic_wupa() {
bool magic_gen1_wupa() {
bool magic_activated = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
@ -23,19 +23,6 @@ bool magic_wupa() {
FuriHalNfcReturn ret = 0;
do {
// Setup nfc poller
furi_hal_nfc_exit_sleep();
furi_hal_nfc_ll_txrx_on();
furi_hal_nfc_ll_poll();
ret = furi_hal_nfc_ll_set_mode(
FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106);
if(ret != FuriHalNfcReturnOk) break;
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER);
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER);
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA);
// Start communication
tx_data[0] = MAGIC_CMD_WUPA;
ret = furi_hal_nfc_ll_txrx_bits(
@ -53,15 +40,10 @@ bool magic_wupa() {
magic_activated = true;
} while(false);
if(!magic_activated) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return magic_activated;
}
bool magic_data_access_cmd() {
bool magic_gen1_data_access_cmd() {
bool write_cmd_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
@ -86,15 +68,10 @@ bool magic_data_access_cmd() {
write_cmd_success = true;
} while(false);
if(!write_cmd_success) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return write_cmd_success;
}
bool magic_read_block(uint8_t block_num, MfClassicBlock* data) {
bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data) {
furi_assert(data);
bool read_success = false;
@ -122,15 +99,10 @@ bool magic_read_block(uint8_t block_num, MfClassicBlock* data) {
read_success = true;
} while(false);
if(!read_success) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return read_success;
}
bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) {
bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) {
furi_assert(data);
bool write_success = false;
@ -170,15 +142,10 @@ bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) {
write_success = true;
} while(false);
if(!write_success) {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_start_sleep();
}
return write_success;
}
bool magic_wipe() {
bool magic_gen1_wipe() {
bool wipe_success = false;
uint8_t tx_data[MAGIC_BUFFER_SIZE] = {};
uint8_t rx_data[MAGIC_BUFFER_SIZE] = {};
@ -206,8 +173,3 @@ bool magic_wipe() {
return wipe_success;
}
void magic_deactivate() {
furi_hal_nfc_ll_txrx_off();
furi_hal_nfc_sleep();
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <lib/nfc/protocols/mifare_classic.h>
bool magic_wupa();
bool magic_read_block(uint8_t block_num, MfClassicBlock* data);
bool magic_data_access_cmd();
bool magic_write_blk(uint8_t block_num, MfClassicBlock* data);
bool magic_wipe();
void magic_deactivate();

View File

@ -0,0 +1,23 @@
#include "types.h"
const char* nfc_magic_type(MagicType type) {
if(type == MagicTypeClassicGen1) {
return "Classic Gen 1A/B";
} else if(type == MagicTypeClassicDirectWrite) {
return "Classic DirectWrite";
} else if(type == MagicTypeClassicAPDU) {
return "Classic APDU";
} else if(type == MagicTypeUltralightGen1) {
return "Ultralight Gen 1";
} else if(type == MagicTypeUltralightDirectWrite) {
return "Ultralight DirectWrite";
} else if(type == MagicTypeUltralightC_Gen1) {
return "Ultralight-C Gen 1";
} else if(type == MagicTypeUltralightC_DirectWrite) {
return "Ultralight-C DirectWrite";
} else if(type == MagicTypeGen4) {
return "Gen 4 GTU";
} else {
return "Unknown";
}
}

View File

@ -0,0 +1,5 @@
#pragma once
#include "common.h"
const char* nfc_magic_type(MagicType type);

View File

@ -48,8 +48,9 @@ NfcMagic* nfc_magic_alloc() {
nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100);
// Nfc device
nfc_magic->nfc_dev = nfc_device_alloc();
furi_string_set(nfc_magic->nfc_dev->folder, NFC_APP_FOLDER);
nfc_magic->dev = malloc(sizeof(NfcMagicDevice));
nfc_magic->source_dev = nfc_device_alloc();
furi_string_set(nfc_magic->source_dev->folder, NFC_APP_FOLDER);
// Open GUI record
nfc_magic->gui = furi_record_open(RECORD_GUI);
@ -81,6 +82,13 @@ NfcMagic* nfc_magic_alloc() {
NfcMagicViewTextInput,
text_input_get_view(nfc_magic->text_input));
// Byte Input
nfc_magic->byte_input = byte_input_alloc();
view_dispatcher_add_view(
nfc_magic->view_dispatcher,
NfcMagicViewByteInput,
byte_input_get_view(nfc_magic->byte_input));
// Custom Widget
nfc_magic->widget = widget_alloc();
view_dispatcher_add_view(
@ -93,7 +101,8 @@ void nfc_magic_free(NfcMagic* nfc_magic) {
furi_assert(nfc_magic);
// Nfc device
nfc_device_free(nfc_magic->nfc_dev);
free(nfc_magic->dev);
nfc_device_free(nfc_magic->source_dev);
// Submenu
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
@ -107,10 +116,14 @@ void nfc_magic_free(NfcMagic* nfc_magic) {
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewLoading);
loading_free(nfc_magic->loading);
// TextInput
// Text Input
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput);
text_input_free(nfc_magic->text_input);
// Byte Input
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput);
byte_input_free(nfc_magic->byte_input);
// Custom Widget
view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
widget_free(nfc_magic->widget);
@ -164,6 +177,7 @@ int32_t nfc_magic_app(void* p) {
view_dispatcher_run(nfc_magic->view_dispatcher);
magic_deactivate();
nfc_magic_free(nfc_magic);
return 0;

View File

@ -1,3 +1,5 @@
#pragma once
typedef struct NfcMagicDevice NfcMagicDevice;
typedef struct NfcMagic NfcMagic;

View File

@ -3,7 +3,10 @@
#include "nfc_magic.h"
#include "nfc_magic_worker.h"
#include "lib/magic/magic.h"
#include "lib/magic/common.h"
#include "lib/magic/types.h"
#include "lib/magic/classic_gen1.h"
#include "lib/magic/gen4.h"
#include <furi.h>
#include <gui/gui.h>
@ -15,6 +18,7 @@
#include <gui/modules/popup.h>
#include <gui/modules/loading.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include <gui/modules/widget.h>
#include <input/input.h>
@ -39,14 +43,22 @@ enum NfcMagicCustomEvent {
NfcMagicCustomEventTextInputDone,
};
struct NfcMagicDevice {
MagicType type;
uint32_t cuid;
uint32_t password;
};
struct NfcMagic {
NfcMagicWorker* worker;
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notifications;
SceneManager* scene_manager;
// NfcMagicDevice* dev;
NfcDevice* nfc_dev;
struct NfcMagicDevice* dev;
NfcDevice* source_dev;
uint32_t new_password;
FuriString* text_box_store;
@ -55,6 +67,7 @@ struct NfcMagic {
Popup* popup;
Loading* loading;
TextInput* text_input;
ByteInput* byte_input;
Widget* widget;
};
@ -63,6 +76,7 @@ typedef enum {
NfcMagicViewPopup,
NfcMagicViewLoading,
NfcMagicViewTextInput,
NfcMagicViewByteInput,
NfcMagicViewWidget,
} NfcMagicView;

View File

@ -1,6 +1,9 @@
#include "nfc_magic_worker_i.h"
#include "lib/magic/magic.h"
#include "nfc_magic_i.h"
#include "lib/magic/common.h"
#include "lib/magic/classic_gen1.h"
#include "lib/magic/gen4.h"
#define TAG "NfcMagicWorker"
@ -43,15 +46,20 @@ void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) {
void nfc_magic_worker_start(
NfcMagicWorker* nfc_magic_worker,
NfcMagicWorkerState state,
NfcMagicDevice* magic_dev,
NfcDeviceData* dev_data,
uint32_t new_password,
NfcMagicWorkerCallback callback,
void* context) {
furi_assert(nfc_magic_worker);
furi_assert(magic_dev);
furi_assert(dev_data);
nfc_magic_worker->callback = callback;
nfc_magic_worker->context = context;
nfc_magic_worker->magic_dev = magic_dev;
nfc_magic_worker->dev_data = dev_data;
nfc_magic_worker->new_password = new_password;
nfc_magic_worker_change_state(nfc_magic_worker, state);
furi_thread_start(nfc_magic_worker->thread);
}
@ -63,6 +71,8 @@ int32_t nfc_magic_worker_task(void* context) {
nfc_magic_worker_check(nfc_magic_worker);
} else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
nfc_magic_worker_write(nfc_magic_worker);
} else if(nfc_magic_worker->state == NfcMagicWorkerStateRekey) {
nfc_magic_worker_rekey(nfc_magic_worker);
} else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
nfc_magic_worker_wipe(nfc_magic_worker);
}
@ -74,59 +84,245 @@ int32_t nfc_magic_worker_task(void* context) {
void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) {
bool card_found_notified = false;
bool done = false;
FuriHalNfcDevData nfc_data = {};
MfClassicData* src_data = &nfc_magic_worker->dev_data->mf_classic_data;
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
NfcDeviceData* dev_data = nfc_magic_worker->dev_data;
NfcProtocol dev_protocol = dev_data->protocol;
while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) {
if(furi_hal_nfc_detect(&nfc_data, 200)) {
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
furi_hal_nfc_sleep();
if(!magic_wupa()) {
FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
break;
}
furi_hal_nfc_sleep();
}
if(magic_wupa()) {
if(!magic_data_access_cmd()) {
FURI_LOG_E(TAG, "No card response to data access command (not a magic card)");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
break;
}
for(size_t i = 0; i < 64; i++) {
FURI_LOG_D(TAG, "Writing block %d", i);
if(!magic_write_blk(i, &src_data->block[i])) {
FURI_LOG_E(TAG, "Failed to write %d block", i);
nfc_magic_worker->callback(NfcMagicWorkerEventFail, nfc_magic_worker->context);
do {
if(furi_hal_nfc_detect(&nfc_data, 200)) {
if(nfc_data.cuid != magic_dev->cuid) break;
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
furi_hal_nfc_sleep();
magic_activate();
if(magic_dev->type == MagicTypeClassicGen1) {
if(dev_protocol != NfcDeviceProtocolMifareClassic) break;
MfClassicData* mfc_data = &dev_data->mf_classic_data;
if(mfc_data->type != MfClassicType1k) break;
if(!magic_gen1_wupa()) {
FURI_LOG_E(TAG, "Not Magic card");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
done = true;
break;
}
if(!magic_gen1_data_access_cmd()) {
FURI_LOG_E(TAG, "Not Magic card");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
done = true;
break;
}
for(size_t i = 0; i < 64; i++) {
FURI_LOG_D(TAG, "Writing block %d", i);
if(!magic_gen1_write_blk(i, &mfc_data->block[i])) {
FURI_LOG_E(TAG, "Failed to write %d block", i);
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
}
nfc_magic_worker->callback(
NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
done = true;
break;
} else if(magic_dev->type == MagicTypeGen4) {
uint8_t gen4_config[28];
uint32_t password = magic_dev->password;
uint32_t cuid;
if(dev_protocol == NfcDeviceProtocolMifareClassic) {
gen4_config[0] = 0x00;
gen4_config[27] = 0x00;
} else if(dev_protocol == NfcDeviceProtocolMifareUl) {
MfUltralightData* mf_ul_data = &dev_data->mf_ul_data;
gen4_config[0] = 0x01;
switch(mf_ul_data->type) {
case MfUltralightTypeUL11:
case MfUltralightTypeUL21:
// UL-C?
// UL?
default:
gen4_config[27] = MagicGen4UltralightModeUL_EV1;
break;
case MfUltralightTypeNTAG203:
case MfUltralightTypeNTAG213:
case MfUltralightTypeNTAG215:
case MfUltralightTypeNTAG216:
case MfUltralightTypeNTAGI2C1K:
case MfUltralightTypeNTAGI2C2K:
case MfUltralightTypeNTAGI2CPlus1K:
case MfUltralightTypeNTAGI2CPlus2K:
gen4_config[27] = MagicGen4UltralightModeNTAG;
break;
}
}
if(dev_data->nfc_data.uid_len == 4) {
gen4_config[1] = MagicGen4UIDLengthSingle;
} else if(dev_data->nfc_data.uid_len == 7) {
gen4_config[1] = MagicGen4UIDLengthDouble;
} else {
FURI_LOG_E(TAG, "Unexpected UID length %d", dev_data->nfc_data.uid_len);
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
gen4_config[2] = (uint8_t)(password >> 24);
gen4_config[3] = (uint8_t)(password >> 16);
gen4_config[4] = (uint8_t)(password >> 8);
gen4_config[5] = (uint8_t)password;
if(dev_protocol == NfcDeviceProtocolMifareUl) {
gen4_config[6] = MagicGen4ShadowModeHighSpeedIgnore;
} else {
gen4_config[6] = MagicGen4ShadowModeIgnore;
}
gen4_config[7] = 0x00;
memset(gen4_config + 8, 0, 16);
gen4_config[24] = dev_data->nfc_data.atqa[0];
gen4_config[25] = dev_data->nfc_data.atqa[1];
gen4_config[26] = dev_data->nfc_data.sak;
furi_hal_nfc_activate_nfca(200, &cuid);
if(!magic_gen4_set_cfg(password, gen4_config, sizeof(gen4_config), false)) {
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
if(dev_protocol == NfcDeviceProtocolMifareClassic) {
MfClassicData* mfc_data = &dev_data->mf_classic_data;
size_t block_count = 64;
if(mfc_data->type == MfClassicType4k) block_count = 256;
for(size_t i = 0; i < block_count; i++) {
FURI_LOG_D(TAG, "Writing block %d", i);
if(!magic_gen4_write_blk(password, i, mfc_data->block[i].value)) {
FURI_LOG_E(TAG, "Failed to write %d block", i);
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
}
} else if(dev_protocol == NfcDeviceProtocolMifareUl) {
MfUltralightData* mf_ul_data = &dev_data->mf_ul_data;
for(size_t i = 0; (i * 4) < mf_ul_data->data_read; i++) {
size_t data_offset = i * 4;
FURI_LOG_D(
TAG,
"Writing page %zu (%zu/%u)",
i,
data_offset,
mf_ul_data->data_read);
uint8_t* block = mf_ul_data->data + data_offset;
if(!magic_gen4_write_blk(password, i, block)) {
FURI_LOG_E(TAG, "Failed to write %zu page", i);
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
}
uint8_t buffer[16] = {0};
for(size_t i = 0; i < 8; i++) {
memcpy(buffer, &mf_ul_data->signature[i * 4], 4); //-V1086
if(!magic_gen4_write_blk(password, 0xF2 + i, buffer)) {
FURI_LOG_E(TAG, "Failed to write signature block %d", i);
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
}
buffer[0] = mf_ul_data->version.header;
buffer[1] = mf_ul_data->version.vendor_id;
buffer[2] = mf_ul_data->version.prod_type;
buffer[3] = mf_ul_data->version.prod_subtype;
if(!magic_gen4_write_blk(password, 0xFA, buffer)) {
FURI_LOG_E(TAG, "Failed to write version block 0");
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
buffer[0] = mf_ul_data->version.prod_ver_major;
buffer[1] = mf_ul_data->version.prod_ver_minor;
buffer[2] = mf_ul_data->version.storage_size;
buffer[3] = mf_ul_data->version.protocol_type;
if(!magic_gen4_write_blk(password, 0xFB, buffer)) {
FURI_LOG_E(TAG, "Failed to write version block 1");
nfc_magic_worker->callback(
NfcMagicWorkerEventFail, nfc_magic_worker->context);
done = true;
break;
}
}
nfc_magic_worker->callback(
NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
done = true;
break;
}
}
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
} else {
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
} while(false);
if(done) break;
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
furi_delay_ms(300);
}
magic_deactivate();
}
void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
bool card_found_notified = false;
uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN];
while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) {
if(magic_wupa()) {
magic_activate();
if(magic_gen1_wupa()) {
magic_dev->type = MagicTypeClassicGen1;
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
furi_hal_nfc_activate_nfca(200, &magic_dev->cuid);
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
}
magic_deactivate();
furi_delay_ms(300);
magic_activate();
furi_hal_nfc_activate_nfca(200, &magic_dev->cuid);
if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) {
magic_dev->type = MagicTypeGen4;
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
@ -135,12 +331,56 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
} else {
}
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
magic_deactivate();
furi_delay_ms(300);
}
magic_deactivate();
}
void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker) {
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
bool card_found_notified = false;
if(magic_dev->type != MagicTypeGen4) {
nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
return;
}
while(nfc_magic_worker->state == NfcMagicWorkerStateRekey) {
magic_activate();
uint32_t cuid;
furi_hal_nfc_activate_nfca(200, &cuid);
if(cuid != magic_dev->cuid) {
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
continue;
}
nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
if(magic_gen4_set_pwd(magic_dev->password, nfc_magic_worker->new_password)) {
magic_dev->password = nfc_magic_worker->new_password;
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
}
if(card_found_notified) { //-V547
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
furi_delay_ms(300);
}
@ -148,6 +388,10 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) {
}
void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev;
bool card_found_notified = false;
bool card_wiped = false;
MfClassicBlock block;
memset(&block, 0, sizeof(MfClassicBlock));
block.value[0] = 0x01;
@ -159,14 +403,48 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
block.value[6] = 0x04;
while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) {
magic_deactivate();
furi_delay_ms(300);
if(!magic_wupa()) continue;
if(!magic_wipe()) continue;
if(!magic_data_access_cmd()) continue;
if(!magic_write_blk(0, &block)) continue;
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
break;
do {
magic_deactivate();
furi_delay_ms(300);
if(!magic_activate()) break;
if(magic_dev->type == MagicTypeClassicGen1) {
if(!magic_gen1_wupa()) break;
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
if(!magic_gen1_wipe()) break;
if(!magic_gen1_data_access_cmd()) break;
if(!magic_gen1_write_blk(0, &block)) break;
card_wiped = true;
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
} else if(magic_dev->type == MagicTypeGen4) {
uint32_t cuid;
if(!furi_hal_nfc_activate_nfca(200, &cuid)) break;
if(cuid != magic_dev->cuid) break;
if(!card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventCardDetected, nfc_magic_worker->context);
card_found_notified = true;
}
if(!magic_gen4_wipe(magic_dev->password)) break;
card_wiped = true;
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
}
} while(false);
if(card_wiped) break;
if(card_found_notified) {
nfc_magic_worker->callback(
NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context);
card_found_notified = false;
}
}
magic_deactivate();
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <lib/nfc/nfc_device.h>
#include "nfc_magic.h"
typedef struct NfcMagicWorker NfcMagicWorker;
@ -9,6 +10,7 @@ typedef enum {
NfcMagicWorkerStateCheck,
NfcMagicWorkerStateWrite,
NfcMagicWorkerStateRekey,
NfcMagicWorkerStateWipe,
NfcMagicWorkerStateStop,
@ -33,6 +35,8 @@ void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_start(
NfcMagicWorker* nfc_magic_worker,
NfcMagicWorkerState state,
NfcMagicDevice* magic_dev,
NfcDeviceData* dev_data,
uint32_t new_password,
NfcMagicWorkerCallback callback,
void* context);

View File

@ -3,11 +3,14 @@
#include <furi.h>
#include "nfc_magic_worker.h"
#include "lib/magic/common.h"
struct NfcMagicWorker {
FuriThread* thread;
NfcMagicDevice* magic_dev;
NfcDeviceData* dev_data;
uint32_t new_password;
NfcMagicWorkerCallback callback;
void* context;
@ -21,4 +24,6 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker);
void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker);

View File

@ -0,0 +1,50 @@
#include "../nfc_magic_i.h"
enum SubmenuIndex {
SubmenuIndexWrite,
SubmenuIndexWipe,
};
void nfc_magic_scene_actions_submenu_callback(void* context, uint32_t index) {
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index);
}
void nfc_magic_scene_actions_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Submenu* submenu = nfc_magic->submenu;
submenu_add_item(
submenu, "Write", SubmenuIndexWrite, nfc_magic_scene_actions_submenu_callback, nfc_magic);
submenu_add_item(
submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_actions_submenu_callback, nfc_magic);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions));
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
}
bool nfc_magic_scene_actions_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexWipe) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
consumed = true;
}
scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions, event.event);
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc_magic->scene_manager, NfcMagicSceneStart);
}
return consumed;
}
void nfc_magic_scene_actions_on_exit(void* context) {
NfcMagic* nfc_magic = context;
submenu_reset(nfc_magic->submenu);
}

View File

@ -42,7 +42,9 @@ void nfc_magic_scene_check_on_enter(void* context) {
nfc_magic_worker_start(
nfc_magic->worker,
NfcMagicWorkerStateCheck,
&nfc_magic->nfc_dev->dev_data,
nfc_magic->dev,
&nfc_magic->source_dev->dev_data,
nfc_magic->new_password,
nfc_magic_check_worker_callback,
nfc_magic);
nfc_magic_blink_start(nfc_magic);

View File

@ -1,4 +1,8 @@
ADD_SCENE(nfc_magic, start, Start)
ADD_SCENE(nfc_magic, key_input, KeyInput)
ADD_SCENE(nfc_magic, actions, Actions)
ADD_SCENE(nfc_magic, gen4_actions, Gen4Actions)
ADD_SCENE(nfc_magic, new_key_input, NewKeyInput)
ADD_SCENE(nfc_magic, file_select, FileSelect)
ADD_SCENE(nfc_magic, write_confirm, WriteConfirm)
ADD_SCENE(nfc_magic, wrong_card, WrongCard)
@ -8,5 +12,7 @@ ADD_SCENE(nfc_magic, success, Success)
ADD_SCENE(nfc_magic, check, Check)
ADD_SCENE(nfc_magic, not_magic, NotMagic)
ADD_SCENE(nfc_magic, magic_info, MagicInfo)
ADD_SCENE(nfc_magic, rekey, Rekey)
ADD_SCENE(nfc_magic, rekey_fail, RekeyFail)
ADD_SCENE(nfc_magic, wipe, Wipe)
ADD_SCENE(nfc_magic, wipe_fail, WipeFail)

View File

@ -1,22 +1,60 @@
#include "../nfc_magic_i.h"
static bool nfc_magic_scene_file_select_is_file_suitable(NfcDevice* nfc_dev) {
return (nfc_dev->format == NfcDeviceSaveFormatMifareClassic) &&
(nfc_dev->dev_data.mf_classic_data.type == MfClassicType1k) &&
(nfc_dev->dev_data.nfc_data.uid_len == 4);
static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) {
NfcDevice* nfc_dev = nfc_magic->source_dev;
if(nfc_dev->format == NfcDeviceSaveFormatMifareClassic) {
switch(nfc_magic->dev->type) {
case MagicTypeClassicGen1:
case MagicTypeClassicDirectWrite:
case MagicTypeClassicAPDU:
if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) ||
(nfc_dev->dev_data.nfc_data.uid_len != 4)) {
return false;
}
return true;
case MagicTypeGen4:
return true;
default:
return false;
}
} else if(
(nfc_dev->format == NfcDeviceSaveFormatMifareUl) &&
(nfc_dev->dev_data.nfc_data.uid_len == 7)) {
switch(nfc_magic->dev->type) {
case MagicTypeUltralightGen1:
case MagicTypeUltralightDirectWrite:
case MagicTypeUltralightC_Gen1:
case MagicTypeUltralightC_DirectWrite:
case MagicTypeGen4:
switch(nfc_dev->dev_data.mf_ul_data.type) {
case MfUltralightTypeNTAGI2C1K:
case MfUltralightTypeNTAGI2C2K:
case MfUltralightTypeNTAGI2CPlus1K:
case MfUltralightTypeNTAGI2CPlus2K:
return false;
default:
return true;
}
default:
return false;
}
}
return false;
}
void nfc_magic_scene_file_select_on_enter(void* context) {
NfcMagic* nfc_magic = context;
// Process file_select return
nfc_device_set_loading_callback(nfc_magic->nfc_dev, nfc_magic_show_loading_popup, nfc_magic);
nfc_device_set_loading_callback(
nfc_magic->source_dev, nfc_magic_show_loading_popup, nfc_magic);
if(!furi_string_size(nfc_magic->nfc_dev->load_path)) {
furi_string_set_str(nfc_magic->nfc_dev->load_path, NFC_APP_FOLDER);
if(!furi_string_size(nfc_magic->source_dev->load_path)) {
furi_string_set_str(nfc_magic->source_dev->load_path, NFC_APP_FOLDER);
}
if(nfc_file_select(nfc_magic->nfc_dev)) {
if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) {
if(nfc_file_select(nfc_magic->source_dev)) {
if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic)) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm);
} else {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard);
@ -34,5 +72,5 @@ bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event
void nfc_magic_scene_file_select_on_exit(void* context) {
NfcMagic* nfc_magic = context;
nfc_device_set_loading_callback(nfc_magic->nfc_dev, NULL, nfc_magic);
nfc_device_set_loading_callback(nfc_magic->source_dev, NULL, nfc_magic);
}

View File

@ -0,0 +1,70 @@
#include "../nfc_magic_i.h"
enum SubmenuIndex {
SubmenuIndexWrite,
SubmenuIndexChangePassword,
SubmenuIndexWipe,
};
void nfc_magic_scene_gen4_actions_submenu_callback(void* context, uint32_t index) {
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index);
}
void nfc_magic_scene_gen4_actions_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Submenu* submenu = nfc_magic->submenu;
submenu_add_item(
submenu,
"Write",
SubmenuIndexWrite,
nfc_magic_scene_gen4_actions_submenu_callback,
nfc_magic);
submenu_add_item(
submenu,
"Change password",
SubmenuIndexChangePassword,
nfc_magic_scene_gen4_actions_submenu_callback,
nfc_magic);
submenu_add_item(
submenu,
"Wipe",
SubmenuIndexWipe,
nfc_magic_scene_gen4_actions_submenu_callback,
nfc_magic);
submenu_set_selected_item(
submenu,
scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneGen4Actions));
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu);
}
bool nfc_magic_scene_gen4_actions_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexChangePassword) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNewKeyInput);
consumed = true;
} else if(event.event == SubmenuIndexWipe) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
consumed = true;
}
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneGen4Actions, event.event);
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc_magic->scene_manager, NfcMagicSceneStart);
}
return consumed;
}
void nfc_magic_scene_gen4_actions_on_exit(void* context) {
NfcMagic* nfc_magic = context;
submenu_reset(nfc_magic->submenu);
}

View File

@ -0,0 +1,45 @@
#include "../nfc_magic_i.h"
void nfc_magic_scene_key_input_byte_input_callback(void* context) {
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(
nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone);
}
void nfc_magic_scene_key_input_on_enter(void* context) {
NfcMagic* nfc_magic = context;
// Setup view
ByteInput* byte_input = nfc_magic->byte_input;
byte_input_set_header_text(byte_input, "Enter the password in hex");
byte_input_set_result_callback(
byte_input,
nfc_magic_scene_key_input_byte_input_callback,
NULL,
nfc_magic,
(uint8_t*)&nfc_magic->dev->password,
4);
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput);
}
bool nfc_magic_scene_key_input_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcMagicCustomEventByteInputDone) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck);
consumed = true;
}
}
return consumed;
}
void nfc_magic_scene_key_input_on_exit(void* context) {
NfcMagic* nfc_magic = context;
// Clear view
byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(nfc_magic->byte_input, "");
}

View File

@ -1,4 +1,5 @@
#include "../nfc_magic_i.h"
#include "../lib/magic/types.h"
void nfc_magic_scene_magic_info_widget_callback(
GuiButtonType result,
@ -13,14 +14,18 @@ void nfc_magic_scene_magic_info_widget_callback(
void nfc_magic_scene_magic_info_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Widget* widget = nfc_magic->widget;
const char* card_type = nfc_magic_type(nfc_magic->dev->type);
notification_message(nfc_magic->notifications, &sequence_success);
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Magic card detected");
widget_add_string_element(widget, 3, 17, AlignLeft, AlignTop, FontSecondary, card_type);
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic);
widget_add_button_element(
widget, GuiButtonTypeRight, "More", nfc_magic_scene_magic_info_widget_callback, nfc_magic);
// Setup and start worker
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
@ -33,6 +38,15 @@ bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc_magic->scene_manager);
} else if(event.event == GuiButtonTypeRight) {
MagicType type = nfc_magic->dev->type;
if(type == MagicTypeGen4) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneGen4Actions);
consumed = true;
} else {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneActions);
consumed = true;
}
}
}
return consumed;

View File

@ -0,0 +1,45 @@
#include "../nfc_magic_i.h"
void nfc_magic_scene_new_key_input_byte_input_callback(void* context) {
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(
nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone);
}
void nfc_magic_scene_new_key_input_on_enter(void* context) {
NfcMagic* nfc_magic = context;
// Setup view
ByteInput* byte_input = nfc_magic->byte_input;
byte_input_set_header_text(byte_input, "Enter the password in hex");
byte_input_set_result_callback(
byte_input,
nfc_magic_scene_new_key_input_byte_input_callback,
NULL,
nfc_magic,
(uint8_t*)&nfc_magic->new_password,
4);
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput);
}
bool nfc_magic_scene_new_key_input_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcMagicCustomEventByteInputDone) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekey);
consumed = true;
}
}
return consumed;
}
void nfc_magic_scene_new_key_input_on_exit(void* context) {
NfcMagic* nfc_magic = context;
// Clear view
byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(nfc_magic->byte_input, "");
}

View File

@ -0,0 +1,95 @@
#include "../nfc_magic_i.h"
enum {
NfcMagicSceneRekeyStateCardSearch,
NfcMagicSceneRekeyStateCardFound,
};
bool nfc_magic_rekey_worker_callback(NfcMagicWorkerEvent event, void* context) {
furi_assert(context);
NfcMagic* nfc_magic = context;
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event);
return true;
}
static void nfc_magic_scene_rekey_setup_view(NfcMagic* nfc_magic) {
Popup* popup = nfc_magic->popup;
popup_reset(popup);
uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneRekey);
if(state == NfcMagicSceneRekeyStateCardSearch) {
popup_set_text(
nfc_magic->popup,
"Apply the\nsame card\nto the back",
128,
32,
AlignRight,
AlignCenter);
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
} else {
popup_set_icon(popup, 12, 23, &I_Loading_24);
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
}
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup);
}
void nfc_magic_scene_rekey_on_enter(void* context) {
NfcMagic* nfc_magic = context;
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch);
nfc_magic_scene_rekey_setup_view(nfc_magic);
// Setup and start worker
nfc_magic_worker_start(
nfc_magic->worker,
NfcMagicWorkerStateRekey,
nfc_magic->dev,
&nfc_magic->source_dev->dev_data,
nfc_magic->new_password,
nfc_magic_rekey_worker_callback,
nfc_magic);
nfc_magic_blink_start(nfc_magic);
}
bool nfc_magic_scene_rekey_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcMagicWorkerEventSuccess) {
nfc_magic->dev->password = nfc_magic->new_password;
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess);
consumed = true;
} else if(event.event == NfcMagicWorkerEventFail) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekeyFail);
consumed = true;
} else if(event.event == NfcMagicWorkerEventCardDetected) {
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardFound);
nfc_magic_scene_rekey_setup_view(nfc_magic);
consumed = true;
} else if(event.event == NfcMagicWorkerEventNoCardDetected) {
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch);
nfc_magic_scene_rekey_setup_view(nfc_magic);
consumed = true;
}
}
return consumed;
}
void nfc_magic_scene_rekey_on_exit(void* context) {
NfcMagic* nfc_magic = context;
nfc_magic_worker_stop(nfc_magic->worker);
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch);
// Clear view
popup_reset(nfc_magic->popup);
nfc_magic_blink_stop(nfc_magic);
}

View File

@ -0,0 +1,50 @@
#include "../nfc_magic_i.h"
void nfc_magic_scene_rekey_fail_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
NfcMagic* nfc_magic = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result);
}
}
void nfc_magic_scene_rekey_fail_on_enter(void* context) {
NfcMagic* nfc_magic = context;
Widget* widget = nfc_magic->widget;
notification_message(nfc_magic->notifications, &sequence_error);
widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
widget_add_string_element(
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Can't change password!");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Finish", nfc_magic_scene_rekey_fail_widget_callback, nfc_magic);
// Setup and start worker
view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget);
}
bool nfc_magic_scene_rekey_fail_on_event(void* context, SceneManagerEvent event) {
NfcMagic* nfc_magic = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc_magic->scene_manager, NfcMagicSceneStart);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc_magic->scene_manager, NfcMagicSceneStart);
}
return consumed;
}
void nfc_magic_scene_rekey_fail_on_exit(void* context) {
NfcMagic* nfc_magic = context;
widget_reset(nfc_magic->widget);
}

View File

@ -1,8 +1,7 @@
#include "../nfc_magic_i.h"
enum SubmenuIndex {
SubmenuIndexCheck,
SubmenuIndexWriteGen1A,
SubmenuIndexWipe,
SubmenuIndexAuthenticateGen4,
};
void nfc_magic_scene_start_submenu_callback(void* context, uint32_t index) {
@ -22,12 +21,10 @@ void nfc_magic_scene_start_on_enter(void* context) {
nfc_magic);
submenu_add_item(
submenu,
"Write Gen1A",
SubmenuIndexWriteGen1A,
"Authenticate Gen4",
SubmenuIndexAuthenticateGen4,
nfc_magic_scene_start_submenu_callback,
nfc_magic);
submenu_add_item(
submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_start_submenu_callback, nfc_magic);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart));
@ -40,23 +37,13 @@ bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexCheck) {
nfc_magic->dev->password = MAGIC_GEN4_DEFAULT_PWD;
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck);
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck);
consumed = true;
} else if(event.event == SubmenuIndexWriteGen1A) {
// Explicitly save state in each branch so that the
// correct option is reselected if the user cancels
// loading a file.
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWriteGen1A);
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect);
consumed = true;
} else if(event.event == SubmenuIndexWipe) {
scene_manager_set_scene_state(
nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWipe);
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe);
consumed = true;
} else if(event.event == SubmenuIndexAuthenticateGen4) {
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneKeyInput);
}
}

View File

@ -22,7 +22,12 @@ static void nfc_magic_scene_wipe_setup_view(NfcMagic* nfc_magic) {
if(state == NfcMagicSceneWipeStateCardSearch) {
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
popup_set_text(
nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter);
nfc_magic->popup,
"Apply the\nsame card\nto the back",
128,
32,
AlignRight,
AlignCenter);
} else {
popup_set_icon(popup, 12, 23, &I_Loading_24);
popup_set_header(popup, "Wiping\nDon't move...", 52, 32, AlignLeft, AlignCenter);
@ -42,7 +47,9 @@ void nfc_magic_scene_wipe_on_enter(void* context) {
nfc_magic_worker_start(
nfc_magic->worker,
NfcMagicWorkerStateWipe,
&nfc_magic->nfc_dev->dev_data,
nfc_magic->dev,
&nfc_magic->source_dev->dev_data,
nfc_magic->new_password,
nfc_magic_wipe_worker_callback,
nfc_magic);
nfc_magic_blink_start(nfc_magic);

View File

@ -21,7 +21,12 @@ static void nfc_magic_scene_write_setup_view(NfcMagic* nfc_magic) {
if(state == NfcMagicSceneWriteStateCardSearch) {
popup_set_text(
nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter);
nfc_magic->popup,
"Apply the\nsame card\nto the back",
128,
32,
AlignRight,
AlignCenter);
popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50);
} else {
popup_set_icon(popup, 12, 23, &I_Loading_24);
@ -42,7 +47,9 @@ void nfc_magic_scene_write_on_enter(void* context) {
nfc_magic_worker_start(
nfc_magic->worker,
NfcMagicWorkerStateWrite,
&nfc_magic->nfc_dev->dev_data,
nfc_magic->dev,
&nfc_magic->source_dev->dev_data,
nfc_magic->new_password,
nfc_magic_write_worker_callback,
nfc_magic);
nfc_magic_blink_start(nfc_magic);

View File

@ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) {
AlignLeft,
AlignTop,
FontSecondary,
"Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k");
"Writing this file is\nnot supported for\nthis magic card.");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic);