[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

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