[FL-2605] NFC new design (#1364)

* nfc: add new read scene

* lib: refactore nfc library

* mifare desfire: add read card fuction

* lib nfc: add auto read worker

* nfc: add supported cards

* nfc: add mifare classic read success scene

* nfc: add troyka support

* submodule: update protobuf

* nfc: mifare classic keys cache

* nfc: rework mifare classic key cache

* Correct spelling

* nfc: add user dictionary

* nfc: introduce block read map in fff

* nfc: rework dict attack

* nfc: improve dict attack

* nfc: rework mifare classic format

* nfc: rework MFC read with Reader

* nfc: add gui for MFC read success scene

* nfc: fix dict attack view gui

* nfc: add retry and exit confirm scenes

* nfc: add retry and exit scenes navigation

* nfc: check user dictionary

* nfc: remove unused scenes

* nfc: rename functions in nfc worker

* nfc: rename mf_classic_dict_attack -> dict_attack

* nfc: change scenes names
* nfc: remove scene tick events
* nfc: rework dict calls with buffer streams
* nfc: fix notifications
* nfc: fix mf desfire navigation
* nfc: remove notification from mf classic read success
* nfc: fix read sectors calculation
* nfc: add fallback for unknown card
* nfc: show file name while emulating
* nfc: fix build
* nfc: fix memory leak
* nfc: fix desfire read
* nfc: add no dict found navigation
* nfc: add read views
* nfc: update card fix
* nfc: fix access bytes save
* nfc: add exit and retry confirm to mf ultralight read success
* nfc: introduce detect reader
* nfc: change record open arg to macros
* nfc: fix start from archive

Co-authored-by: Astra <astra@astrra.space>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
gornekich
2022-07-26 18:30:49 +03:00
committed by GitHub
parent ec19c11dbe
commit 9c59bcd776
89 changed files with 2755 additions and 2012 deletions

View File

@@ -70,6 +70,7 @@ libs = env.BuildModules(
"infrared",
"littlefs",
"subghz",
"nfc",
"appframe",
"misc",
"mbedtls",

View File

@@ -7,7 +7,6 @@ env.Append(
"#/lib/heatshrink",
"#/lib/micro-ecc",
"#/lib/nanopb",
"#/lib/nfc_protocols",
"#/lib/u8g2",
],
CPPDEFINES=[
@@ -24,7 +23,6 @@ sources = []
libs_recurse = [
"digital_signal",
"micro-ecc",
"nfc_protocols",
"one_wire",
"u8g2",
"update_util",

16
lib/nfc/SConscript Normal file
View File

@@ -0,0 +1,16 @@
Import("env")
env.Append(
CPPPATH=[
"#/lib/nfc",
],
)
libenv = env.Clone(FW_LIB_NAME="nfc")
libenv.ApplyLibFlags()
sources = libenv.GlobRecursive("*.c*")
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
Return("lib")

View File

@@ -0,0 +1,148 @@
#include "mf_classic_dict.h"
#include <lib/toolbox/args.h>
#include <lib/flipper_format/flipper_format.h>
#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
#define TAG "MfClassicDict"
#define NFC_MF_CLASSIC_KEY_LEN (13)
struct MfClassicDict {
Stream* stream;
uint32_t total_keys;
};
bool mf_classic_dict_check_presence(MfClassicDictType dict_type) {
Storage* storage = furi_record_open(RECORD_STORAGE);
bool dict_present = false;
if(dict_type == MfClassicDictTypeFlipper) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK;
} else if(dict_type == MfClassicDictTypeUser) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK;
}
furi_record_close(RECORD_STORAGE);
return dict_present;
}
MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) {
MfClassicDict* dict = malloc(sizeof(MfClassicDict));
Storage* storage = furi_record_open(RECORD_STORAGE);
dict->stream = buffered_file_stream_alloc(storage);
furi_record_close(RECORD_STORAGE);
bool dict_loaded = false;
do {
if(dict_type == MfClassicDictTypeFlipper) {
if(!buffered_file_stream_open(
dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
buffered_file_stream_close(dict->stream);
break;
}
} else if(dict_type == MfClassicDictTypeUser) {
if(!buffered_file_stream_open(
dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
buffered_file_stream_close(dict->stream);
break;
}
}
// Read total amount of keys
string_t next_line;
string_init(next_line);
while(true) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
dict->total_keys++;
}
string_clear(next_line);
stream_rewind(dict->stream);
dict_loaded = true;
FURI_LOG_I(TAG, "Loaded dictionary with %d keys", dict->total_keys);
} while(false);
if(!dict_loaded) {
buffered_file_stream_close(dict->stream);
free(dict);
dict = NULL;
}
return dict;
}
void mf_classic_dict_free(MfClassicDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
buffered_file_stream_close(dict->stream);
stream_free(dict->stream);
free(dict);
}
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) {
furi_assert(dict);
return dict->total_keys;
}
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
uint8_t key_byte_tmp = 0;
string_t next_line;
string_init(next_line);
bool key_read = false;
*key = 0ULL;
while(!key_read) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
for(uint8_t i = 0; i < 12; i += 2) {
args_char_to_hex(
string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp);
*key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2);
}
key_read = true;
}
string_clear(next_line);
return key_read;
}
bool mf_classic_dict_rewind(MfClassicDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
return stream_rewind(dict->stream);
}
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
string_t key_str;
string_init(key_str);
for(size_t i = 0; i < 6; i++) {
string_cat_printf(key_str, "%02X", key[i]);
}
string_cat_printf(key_str, "\n");
bool key_added = false;
do {
if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
if(!stream_insert_string(dict->stream, key_str)) break;
key_added = true;
} while(false);
string_clear(key_str);
return key_added;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <stdbool.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/toolbox/stream/file_stream.h>
#include <lib/toolbox/stream/buffered_file_stream.h>
typedef enum {
MfClassicDictTypeUser,
MfClassicDictTypeFlipper,
} MfClassicDictType;
typedef struct MfClassicDict MfClassicDict;
bool mf_classic_dict_check_presence(MfClassicDictType dict_type);
MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type);
void mf_classic_dict_free(MfClassicDict* dict);
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict);
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key);
bool mf_classic_dict_rewind(MfClassicDict* dict);
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key);

View File

@@ -0,0 +1,166 @@
#include "nfc_debug_pcap.h"
#include <furi_hal_rtc.h>
#include <stream_buffer.h>
#define TAG "NfcDebugPcap"
#define PCAP_MAGIC 0xa1b2c3d4
#define PCAP_MAJOR 2
#define PCAP_MINOR 4
#define DLT_ISO_14443 264
#define DATA_PICC_TO_PCD 0xFF
#define DATA_PCD_TO_PICC 0xFE
#define DATA_PICC_TO_PCD_CRC_DROPPED 0xFB
#define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap")
#define NFC_DEBUG_PCAP_BUFFER_SIZE 64
struct NfcDebugPcapWorker {
bool alive;
Storage* storage;
File* file;
StreamBufferHandle_t stream;
FuriThread* thread;
};
static File* nfc_debug_pcap_open(Storage* storage) {
File* file = storage_file_alloc(storage);
if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
storage_file_free(file);
return NULL;
}
if(!storage_file_tell(file)) {
struct {
uint32_t magic;
uint16_t major, minor;
uint32_t reserved[2];
uint32_t snaplen;
uint32_t link_type;
} __attribute__((__packed__)) pcap_hdr = {
.magic = PCAP_MAGIC,
.major = PCAP_MAJOR,
.minor = PCAP_MINOR,
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
.link_type = DLT_ISO_14443,
};
if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
FURI_LOG_E(TAG, "Failed to write pcap header");
}
}
return file;
}
static void
nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) {
FuriHalRtcDateTime datetime;
furi_hal_rtc_get_datetime(&datetime);
struct {
// https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
uint32_t ts_sec;
uint32_t ts_usec;
uint32_t incl_len;
uint32_t orig_len;
// https://www.kaiser.cx/posts/pcap-iso14443/#_packet_data
uint8_t version;
uint8_t event;
uint16_t len;
} __attribute__((__packed__)) pkt_hdr = {
.ts_sec = furi_hal_rtc_datetime_to_timestamp(&datetime),
.ts_usec = 0,
.incl_len = len + 4,
.orig_len = len + 4,
.version = 0,
.event = event,
.len = len << 8 | len >> 8,
};
xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever);
xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
}
static void
nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC;
nfc_debug_pcap_write(instance, event, data, bits / 8);
}
static void
nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD;
nfc_debug_pcap_write(instance, event, data, bits / 8);
}
int32_t nfc_debug_pcap_thread(void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE];
while(instance->alive) {
size_t ret =
xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50);
if(storage_file_write(instance->file, buffer, ret) != ret) {
FURI_LOG_E(TAG, "Failed to write pcap data");
}
}
return 0;
}
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) {
NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker));
instance->alive = true;
instance->storage = storage;
instance->file = nfc_debug_pcap_open(storage);
instance->stream = xStreamBufferCreate(4096, 1);
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "PcapWorker");
furi_thread_set_stack_size(instance->thread, 1024);
furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread);
furi_thread_set_context(instance->thread, instance);
furi_thread_start(instance->thread);
return instance;
}
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) {
furi_assert(instance);
instance->alive = false;
furi_thread_join(instance->thread);
furi_thread_free(instance->thread);
vStreamBufferDelete(instance->stream);
if(instance->file) storage_file_free(instance->file);
instance->storage = NULL;
free(instance);
}
void nfc_debug_pcap_prepare_tx_rx(
NfcDebugPcapWorker* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc) {
if(!instance || !instance->file) return;
if(is_picc) {
tx_rx->sniff_tx = nfc_debug_pcap_write_rx;
tx_rx->sniff_rx = nfc_debug_pcap_write_tx;
} else {
tx_rx->sniff_tx = nfc_debug_pcap_write_tx;
tx_rx->sniff_rx = nfc_debug_pcap_write_rx;
}
tx_rx->sniff_context = instance;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <furi_hal_nfc.h>
#include <storage/storage.h>
typedef struct NfcDebugPcapWorker NfcDebugPcapWorker;
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage);
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance);
/** Prepare tx/rx context for debug pcap logging, if enabled.
*
* @param instance NfcDebugPcapWorker* instance, can be NULL
* @param tx_rx TX/RX context to log
* @param is_picc if true, record Flipper as PICC, else PCD.
*/
void nfc_debug_pcap_prepare_tx_rx(
NfcDebugPcapWorker* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc);

1278
lib/nfc/nfc_device.c Normal file

File diff suppressed because it is too large Load Diff

94
lib/nfc/nfc_device.h Normal file
View File

@@ -0,0 +1,94 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#include <furi_hal_nfc.h>
#include <lib/nfc/protocols/emv.h>
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#define NFC_DEV_NAME_MAX_LEN 22
#define NFC_READER_DATA_MAX_SIZE 64
#define NFC_APP_FOLDER ANY_PATH("nfc")
#define NFC_APP_EXTENSION ".nfc"
#define NFC_APP_SHADOW_EXTENSION ".shd"
typedef void (*NfcLoadingCallback)(void* context, bool state);
typedef enum {
NfcDeviceProtocolUnknown,
NfcDeviceProtocolEMV,
NfcDeviceProtocolMifareUl,
NfcDeviceProtocolMifareClassic,
NfcDeviceProtocolMifareDesfire,
} NfcProtocol;
typedef enum {
NfcDeviceSaveFormatUid,
NfcDeviceSaveFormatBankCard,
NfcDeviceSaveFormatMifareUl,
NfcDeviceSaveFormatMifareClassic,
NfcDeviceSaveFormatMifareDesfire,
} NfcDeviceSaveFormat;
typedef struct {
uint8_t data[NFC_READER_DATA_MAX_SIZE];
uint16_t size;
} NfcReaderRequestData;
typedef struct {
FuriHalNfcDevData nfc_data;
NfcProtocol protocol;
NfcReaderRequestData reader_data;
union {
EmvData emv_data;
MfUltralightData mf_ul_data;
MfClassicData mf_classic_data;
MifareDesfireData mf_df_data;
};
string_t parsed_data;
} NfcDeviceData;
typedef struct {
Storage* storage;
DialogsApp* dialogs;
NfcDeviceData dev_data;
char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
string_t load_path;
NfcDeviceSaveFormat format;
bool shadow_file_exist;
NfcLoadingCallback loading_cb;
void* loading_cb_ctx;
} NfcDevice;
NfcDevice* nfc_device_alloc();
void nfc_device_free(NfcDevice* nfc_dev);
void nfc_device_set_name(NfcDevice* dev, const char* name);
bool nfc_device_save(NfcDevice* dev, const char* dev_name);
bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name);
bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog);
bool nfc_device_load_key_cache(NfcDevice* dev);
bool nfc_file_select(NfcDevice* dev);
void nfc_device_data_clear(NfcDeviceData* dev);
void nfc_device_clear(NfcDevice* dev);
bool nfc_device_delete(NfcDevice* dev, bool use_load_path);
bool nfc_device_restore(NfcDevice* dev, bool use_load_path);
void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context);

65
lib/nfc/nfc_types.c Normal file
View File

@@ -0,0 +1,65 @@
#include "nfc_types.h"
const char* nfc_get_dev_type(FuriHalNfcType type) {
if(type == FuriHalNfcTypeA) {
return "NFC-A";
} else if(type == FuriHalNfcTypeB) {
return "NFC-B";
} else if(type == FuriHalNfcTypeF) {
return "NFC-F";
} else if(type == FuriHalNfcTypeV) {
return "NFC-V";
} else {
return "Unknown";
}
}
const char* nfc_guess_protocol(NfcProtocol protocol) {
if(protocol == NfcDeviceProtocolEMV) {
return "EMV bank card";
} else if(protocol == NfcDeviceProtocolMifareUl) {
return "Mifare Ultral/NTAG";
} else if(protocol == NfcDeviceProtocolMifareClassic) {
return "Mifare Classic";
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
return "Mifare DESFire";
} else {
return "Unrecognized";
}
}
const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) {
if(type == MfUltralightTypeNTAG213) {
return "NTAG213";
} else if(type == MfUltralightTypeNTAG215) {
return "NTAG215";
} else if(type == MfUltralightTypeNTAG216) {
return "NTAG216";
} else if(type == MfUltralightTypeNTAGI2C1K) {
return "NTAG I2C 1K";
} else if(type == MfUltralightTypeNTAGI2C2K) {
return "NTAG I2C 2K";
} else if(type == MfUltralightTypeNTAGI2CPlus1K) {
return "NTAG I2C Plus 1K";
} else if(type == MfUltralightTypeNTAGI2CPlus2K) {
return "NTAG I2C Plus 2K";
} else if(type == MfUltralightTypeNTAG203) {
return "NTAG203";
} else if(type == MfUltralightTypeUL11 && full_name) {
return "Mifare Ultralight 11";
} else if(type == MfUltralightTypeUL21 && full_name) {
return "Mifare Ultralight 21";
} else {
return "Mifare Ultralight";
}
}
const char* nfc_mf_classic_type(MfClassicType type) {
if(type == MfClassicType1k) {
return "Mifare Classic 1K";
} else if(type == MfClassicType4k) {
return "Mifare Classic 4K";
} else {
return "Mifare Classic";
}
}

11
lib/nfc/nfc_types.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include "nfc_device.h"
const char* nfc_get_dev_type(FuriHalNfcType type);
const char* nfc_guess_protocol(NfcProtocol protocol);
const char* nfc_mf_ul_type(MfUltralightType type, bool full_name);
const char* nfc_mf_classic_type(MfClassicType type);

510
lib/nfc/nfc_worker.c Normal file
View File

@@ -0,0 +1,510 @@
#include "nfc_worker_i.h"
#include <furi_hal.h>
#include <platform.h>
#include "parsers/nfc_supported_card.h"
#define TAG "NfcWorker"
/***************************** NFC Worker API *******************************/
NfcWorker* nfc_worker_alloc() {
NfcWorker* nfc_worker = malloc(sizeof(NfcWorker));
// Worker thread attributes
nfc_worker->thread = furi_thread_alloc();
furi_thread_set_name(nfc_worker->thread, "NfcWorker");
furi_thread_set_stack_size(nfc_worker->thread, 8192);
furi_thread_set_callback(nfc_worker->thread, nfc_worker_task);
furi_thread_set_context(nfc_worker->thread, nfc_worker);
nfc_worker->callback = NULL;
nfc_worker->context = NULL;
nfc_worker->storage = furi_record_open(RECORD_STORAGE);
// Initialize rfal
while(furi_hal_nfc_is_busy()) {
furi_delay_ms(10);
}
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage);
}
return nfc_worker;
}
void nfc_worker_free(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
furi_thread_free(nfc_worker->thread);
furi_record_close(RECORD_STORAGE);
if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker);
free(nfc_worker);
}
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
return nfc_worker->state;
}
void nfc_worker_start(
NfcWorker* nfc_worker,
NfcWorkerState state,
NfcDeviceData* dev_data,
NfcWorkerCallback callback,
void* context) {
furi_assert(nfc_worker);
furi_assert(dev_data);
while(furi_hal_nfc_is_busy()) {
furi_delay_ms(10);
}
nfc_worker->callback = callback;
nfc_worker->context = context;
nfc_worker->dev_data = dev_data;
nfc_worker_change_state(nfc_worker, state);
furi_thread_start(nfc_worker->thread);
}
void nfc_worker_stop(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) {
return;
}
furi_hal_nfc_stop();
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
furi_thread_join(nfc_worker->thread);
}
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
nfc_worker->state = state;
}
/***************************** NFC Worker Thread *******************************/
int32_t nfc_worker_task(void* context) {
NfcWorker* nfc_worker = context;
furi_hal_nfc_exit_sleep();
if(nfc_worker->state == NfcWorkerStateRead) {
nfc_worker_read(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
nfc_worker_emulate_uid(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
nfc_worker_emulate_apdu(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) {
nfc_worker_emulate_mf_ultralight(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) {
nfc_worker_emulate_mf_classic(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) {
nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeUser);
} else if(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack) {
nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeFlipper);
}
furi_hal_nfc_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
return 0;
}
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false;
MfUltralightReader reader = {};
MfUltralightData data = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
if(!mf_ul_read_card(tx_rx, &reader, &data)) break;
// Copy data
nfc_worker->dev_data->mf_ul_data = data;
read_success = true;
} while(false);
return read_success;
}
static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker->callback);
bool read_success = false;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
do {
// Try to read supported card
FURI_LOG_I(TAG, "Try read supported card ...");
for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) {
if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) {
if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) {
if(nfc_supported_card[i].read(nfc_worker, tx_rx)) {
read_success = true;
nfc_supported_card[i].parse(nfc_worker);
}
}
}
}
if(read_success) break;
// Try to read card with key cache
FURI_LOG_I(TAG, "Search for key cache ...");
if(nfc_worker->callback(NfcWorkerEventReadMfClassicLoadKeyCache, nfc_worker->context)) {
FURI_LOG_I(TAG, "Load keys cache success. Start reading");
uint8_t sectors_read =
mf_classic_update_card(tx_rx, &nfc_worker->dev_data->mf_classic_data);
uint8_t sectors_total =
mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type);
FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total);
read_success = (sectors_read == sectors_total);
}
} while(false);
return read_success;
}
static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false;
MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
do {
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
if(!mf_df_read_card(tx_rx, data)) break;
read_success = true;
} while(false);
return read_success;
}
static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool read_success = false;
EmvApplication emv_app = {};
EmvData* result = &nfc_worker->dev_data->emv_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
if(!emv_read_bank_card(tx_rx, &emv_app)) break;
// Copy data
// TODO Set EmvData to reader or like in mifare ultralight!
result->number_len = emv_app.card_number_len;
memcpy(result->number, emv_app.card_number, result->number_len);
result->aid_len = emv_app.aid_len;
memcpy(result->aid, emv_app.aid, result->aid_len);
if(emv_app.name_found) {
memcpy(result->name, emv_app.name, sizeof(emv_app.name));
}
if(emv_app.exp_month) {
result->exp_mon = emv_app.exp_month;
result->exp_year = emv_app.exp_year;
}
if(emv_app.country_code) {
result->country_code = emv_app.country_code;
}
if(emv_app.currency_code) {
result->currency_code = emv_app.currency_code;
}
read_success = true;
} while(false);
return read_success;
}
static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
bool card_read = false;
furi_hal_nfc_sleep();
if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
FURI_LOG_I(TAG, "Mifare Ultralight / NTAG detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl;
card_read = nfc_worker_read_mf_ultralight(nfc_worker, tx_rx);
} else if(mf_classic_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
FURI_LOG_I(TAG, "Mifare Classic detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic;
nfc_worker->dev_data->mf_classic_data.type =
mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
card_read = nfc_worker_read_mf_classic(nfc_worker, tx_rx);
} else if(mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) {
FURI_LOG_I(TAG, "Mifare DESFire detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire;
if(!nfc_worker_read_mf_desfire(nfc_worker, tx_rx)) {
FURI_LOG_I(TAG, "Unknown card. Save UID");
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
}
card_read = true;
} else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) {
FURI_LOG_I(TAG, "ISO14443-4 card detected");
nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV;
if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) {
FURI_LOG_I(TAG, "Unknown card. Save UID");
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
}
card_read = true;
}
return card_read;
}
void nfc_worker_read(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
furi_assert(nfc_worker->callback);
nfc_device_data_clear(nfc_worker->dev_data);
NfcDeviceData* dev_data = nfc_worker->dev_data;
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
FuriHalNfcTxRxContext tx_rx = {};
NfcWorkerEvent event = 0;
bool card_not_detected_notified = false;
while(nfc_worker->state == NfcWorkerStateRead) {
if(furi_hal_nfc_detect(nfc_data, 300)) {
// Process first found device
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
card_not_detected_notified = false;
if(nfc_data->type == FuriHalNfcTypeA) {
if(nfc_worker_read_nfca(nfc_worker, &tx_rx)) {
if(dev_data->protocol == NfcDeviceProtocolMifareUl) {
event = NfcWorkerEventReadMfUltralight;
break;
} else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) {
event = NfcWorkerEventReadMfClassicDone;
break;
} else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) {
event = NfcWorkerEventReadMfDesfire;
break;
} else if(dev_data->protocol == NfcDeviceProtocolEMV) {
event = NfcWorkerEventReadBankCard;
break;
} else if(dev_data->protocol == NfcDeviceProtocolUnknown) {
event = NfcWorkerEventReadUidNfcA;
break;
}
} else {
if(dev_data->protocol == NfcDeviceProtocolMifareClassic) {
event = NfcWorkerEventReadMfClassicDictAttackRequired;
break;
}
}
} else if(nfc_data->type == FuriHalNfcTypeB) {
event = NfcWorkerEventReadUidNfcB;
break;
} else if(nfc_data->type == FuriHalNfcTypeF) {
event = NfcWorkerEventReadUidNfcF;
break;
} else if(nfc_data->type == FuriHalNfcTypeV) {
event = NfcWorkerEventReadUidNfcV;
break;
}
} else {
if(!card_not_detected_notified) {
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
card_not_detected_notified = true;
}
}
furi_hal_nfc_sleep();
furi_delay_ms(100);
}
// Notify caller and exit
if(event > NfcWorkerEventReserved) {
nfc_worker->callback(event, nfc_worker->context);
}
}
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
while(nfc_worker->state == NfcWorkerStateUidEmulate) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) {
if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
reader_data->size = tx_rx.rx_bits / 8;
if(reader_data->size > 0) {
memcpy(reader_data->data, tx_rx.rx_data, reader_data->size);
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
}
} else {
FURI_LOG_E(TAG, "Failed to get reader commands");
}
}
}
}
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData params = {
.uid = {0xCF, 0x72, 0xd4, 0x40},
.uid_len = 4,
.atqa = {0x00, 0x04},
.sak = 0x20,
.type = FuriHalNfcTypeA,
};
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
FURI_LOG_D(TAG, "POS terminal detected");
if(emv_card_emulation(&tx_rx)) {
FURI_LOG_D(TAG, "EMV card emulated");
}
} else {
FURI_LOG_D(TAG, "Can't find reader");
}
furi_hal_nfc_sleep();
furi_delay_ms(20);
}
}
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfUltralightEmulator emulator = {};
mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data);
while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) {
mf_ul_reset_emulation(&emulator, true);
furi_hal_nfc_emulate_nfca(
nfc_data->uid,
nfc_data->uid_len,
nfc_data->atqa,
nfc_data->sak,
mf_ul_prepare_emulation_response,
&emulator,
5000);
// Check if data was modified
if(emulator.data_changed) {
nfc_worker->dev_data->mf_ul_data = emulator.data;
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
emulator.data_changed = false;
}
}
}
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type) {
furi_assert(nfc_worker);
furi_assert(nfc_worker->callback);
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type);
uint64_t key = 0;
FuriHalNfcTxRxContext tx_rx = {};
bool card_found_notified = true;
bool card_removed_notified = false;
// Load dictionary
MfClassicDict* dict = mf_classic_dict_alloc(type);
if(!dict) {
FURI_LOG_E(TAG, "Dictionary not found");
nfc_worker->callback(NfcWorkerEventNoDictFound, nfc_worker->context);
mf_classic_dict_free(dict);
return;
}
FURI_LOG_D(TAG, "Start Dictionary attack");
for(size_t i = 0; i < total_sectors; i++) {
FURI_LOG_I(TAG, "Sector %d", i);
nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context);
uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i);
if(mf_classic_is_sector_read(data, i)) continue;
bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA);
bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB);
while(mf_classic_dict_get_next_key(dict, &key)) {
furi_hal_nfc_sleep();
if(furi_hal_nfc_activate_nfca(200, NULL)) {
furi_hal_nfc_sleep();
if(!card_found_notified) {
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
card_found_notified = true;
card_removed_notified = false;
}
FURI_LOG_D(
TAG,
"Try to auth to sector %d with key %04lx%08lx",
i,
(uint32_t)(key >> 32),
(uint32_t)key);
if(!is_key_a_found) {
is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA);
if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyA)) {
mf_classic_set_key_found(data, i, MfClassicKeyA, key);
nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context);
}
furi_hal_nfc_sleep();
}
if(!is_key_b_found) {
is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB);
if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyB)) {
mf_classic_set_key_found(data, i, MfClassicKeyB, key);
nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context);
}
}
if(is_key_a_found && is_key_b_found) break;
if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) ||
(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)))
break;
} else {
if(!card_removed_notified) {
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
card_removed_notified = true;
card_found_notified = false;
}
if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) ||
(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)))
break;
}
}
if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) ||
(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)))
break;
mf_classic_read_sector(&tx_rx, data, i);
mf_classic_dict_rewind(dict);
}
mf_classic_dict_free(dict);
if((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) ||
(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
} else {
nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context);
}
}
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
.data = nfc_worker->dev_data->mf_classic_data,
.data_changed = false,
};
NfcaSignal* nfca_signal = nfca_signal_alloc();
tx_rx.nfca_signal = nfca_signal;
rfal_platform_spi_acquire();
furi_hal_nfc_listen_start(nfc_data);
while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) {
if(furi_hal_nfc_listen_rx(&tx_rx, 300)) {
mf_classic_emulator(&emulator, &tx_rx);
}
}
if(emulator.data_changed) {
nfc_worker->dev_data->mf_classic_data = emulator.data;
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
}
emulator.data_changed = false;
}
nfca_signal_free(nfca_signal);
rfal_platform_spi_release();
}

71
lib/nfc/nfc_worker.h Executable file
View File

@@ -0,0 +1,71 @@
#pragma once
#include "nfc_device.h"
typedef struct NfcWorker NfcWorker;
typedef enum {
// Init states
NfcWorkerStateNone,
NfcWorkerStateBroken,
NfcWorkerStateReady,
// Main worker states
NfcWorkerStateRead,
NfcWorkerStateUidEmulate,
NfcWorkerStateMfUltralightEmulate,
NfcWorkerStateMfClassicEmulate,
NfcWorkerStateMfClassicUserDictAttack,
NfcWorkerStateMfClassicFlipperDictAttack,
// Debug
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
// Transition
NfcWorkerStateStop,
} NfcWorkerState;
typedef enum {
// Reserve first 50 events for application events
NfcWorkerEventReserved = 50,
// Nfc read events
NfcWorkerEventReadUidNfcB,
NfcWorkerEventReadUidNfcV,
NfcWorkerEventReadUidNfcF,
NfcWorkerEventReadUidNfcA,
NfcWorkerEventReadMfUltralight,
NfcWorkerEventReadMfDesfire,
NfcWorkerEventReadMfClassicDone,
NfcWorkerEventReadMfClassicLoadKeyCache,
NfcWorkerEventReadMfClassicDictAttackRequired,
NfcWorkerEventReadBankCard,
// Nfc worker common events
NfcWorkerEventSuccess,
NfcWorkerEventFail,
NfcWorkerEventAborted,
NfcWorkerEventCardDetected,
NfcWorkerEventNoCardDetected,
// Mifare Classic events
NfcWorkerEventNoDictFound,
NfcWorkerEventNewSector,
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
NfcWorker* nfc_worker_alloc();
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
void nfc_worker_free(NfcWorker* nfc_worker);
void nfc_worker_start(
NfcWorker* nfc_worker,
NfcWorkerState state,
NfcDeviceData* dev_data,
NfcWorkerCallback callback,
void* context);
void nfc_worker_stop(NfcWorker* nfc_worker);

48
lib/nfc/nfc_worker_i.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include "nfc_worker.h"
#include <furi.h>
#include <lib/toolbox/stream/file_stream.h>
#include <lib/nfc/protocols/nfc_util.h>
#include <lib/nfc/protocols/emv.h>
#include <lib/nfc/protocols/mifare_common.h>
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfca.h>
#include "helpers/mf_classic_dict.h"
#include "helpers/nfc_debug_pcap.h"
struct NfcWorker {
FuriThread* thread;
Storage* storage;
Stream* dict_stream;
NfcDeviceData* dev_data;
NfcWorkerCallback callback;
void* context;
NfcWorkerState state;
NfcDebugPcapWorker* debug_pcap_worker;
};
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
int32_t nfc_worker_task(void* context);
void nfc_worker_read(NfcWorker* nfc_worker);
void nfc_worker_emulate_uid(NfcWorker* nfc_worker);
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker);
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type);
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);

View File

@@ -0,0 +1,12 @@
#include "nfc_supported_card.h"
#include "troyka_parser.h"
NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
[NfcSupportedCardTypeTroyka] = {
.protocol = NfcDeviceProtocolMifareClassic,
.verify = troyka_parser_verify,
.read = troyka_parser_read,
.parse = troyka_parser_parse,
},
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include <furi_hal_nfc.h>
#include "../nfc_worker.h"
#include <m-string.h>
typedef enum {
NfcSupportedCardTypeTroyka,
NfcSupportedCardTypeEnd,
} NfcSupportedCardType;
typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
typedef bool (*NfcSupportedCardParse)(NfcWorker* nfc_worker);
typedef struct {
NfcProtocol protocol;
NfcSupportedCardVerify verify;
NfcSupportedCardRead read;
NfcSupportedCardParse parse;
} NfcSupportedCard;
extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd];

View File

@@ -0,0 +1,70 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
static const MfClassicAuthContext troyka_keys[] = {
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
{.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
{.sector = 5, .key_a = 0xfbc2793d540b, .key_b = 0xd3a297dc2698},
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dba},
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
{.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d},
{.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b},
{.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0},
{.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75},
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
};
bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
MfClassicAuthContext auth_ctx = {
.key_a = MF_CLASSIC_NO_KEY,
.key_b = MF_CLASSIC_NO_KEY,
.sector = 8,
};
return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333);
}
bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
mf_classic_get_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak, &reader);
for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) {
mf_classic_reader_add_sector(
&reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
}
bool troyka_parser_parse(NfcWorker* nfc_worker) {
MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
temp_ptr = &data->block[8 * 4].value[3];
uint32_t number = 0;
for(size_t i = 0; i < 4; i++) {
number <<= 8;
number |= temp_ptr[i];
}
number >>= 4;
string_printf(
nfc_worker->dev_data->parsed_data,
"Troyka Transport card\nNumber: %ld\nBalance: %d rub",
number,
balance);
return true;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "nfc_supported_card.h"
bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troyka_parser_parse(NfcWorker* nfc_worker);

View File

@@ -25,6 +25,16 @@ typedef enum {
MfClassicActionACWrite,
} MfClassicAction;
const char* mf_classic_get_type_str(MfClassicType type) {
if(type == MfClassicType1k) {
return "MIFARE Classic 1K";
} else if(type == MfClassicType4k) {
return "MIFARE Classic 4K";
} else {
return "Unknown";
}
}
static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {
furi_assert(sector < 40);
if(sector < 32) {
@@ -34,7 +44,16 @@ static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {
}
}
static uint8_t mf_classic_get_sector_by_block(uint8_t block) {
uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) {
furi_assert(sector < 40);
if(sector < 32) {
return sector * 4 + 3;
} else {
return 32 * 4 + (sector - 32) * 16 + 15;
}
}
uint8_t mf_classic_get_sector_by_block(uint8_t block) {
if(block < 128) {
return (block | 0x03) / 4;
} else {
@@ -47,7 +66,7 @@ static uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) {
return sector < 32 ? 4 : 16;
}
static uint8_t mf_classic_get_sector_trailer(uint8_t block) {
uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) {
if(block < 128) {
return block | 0x03;
} else {
@@ -55,15 +74,21 @@ static uint8_t mf_classic_get_sector_trailer(uint8_t block) {
}
}
static bool mf_classic_is_sector_trailer(uint8_t block) {
return block == mf_classic_get_sector_trailer(block);
bool mf_classic_is_sector_trailer(uint8_t block) {
return block == mf_classic_get_sector_trailer_num_by_block(block);
}
uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader) {
furi_assert(reader);
if(reader->type == MfClassicType1k) {
MfClassicSectorTrailer*
mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector) {
furi_assert(data);
uint8_t sec_tr_block_num = mf_classic_get_sector_trailer_block_num_by_sector(sector);
return (MfClassicSectorTrailer*)data->block[sec_tr_block_num].value;
}
uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
if(type == MfClassicType1k) {
return MF_CLASSIC_1K_TOTAL_SECTORS_NUM;
} else if(reader->type == MfClassicType4k) {
} else if(type == MfClassicType4k) {
return MF_CLASSIC_4K_TOTAL_SECTORS_NUM;
} else {
return 0;
@@ -80,6 +105,104 @@ static uint16_t mf_classic_get_total_block_num(MfClassicType type) {
}
}
bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num) {
furi_assert(data);
return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1);
}
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) {
furi_assert(data);
if(mf_classic_is_sector_trailer(block_num)) {
memcpy(&data->block[block_num].value[6], &block_data->value[6], 4);
} else {
memcpy(data->block[block_num].value, block_data->value, MF_CLASSIC_BLOCK_SIZE);
}
FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
}
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
furi_assert(data);
bool key_found = false;
if(key_type == MfClassicKeyA) {
key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1);
} else if(key_type == MfClassicKeyB) {
key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1);
}
return key_found;
}
void mf_classic_set_key_found(
MfClassicData* data,
uint8_t sector_num,
MfClassicKey key_type,
uint64_t key) {
furi_assert(data);
uint8_t key_arr[6] = {};
MfClassicSectorTrailer* sec_trailer =
mf_classic_get_sector_trailer_by_sector(data, sector_num);
nfc_util_num2bytes(key, 6, key_arr);
if(key_type == MfClassicKeyA) {
memcpy(sec_trailer->key_a, key_arr, sizeof(sec_trailer->key_a));
FURI_BIT_SET(data->key_a_mask, sector_num);
} else if(key_type == MfClassicKeyB) {
memcpy(sec_trailer->key_b, key_arr, sizeof(sec_trailer->key_b));
FURI_BIT_SET(data->key_b_mask, sector_num);
}
}
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) {
furi_assert(data);
bool sector_read = false;
do {
if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) break;
if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) break;
uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num);
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);
uint8_t block_read = true;
for(size_t i = start_block; i < start_block + total_blocks; i++) {
block_read = mf_classic_is_block_read(data, i);
if(!block_read) break;
}
sector_read = block_read;
} while(false);
return sector_read;
}
void mf_classic_get_read_sectors_and_keys(
MfClassicData* data,
uint8_t* sectors_read,
uint8_t* keys_found) {
furi_assert(data);
*sectors_read = 0;
*keys_found = 0;
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
for(size_t i = 0; i < sectors_total; i++) {
if(mf_classic_is_key_found(data, i, MfClassicKeyA)) {
*keys_found += 1;
}
if(mf_classic_is_key_found(data, i, MfClassicKeyB)) {
*keys_found += 1;
}
uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i);
bool blocks_read = true;
for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) {
blocks_read = mf_classic_is_block_read(data, i);
if(!blocks_read) break;
}
if(blocks_read) {
*sectors_read += 1;
}
}
}
static bool mf_classic_is_allowed_access_sector_trailer(
MfClassicEmulator* emulator,
uint8_t block_num,
@@ -126,7 +249,8 @@ static bool mf_classic_is_allowed_access_data_block(
uint8_t block_num,
MfClassicKey key,
MfClassicAction action) {
uint8_t* sector_trailer = emulator->data.block[mf_classic_get_sector_trailer(block_num)].value;
uint8_t* sector_trailer =
emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
uint8_t sector_block;
if(block_num <= 128) {
@@ -207,15 +331,18 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
}
}
bool mf_classic_get_type(
uint8_t* uid,
uint8_t uid_len,
uint8_t ATQA0,
uint8_t ATQA1,
uint8_t SAK,
MfClassicReader* reader) {
MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) {
UNUSED(ATQA1);
if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) {
return MfClassicType1k;
} else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) {
return MfClassicType4k;
}
return MfClassicType1k;
}
bool mf_classic_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK, MfClassicReader* reader) {
UNUSED(ATQA1);
furi_assert(uid);
furi_assert(reader);
memset(reader, 0, sizeof(MfClassicReader));
@@ -226,14 +353,6 @@ bool mf_classic_get_type(
} else {
return false;
}
uint8_t* cuid_start = uid;
if(uid_len == 7) {
cuid_start = &uid[3];
}
reader->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) |
(cuid_start[3]);
return true;
}
@@ -246,7 +365,7 @@ void mf_classic_reader_add_sector(
furi_assert(sector < MF_CLASSIC_SECTORS_MAX);
furi_assert((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY));
if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX - 1) {
if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX) {
reader->sector_reader[reader->sectors_to_read].key_a = key_a;
reader->sector_reader[reader->sectors_to_read].key_b = key_b;
reader->sector_reader[reader->sectors_to_read].sector_num = sector;
@@ -254,9 +373,8 @@ void mf_classic_reader_add_sector(
}
}
void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, uint8_t sector) {
void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) {
furi_assert(auth_ctx);
auth_ctx->cuid = cuid;
auth_ctx->sector = sector;
auth_ctx->key_a = MF_CLASSIC_NO_KEY;
auth_ctx->key_b = MF_CLASSIC_NO_KEY;
@@ -264,17 +382,18 @@ void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid,
static bool mf_classic_auth(
FuriHalNfcTxRxContext* tx_rx,
uint32_t cuid,
uint32_t block,
uint64_t key,
MfClassicKey key_type,
Crypto1* crypto) {
bool auth_success = false;
uint32_t cuid = 0;
memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data));
memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity));
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
do {
if(!furi_hal_nfc_activate_nfca(200, &cuid)) break;
if(key_type == MfClassicKeyA) {
tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_A_CMD;
} else {
@@ -315,6 +434,19 @@ static bool mf_classic_auth(
return auth_success;
}
bool mf_classic_authenticate(
FuriHalNfcTxRxContext* tx_rx,
uint8_t block_num,
uint64_t key,
MfClassicKey key_type) {
furi_assert(tx_rx);
Crypto1 crypto = {};
bool key_found = mf_classic_auth(tx_rx, block_num, key, key_type, &crypto);
furi_hal_nfc_sleep();
return key_found;
}
bool mf_classic_auth_attempt(
FuriHalNfcTxRxContext* tx_rx,
MfClassicAuthContext* auth_ctx,
@@ -330,7 +462,6 @@ bool mf_classic_auth_attempt(
// Try AUTH with key A
if(mf_classic_auth(
tx_rx,
auth_ctx->cuid,
mf_classic_get_first_block_num_of_sector(auth_ctx->sector),
key,
MfClassicKeyA,
@@ -342,14 +473,12 @@ bool mf_classic_auth_attempt(
if(need_halt) {
furi_hal_nfc_sleep();
furi_hal_nfc_activate_nfca(300, &auth_ctx->cuid);
}
if(auth_ctx->key_b == MF_CLASSIC_NO_KEY) {
// Try AUTH with key B
if(mf_classic_auth(
tx_rx,
auth_ctx->cuid,
mf_classic_get_first_block_num_of_sector(auth_ctx->sector),
key,
MfClassicKeyB,
@@ -410,7 +539,60 @@ bool mf_classic_read_block(
return read_block_success;
}
bool mf_classic_read_sector(
void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num) {
furi_assert(tx_rx);
furi_assert(data);
furi_hal_nfc_sleep();
bool key_a_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyA);
bool key_b_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyB);
uint8_t start_block = mf_classic_get_first_block_num_of_sector(sec_num);
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num);
MfClassicBlock block_tmp = {};
uint64_t key = 0;
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num);
Crypto1 crypto = {};
uint8_t blocks_read = 0;
do {
if(!key_a_found) break;
FURI_LOG_D(TAG, "Try to read blocks with key A");
key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a));
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto)) break;
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
}
} else {
blocks_read++;
}
}
FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks);
} while(false);
do {
if(blocks_read == total_blocks) break;
if(!key_b_found) break;
FURI_LOG_D(TAG, "Try to read blocks with key B");
key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b));
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto)) break;
for(size_t i = start_block; i < start_block + total_blocks; i++) {
if(!mf_classic_is_block_read(data, i)) {
if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) {
mf_classic_set_block_read(data, i, &block_tmp);
blocks_read++;
}
} else {
blocks_read++;
}
}
FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks);
} while(false);
}
static bool mf_classic_read_sector_with_reader(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
MfClassicSectorReader* sector_reader,
@@ -419,7 +601,6 @@ bool mf_classic_read_sector(
furi_assert(sector_reader);
furi_assert(sector);
uint32_t cuid = 0;
uint64_t key;
MfClassicKey key_type;
uint8_t first_block;
@@ -428,7 +609,6 @@ bool mf_classic_read_sector(
furi_hal_nfc_sleep();
do {
// Activate card
if(!furi_hal_nfc_activate_nfca(200, &cuid)) break;
first_block = mf_classic_get_first_block_num_of_sector(sector_reader->sector_num);
if(sector_reader->key_a != MF_CLASSIC_NO_KEY) {
key = sector_reader->key_a;
@@ -441,7 +621,7 @@ bool mf_classic_read_sector(
}
// Auth to first block in sector
if(!mf_classic_auth(tx_rx, cuid, first_block, key, key_type, crypto)) break;
if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break;
sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num);
// Read blocks
@@ -478,18 +658,26 @@ uint8_t mf_classic_read_card(
data->key_b_mask = 0;
MfClassicSector temp_sector = {};
for(uint8_t i = 0; i < reader->sectors_to_read; i++) {
if(mf_classic_read_sector(
if(mf_classic_read_sector_with_reader(
tx_rx, &reader->crypto, &reader->sector_reader[i], &temp_sector)) {
uint8_t first_block =
mf_classic_get_first_block_num_of_sector(reader->sector_reader[i].sector_num);
for(uint8_t j = 0; j < temp_sector.total_blocks; j++) {
data->block[first_block + j] = temp_sector.block[j];
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
}
if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) {
data->key_a_mask |= 1 << reader->sector_reader[i].sector_num;
mf_classic_set_key_found(
data,
reader->sector_reader[i].sector_num,
MfClassicKeyA,
reader->sector_reader[i].key_a);
}
if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) {
data->key_b_mask |= 1 << reader->sector_reader[i].sector_num;
mf_classic_set_key_found(
data,
reader->sector_reader[i].sector_num,
MfClassicKeyB,
reader->sector_reader[i].key_b);
}
sectors_read++;
}
@@ -498,6 +686,46 @@ uint8_t mf_classic_read_card(
return sectors_read;
}
uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data) {
furi_assert(tx_rx);
furi_assert(data);
uint8_t sectors_read = 0;
Crypto1 crypto = {};
uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type);
uint64_t key_a = 0;
uint64_t key_b = 0;
MfClassicSectorReader sec_reader = {};
MfClassicSector temp_sector = {};
for(size_t i = 0; i < total_sectors; i++) {
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i);
// Load key A
if(mf_classic_is_key_found(data, i, MfClassicKeyA)) {
sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6);
} else {
sec_reader.key_a = MF_CLASSIC_NO_KEY;
}
// Load key B
if(mf_classic_is_key_found(data, i, MfClassicKeyB)) {
sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6);
} else {
sec_reader.key_b = MF_CLASSIC_NO_KEY;
}
if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) {
sec_reader.sector_num = i;
if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) {
uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
for(uint8_t j = 0; j < temp_sector.total_blocks; j++) {
mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]);
}
sectors_read++;
}
}
}
return sectors_read;
}
void mf_crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
@@ -573,7 +801,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
} else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) {
uint8_t block = plain_data[1];
uint64_t key = 0;
uint8_t sector_trailer_block = mf_classic_get_sector_trailer(block);
uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block);
MfClassicSectorTrailer* sector_trailer =
(MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value;
if(plain_data[0] == 0x60) {
@@ -635,21 +863,6 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
nr,
ar);
// Check if we store valid key
if(access_key == MfClassicKeyA) {
if(FURI_BIT(emulator->data.key_a_mask, mf_classic_get_sector_by_block(block)) ==
0) {
FURI_LOG_D(TAG, "Unsupported sector key A for block %d", sector_trailer_block);
break;
}
} else if(access_key == MfClassicKeyB) {
if(FURI_BIT(emulator->data.key_b_mask, mf_classic_get_sector_by_block(block)) ==
0) {
FURI_LOG_D(TAG, "Unsupported sector key B for block %d", sector_trailer_block);
break;
}
}
crypto1_word(&emulator->crypto, nr, 1);
uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0);
if(cardRr != prng_successor(nonce, 64)) {

View File

@@ -14,6 +14,8 @@
#define MF_CLASSIC_NO_KEY (0xFFFFFFFFFFFFFFFF)
#define MF_CLASSIC_MAX_DATA_SIZE (16)
#define MF_CLASSIC_KEY_SIZE (6)
#define MF_CLASSIC_ACCESS_BYTES_SIZE (4)
typedef enum {
MfClassicType1k,
@@ -30,9 +32,9 @@ typedef struct {
} MfClassicBlock;
typedef struct {
uint8_t key_a[6];
uint8_t access_bits[4];
uint8_t key_b[6];
uint8_t key_a[MF_CLASSIC_KEY_SIZE];
uint8_t access_bits[MF_CLASSIC_ACCESS_BYTES_SIZE];
uint8_t key_b[MF_CLASSIC_KEY_SIZE];
} MfClassicSectorTrailer;
typedef struct {
@@ -42,13 +44,13 @@ typedef struct {
typedef struct {
MfClassicType type;
uint32_t block_read_mask[MF_CLASSIC_TOTAL_BLOCKS_MAX / 32];
uint64_t key_a_mask;
uint64_t key_b_mask;
MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX];
} MfClassicData;
typedef struct {
uint32_t cuid;
uint8_t sector;
uint64_t key_a;
uint64_t key_b;
@@ -62,9 +64,8 @@ typedef struct {
typedef struct {
MfClassicType type;
uint32_t cuid;
uint8_t sectors_to_read;
Crypto1 crypto;
uint8_t sectors_to_read;
MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX];
} MfClassicReader;
@@ -75,19 +76,51 @@ typedef struct {
bool data_changed;
} MfClassicEmulator;
const char* mf_classic_get_type_str(MfClassicType type);
bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK);
bool mf_classic_get_type(
uint8_t* uid,
uint8_t uid_len,
uint8_t ATQA0,
uint8_t ATQA1,
uint8_t SAK,
MfClassicReader* reader);
MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK);
uint8_t mf_classic_get_total_sectors_num(MfClassicReader* reader);
bool mf_classic_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK, MfClassicReader* reader);
void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint32_t cuid, uint8_t sector);
uint8_t mf_classic_get_total_sectors_num(MfClassicType type);
uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector);
bool mf_classic_is_sector_trailer(uint8_t block);
uint8_t mf_classic_get_sector_by_block(uint8_t block);
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
void mf_classic_set_key_found(
MfClassicData* data,
uint8_t sector_num,
MfClassicKey key_type,
uint64_t key);
bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
void mf_classic_get_read_sectors_and_keys(
MfClassicData* data,
uint8_t* sectors_read,
uint8_t* keys_found);
MfClassicSectorTrailer*
mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector);
void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector);
bool mf_classic_authenticate(
FuriHalNfcTxRxContext* tx_rx,
uint8_t block_num,
uint64_t key,
MfClassicKey key_type);
bool mf_classic_auth_attempt(
FuriHalNfcTxRxContext* tx_rx,
@@ -100,15 +133,13 @@ void mf_classic_reader_add_sector(
uint64_t key_a,
uint64_t key_b);
bool mf_classic_read_sector(
FuriHalNfcTxRxContext* tx_rx,
Crypto1* crypto,
MfClassicSectorReader* sector_reader,
MfClassicSector* sector);
void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num);
uint8_t mf_classic_read_card(
FuriHalNfcTxRxContext* tx_rx,
MfClassicReader* reader,
MfClassicData* data);
uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data);
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx);

View File

@@ -2,6 +2,8 @@
#include <furi.h>
#include <furi_hal_nfc.h>
#define TAG "MifareDESFire"
void mf_df_clear(MifareDesfireData* data) {
free(data->free_memory);
if(data->master_key_settings) {
@@ -449,3 +451,173 @@ bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFil
memcpy(out->contents, buf, len);
return true;
}
bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data) {
furi_assert(tx_rx);
furi_assert(data);
bool card_read = false;
do {
// Get version
tx_rx->tx_bits = 8 * mf_df_prepare_get_version(tx_rx->tx_data);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting version");
break;
}
if(!mf_df_parse_get_version_response(tx_rx->rx_data, tx_rx->rx_bits / 8, &data->version)) {
FURI_LOG_W(TAG, "Bad DESFire GET_VERSION responce");
}
// Get free memory
tx_rx->tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx->tx_data);
if(furi_hal_nfc_tx_rx_full(tx_rx)) {
data->free_memory = malloc(sizeof(MifareDesfireFreeMemory));
if(!mf_df_parse_get_free_memory_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, data->free_memory)) {
FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)");
free(data->free_memory);
data->free_memory = NULL;
}
}
// Get key settings
tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_D(TAG, "Bad exchange getting key settings");
} else {
data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings));
if(!mf_df_parse_get_key_settings_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, data->master_key_settings)) {
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
free(data->master_key_settings);
data->master_key_settings = NULL;
} else {
MifareDesfireKeyVersion** key_version_head =
&data->master_key_settings->key_version_head;
for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) {
tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting key version");
continue;
}
MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion));
memset(key_version, 0, sizeof(MifareDesfireKeyVersion));
key_version->id = key_id;
if(!mf_df_parse_get_key_version_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) {
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response");
free(key_version);
continue;
}
*key_version_head = key_version;
key_version_head = &key_version->next;
}
}
}
// Get application IDs
tx_rx->tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx->tx_data);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting application IDs");
break;
} else {
if(!mf_df_parse_get_application_ids_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, &data->app_head)) {
FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response");
break;
}
}
for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
tx_rx->tx_bits = 8 * mf_df_prepare_select_application(tx_rx->tx_data, app->id);
if(!furi_hal_nfc_tx_rx_full(tx_rx) ||
!mf_df_parse_select_application_response(tx_rx->rx_data, tx_rx->rx_bits / 8)) {
FURI_LOG_W(TAG, "Bad exchange selecting application");
continue;
}
tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting key settings");
} else {
app->key_settings = malloc(sizeof(MifareDesfireKeySettings));
memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings));
if(!mf_df_parse_get_key_settings_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, app->key_settings)) {
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response");
free(app->key_settings);
app->key_settings = NULL;
continue;
}
MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head;
for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) {
tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting key version");
continue;
}
MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion));
memset(key_version, 0, sizeof(MifareDesfireKeyVersion));
key_version->id = key_id;
if(!mf_df_parse_get_key_version_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) {
FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response");
free(key_version);
continue;
}
*key_version_head = key_version;
key_version_head = &key_version->next;
}
}
tx_rx->tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx->tx_data);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting file IDs");
} else {
if(!mf_df_parse_get_file_ids_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, &app->file_head)) {
FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response");
}
}
for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
tx_rx->tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx->tx_data, file->id);
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange getting file settings");
continue;
}
if(!mf_df_parse_get_file_settings_response(
tx_rx->rx_data, tx_rx->rx_bits / 8, file)) {
FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response");
continue;
}
switch(file->type) {
case MifareDesfireFileTypeStandard:
case MifareDesfireFileTypeBackup:
tx_rx->tx_bits = 8 * mf_df_prepare_read_data(tx_rx->tx_data, file->id, 0, 0);
break;
case MifareDesfireFileTypeValue:
tx_rx->tx_bits = 8 * mf_df_prepare_get_value(tx_rx->tx_data, file->id);
break;
case MifareDesfireFileTypeLinearRecord:
case MifareDesfireFileTypeCyclicRecord:
tx_rx->tx_bits =
8 * mf_df_prepare_read_records(tx_rx->tx_data, file->id, 0, 0);
break;
}
if(!furi_hal_nfc_tx_rx_full(tx_rx)) {
FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id);
continue;
}
if(!mf_df_parse_read_data_response(tx_rx->rx_data, tx_rx->rx_bits / 8, file)) {
FURI_LOG_W(TAG, "Bad response reading file %d", file->id);
continue;
}
}
}
card_read = true;
} while(false);
return card_read;
}

View File

@@ -4,6 +4,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <furi_hal_nfc.h>
#define MF_DF_GET_VERSION (0x60)
#define MF_DF_GET_FREE_MEMORY (0x6E)
#define MF_DF_GET_KEY_SETTINGS (0x45)
@@ -163,3 +165,5 @@ uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset
uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id);
uint16_t mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len);
bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out);
bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data);