mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-28 01:58:11 -07:00
NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring. Starring: - @gornekich - NFC refactoring project lead, architect, senior developer - @gsurkov - architect, senior developer - @RebornedBrain - senior developer Supporting roles: - @skotopes, @DrZlo13, @hedger - general architecture advisors, code review - @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance Special thanks: @bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
#include "felica_crc.h"
|
||||
|
||||
#include <furi/furi.h>
|
||||
|
||||
#define FELICA_CRC_POLY (0x1021U) // Polynomial: x^16 + x^12 + x^5 + 1
|
||||
#define FELICA_CRC_INIT (0x0000U)
|
||||
|
||||
uint16_t felica_crc_calculate(const uint8_t* data, size_t length) {
|
||||
uint16_t crc = FELICA_CRC_INIT;
|
||||
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
crc ^= ((uint16_t)data[i] << 8);
|
||||
for(size_t j = 0; j < 8; j++) {
|
||||
if(crc & 0x8000) {
|
||||
crc <<= 1;
|
||||
crc ^= FELICA_CRC_POLY;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (crc << 8) | (crc >> 8);
|
||||
}
|
||||
|
||||
void felica_crc_append(BitBuffer* buf) {
|
||||
const uint8_t* data = bit_buffer_get_data(buf);
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
|
||||
const uint16_t crc = felica_crc_calculate(data, data_size);
|
||||
bit_buffer_append_bytes(buf, (const uint8_t*)&crc, FELICA_CRC_SIZE);
|
||||
}
|
||||
|
||||
bool felica_crc_check(const BitBuffer* buf) {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
if(data_size <= FELICA_CRC_SIZE) return false;
|
||||
|
||||
uint16_t crc_received;
|
||||
bit_buffer_write_bytes_mid(buf, &crc_received, data_size - FELICA_CRC_SIZE, FELICA_CRC_SIZE);
|
||||
|
||||
const uint8_t* data = bit_buffer_get_data(buf);
|
||||
const uint16_t crc_calc = felica_crc_calculate(data, data_size - FELICA_CRC_SIZE);
|
||||
|
||||
return (crc_calc == crc_received);
|
||||
}
|
||||
|
||||
void felica_crc_trim(BitBuffer* buf) {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
furi_assert(data_size > FELICA_CRC_SIZE);
|
||||
|
||||
bit_buffer_set_size_bytes(buf, data_size - FELICA_CRC_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "bit_buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FELICA_CRC_SIZE sizeof(uint16_t)
|
||||
|
||||
void felica_crc_append(BitBuffer* buf);
|
||||
|
||||
bool felica_crc_check(const BitBuffer* buf);
|
||||
|
||||
void felica_crc_trim(BitBuffer* buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "iso13239_crc.h"
|
||||
|
||||
#include <core/check.h>
|
||||
|
||||
#define ISO13239_CRC_INIT_DEFAULT (0xFFFFU)
|
||||
#define ISO13239_CRC_INIT_PICOPASS (0xE012U)
|
||||
#define ISO13239_CRC_POLY (0x8408U)
|
||||
|
||||
static uint16_t
|
||||
iso13239_crc_calculate(Iso13239CrcType type, const uint8_t* data, size_t data_size) {
|
||||
uint16_t crc;
|
||||
|
||||
if(type == Iso13239CrcTypeDefault) {
|
||||
crc = ISO13239_CRC_INIT_DEFAULT;
|
||||
} else if(type == Iso13239CrcTypePicopass) {
|
||||
crc = ISO13239_CRC_INIT_PICOPASS;
|
||||
} else {
|
||||
furi_crash("Wrong ISO13239 CRC type");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
crc ^= (uint16_t)data[i];
|
||||
for(size_t j = 0; j < 8; ++j) {
|
||||
if(crc & 1U) {
|
||||
crc = (crc >> 1) ^ ISO13239_CRC_POLY;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type == Iso13239CrcTypePicopass ? crc : ~crc;
|
||||
}
|
||||
|
||||
void iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf) {
|
||||
const uint8_t* data = bit_buffer_get_data(buf);
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
|
||||
const uint16_t crc = iso13239_crc_calculate(type, data, data_size);
|
||||
bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO13239_CRC_SIZE);
|
||||
}
|
||||
|
||||
bool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf) {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
if(data_size <= ISO13239_CRC_SIZE) return false;
|
||||
|
||||
uint16_t crc_received;
|
||||
bit_buffer_write_bytes_mid(
|
||||
buf, &crc_received, data_size - ISO13239_CRC_SIZE, ISO13239_CRC_SIZE);
|
||||
|
||||
const uint8_t* data = bit_buffer_get_data(buf);
|
||||
const uint16_t crc_calc = iso13239_crc_calculate(type, data, data_size - ISO13239_CRC_SIZE);
|
||||
|
||||
return (crc_calc == crc_received);
|
||||
}
|
||||
|
||||
void iso13239_crc_trim(BitBuffer* buf) {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
furi_assert(data_size > ISO13239_CRC_SIZE);
|
||||
|
||||
bit_buffer_set_size_bytes(buf, data_size - ISO13239_CRC_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ISO13239_CRC_SIZE sizeof(uint16_t)
|
||||
|
||||
typedef enum {
|
||||
Iso13239CrcTypeDefault,
|
||||
Iso13239CrcTypePicopass,
|
||||
} Iso13239CrcType;
|
||||
|
||||
void iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf);
|
||||
|
||||
bool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf);
|
||||
|
||||
void iso13239_crc_trim(BitBuffer* buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "iso14443_4_layer.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define ISO14443_4_BLOCK_PCB (1U << 1)
|
||||
#define ISO14443_4_BLOCK_PCB_I (0U)
|
||||
#define ISO14443_4_BLOCK_PCB_R (5U << 5)
|
||||
#define ISO14443_4_BLOCK_PCB_S (3U << 6)
|
||||
|
||||
struct Iso14443_4Layer {
|
||||
uint8_t pcb;
|
||||
uint8_t pcb_prev;
|
||||
};
|
||||
|
||||
static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance) {
|
||||
instance->pcb_prev = instance->pcb;
|
||||
instance->pcb ^= (uint8_t)0x01;
|
||||
}
|
||||
|
||||
Iso14443_4Layer* iso14443_4_layer_alloc() {
|
||||
Iso14443_4Layer* instance = malloc(sizeof(Iso14443_4Layer));
|
||||
|
||||
iso14443_4_layer_reset(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void iso14443_4_layer_free(Iso14443_4Layer* instance) {
|
||||
furi_assert(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void iso14443_4_layer_reset(Iso14443_4Layer* instance) {
|
||||
furi_assert(instance);
|
||||
instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB;
|
||||
}
|
||||
|
||||
void iso14443_4_layer_encode_block(
|
||||
Iso14443_4Layer* instance,
|
||||
const BitBuffer* input_data,
|
||||
BitBuffer* block_data) {
|
||||
furi_assert(instance);
|
||||
|
||||
bit_buffer_append_byte(block_data, instance->pcb);
|
||||
bit_buffer_append(block_data, input_data);
|
||||
|
||||
iso14443_4_layer_update_pcb(instance);
|
||||
}
|
||||
|
||||
bool iso14443_4_layer_decode_block(
|
||||
Iso14443_4Layer* instance,
|
||||
BitBuffer* output_data,
|
||||
const BitBuffer* block_data) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
do {
|
||||
if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break;
|
||||
bit_buffer_copy_right(output_data, block_data, 1);
|
||||
ret = true;
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Iso14443_4Layer Iso14443_4Layer;
|
||||
|
||||
Iso14443_4Layer* iso14443_4_layer_alloc();
|
||||
|
||||
void iso14443_4_layer_free(Iso14443_4Layer* instance);
|
||||
|
||||
void iso14443_4_layer_reset(Iso14443_4Layer* instance);
|
||||
|
||||
void iso14443_4_layer_encode_block(
|
||||
Iso14443_4Layer* instance,
|
||||
const BitBuffer* input_data,
|
||||
BitBuffer* block_data);
|
||||
|
||||
bool iso14443_4_layer_decode_block(
|
||||
Iso14443_4Layer* instance,
|
||||
BitBuffer* output_data,
|
||||
const BitBuffer* block_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "iso14443_crc.h"
|
||||
|
||||
#include <core/check.h>
|
||||
|
||||
#define ISO14443_3A_CRC_INIT (0x6363U)
|
||||
#define ISO14443_3B_CRC_INIT (0xFFFFU)
|
||||
|
||||
static uint16_t
|
||||
iso14443_crc_calculate(Iso14443CrcType type, const uint8_t* data, size_t data_size) {
|
||||
uint16_t crc;
|
||||
|
||||
if(type == Iso14443CrcTypeA) {
|
||||
crc = ISO14443_3A_CRC_INIT;
|
||||
} else if(type == Iso14443CrcTypeB) {
|
||||
crc = ISO14443_3B_CRC_INIT;
|
||||
} else {
|
||||
furi_crash("Wrong ISO14443 CRC type");
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < data_size; i++) {
|
||||
uint8_t byte = data[i];
|
||||
byte ^= (uint8_t)(crc & 0xff);
|
||||
byte ^= byte << 4;
|
||||
crc = (crc >> 8) ^ (((uint16_t)byte) << 8) ^ (((uint16_t)byte) << 3) ^ (byte >> 4);
|
||||
}
|
||||
|
||||
return type == Iso14443CrcTypeA ? crc : ~crc;
|
||||
}
|
||||
|
||||
void iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf) {
|
||||
const uint8_t* data = bit_buffer_get_data(buf);
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
|
||||
const uint16_t crc = iso14443_crc_calculate(type, data, data_size);
|
||||
bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO14443_CRC_SIZE);
|
||||
}
|
||||
|
||||
bool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf) {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
if(data_size <= ISO14443_CRC_SIZE) return false;
|
||||
|
||||
uint16_t crc_received;
|
||||
bit_buffer_write_bytes_mid(
|
||||
buf, &crc_received, data_size - ISO14443_CRC_SIZE, ISO14443_CRC_SIZE);
|
||||
|
||||
const uint8_t* data = bit_buffer_get_data(buf);
|
||||
const uint16_t crc_calc = iso14443_crc_calculate(type, data, data_size - ISO14443_CRC_SIZE);
|
||||
|
||||
return (crc_calc == crc_received);
|
||||
}
|
||||
|
||||
void iso14443_crc_trim(BitBuffer* buf) {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
furi_assert(data_size > ISO14443_CRC_SIZE);
|
||||
|
||||
bit_buffer_set_size_bytes(buf, data_size - ISO14443_CRC_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ISO14443_CRC_SIZE sizeof(uint16_t)
|
||||
|
||||
typedef enum {
|
||||
Iso14443CrcTypeA,
|
||||
Iso14443CrcTypeB,
|
||||
} Iso14443CrcType;
|
||||
|
||||
void iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf);
|
||||
|
||||
bool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf);
|
||||
|
||||
void iso14443_crc_trim(BitBuffer* buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,346 +0,0 @@
|
||||
#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 MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_classic_dict.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 == MfClassicDictTypeSystem) {
|
||||
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;
|
||||
} else if(dict_type == MfClassicDictTypeUnitTest) {
|
||||
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_UNIT_TEST_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 == MfClassicDictTypeSystem) {
|
||||
if(!buffered_file_stream_open(
|
||||
dict->stream,
|
||||
MF_CLASSIC_DICT_FLIPPER_PATH,
|
||||
FSAM_READ_WRITE,
|
||||
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;
|
||||
}
|
||||
} else if(dict_type == MfClassicDictTypeUnitTest) {
|
||||
if(!buffered_file_stream_open(
|
||||
dict->stream,
|
||||
MF_CLASSIC_DICT_UNIT_TEST_PATH,
|
||||
FSAM_READ_WRITE,
|
||||
FSOM_OPEN_ALWAYS)) {
|
||||
buffered_file_stream_close(dict->stream);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for new line ending
|
||||
if(!stream_eof(dict->stream)) {
|
||||
if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break;
|
||||
uint8_t last_char = 0;
|
||||
if(stream_read(dict->stream, &last_char, 1) != 1) break;
|
||||
if(last_char != '\n') {
|
||||
FURI_LOG_D(TAG, "Adding new line ending");
|
||||
if(stream_write_char(dict->stream, '\n') != 1) break;
|
||||
}
|
||||
if(!stream_rewind(dict->stream)) break;
|
||||
}
|
||||
|
||||
// Read total amount of keys
|
||||
FuriString* next_line;
|
||||
next_line = furi_string_alloc();
|
||||
while(true) {
|
||||
if(!stream_read_line(dict->stream, next_line)) {
|
||||
FURI_LOG_T(TAG, "No keys left in dict");
|
||||
break;
|
||||
}
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"Read line: %s, len: %zu",
|
||||
furi_string_get_cstr(next_line),
|
||||
furi_string_size(next_line));
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
|
||||
dict->total_keys++;
|
||||
}
|
||||
furi_string_free(next_line);
|
||||
stream_rewind(dict->stream);
|
||||
|
||||
dict_loaded = true;
|
||||
FURI_LOG_I(TAG, "Loaded dictionary with %lu 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);
|
||||
}
|
||||
|
||||
static void mf_classic_dict_int_to_str(uint8_t* key_int, FuriString* key_str) {
|
||||
furi_string_reset(key_str);
|
||||
for(size_t i = 0; i < 6; i++) {
|
||||
furi_string_cat_printf(key_str, "%02X", key_int[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void mf_classic_dict_str_to_int(FuriString* key_str, uint64_t* key_int) {
|
||||
uint8_t key_byte_tmp;
|
||||
|
||||
*key_int = 0ULL;
|
||||
for(uint8_t i = 0; i < 12; i += 2) {
|
||||
args_char_to_hex(
|
||||
furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp);
|
||||
*key_int |= (uint64_t)key_byte_tmp << (8 * (5 - i / 2));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) {
|
||||
furi_assert(dict);
|
||||
|
||||
return dict->total_keys;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_rewind(MfClassicDict* dict) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
return stream_rewind(dict->stream);
|
||||
}
|
||||
|
||||
bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, FuriString* key) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
bool key_read = false;
|
||||
furi_string_reset(key);
|
||||
while(!key_read) {
|
||||
if(!stream_read_line(dict->stream, key)) break;
|
||||
if(furi_string_get_char(key, 0) == '#') continue;
|
||||
if(furi_string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue;
|
||||
furi_string_left(key, 12);
|
||||
key_read = true;
|
||||
}
|
||||
|
||||
return key_read;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* temp_key;
|
||||
temp_key = furi_string_alloc();
|
||||
bool key_read = mf_classic_dict_get_next_key_str(dict, temp_key);
|
||||
if(key_read) {
|
||||
mf_classic_dict_str_to_int(temp_key, key);
|
||||
}
|
||||
furi_string_free(temp_key);
|
||||
return key_read;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, FuriString* key) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* next_line;
|
||||
next_line = furi_string_alloc();
|
||||
|
||||
bool key_found = false;
|
||||
stream_rewind(dict->stream);
|
||||
while(!key_found) { //-V654
|
||||
if(!stream_read_line(dict->stream, next_line)) break;
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
|
||||
furi_string_left(next_line, 12);
|
||||
if(!furi_string_equal(key, next_line)) continue;
|
||||
key_found = true;
|
||||
}
|
||||
|
||||
furi_string_free(next_line);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) {
|
||||
FuriString* temp_key;
|
||||
|
||||
temp_key = furi_string_alloc();
|
||||
mf_classic_dict_int_to_str(key, temp_key);
|
||||
bool key_found = mf_classic_dict_is_key_present_str(dict, temp_key);
|
||||
furi_string_free(temp_key);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_add_key_str(MfClassicDict* dict, FuriString* key) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
furi_string_cat_printf(key, "\n");
|
||||
|
||||
bool key_added = false;
|
||||
do {
|
||||
if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
|
||||
if(!stream_insert_string(dict->stream, key)) break;
|
||||
dict->total_keys++;
|
||||
key_added = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_left(key, 12);
|
||||
return key_added;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* temp_key;
|
||||
temp_key = furi_string_alloc();
|
||||
mf_classic_dict_int_to_str(key, temp_key);
|
||||
bool key_added = mf_classic_dict_add_key_str(dict, temp_key);
|
||||
|
||||
furi_string_free(temp_key);
|
||||
return key_added;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, FuriString* key, uint32_t target) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* next_line;
|
||||
uint32_t index = 0;
|
||||
next_line = furi_string_alloc();
|
||||
furi_string_reset(key);
|
||||
|
||||
bool key_found = false;
|
||||
while(!key_found) {
|
||||
if(!stream_read_line(dict->stream, next_line)) break;
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
|
||||
if(index++ != target) continue;
|
||||
furi_string_set_n(key, next_line, 0, 12);
|
||||
key_found = true;
|
||||
}
|
||||
|
||||
furi_string_free(next_line);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* temp_key;
|
||||
temp_key = furi_string_alloc();
|
||||
bool key_found = mf_classic_dict_get_key_at_index_str(dict, temp_key, target);
|
||||
if(key_found) {
|
||||
mf_classic_dict_str_to_int(temp_key, key);
|
||||
}
|
||||
furi_string_free(temp_key);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_find_index_str(MfClassicDict* dict, FuriString* key, uint32_t* target) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* next_line;
|
||||
next_line = furi_string_alloc();
|
||||
|
||||
bool key_found = false;
|
||||
uint32_t index = 0;
|
||||
stream_rewind(dict->stream);
|
||||
while(!key_found) { //-V654
|
||||
if(!stream_read_line(dict->stream, next_line)) break;
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
|
||||
furi_string_left(next_line, 12);
|
||||
if(!furi_string_equal(key, next_line)) continue;
|
||||
key_found = true;
|
||||
*target = index;
|
||||
}
|
||||
|
||||
furi_string_free(next_line);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* temp_key;
|
||||
temp_key = furi_string_alloc();
|
||||
mf_classic_dict_int_to_str(key, temp_key);
|
||||
bool key_found = mf_classic_dict_find_index_str(dict, temp_key, target);
|
||||
|
||||
furi_string_free(temp_key);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target) {
|
||||
furi_assert(dict);
|
||||
furi_assert(dict->stream);
|
||||
|
||||
FuriString* next_line;
|
||||
next_line = furi_string_alloc();
|
||||
uint32_t index = 0;
|
||||
|
||||
bool key_removed = false;
|
||||
while(!key_removed) {
|
||||
if(!stream_read_line(dict->stream, next_line)) break;
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
|
||||
if(index++ != target) continue;
|
||||
stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent);
|
||||
if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break;
|
||||
dict->total_keys--;
|
||||
key_removed = true;
|
||||
}
|
||||
|
||||
furi_string_free(next_line);
|
||||
return key_removed;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#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>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
MfClassicDictTypeUser,
|
||||
MfClassicDictTypeSystem,
|
||||
MfClassicDictTypeUnitTest,
|
||||
} MfClassicDictType;
|
||||
|
||||
typedef struct MfClassicDict MfClassicDict;
|
||||
|
||||
bool mf_classic_dict_check_presence(MfClassicDictType dict_type);
|
||||
|
||||
/** Allocate MfClassicDict instance
|
||||
*
|
||||
* @param[in] dict_type The dictionary type
|
||||
*
|
||||
* @return MfClassicDict instance
|
||||
*/
|
||||
MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type);
|
||||
|
||||
/** Free MfClassicDict instance
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
*/
|
||||
void mf_classic_dict_free(MfClassicDict* dict);
|
||||
|
||||
/** Get total keys count
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
*
|
||||
* @return total keys count
|
||||
*/
|
||||
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict);
|
||||
|
||||
/** Rewind to the beginning
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool mf_classic_dict_rewind(MfClassicDict* dict);
|
||||
|
||||
bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key);
|
||||
|
||||
bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, FuriString* key);
|
||||
|
||||
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key);
|
||||
|
||||
bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, FuriString* key);
|
||||
|
||||
/** Get key at target offset as uint64_t
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
* @param[out] key Pointer to the uint64_t key
|
||||
* @param[in] target Target offset from current position
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target);
|
||||
|
||||
/** Get key at target offset as string_t
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
* @param[out] key Found key destination buffer
|
||||
* @param[in] target Target offset from current position
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, FuriString* key, uint32_t target);
|
||||
|
||||
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key);
|
||||
|
||||
/** Add string representation of the key
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
* @param[in] key String representation of the key
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool mf_classic_dict_add_key_str(MfClassicDict* dict, FuriString* key);
|
||||
|
||||
bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target);
|
||||
|
||||
bool mf_classic_dict_find_index_str(MfClassicDict* dict, FuriString* key, uint32_t* target);
|
||||
|
||||
/** Delete key at target offset
|
||||
*
|
||||
* @param dict MfClassicDict instance
|
||||
* @param[in] target Target offset from current position
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
#include "mfkey32.h"
|
||||
|
||||
#include <furi/furi.h>
|
||||
#include <storage/storage.h>
|
||||
#include <stream/stream.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <lib/nfc/protocols/nfc_util.h>
|
||||
|
||||
#define TAG "Mfkey32"
|
||||
|
||||
#define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log")
|
||||
|
||||
typedef enum {
|
||||
Mfkey32StateIdle,
|
||||
Mfkey32StateAuthReceived,
|
||||
Mfkey32StateAuthNtSent,
|
||||
Mfkey32StateAuthArNrReceived,
|
||||
} Mfkey32State;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cuid;
|
||||
uint8_t sector;
|
||||
MfClassicKey key;
|
||||
uint32_t nt0;
|
||||
uint32_t nr0;
|
||||
uint32_t ar0;
|
||||
uint32_t nt1;
|
||||
uint32_t nr1;
|
||||
uint32_t ar1;
|
||||
} Mfkey32Params;
|
||||
|
||||
ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST);
|
||||
|
||||
typedef struct {
|
||||
uint8_t sector;
|
||||
MfClassicKey key;
|
||||
uint32_t nt;
|
||||
uint32_t nr;
|
||||
uint32_t ar;
|
||||
} Mfkey32Nonce;
|
||||
|
||||
struct Mfkey32 {
|
||||
Mfkey32State state;
|
||||
Stream* file_stream;
|
||||
Mfkey32Params_t params_arr;
|
||||
Mfkey32Nonce nonce;
|
||||
uint32_t cuid;
|
||||
Mfkey32ParseDataCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
Mfkey32* mfkey32_alloc(uint32_t cuid) {
|
||||
Mfkey32* instance = malloc(sizeof(Mfkey32));
|
||||
instance->cuid = cuid;
|
||||
instance->state = Mfkey32StateIdle;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->file_stream = buffered_file_stream_alloc(storage);
|
||||
if(!buffered_file_stream_open(
|
||||
instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
} else {
|
||||
Mfkey32Params_init(instance->params_arr);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void mfkey32_free(Mfkey32* instance) {
|
||||
furi_assert(instance != NULL);
|
||||
|
||||
Mfkey32Params_clear(instance->params_arr);
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) {
|
||||
FuriString* str = furi_string_alloc_printf(
|
||||
"Sec %d key %c cuid %08lx nt0 %08lx nr0 %08lx ar0 %08lx nt1 %08lx nr1 %08lx ar1 %08lx\n",
|
||||
params->sector,
|
||||
params->key == MfClassicKeyA ? 'A' : 'B',
|
||||
params->cuid,
|
||||
params->nt0,
|
||||
params->nr0,
|
||||
params->ar0,
|
||||
params->nt1,
|
||||
params->nr1,
|
||||
params->ar1);
|
||||
bool write_success = stream_write_string(instance->file_stream, str);
|
||||
furi_string_free(str);
|
||||
return write_success;
|
||||
}
|
||||
|
||||
static void mfkey32_add_params(Mfkey32* instance) {
|
||||
Mfkey32Nonce* nonce = &instance->nonce;
|
||||
bool nonce_added = false;
|
||||
// Search if we partially collected params
|
||||
if(Mfkey32Params_size(instance->params_arr)) {
|
||||
Mfkey32Params_it_t it;
|
||||
for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it);
|
||||
Mfkey32Params_next(it)) {
|
||||
Mfkey32Params* params = Mfkey32Params_ref(it);
|
||||
if((params->sector == nonce->sector) && (params->key == nonce->key)) {
|
||||
params->nt1 = nonce->nt;
|
||||
params->nr1 = nonce->nr;
|
||||
params->ar1 = nonce->ar;
|
||||
nonce_added = true;
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Params for sector %d key %c collected",
|
||||
params->sector,
|
||||
params->key == MfClassicKeyA ? 'A' : 'B');
|
||||
// Write on sd card
|
||||
if(mfkey32_write_params(instance, params)) {
|
||||
Mfkey32Params_remove(instance->params_arr, it);
|
||||
if(instance->callback) {
|
||||
instance->callback(Mfkey32EventParamCollected, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!nonce_added) {
|
||||
Mfkey32Params params = {
|
||||
.sector = nonce->sector,
|
||||
.key = nonce->key,
|
||||
.cuid = instance->cuid,
|
||||
.nt0 = nonce->nt,
|
||||
.nr0 = nonce->nr,
|
||||
.ar0 = nonce->ar,
|
||||
};
|
||||
Mfkey32Params_push_back(instance->params_arr, params);
|
||||
}
|
||||
}
|
||||
|
||||
void mfkey32_process_data(
|
||||
Mfkey32* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
Mfkey32Nonce* nonce = &instance->nonce;
|
||||
uint16_t data_len = len;
|
||||
if((data_len > 3) && !crc_dropped) {
|
||||
data_len -= 2;
|
||||
}
|
||||
|
||||
bool data_processed = false;
|
||||
if(instance->state == Mfkey32StateIdle) {
|
||||
if(reader_to_tag) {
|
||||
if((data[0] == 0x60) || (data[0] == 0x61)) {
|
||||
nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB;
|
||||
nonce->sector = mf_classic_get_sector_by_block(data[1]);
|
||||
instance->state = Mfkey32StateAuthReceived;
|
||||
data_processed = true;
|
||||
}
|
||||
}
|
||||
} else if(instance->state == Mfkey32StateAuthReceived) {
|
||||
if(!reader_to_tag) {
|
||||
if(len == 4) {
|
||||
nonce->nt = nfc_util_bytes2num(data, 4);
|
||||
instance->state = Mfkey32StateAuthNtSent;
|
||||
data_processed = true;
|
||||
}
|
||||
}
|
||||
} else if(instance->state == Mfkey32StateAuthNtSent) {
|
||||
if(reader_to_tag) {
|
||||
if(len == 8) {
|
||||
nonce->nr = nfc_util_bytes2num(data, 4);
|
||||
nonce->ar = nfc_util_bytes2num(&data[4], 4);
|
||||
mfkey32_add_params(instance);
|
||||
instance->state = Mfkey32StateIdle;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!data_processed) {
|
||||
instance->state = Mfkey32StateIdle;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t mfkey32_get_auth_sectors(FuriString* data_str) {
|
||||
furi_assert(data_str);
|
||||
|
||||
uint16_t nonces_num = 0;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* file_stream = buffered_file_stream_alloc(storage);
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(!buffered_file_stream_open(
|
||||
file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING))
|
||||
break;
|
||||
while(true) {
|
||||
if(!stream_read_line(file_stream, temp_str)) break;
|
||||
size_t uid_pos = furi_string_search(temp_str, "cuid");
|
||||
furi_string_left(temp_str, uid_pos);
|
||||
furi_string_push_back(temp_str, '\n');
|
||||
furi_string_cat(data_str, temp_str);
|
||||
nonces_num++;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
buffered_file_stream_close(file_stream);
|
||||
stream_free(file_stream);
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return nonces_num;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Mfkey32 Mfkey32;
|
||||
|
||||
typedef enum {
|
||||
Mfkey32EventParamCollected,
|
||||
} Mfkey32Event;
|
||||
|
||||
typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context);
|
||||
|
||||
Mfkey32* mfkey32_alloc(uint32_t cuid);
|
||||
|
||||
void mfkey32_free(Mfkey32* instance);
|
||||
|
||||
void mfkey32_process_data(
|
||||
Mfkey32* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped);
|
||||
|
||||
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context);
|
||||
|
||||
uint16_t mfkey32_get_auth_sectors(FuriString* string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,566 @@
|
||||
#include "nfc_data_generator.h"
|
||||
|
||||
#include <furi/furi.h>
|
||||
#include <furi_hal_random.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
|
||||
#define NXP_MANUFACTURER_ID (0x04)
|
||||
|
||||
typedef void (*NfcDataGeneratorHandler)(NfcDevice* nfc_device);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
NfcDataGeneratorHandler handler;
|
||||
} NfcDataGenerator;
|
||||
|
||||
static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03};
|
||||
static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03};
|
||||
static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03};
|
||||
static const uint8_t default_data_ntag203[] =
|
||||
{0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00};
|
||||
|
||||
static void nfc_generate_mf_ul_uid(uint8_t* uid) {
|
||||
uid[0] = NXP_MANUFACTURER_ID;
|
||||
furi_hal_random_fill_buf(&uid[1], 6);
|
||||
// I'm not sure how this is generated, but the upper nybble always seems to be 8
|
||||
uid[6] &= 0x0F;
|
||||
uid[6] |= 0x80;
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_common(MfUltralightData* mfu_data) {
|
||||
mfu_data->iso14443_3a_data->uid_len = 7;
|
||||
nfc_generate_mf_ul_uid(mfu_data->iso14443_3a_data->uid);
|
||||
mfu_data->iso14443_3a_data->atqa[0] = 0x44;
|
||||
mfu_data->iso14443_3a_data->atqa[1] = 0x00;
|
||||
mfu_data->iso14443_3a_data->sak = 0x00;
|
||||
}
|
||||
|
||||
static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) {
|
||||
*bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2];
|
||||
*bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6];
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_copy_uid_with_bcc(MfUltralightData* mfu_data) {
|
||||
memcpy(mfu_data->page[0].data, mfu_data->iso14443_3a_data->uid, 3);
|
||||
memcpy(mfu_data->page[1].data, &mfu_data->iso14443_3a_data->uid[3], 4);
|
||||
|
||||
nfc_generate_calc_bcc(
|
||||
mfu_data->iso14443_3a_data->uid, &mfu_data->page[0].data[3], &mfu_data->page[2].data[0]);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_orig(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
nfc_generate_mf_ul_common(mfu_data);
|
||||
|
||||
mfu_data->type = MfUltralightTypeUnknown;
|
||||
mfu_data->pages_total = 16;
|
||||
mfu_data->pages_read = 16;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data);
|
||||
memset(&mfu_data->page[4], 0xff, sizeof(MfUltralightPage));
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_with_config_common(MfUltralightData* mfu_data, uint8_t num_pages) {
|
||||
nfc_generate_mf_ul_common(mfu_data);
|
||||
|
||||
mfu_data->pages_total = num_pages;
|
||||
mfu_data->pages_read = num_pages;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data);
|
||||
uint16_t config_index = (num_pages - 4);
|
||||
mfu_data->page[config_index].data[0] = 0x04; // STRG_MOD_EN
|
||||
mfu_data->page[config_index].data[3] = 0xff; // AUTH0
|
||||
mfu_data->page[config_index + 1].data[1] = 0x05; // VCTID
|
||||
memset(&mfu_data->page[config_index + 2], 0xff, sizeof(MfUltralightPage)); // Default PWD
|
||||
if(num_pages > 20) {
|
||||
mfu_data->page[config_index - 1].data[3] = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_ev1_common(MfUltralightData* mfu_data, uint8_t num_pages) {
|
||||
nfc_generate_mf_ul_with_config_common(mfu_data, num_pages);
|
||||
memcpy(&mfu_data->version, version_bytes_mf0ulx1, sizeof(MfUltralightVersion));
|
||||
for(size_t i = 0; i < 3; ++i) {
|
||||
mfu_data->tearing_flag[i].data = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_11(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_mf_ul_ev1_common(mfu_data, 20);
|
||||
mfu_data->type = MfUltralightTypeUL11;
|
||||
mfu_data->version.prod_subtype = 0x01;
|
||||
mfu_data->version.storage_size = 0x0B;
|
||||
mfu_data->page[16].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_h11(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_mf_ul_ev1_common(mfu_data, 20);
|
||||
mfu_data->type = MfUltralightTypeUL11;
|
||||
mfu_data->version.prod_subtype = 0x02;
|
||||
mfu_data->version.storage_size = 0x0B;
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_21(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_mf_ul_ev1_common(mfu_data, 41);
|
||||
mfu_data->type = MfUltralightTypeUL21;
|
||||
mfu_data->version.prod_subtype = 0x01;
|
||||
mfu_data->version.storage_size = 0x0E;
|
||||
mfu_data->page[37].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_h21(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_mf_ul_ev1_common(mfu_data, 41);
|
||||
mfu_data->type = MfUltralightTypeUL21;
|
||||
mfu_data->version.prod_subtype = 0x02;
|
||||
mfu_data->version.storage_size = 0x0E;
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag203(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_mf_ul_common(mfu_data);
|
||||
mfu_data->type = MfUltralightTypeNTAG203;
|
||||
mfu_data->pages_total = 42;
|
||||
mfu_data->pages_read = 42;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data);
|
||||
mfu_data->page[2].data[1] = 0x48; // Internal byte
|
||||
memcpy(&mfu_data->page[3], default_data_ntag203, sizeof(MfUltralightPage)); //-V1086
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag21x_common(MfUltralightData* mfu_data, uint8_t num_pages) {
|
||||
nfc_generate_mf_ul_with_config_common(mfu_data, num_pages);
|
||||
memcpy(&mfu_data->version, version_bytes_ntag21x, sizeof(MfUltralightVersion));
|
||||
mfu_data->page[2].data[1] = 0x48; // Internal byte
|
||||
// Capability container
|
||||
mfu_data->page[3].data[0] = 0xE1;
|
||||
mfu_data->page[3].data[1] = 0x10;
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag213(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag21x_common(mfu_data, 45);
|
||||
mfu_data->type = MfUltralightTypeNTAG213;
|
||||
mfu_data->version.storage_size = 0x0F;
|
||||
mfu_data->page[3].data[2] = 0x12;
|
||||
// Default contents
|
||||
memcpy(&mfu_data->page[4], default_data_ntag213, sizeof(default_data_ntag213));
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag215(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag21x_common(mfu_data, 135);
|
||||
mfu_data->type = MfUltralightTypeNTAG215;
|
||||
mfu_data->version.storage_size = 0x11;
|
||||
mfu_data->page[3].data[2] = 0x3E;
|
||||
// Default contents
|
||||
memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216));
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag216(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag21x_common(mfu_data, 231);
|
||||
mfu_data->type = MfUltralightTypeNTAG216;
|
||||
mfu_data->version.storage_size = 0x13;
|
||||
mfu_data->page[3].data[2] = 0x6D;
|
||||
// Default contents
|
||||
memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216));
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_common(
|
||||
MfUltralightData* mfu_data,
|
||||
MfUltralightType type,
|
||||
uint16_t num_pages) {
|
||||
nfc_generate_mf_ul_common(mfu_data);
|
||||
|
||||
mfu_data->type = type;
|
||||
memcpy(&mfu_data->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c));
|
||||
mfu_data->pages_total = num_pages;
|
||||
mfu_data->pages_read = num_pages;
|
||||
memcpy(
|
||||
mfu_data->page[0].data,
|
||||
mfu_data->iso14443_3a_data->uid,
|
||||
mfu_data->iso14443_3a_data->uid_len);
|
||||
mfu_data->page[1].data[3] = mfu_data->iso14443_3a_data->sak;
|
||||
mfu_data->page[2].data[0] = mfu_data->iso14443_3a_data->atqa[0];
|
||||
mfu_data->page[2].data[1] = mfu_data->iso14443_3a_data->atqa[1];
|
||||
|
||||
uint16_t config_register_page = 0;
|
||||
uint16_t session_register_page = 0;
|
||||
|
||||
// Sync with mifare_ultralight.c
|
||||
switch(type) {
|
||||
case MfUltralightTypeNTAGI2C1K:
|
||||
config_register_page = 227;
|
||||
session_register_page = 229;
|
||||
break;
|
||||
case MfUltralightTypeNTAGI2C2K:
|
||||
config_register_page = 481;
|
||||
session_register_page = 483;
|
||||
break;
|
||||
case MfUltralightTypeNTAGI2CPlus1K:
|
||||
case MfUltralightTypeNTAGI2CPlus2K:
|
||||
config_register_page = 232;
|
||||
session_register_page = 234;
|
||||
break;
|
||||
default:
|
||||
furi_crash("Unknown MFUL");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(
|
||||
&mfu_data->page[config_register_page],
|
||||
default_config_ntag_i2c,
|
||||
sizeof(default_config_ntag_i2c));
|
||||
memcpy(
|
||||
&mfu_data->page[session_register_page],
|
||||
default_config_ntag_i2c,
|
||||
sizeof(default_config_ntag_i2c));
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_1k(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C1K, 231);
|
||||
mfu_data->version.prod_ver_minor = 0x01;
|
||||
mfu_data->version.storage_size = 0x13;
|
||||
memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c));
|
||||
mfu_data->page[3].data[2] = 0x6D; // Size of tag in CC
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_2k(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C2K, 485);
|
||||
mfu_data->version.prod_ver_minor = 0x01;
|
||||
mfu_data->version.storage_size = 0x15;
|
||||
memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c));
|
||||
mfu_data->page[3].data[2] = 0xEA; // Size of tag in CC
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_plus_common(
|
||||
MfUltralightData* mfu_data,
|
||||
MfUltralightType type,
|
||||
uint16_t num_pages) {
|
||||
nfc_generate_ntag_i2c_common(mfu_data, type, num_pages);
|
||||
|
||||
uint16_t config_index = 227;
|
||||
mfu_data->page[config_index].data[3] = 0xff; // AUTH0
|
||||
|
||||
memset(&mfu_data->page[config_index + 2], 0xFF, sizeof(MfUltralightPage)); // Default PWD
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_plus_1k(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus1K, 236);
|
||||
mfu_data->version.prod_ver_minor = 0x02;
|
||||
mfu_data->version.storage_size = 0x13;
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_plus_2k(NfcDevice* nfc_device) {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus2K, 492);
|
||||
mfu_data->version.prod_ver_minor = 0x02;
|
||||
mfu_data->version.storage_size = 0x15;
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data);
|
||||
mf_ultralight_free(mfu_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) {
|
||||
uid[0] = NXP_MANUFACTURER_ID;
|
||||
furi_hal_random_fill_buf(&uid[1], length - 1);
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_generate_mf_classic_common(MfClassicData* data, uint8_t uid_len, MfClassicType type) {
|
||||
data->iso14443_3a_data->uid_len = uid_len;
|
||||
data->iso14443_3a_data->atqa[0] = 0x44;
|
||||
data->iso14443_3a_data->atqa[1] = 0x00;
|
||||
data->iso14443_3a_data->sak = 0x08;
|
||||
data->type = type;
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) {
|
||||
// All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h.
|
||||
MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].data;
|
||||
sec_tr->access_bits.data[0] = 0xFF;
|
||||
sec_tr->access_bits.data[1] = 0x07;
|
||||
sec_tr->access_bits.data[2] = 0x80;
|
||||
sec_tr->access_bits.data[3] = 0x69; // Nice
|
||||
|
||||
mf_classic_set_block_read(data, block, &data->block[block]);
|
||||
mf_classic_set_key_found(
|
||||
data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeA, 0xFFFFFFFFFFFF);
|
||||
mf_classic_set_key_found(
|
||||
data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeB, 0xFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_block_0(
|
||||
uint8_t* block,
|
||||
uint8_t uid_len,
|
||||
uint8_t sak,
|
||||
uint8_t atqa0,
|
||||
uint8_t atqa1) {
|
||||
// Block length is always 16 bytes, and the UID can be either 4 or 7 bytes
|
||||
furi_assert(uid_len == 4 || uid_len == 7);
|
||||
furi_assert(block);
|
||||
|
||||
if(uid_len == 4) {
|
||||
// Calculate BCC
|
||||
block[uid_len] = 0;
|
||||
|
||||
for(int i = 0; i < uid_len; i++) {
|
||||
block[uid_len] ^= block[i];
|
||||
}
|
||||
} else {
|
||||
uid_len -= 1;
|
||||
}
|
||||
|
||||
block[uid_len + 1] = sak;
|
||||
block[uid_len + 2] = atqa0;
|
||||
block[uid_len + 3] = atqa1;
|
||||
|
||||
for(int i = uid_len + 4; i < 16; i++) {
|
||||
block[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfClassicType type) {
|
||||
MfClassicData* mfc_data = mf_classic_alloc();
|
||||
|
||||
nfc_generate_mf_classic_uid(mfc_data->block[0].data, uid_len);
|
||||
nfc_generate_mf_classic_common(mfc_data, uid_len, type);
|
||||
|
||||
// Set the UID
|
||||
mfc_data->iso14443_3a_data->uid[0] = NXP_MANUFACTURER_ID;
|
||||
for(int i = 1; i < uid_len; i++) {
|
||||
mfc_data->iso14443_3a_data->uid[i] = mfc_data->block[0].data[i];
|
||||
}
|
||||
|
||||
mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]);
|
||||
|
||||
uint16_t block_num = mf_classic_get_total_block_num(type);
|
||||
if(type == MfClassicType4k) {
|
||||
// Set every block to 0xFF
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0xFF, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
}
|
||||
// Set SAK to 18
|
||||
mfc_data->iso14443_3a_data->sak = 0x18;
|
||||
} else if(type == MfClassicType1k) {
|
||||
// Set every block to 0xFF
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0xFF, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
}
|
||||
// Set SAK to 08
|
||||
mfc_data->iso14443_3a_data->sak = 0x08;
|
||||
} else if(type == MfClassicTypeMini) {
|
||||
// Set every block to 0xFF
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0xFF, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
}
|
||||
// Set SAK to 09
|
||||
mfc_data->iso14443_3a_data->sak = 0x09;
|
||||
}
|
||||
|
||||
nfc_generate_mf_classic_block_0(
|
||||
mfc_data->block[0].data,
|
||||
uid_len,
|
||||
mfc_data->iso14443_3a_data->sak,
|
||||
mfc_data->iso14443_3a_data->atqa[0],
|
||||
mfc_data->iso14443_3a_data->atqa[1]);
|
||||
|
||||
mfc_data->type = type;
|
||||
|
||||
nfc_device_set_data(nfc_device, NfcProtocolMfClassic, mfc_data);
|
||||
mf_classic_free(mfc_data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_mini(NfcDevice* nfc_device) {
|
||||
nfc_generate_mf_classic(nfc_device, 4, MfClassicTypeMini);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_1k_4b_uid(NfcDevice* nfc_device) {
|
||||
nfc_generate_mf_classic(nfc_device, 4, MfClassicType1k);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_1k_7b_uid(NfcDevice* nfc_device) {
|
||||
nfc_generate_mf_classic(nfc_device, 7, MfClassicType1k);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_4k_4b_uid(NfcDevice* nfc_device) {
|
||||
nfc_generate_mf_classic(nfc_device, 4, MfClassicType4k);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) {
|
||||
nfc_generate_mf_classic(nfc_device, 7, MfClassicType4k);
|
||||
}
|
||||
|
||||
static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = {
|
||||
[NfcDataGeneratorTypeMfUltralight] =
|
||||
{
|
||||
.name = "Mifare Ultralight",
|
||||
.handler = nfc_generate_mf_ul_orig,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfUltralightEV1_11] =
|
||||
{
|
||||
.name = "Mifare Ultralight EV1 11",
|
||||
.handler = nfc_generate_mf_ul_11,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfUltralightEV1_H11] =
|
||||
{
|
||||
.name = "Mifare Ultralight EV1 H11",
|
||||
.handler = nfc_generate_mf_ul_h11,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfUltralightEV1_21] =
|
||||
{
|
||||
.name = "Mifare Ultralight EV1 21",
|
||||
.handler = nfc_generate_mf_ul_21,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfUltralightEV1_H21] =
|
||||
{
|
||||
.name = "Mifare Ultralight EV1 H21",
|
||||
.handler = nfc_generate_mf_ul_h21,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAG203] =
|
||||
{
|
||||
.name = "NTAG203",
|
||||
.handler = nfc_generate_ntag203,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAG213] =
|
||||
{
|
||||
.name = "NTAG213",
|
||||
.handler = nfc_generate_ntag213,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAG215] =
|
||||
{
|
||||
.name = "NTAG215",
|
||||
.handler = nfc_generate_ntag215,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAG216] =
|
||||
{
|
||||
.name = "NTAG216",
|
||||
.handler = nfc_generate_ntag216,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAGI2C1k] =
|
||||
{
|
||||
.name = "NTAG I2C 1k",
|
||||
.handler = nfc_generate_ntag_i2c_1k,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAGI2C2k] =
|
||||
{
|
||||
.name = "NTAG I2C 2k",
|
||||
.handler = nfc_generate_ntag_i2c_2k,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAGI2CPlus1k] =
|
||||
{
|
||||
.name = "NTAG I2C Plus 1k",
|
||||
.handler = nfc_generate_ntag_i2c_plus_1k,
|
||||
},
|
||||
[NfcDataGeneratorTypeNTAGI2CPlus2k] =
|
||||
{
|
||||
.name = "NTAG I2C Plus 2k",
|
||||
.handler = nfc_generate_ntag_i2c_plus_2k,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfClassicMini] =
|
||||
{
|
||||
.name = "Mifare Mini",
|
||||
.handler = nfc_generate_mf_classic_mini,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfClassic1k_4b] =
|
||||
{
|
||||
.name = "Mifare Classic 1k 4byte UID",
|
||||
.handler = nfc_generate_mf_classic_1k_4b_uid,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfClassic1k_7b] =
|
||||
{
|
||||
.name = "Mifare Classic 1k 7byte UID",
|
||||
.handler = nfc_generate_mf_classic_1k_7b_uid,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfClassic4k_4b] =
|
||||
{
|
||||
.name = "Mifare Classic 4k 4byte UID",
|
||||
.handler = nfc_generate_mf_classic_4k_4b_uid,
|
||||
},
|
||||
[NfcDataGeneratorTypeMfClassic4k_7b] =
|
||||
{
|
||||
.name = "Mifare Classic 4k 7byte UID",
|
||||
.handler = nfc_generate_mf_classic_4k_7b_uid,
|
||||
},
|
||||
};
|
||||
|
||||
const char* nfc_data_generator_get_name(NfcDataGeneratorType type) {
|
||||
return nfc_data_generator[type].name;
|
||||
}
|
||||
|
||||
void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device) {
|
||||
nfc_data_generator[type].handler(nfc_device);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/nfc_device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
NfcDataGeneratorTypeMfUltralight,
|
||||
NfcDataGeneratorTypeMfUltralightEV1_11,
|
||||
NfcDataGeneratorTypeMfUltralightEV1_H11,
|
||||
NfcDataGeneratorTypeMfUltralightEV1_21,
|
||||
NfcDataGeneratorTypeMfUltralightEV1_H21,
|
||||
NfcDataGeneratorTypeNTAG203,
|
||||
NfcDataGeneratorTypeNTAG213,
|
||||
NfcDataGeneratorTypeNTAG215,
|
||||
NfcDataGeneratorTypeNTAG216,
|
||||
NfcDataGeneratorTypeNTAGI2C1k,
|
||||
NfcDataGeneratorTypeNTAGI2C2k,
|
||||
NfcDataGeneratorTypeNTAGI2CPlus1k,
|
||||
NfcDataGeneratorTypeNTAGI2CPlus2k,
|
||||
|
||||
NfcDataGeneratorTypeMfClassicMini,
|
||||
NfcDataGeneratorTypeMfClassic1k_4b,
|
||||
NfcDataGeneratorTypeMfClassic1k_7b,
|
||||
NfcDataGeneratorTypeMfClassic4k_4b,
|
||||
NfcDataGeneratorTypeMfClassic4k_7b,
|
||||
|
||||
NfcDataGeneratorTypeNum,
|
||||
|
||||
} NfcDataGeneratorType;
|
||||
|
||||
const char* nfc_data_generator_get_name(NfcDataGeneratorType type);
|
||||
|
||||
void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "nfc_debug_log.h"
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
|
||||
#define TAG "NfcDebugLog"
|
||||
|
||||
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt")
|
||||
|
||||
struct NfcDebugLog {
|
||||
Stream* file_stream;
|
||||
FuriString* data_str;
|
||||
};
|
||||
|
||||
NfcDebugLog* nfc_debug_log_alloc() {
|
||||
NfcDebugLog* instance = malloc(sizeof(NfcDebugLog));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->file_stream = buffered_file_stream_alloc(storage);
|
||||
|
||||
if(!buffered_file_stream_open(
|
||||
instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
instance->file_stream = NULL;
|
||||
}
|
||||
|
||||
if(!instance->file_stream) {
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
} else {
|
||||
instance->data_str = furi_string_alloc();
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_debug_log_free(NfcDebugLog* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->file_stream);
|
||||
furi_assert(instance->data_str);
|
||||
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
furi_string_free(instance->data_str);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_debug_log_process_data(
|
||||
NfcDebugLog* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->file_stream);
|
||||
furi_assert(instance->data_str);
|
||||
furi_assert(data);
|
||||
UNUSED(crc_dropped);
|
||||
|
||||
furi_string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T');
|
||||
uint16_t data_len = len;
|
||||
for(size_t i = 0; i < data_len; i++) {
|
||||
furi_string_cat_printf(instance->data_str, " %02x", data[i]);
|
||||
}
|
||||
furi_string_push_back(instance->data_str, '\n');
|
||||
|
||||
stream_write_string(instance->file_stream, instance->data_str);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct NfcDebugLog NfcDebugLog;
|
||||
|
||||
NfcDebugLog* nfc_debug_log_alloc();
|
||||
|
||||
void nfc_debug_log_free(NfcDebugLog* instance);
|
||||
|
||||
void nfc_debug_log_process_data(
|
||||
NfcDebugLog* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped);
|
||||
@@ -1,130 +0,0 @@
|
||||
#include "nfc_debug_pcap.h"
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
#include <furi_hal_nfc.h>
|
||||
#include <furi_hal_rtc.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")
|
||||
|
||||
struct NfcDebugPcap {
|
||||
Stream* file_stream;
|
||||
};
|
||||
|
||||
static Stream* nfc_debug_pcap_open(Storage* storage) {
|
||||
Stream* stream = NULL;
|
||||
stream = buffered_file_stream_alloc(storage);
|
||||
if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
|
||||
buffered_file_stream_close(stream);
|
||||
stream_free(stream);
|
||||
stream = NULL;
|
||||
} else {
|
||||
if(!stream_tell(stream)) {
|
||||
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(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
|
||||
FURI_LOG_E(TAG, "Failed to write pcap header");
|
||||
buffered_file_stream_close(stream);
|
||||
stream_free(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
NfcDebugPcap* nfc_debug_pcap_alloc() {
|
||||
NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap));
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->file_stream = nfc_debug_pcap_open(storage);
|
||||
if(!instance->file_stream) {
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_debug_pcap_free(NfcDebugPcap* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->file_stream);
|
||||
|
||||
buffered_file_stream_close(instance->file_stream);
|
||||
stream_free(instance->file_stream);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void nfc_debug_pcap_process_data(
|
||||
NfcDebugPcap* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
FuriHalRtcDateTime datetime;
|
||||
furi_hal_rtc_get_datetime(&datetime);
|
||||
|
||||
uint8_t event = 0;
|
||||
if(reader_to_tag) {
|
||||
if(crc_dropped) {
|
||||
event = DATA_PCD_TO_PICC_CRC_DROPPED;
|
||||
} else {
|
||||
event = DATA_PCD_TO_PICC;
|
||||
}
|
||||
} else {
|
||||
if(crc_dropped) {
|
||||
event = DATA_PICC_TO_PCD_CRC_DROPPED;
|
||||
} else {
|
||||
event = DATA_PICC_TO_PCD;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr));
|
||||
stream_write(instance->file_stream, data, len);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct NfcDebugPcap NfcDebugPcap;
|
||||
|
||||
NfcDebugPcap* nfc_debug_pcap_alloc();
|
||||
|
||||
void nfc_debug_pcap_free(NfcDebugPcap* instance);
|
||||
|
||||
void nfc_debug_pcap_process_data(
|
||||
NfcDebugPcap* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped);
|
||||
@@ -0,0 +1,270 @@
|
||||
#include "nfc_dict.h"
|
||||
|
||||
#include <storage/storage.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#include <nfc/helpers/nfc_util.h>
|
||||
|
||||
#define TAG "NfcDict"
|
||||
|
||||
struct NfcDict {
|
||||
Stream* stream;
|
||||
size_t key_size;
|
||||
size_t key_size_symbols;
|
||||
uint32_t total_keys;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* path;
|
||||
FS_OpenMode open_mode;
|
||||
} NfcDictFile;
|
||||
|
||||
bool nfc_dict_check_presence(const char* path) {
|
||||
furi_assert(path);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK;
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return dict_present;
|
||||
}
|
||||
|
||||
NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size) {
|
||||
furi_assert(path);
|
||||
|
||||
NfcDict* instance = malloc(sizeof(NfcDict));
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->stream = buffered_file_stream_alloc(storage);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
FS_OpenMode open_mode = FSOM_OPEN_EXISTING;
|
||||
if(mode == NfcDictModeOpenAlways) {
|
||||
open_mode = FSOM_OPEN_ALWAYS;
|
||||
}
|
||||
instance->key_size = key_size;
|
||||
// Byte = 2 symbols + 1 end of line
|
||||
instance->key_size_symbols = key_size * 2 + 1;
|
||||
|
||||
bool dict_loaded = false;
|
||||
do {
|
||||
if(!buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode)) {
|
||||
buffered_file_stream_close(instance->stream);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for new line ending
|
||||
if(!stream_eof(instance->stream)) {
|
||||
if(!stream_seek(instance->stream, -1, StreamOffsetFromEnd)) break;
|
||||
uint8_t last_char = 0;
|
||||
if(stream_read(instance->stream, &last_char, 1) != 1) break;
|
||||
if(last_char != '\n') {
|
||||
FURI_LOG_D(TAG, "Adding new line ending");
|
||||
if(stream_write_char(instance->stream, '\n') != 1) break;
|
||||
}
|
||||
if(!stream_rewind(instance->stream)) break;
|
||||
}
|
||||
|
||||
// Read total amount of keys
|
||||
FuriString* next_line;
|
||||
next_line = furi_string_alloc();
|
||||
while(true) {
|
||||
if(!stream_read_line(instance->stream, next_line)) {
|
||||
FURI_LOG_T(TAG, "No keys left in dict");
|
||||
break;
|
||||
}
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"Read line: %s, len: %zu",
|
||||
furi_string_get_cstr(next_line),
|
||||
furi_string_size(next_line));
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != instance->key_size_symbols) continue;
|
||||
instance->total_keys++;
|
||||
}
|
||||
furi_string_free(next_line);
|
||||
stream_rewind(instance->stream);
|
||||
|
||||
dict_loaded = true;
|
||||
FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", instance->total_keys);
|
||||
} while(false);
|
||||
|
||||
if(!dict_loaded) {
|
||||
buffered_file_stream_close(instance->stream);
|
||||
free(instance);
|
||||
instance = NULL;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void nfc_dict_free(NfcDict* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
|
||||
buffered_file_stream_close(instance->stream);
|
||||
stream_free(instance->stream);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static void nfc_dict_int_to_str(NfcDict* instance, const uint8_t* key_int, FuriString* key_str) {
|
||||
furi_string_reset(key_str);
|
||||
for(size_t i = 0; i < instance->key_size; i++) {
|
||||
furi_string_cat_printf(key_str, "%02X", key_int[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_dict_str_to_int(NfcDict* instance, FuriString* key_str, uint64_t* key_int) {
|
||||
uint8_t key_byte_tmp;
|
||||
|
||||
*key_int = 0ULL;
|
||||
for(uint8_t i = 0; i < instance->key_size * 2; i += 2) {
|
||||
args_char_to_hex(
|
||||
furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp);
|
||||
*key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nfc_dict_get_total_keys(NfcDict* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->total_keys;
|
||||
}
|
||||
|
||||
bool nfc_dict_rewind(NfcDict* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
|
||||
return stream_rewind(instance->stream);
|
||||
}
|
||||
|
||||
static bool nfc_dict_get_next_key_str(NfcDict* instance, FuriString* key) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
|
||||
bool key_read = false;
|
||||
furi_string_reset(key);
|
||||
while(!key_read) {
|
||||
if(!stream_read_line(instance->stream, key)) break;
|
||||
if(furi_string_get_char(key, 0) == '#') continue;
|
||||
if(furi_string_size(key) != instance->key_size_symbols) continue;
|
||||
furi_string_left(key, instance->key_size_symbols - 1);
|
||||
key_read = true;
|
||||
}
|
||||
|
||||
return key_read;
|
||||
}
|
||||
|
||||
bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
furi_assert(instance->key_size == key_size);
|
||||
|
||||
FuriString* temp_key = furi_string_alloc();
|
||||
uint64_t key_int = 0;
|
||||
bool key_read = nfc_dict_get_next_key_str(instance, temp_key);
|
||||
if(key_read) {
|
||||
nfc_dict_str_to_int(instance, temp_key, &key_int);
|
||||
nfc_util_num2bytes(key_int, key_size, key);
|
||||
}
|
||||
furi_string_free(temp_key);
|
||||
return key_read;
|
||||
}
|
||||
|
||||
static bool nfc_dict_is_key_present_str(NfcDict* instance, FuriString* key) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
|
||||
FuriString* next_line;
|
||||
next_line = furi_string_alloc();
|
||||
|
||||
bool key_found = false;
|
||||
stream_rewind(instance->stream);
|
||||
while(!key_found) { //-V654
|
||||
if(!stream_read_line(instance->stream, next_line)) break;
|
||||
if(furi_string_get_char(next_line, 0) == '#') continue;
|
||||
if(furi_string_size(next_line) != instance->key_size_symbols) continue;
|
||||
furi_string_left(next_line, instance->key_size_symbols - 1);
|
||||
if(!furi_string_equal(key, next_line)) continue;
|
||||
key_found = true;
|
||||
}
|
||||
|
||||
furi_string_free(next_line);
|
||||
return key_found;
|
||||
}
|
||||
|
||||
bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(key);
|
||||
furi_assert(instance->stream);
|
||||
furi_assert(instance->key_size == key_size);
|
||||
|
||||
FuriString* temp_key = furi_string_alloc();
|
||||
nfc_dict_int_to_str(instance, key, temp_key);
|
||||
bool key_found = nfc_dict_is_key_present_str(instance, temp_key);
|
||||
furi_string_free(temp_key);
|
||||
|
||||
return key_found;
|
||||
}
|
||||
|
||||
static bool nfc_dict_add_key_str(NfcDict* instance, FuriString* key) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
|
||||
furi_string_cat_printf(key, "\n");
|
||||
|
||||
bool key_added = false;
|
||||
do {
|
||||
if(!stream_seek(instance->stream, 0, StreamOffsetFromEnd)) break;
|
||||
if(!stream_insert_string(instance->stream, key)) break;
|
||||
instance->total_keys++;
|
||||
key_added = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_left(key, instance->key_size_symbols - 1);
|
||||
return key_added;
|
||||
}
|
||||
|
||||
bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(key);
|
||||
furi_assert(instance->stream);
|
||||
furi_assert(instance->key_size == key_size);
|
||||
|
||||
FuriString* temp_key = furi_string_alloc();
|
||||
nfc_dict_int_to_str(instance, key, temp_key);
|
||||
bool key_added = nfc_dict_add_key_str(instance, temp_key);
|
||||
furi_string_free(temp_key);
|
||||
|
||||
return key_added;
|
||||
}
|
||||
|
||||
bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->stream);
|
||||
furi_assert(key);
|
||||
furi_assert(instance->key_size == key_size);
|
||||
|
||||
bool key_removed = false;
|
||||
uint8_t* temp_key = malloc(key_size);
|
||||
|
||||
nfc_dict_rewind(instance);
|
||||
while(!key_removed) {
|
||||
if(!nfc_dict_get_next_key(instance, temp_key, key_size)) break;
|
||||
if(memcmp(temp_key, key, key_size) == 0) {
|
||||
int32_t offset = (-1) * (instance->key_size_symbols);
|
||||
stream_seek(instance->stream, offset, StreamOffsetFromCurrent);
|
||||
if(!stream_delete(instance->stream, instance->key_size_symbols)) break;
|
||||
instance->total_keys--;
|
||||
key_removed = true;
|
||||
}
|
||||
}
|
||||
nfc_dict_rewind(instance);
|
||||
free(temp_key);
|
||||
|
||||
return key_removed;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
NfcDictModeOpenExisting,
|
||||
NfcDictModeOpenAlways,
|
||||
} NfcDictMode;
|
||||
|
||||
typedef struct NfcDict NfcDict;
|
||||
|
||||
/** Check dictionary presence
|
||||
*
|
||||
* @param path - dictionary path
|
||||
*
|
||||
* @return true if dictionary exists, false otherwise
|
||||
*/
|
||||
bool nfc_dict_check_presence(const char* path);
|
||||
|
||||
/** Open or create dictionary
|
||||
* Depending on mode, dictionary will be opened or created.
|
||||
*
|
||||
* @param path - dictionary path
|
||||
* @param mode - NfcDictMode value
|
||||
* @param key_size - size of dictionary keys in bytes
|
||||
*
|
||||
* @return NfcDict dictionary instance
|
||||
*/
|
||||
NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size);
|
||||
|
||||
/** Close dictionary
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
*/
|
||||
void nfc_dict_free(NfcDict* instance);
|
||||
|
||||
/** Get total number of keys in dictionary
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
*
|
||||
* @return total number of keys in dictionary
|
||||
*/
|
||||
uint32_t nfc_dict_get_total_keys(NfcDict* instance);
|
||||
|
||||
/** Rewind dictionary
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
*
|
||||
* @return true if rewind was successful, false otherwise
|
||||
*/
|
||||
bool nfc_dict_rewind(NfcDict* instance);
|
||||
|
||||
/** Check if key is present in dictionary
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
* @param key - key to check
|
||||
* @param key_size - size of key in bytes
|
||||
*
|
||||
* @return true if key is present, false otherwise
|
||||
*/
|
||||
bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size);
|
||||
|
||||
/** Get next key from dictionary
|
||||
* This function will return next key from dictionary. If there are no more
|
||||
* keys, it will return false, and nfc_dict_rewind() should be called.
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
* @param key - buffer to store key
|
||||
* @param key_size - size of key in bytes
|
||||
*
|
||||
* @return true if key was successfully retrieved, false otherwise
|
||||
*/
|
||||
bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size);
|
||||
|
||||
/** Add key to dictionary
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
* @param key - key to add
|
||||
* @param key_size - size of key in bytes
|
||||
*
|
||||
* @return true if key was successfully added, false otherwise
|
||||
*/
|
||||
bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size);
|
||||
|
||||
/** Delete key from dictionary
|
||||
*
|
||||
* @param instance - NfcDict dictionary instance
|
||||
* @param key - key to delete
|
||||
* @param key_size - size of key in bytes
|
||||
*
|
||||
* @return true if key was successfully deleted, false otherwise
|
||||
*/
|
||||
bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,528 +0,0 @@
|
||||
#include <furi_hal_random.h>
|
||||
#include "nfc_generators.h"
|
||||
|
||||
#define NXP_MANUFACTURER_ID (0x04)
|
||||
|
||||
static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03};
|
||||
static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03};
|
||||
static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03};
|
||||
static const uint8_t default_data_ntag203[] =
|
||||
{0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE};
|
||||
static const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00};
|
||||
|
||||
static void nfc_generate_common_start(NfcDeviceData* data) {
|
||||
nfc_device_data_clear(data);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_uid(uint8_t* uid) {
|
||||
uid[0] = NXP_MANUFACTURER_ID;
|
||||
furi_hal_random_fill_buf(&uid[1], 6);
|
||||
// I'm not sure how this is generated, but the upper nybble always seems to be 8
|
||||
uid[6] &= 0x0F;
|
||||
uid[6] |= 0x80;
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) {
|
||||
uid[0] = NXP_MANUFACTURER_ID;
|
||||
furi_hal_random_fill_buf(&uid[1], length - 1);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_block_0(
|
||||
uint8_t* block,
|
||||
uint8_t uid_len,
|
||||
uint8_t sak,
|
||||
uint8_t atqa0,
|
||||
uint8_t atqa1) {
|
||||
// Block length is always 16 bytes, and the UID can be either 4 or 7 bytes
|
||||
furi_assert(uid_len == 4 || uid_len == 7);
|
||||
furi_assert(block);
|
||||
|
||||
if(uid_len == 4) {
|
||||
// Calculate BCC
|
||||
block[uid_len] = 0;
|
||||
|
||||
for(int i = 0; i < uid_len; i++) {
|
||||
block[uid_len] ^= block[i];
|
||||
}
|
||||
} else {
|
||||
uid_len -= 1;
|
||||
}
|
||||
|
||||
block[uid_len + 1] = sak;
|
||||
block[uid_len + 2] = atqa0;
|
||||
block[uid_len + 3] = atqa1;
|
||||
|
||||
for(int i = uid_len + 4; i < 16; i++) {
|
||||
block[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) {
|
||||
// All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h.
|
||||
MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].value;
|
||||
sec_tr->access_bits[0] = 0xFF;
|
||||
sec_tr->access_bits[1] = 0x07;
|
||||
sec_tr->access_bits[2] = 0x80;
|
||||
sec_tr->access_bits[3] = 0x69; // Nice
|
||||
|
||||
memset(sec_tr->key_a, 0xff, sizeof(sec_tr->key_a));
|
||||
memset(sec_tr->key_b, 0xff, sizeof(sec_tr->key_b));
|
||||
|
||||
mf_classic_set_block_read(data, block, &data->block[block]);
|
||||
mf_classic_set_key_found(
|
||||
data, mf_classic_get_sector_by_block(block), MfClassicKeyA, 0xFFFFFFFFFFFF);
|
||||
mf_classic_set_key_found(
|
||||
data, mf_classic_get_sector_by_block(block), MfClassicKeyB, 0xFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_common(NfcDeviceData* data) {
|
||||
data->nfc_data.type = FuriHalNfcTypeA;
|
||||
data->nfc_data.interface = FuriHalNfcInterfaceRf;
|
||||
data->nfc_data.uid_len = 7;
|
||||
nfc_generate_mf_ul_uid(data->nfc_data.uid);
|
||||
data->nfc_data.atqa[0] = 0x44;
|
||||
data->nfc_data.atqa[1] = 0x00;
|
||||
data->nfc_data.sak = 0x00;
|
||||
data->protocol = NfcDeviceProtocolMifareUl;
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_generate_mf_classic_common(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) {
|
||||
data->nfc_data.type = FuriHalNfcTypeA;
|
||||
data->nfc_data.interface = FuriHalNfcInterfaceRf;
|
||||
data->nfc_data.uid_len = uid_len;
|
||||
data->nfc_data.atqa[0] = 0x44;
|
||||
data->nfc_data.atqa[1] = 0x00;
|
||||
data->nfc_data.sak = 0x08;
|
||||
data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
data->mf_classic_data.type = type;
|
||||
}
|
||||
|
||||
static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) {
|
||||
*bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2];
|
||||
*bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6];
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_copy_uid_with_bcc(NfcDeviceData* data) {
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
memcpy(mful->data, data->nfc_data.uid, 3);
|
||||
memcpy(&mful->data[4], &data->nfc_data.uid[3], 4);
|
||||
nfc_generate_calc_bcc(data->nfc_data.uid, &mful->data[3], &mful->data[8]);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_orig(NfcDeviceData* data) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_ul_common(data);
|
||||
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeUnknown;
|
||||
mful->data_size = 16 * 4;
|
||||
mful->data_read = mful->data_size;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(data);
|
||||
// TODO: what's internal byte on page 2?
|
||||
memset(&mful->data[4 * 4], 0xFF, 4);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_ul_common(data);
|
||||
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeNTAG203;
|
||||
mful->data_size = 42 * 4;
|
||||
mful->data_read = mful->data_size;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(data);
|
||||
mful->data[9] = 0x48; // Internal byte
|
||||
memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203));
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_ul_common(data);
|
||||
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->data_size = num_pages * 4;
|
||||
mful->data_read = mful->data_size;
|
||||
nfc_generate_mf_ul_copy_uid_with_bcc(data);
|
||||
uint16_t config_index = (num_pages - 4) * 4;
|
||||
mful->data[config_index] = 0x04; // STRG_MOD_EN
|
||||
mful->data[config_index + 3] = 0xFF; // AUTH0
|
||||
mful->data[config_index + 5] = 0x05; // VCTID
|
||||
memset(&mful->data[config_index + 8], 0xFF, 4); // Default PWD
|
||||
if(num_pages > 20) mful->data[config_index - 1] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_ev1_common(NfcDeviceData* data, uint8_t num_pages) {
|
||||
nfc_generate_mf_ul_with_config_common(data, num_pages);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
memcpy(&mful->version, version_bytes_mf0ulx1, sizeof(version_bytes_mf0ulx1));
|
||||
for(size_t i = 0; i < 3; ++i) {
|
||||
mful->tearing[i] = MF_UL_TEARING_FLAG_DEFAULT;
|
||||
}
|
||||
// TODO: what's internal byte on page 2?
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_11(NfcDeviceData* data) {
|
||||
nfc_generate_mf_ul_ev1_common(data, 20);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeUL11;
|
||||
mful->version.prod_subtype = 0x01;
|
||||
mful->version.storage_size = 0x0B;
|
||||
mful->data[16 * 4] = 0x00; // Low capacitance version does not have STRG_MOD_EN
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_h11(NfcDeviceData* data) {
|
||||
nfc_generate_mf_ul_ev1_common(data, 20);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeUL11;
|
||||
mful->version.prod_subtype = 0x02;
|
||||
mful->version.storage_size = 0x0B;
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_21(NfcDeviceData* data) {
|
||||
nfc_generate_mf_ul_ev1_common(data, 41);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeUL21;
|
||||
mful->version.prod_subtype = 0x01;
|
||||
mful->version.storage_size = 0x0E;
|
||||
mful->data[37 * 4] = 0x00; // Low capacitance version does not have STRG_MOD_EN
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_ul_h21(NfcDeviceData* data) {
|
||||
nfc_generate_mf_ul_ev1_common(data, 41);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeUL21;
|
||||
mful->version.prod_subtype = 0x02;
|
||||
mful->version.storage_size = 0x0E;
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag21x_common(NfcDeviceData* data, uint8_t num_pages) {
|
||||
nfc_generate_mf_ul_with_config_common(data, num_pages);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
memcpy(&mful->version, version_bytes_ntag21x, sizeof(version_bytes_mf0ulx1));
|
||||
mful->data[9] = 0x48; // Internal byte
|
||||
// Capability container
|
||||
mful->data[12] = 0xE1;
|
||||
mful->data[13] = 0x10;
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag213(NfcDeviceData* data) {
|
||||
nfc_generate_ntag21x_common(data, 45);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeNTAG213;
|
||||
mful->version.storage_size = 0x0F;
|
||||
mful->data[14] = 0x12;
|
||||
// Default contents
|
||||
memcpy(&mful->data[16], default_data_ntag213, sizeof(default_data_ntag213));
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag215(NfcDeviceData* data) {
|
||||
nfc_generate_ntag21x_common(data, 135);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeNTAG215;
|
||||
mful->version.storage_size = 0x11;
|
||||
mful->data[14] = 0x3E;
|
||||
// Default contents
|
||||
memcpy(&mful->data[16], default_data_ntag215_216, sizeof(default_data_ntag215_216));
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag216(NfcDeviceData* data) {
|
||||
nfc_generate_ntag21x_common(data, 231);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = MfUltralightTypeNTAG216;
|
||||
mful->version.storage_size = 0x13;
|
||||
mful->data[14] = 0x6D;
|
||||
// Default contents
|
||||
memcpy(&mful->data[16], default_data_ntag215_216, sizeof(default_data_ntag215_216));
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_generate_ntag_i2c_common(NfcDeviceData* data, MfUltralightType type, uint16_t num_pages) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_ul_common(data);
|
||||
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->type = type;
|
||||
memcpy(&mful->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c));
|
||||
mful->data_size = num_pages * 4;
|
||||
mful->data_read = mful->data_size;
|
||||
memcpy(mful->data, data->nfc_data.uid, data->nfc_data.uid_len);
|
||||
mful->data[7] = data->nfc_data.sak;
|
||||
mful->data[8] = data->nfc_data.atqa[0];
|
||||
mful->data[9] = data->nfc_data.atqa[1];
|
||||
|
||||
uint16_t config_register_page;
|
||||
uint16_t session_register_page;
|
||||
|
||||
// Sync with mifare_ultralight.c
|
||||
switch(type) {
|
||||
case MfUltralightTypeNTAGI2C1K:
|
||||
config_register_page = 227;
|
||||
session_register_page = 229;
|
||||
break;
|
||||
case MfUltralightTypeNTAGI2C2K:
|
||||
config_register_page = 481;
|
||||
session_register_page = 483;
|
||||
break;
|
||||
case MfUltralightTypeNTAGI2CPlus1K:
|
||||
case MfUltralightTypeNTAGI2CPlus2K:
|
||||
config_register_page = 232;
|
||||
session_register_page = 234;
|
||||
break;
|
||||
default:
|
||||
furi_crash("Unknown MFUL");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(
|
||||
&mful->data[config_register_page * 4],
|
||||
default_config_ntag_i2c,
|
||||
sizeof(default_config_ntag_i2c));
|
||||
memcpy(
|
||||
&mful->data[session_register_page * 4],
|
||||
default_config_ntag_i2c,
|
||||
sizeof(default_config_ntag_i2c));
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_1k(NfcDeviceData* data) {
|
||||
nfc_generate_ntag_i2c_common(data, MfUltralightTypeNTAGI2C1K, 231);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->version.prod_ver_minor = 0x01;
|
||||
mful->version.storage_size = 0x13;
|
||||
|
||||
memcpy(&mful->data[12], default_data_ntag_i2c, sizeof(default_data_ntag_i2c));
|
||||
mful->data[14] = 0x6D; // Size of tag in CC
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_2k(NfcDeviceData* data) {
|
||||
nfc_generate_ntag_i2c_common(data, MfUltralightTypeNTAGI2C2K, 485);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->version.prod_ver_minor = 0x01;
|
||||
mful->version.storage_size = 0x15;
|
||||
|
||||
memcpy(&mful->data[12], default_data_ntag_i2c, sizeof(default_data_ntag_i2c));
|
||||
mful->data[14] = 0xEA; // Size of tag in CC
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_plus_common(
|
||||
NfcDeviceData* data,
|
||||
MfUltralightType type,
|
||||
uint16_t num_pages) {
|
||||
nfc_generate_ntag_i2c_common(data, type, num_pages);
|
||||
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
uint16_t config_index = 227 * 4;
|
||||
mful->data[config_index + 3] = 0xFF; // AUTH0
|
||||
memset(&mful->data[config_index + 8], 0xFF, 4); // Default PWD
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_plus_1k(NfcDeviceData* data) {
|
||||
nfc_generate_ntag_i2c_plus_common(data, MfUltralightTypeNTAGI2CPlus1K, 236);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->version.prod_ver_minor = 0x02;
|
||||
mful->version.storage_size = 0x13;
|
||||
}
|
||||
|
||||
static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) {
|
||||
nfc_generate_ntag_i2c_plus_common(data, MfUltralightTypeNTAGI2CPlus2K, 492);
|
||||
MfUltralightData* mful = &data->mf_ul_data;
|
||||
mful->version.prod_ver_minor = 0x02;
|
||||
mful->version.storage_size = 0x15;
|
||||
}
|
||||
|
||||
void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) {
|
||||
nfc_generate_common_start(data);
|
||||
nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len);
|
||||
nfc_generate_mf_classic_common(data, uid_len, type);
|
||||
|
||||
// Set the UID
|
||||
data->nfc_data.uid[0] = NXP_MANUFACTURER_ID;
|
||||
for(int i = 1; i < uid_len; i++) {
|
||||
data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i];
|
||||
}
|
||||
|
||||
MfClassicData* mfc = &data->mf_classic_data;
|
||||
mf_classic_set_block_read(mfc, 0, &mfc->block[0]);
|
||||
|
||||
if(type == MfClassicType4k) {
|
||||
// Set every block to 0xFF
|
||||
for(uint16_t i = 1; i < 256; i += 1) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc, i);
|
||||
} else {
|
||||
memset(&mfc->block[i].value, 0xFF, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc, i, &mfc->block[i]);
|
||||
}
|
||||
// Set SAK to 18
|
||||
data->nfc_data.sak = 0x18;
|
||||
} else if(type == MfClassicType1k) {
|
||||
// Set every block to 0xFF
|
||||
for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc, i);
|
||||
} else {
|
||||
memset(&mfc->block[i].value, 0xFF, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc, i, &mfc->block[i]);
|
||||
}
|
||||
// Set SAK to 08
|
||||
data->nfc_data.sak = 0x08;
|
||||
} else if(type == MfClassicTypeMini) {
|
||||
// Set every block to 0xFF
|
||||
for(uint16_t i = 1; i < MF_MINI_TOTAL_SECTORS_NUM * 4; i += 1) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc, i);
|
||||
} else {
|
||||
memset(&mfc->block[i].value, 0xFF, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc, i, &mfc->block[i]);
|
||||
}
|
||||
// Set SAK to 09
|
||||
data->nfc_data.sak = 0x09;
|
||||
}
|
||||
|
||||
nfc_generate_mf_classic_block_0(
|
||||
data->mf_classic_data.block[0].value,
|
||||
uid_len,
|
||||
data->nfc_data.sak,
|
||||
data->nfc_data.atqa[0],
|
||||
data->nfc_data.atqa[1]);
|
||||
|
||||
mfc->type = type;
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_mini(NfcDeviceData* data) {
|
||||
nfc_generate_mf_classic(data, 4, MfClassicTypeMini);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceData* data) {
|
||||
nfc_generate_mf_classic(data, 4, MfClassicType1k);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_1k_7b_uid(NfcDeviceData* data) {
|
||||
nfc_generate_mf_classic(data, 7, MfClassicType1k);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_4k_4b_uid(NfcDeviceData* data) {
|
||||
nfc_generate_mf_classic(data, 4, MfClassicType4k);
|
||||
}
|
||||
|
||||
static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceData* data) {
|
||||
nfc_generate_mf_classic(data, 7, MfClassicType4k);
|
||||
}
|
||||
|
||||
static const NfcGenerator mf_ul_generator = {
|
||||
.name = "Mifare Ultralight",
|
||||
.generator_func = nfc_generate_mf_ul_orig,
|
||||
};
|
||||
|
||||
static const NfcGenerator mf_ul_11_generator = {
|
||||
.name = "Mifare Ultralight EV1 11",
|
||||
.generator_func = nfc_generate_mf_ul_11,
|
||||
};
|
||||
|
||||
static const NfcGenerator mf_ul_h11_generator = {
|
||||
.name = "Mifare Ultralight EV1 H11",
|
||||
.generator_func = nfc_generate_mf_ul_h11,
|
||||
};
|
||||
|
||||
static const NfcGenerator mf_ul_21_generator = {
|
||||
.name = "Mifare Ultralight EV1 21",
|
||||
.generator_func = nfc_generate_mf_ul_21,
|
||||
};
|
||||
|
||||
static const NfcGenerator mf_ul_h21_generator = {
|
||||
.name = "Mifare Ultralight EV1 H21",
|
||||
.generator_func = nfc_generate_mf_ul_h21,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag203_generator = {
|
||||
.name = "NTAG203",
|
||||
.generator_func = nfc_generate_mf_ul_ntag203,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag213_generator = {
|
||||
.name = "NTAG213",
|
||||
.generator_func = nfc_generate_ntag213,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag215_generator = {
|
||||
.name = "NTAG215",
|
||||
.generator_func = nfc_generate_ntag215,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag216_generator = {
|
||||
.name = "NTAG216",
|
||||
.generator_func = nfc_generate_ntag216,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag_i2c_1k_generator = {
|
||||
.name = "NTAG I2C 1k",
|
||||
.generator_func = nfc_generate_ntag_i2c_1k,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag_i2c_2k_generator = {
|
||||
.name = "NTAG I2C 2k",
|
||||
.generator_func = nfc_generate_ntag_i2c_2k,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag_i2c_plus_1k_generator = {
|
||||
.name = "NTAG I2C Plus 1k",
|
||||
.generator_func = nfc_generate_ntag_i2c_plus_1k,
|
||||
};
|
||||
|
||||
static const NfcGenerator ntag_i2c_plus_2k_generator = {
|
||||
.name = "NTAG I2C Plus 2k",
|
||||
.generator_func = nfc_generate_ntag_i2c_plus_2k,
|
||||
};
|
||||
|
||||
static const NfcGenerator mifare_mini_generator = {
|
||||
.name = "Mifare Mini",
|
||||
.generator_func = nfc_generate_mf_mini,
|
||||
};
|
||||
|
||||
static const NfcGenerator mifare_classic_1k_4b_uid_generator = {
|
||||
.name = "Mifare Classic 1k 4byte UID",
|
||||
.generator_func = nfc_generate_mf_classic_1k_4b_uid,
|
||||
};
|
||||
|
||||
static const NfcGenerator mifare_classic_1k_7b_uid_generator = {
|
||||
.name = "Mifare Classic 1k 7byte UID",
|
||||
.generator_func = nfc_generate_mf_classic_1k_7b_uid,
|
||||
};
|
||||
|
||||
static const NfcGenerator mifare_classic_4k_4b_uid_generator = {
|
||||
.name = "Mifare Classic 4k 4byte UID",
|
||||
.generator_func = nfc_generate_mf_classic_4k_4b_uid,
|
||||
};
|
||||
|
||||
static const NfcGenerator mifare_classic_4k_7b_uid_generator = {
|
||||
.name = "Mifare Classic 4k 7byte UID",
|
||||
.generator_func = nfc_generate_mf_classic_4k_7b_uid,
|
||||
};
|
||||
|
||||
const NfcGenerator* const nfc_generators[] = {
|
||||
&mf_ul_generator,
|
||||
&mf_ul_11_generator,
|
||||
&mf_ul_h11_generator,
|
||||
&mf_ul_21_generator,
|
||||
&mf_ul_h21_generator,
|
||||
&ntag203_generator,
|
||||
&ntag213_generator,
|
||||
&ntag215_generator,
|
||||
&ntag216_generator,
|
||||
&ntag_i2c_1k_generator,
|
||||
&ntag_i2c_2k_generator,
|
||||
&ntag_i2c_plus_1k_generator,
|
||||
&ntag_i2c_plus_2k_generator,
|
||||
&mifare_mini_generator,
|
||||
&mifare_classic_1k_4b_uid_generator,
|
||||
&mifare_classic_1k_7b_uid_generator,
|
||||
&mifare_classic_4k_4b_uid_generator,
|
||||
&mifare_classic_4k_7b_uid_generator,
|
||||
NULL,
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_device.h"
|
||||
|
||||
typedef void (*NfcGeneratorFunc)(NfcDeviceData* data);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
NfcGeneratorFunc generator_func;
|
||||
} NfcGenerator;
|
||||
|
||||
extern const NfcGenerator* const nfc_generators[];
|
||||
|
||||
void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type);
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "nfc_util.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
static const uint8_t nfc_util_odd_byte_parity[256] = {
|
||||
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0,
|
||||
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1,
|
||||
1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1,
|
||||
0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0,
|
||||
1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1,
|
||||
0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0,
|
||||
0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1,
|
||||
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
|
||||
1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
|
||||
|
||||
void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest) {
|
||||
furi_assert(dest);
|
||||
furi_assert(len <= 8);
|
||||
|
||||
while(len--) {
|
||||
dest[len] = (uint8_t)src;
|
||||
src >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) {
|
||||
furi_assert(src);
|
||||
furi_assert(len <= 8);
|
||||
|
||||
uint64_t res = 0;
|
||||
while(len--) {
|
||||
res = (res << 8) | (*src);
|
||||
src++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t nfc_util_even_parity32(uint32_t data) {
|
||||
// data ^= data >> 16;
|
||||
// data ^= data >> 8;
|
||||
// return !nfc_util_odd_byte_parity[data];
|
||||
return (__builtin_parity(data) & 0xFF);
|
||||
}
|
||||
|
||||
uint8_t nfc_util_odd_parity8(uint8_t data) {
|
||||
return nfc_util_odd_byte_parity[data];
|
||||
}
|
||||
|
||||
void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len) {
|
||||
furi_assert(src);
|
||||
furi_assert(dst);
|
||||
|
||||
uint8_t parity = 0;
|
||||
uint8_t bit = 0;
|
||||
while(len--) {
|
||||
parity |= nfc_util_odd_parity8(*src) << (7 - bit); // parity is MSB first
|
||||
bit++;
|
||||
if(bit == 8) {
|
||||
*dst = parity;
|
||||
dst++;
|
||||
parity = 0;
|
||||
bit = 0;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
|
||||
if(bit) {
|
||||
*dst = parity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest);
|
||||
|
||||
uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len);
|
||||
|
||||
uint8_t nfc_util_even_parity32(uint32_t data);
|
||||
|
||||
uint8_t nfc_util_odd_parity8(uint8_t data);
|
||||
|
||||
void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,265 +0,0 @@
|
||||
#include "reader_analyzer.h"
|
||||
#include <lib/nfc/protocols/nfc_util.h>
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#include "mfkey32.h"
|
||||
#include "nfc_debug_pcap.h"
|
||||
#include "nfc_debug_log.h"
|
||||
|
||||
#define TAG "ReaderAnalyzer"
|
||||
|
||||
#define READER_ANALYZER_MAX_BUFF_SIZE (1024)
|
||||
|
||||
typedef struct {
|
||||
bool reader_to_tag;
|
||||
bool crc_dropped;
|
||||
uint16_t len;
|
||||
} ReaderAnalyzerHeader;
|
||||
|
||||
typedef enum {
|
||||
ReaderAnalyzerNfcDataMfClassic,
|
||||
} ReaderAnalyzerNfcData;
|
||||
|
||||
struct ReaderAnalyzer {
|
||||
FuriHalNfcDevData nfc_data;
|
||||
|
||||
bool alive;
|
||||
FuriStreamBuffer* stream;
|
||||
FuriThread* thread;
|
||||
|
||||
ReaderAnalyzerParseDataCallback callback;
|
||||
void* context;
|
||||
|
||||
ReaderAnalyzerMode mode;
|
||||
Mfkey32* mfkey32;
|
||||
NfcDebugLog* debug_log;
|
||||
NfcDebugPcap* pcap;
|
||||
};
|
||||
|
||||
const FuriHalNfcDevData reader_analyzer_nfc_data[] = {
|
||||
[ReaderAnalyzerNfcDataMfClassic] =
|
||||
{.sak = 0x08,
|
||||
.atqa = {0x44, 0x00},
|
||||
.interface = FuriHalNfcInterfaceRf,
|
||||
.type = FuriHalNfcTypeA,
|
||||
.uid_len = 7,
|
||||
.uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80},
|
||||
.cuid = 0x2A234F80},
|
||||
};
|
||||
|
||||
void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) {
|
||||
if(size < sizeof(ReaderAnalyzerHeader)) return;
|
||||
|
||||
size_t bytes_i = 0;
|
||||
while(bytes_i < size) {
|
||||
ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i];
|
||||
uint16_t len = header->len;
|
||||
if(bytes_i + len > size) break;
|
||||
bytes_i += sizeof(ReaderAnalyzerHeader);
|
||||
if(instance->mfkey32) {
|
||||
mfkey32_process_data(
|
||||
instance->mfkey32,
|
||||
&buffer[bytes_i],
|
||||
len,
|
||||
header->reader_to_tag,
|
||||
header->crc_dropped);
|
||||
}
|
||||
if(instance->pcap) {
|
||||
nfc_debug_pcap_process_data(
|
||||
instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped);
|
||||
}
|
||||
if(instance->debug_log) {
|
||||
nfc_debug_log_process_data(
|
||||
instance->debug_log,
|
||||
&buffer[bytes_i],
|
||||
len,
|
||||
header->reader_to_tag,
|
||||
header->crc_dropped);
|
||||
}
|
||||
bytes_i += len;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t reader_analyzer_thread(void* context) {
|
||||
ReaderAnalyzer* reader_analyzer = context;
|
||||
uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {};
|
||||
|
||||
while(reader_analyzer->alive || !furi_stream_buffer_is_empty(reader_analyzer->stream)) {
|
||||
size_t ret = furi_stream_buffer_receive(
|
||||
reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50);
|
||||
if(ret) {
|
||||
reader_analyzer_parse(reader_analyzer, buffer, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ReaderAnalyzer* reader_analyzer_alloc() {
|
||||
ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer));
|
||||
|
||||
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
|
||||
instance->alive = false;
|
||||
instance->stream =
|
||||
furi_stream_buffer_alloc(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader));
|
||||
|
||||
instance->thread =
|
||||
furi_thread_alloc_ex("ReaderAnalyzerWorker", 2048, reader_analyzer_thread, instance);
|
||||
furi_thread_set_priority(instance->thread, FuriThreadPriorityLow);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) {
|
||||
furi_assert(context);
|
||||
ReaderAnalyzer* instance = context;
|
||||
|
||||
if(event == Mfkey32EventParamCollected) {
|
||||
if(instance->callback) {
|
||||
instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_stream_buffer_reset(instance->stream);
|
||||
if(mode & ReaderAnalyzerModeDebugLog) {
|
||||
instance->debug_log = nfc_debug_log_alloc();
|
||||
}
|
||||
if(mode & ReaderAnalyzerModeMfkey) {
|
||||
instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid);
|
||||
if(instance->mfkey32) {
|
||||
mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance);
|
||||
}
|
||||
}
|
||||
if(mode & ReaderAnalyzerModeDebugPcap) {
|
||||
instance->pcap = nfc_debug_pcap_alloc();
|
||||
}
|
||||
|
||||
instance->alive = true;
|
||||
furi_thread_start(instance->thread);
|
||||
}
|
||||
|
||||
void reader_analyzer_stop(ReaderAnalyzer* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->alive = false;
|
||||
furi_thread_join(instance->thread);
|
||||
|
||||
if(instance->debug_log) {
|
||||
nfc_debug_log_free(instance->debug_log);
|
||||
instance->debug_log = NULL;
|
||||
}
|
||||
if(instance->mfkey32) {
|
||||
mfkey32_free(instance->mfkey32);
|
||||
instance->mfkey32 = NULL;
|
||||
}
|
||||
if(instance->pcap) {
|
||||
nfc_debug_pcap_free(instance->pcap);
|
||||
instance->pcap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void reader_analyzer_free(ReaderAnalyzer* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
reader_analyzer_stop(instance);
|
||||
furi_thread_free(instance->thread);
|
||||
furi_stream_buffer_free(instance->stream);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void reader_analyzer_set_callback(
|
||||
ReaderAnalyzer* instance,
|
||||
ReaderAnalyzerParseDataCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
NfcProtocol
|
||||
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) {
|
||||
furi_assert(instance);
|
||||
furi_assert(buff_rx);
|
||||
UNUSED(len);
|
||||
NfcProtocol protocol = NfcDeviceProtocolUnknown;
|
||||
|
||||
if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) {
|
||||
protocol = NfcDeviceProtocolMifareClassic;
|
||||
}
|
||||
|
||||
return protocol;
|
||||
}
|
||||
|
||||
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
|
||||
furi_assert(instance);
|
||||
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
|
||||
return &instance->nfc_data;
|
||||
}
|
||||
|
||||
void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(nfc_data);
|
||||
|
||||
memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData));
|
||||
}
|
||||
|
||||
static void reader_analyzer_write(
|
||||
ReaderAnalyzer* instance,
|
||||
uint8_t* data,
|
||||
uint16_t len,
|
||||
bool reader_to_tag,
|
||||
bool crc_dropped) {
|
||||
ReaderAnalyzerHeader header = {
|
||||
.reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len};
|
||||
size_t data_sent = 0;
|
||||
data_sent = furi_stream_buffer_send(
|
||||
instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever);
|
||||
if(data_sent != sizeof(ReaderAnalyzerHeader)) {
|
||||
FURI_LOG_W(TAG, "Sent %zu out of %zu bytes", data_sent, sizeof(ReaderAnalyzerHeader));
|
||||
}
|
||||
data_sent = furi_stream_buffer_send(instance->stream, data, len, FuriWaitForever);
|
||||
if(data_sent != len) {
|
||||
FURI_LOG_W(TAG, "Sent %zu out of %u bytes", data_sent, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
|
||||
UNUSED(crc_dropped);
|
||||
ReaderAnalyzer* reader_analyzer = context;
|
||||
uint16_t bytes = bits < 8 ? 1 : bits / 8;
|
||||
reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped);
|
||||
}
|
||||
|
||||
static void
|
||||
reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
|
||||
UNUSED(crc_dropped);
|
||||
ReaderAnalyzer* reader_analyzer = context;
|
||||
uint16_t bytes = bits < 8 ? 1 : bits / 8;
|
||||
reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped);
|
||||
}
|
||||
|
||||
void reader_analyzer_prepare_tx_rx(
|
||||
ReaderAnalyzer* instance,
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
bool is_picc) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_rx);
|
||||
|
||||
if(is_picc) {
|
||||
tx_rx->sniff_tx = reader_analyzer_write_rx;
|
||||
tx_rx->sniff_rx = reader_analyzer_write_tx;
|
||||
} else {
|
||||
tx_rx->sniff_rx = reader_analyzer_write_rx;
|
||||
tx_rx->sniff_tx = reader_analyzer_write_tx;
|
||||
}
|
||||
|
||||
tx_rx->sniff_context = instance;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <lib/nfc/nfc_device.h>
|
||||
|
||||
typedef enum {
|
||||
ReaderAnalyzerModeDebugLog = 0x01,
|
||||
ReaderAnalyzerModeMfkey = 0x02,
|
||||
ReaderAnalyzerModeDebugPcap = 0x04,
|
||||
} ReaderAnalyzerMode;
|
||||
|
||||
typedef enum {
|
||||
ReaderAnalyzerEventMfkeyCollected,
|
||||
} ReaderAnalyzerEvent;
|
||||
|
||||
typedef struct ReaderAnalyzer ReaderAnalyzer;
|
||||
|
||||
typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context);
|
||||
|
||||
ReaderAnalyzer* reader_analyzer_alloc();
|
||||
|
||||
void reader_analyzer_free(ReaderAnalyzer* instance);
|
||||
|
||||
void reader_analyzer_set_callback(
|
||||
ReaderAnalyzer* instance,
|
||||
ReaderAnalyzerParseDataCallback callback,
|
||||
void* context);
|
||||
|
||||
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode);
|
||||
|
||||
void reader_analyzer_stop(ReaderAnalyzer* instance);
|
||||
|
||||
NfcProtocol
|
||||
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len);
|
||||
|
||||
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
|
||||
|
||||
void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data);
|
||||
|
||||
void reader_analyzer_prepare_tx_rx(
|
||||
ReaderAnalyzer* instance,
|
||||
FuriHalNfcTxRxContext* tx_rx,
|
||||
bool is_picc);
|
||||
Reference in New Issue
Block a user