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:
gornekich
2023-10-24 07:08:09 +04:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41488 additions and 68125 deletions
+40 -7
View File
@@ -5,20 +5,53 @@ env.Append(
"#/lib/nfc",
],
SDK_HEADERS=[
# Main
File("nfc.h"),
File("nfc_device.h"),
File("nfc_worker.h"),
File("nfc_types.h"),
File("helpers/mfkey32.h"),
File("parsers/nfc_supported_card.h"),
File("helpers/nfc_generators.h"),
File("protocols/nfc_util.h"),
File("nfc_listener.h"),
File("nfc_poller.h"),
File("nfc_scanner.h"),
# Protocols
File("protocols/iso14443_3a/iso14443_3a.h"),
File("protocols/iso14443_3b/iso14443_3b.h"),
File("protocols/iso14443_4a/iso14443_4a.h"),
File("protocols/iso14443_4b/iso14443_4b.h"),
File("protocols/mf_ultralight/mf_ultralight.h"),
File("protocols/mf_classic/mf_classic.h"),
File("protocols/mf_desfire/mf_desfire.h"),
File("protocols/slix/slix.h"),
File("protocols/st25tb/st25tb.h"),
# Pollers
File("protocols/iso14443_3a/iso14443_3a_poller.h"),
File("protocols/iso14443_3b/iso14443_3b_poller.h"),
File("protocols/iso14443_4a/iso14443_4a_poller.h"),
File("protocols/iso14443_4b/iso14443_4b_poller.h"),
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
File("protocols/mf_classic/mf_classic_poller.h"),
File("protocols/mf_desfire/mf_desfire_poller.h"),
File("protocols/st25tb/st25tb_poller.h"),
# Listeners
File("protocols/iso14443_3a/iso14443_3a_listener.h"),
File("protocols/iso14443_4a/iso14443_4a_listener.h"),
File("protocols/mf_ultralight/mf_ultralight_listener.h"),
File("protocols/mf_classic/mf_classic_listener.h"),
# Sync API
File("protocols/iso14443_3a/iso14443_3a_poller_sync_api.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync_api.h"),
File("protocols/mf_classic/mf_classic_poller_sync_api.h"),
# Misc
File("helpers/nfc_util.h"),
File("helpers/iso14443_crc.h"),
File("helpers/iso13239_crc.h"),
File("helpers/nfc_data_generator.h"),
File("helpers/nfc_dict.h"),
],
)
libenv = env.Clone(FW_LIB_NAME="nfc")
libenv.ApplyLibFlags()
sources = libenv.GlobRecursive("*.c*")
sources = libenv.GlobRecursive("*.c*", exclude="deprecated/*c")
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
+52
View File
@@ -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);
}
+22
View File
@@ -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
+62
View File
@@ -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);
}
+27
View File
@@ -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
+64
View File
@@ -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;
}
+29
View File
@@ -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
+57
View File
@@ -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);
}
+27
View File
@@ -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
-346
View File
@@ -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;
}
-107
View File
@@ -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
-228
View File
@@ -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;
}
-34
View File
@@ -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
+566
View File
@@ -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);
}
+40
View File
@@ -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
-71
View File
@@ -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);
}
-17
View File
@@ -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);
-130
View File
@@ -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);
}
-17
View File
@@ -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);
+270
View File
@@ -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;
}
+103
View File
@@ -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
-528
View File
@@ -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,
};
-14
View File
@@ -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);
-265
View File
@@ -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;
}
-43
View File
@@ -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);
+649
View File
@@ -0,0 +1,649 @@
#ifndef FW_CFG_unit_tests
#include "nfc.h"
#include <furi_hal_nfc.h>
#include <furi/furi.h>
#define TAG "Nfc"
#define NFC_MAX_BUFFER_SIZE (256)
typedef enum {
NfcStateIdle,
NfcStateRunning,
} NfcState;
typedef enum {
NfcPollerStateStart,
NfcPollerStateReady,
NfcPollerStateReset,
NfcPollerStateStop,
NfcPollerStateNum,
} NfcPollerState;
typedef enum {
NfcCommStateIdle,
NfcCommStateWaitBlockTxTimer,
NfcCommStateReadyTx,
NfcCommStateWaitTxEnd,
NfcCommStateWaitRxStart,
NfcCommStateWaitRxEnd,
NfcCommStateFailed,
} NfcCommState;
typedef enum {
NfcConfigurationStateIdle,
NfcConfigurationStateDone,
} NfcConfigurationState;
struct Nfc {
NfcState state;
NfcPollerState poller_state;
NfcCommState comm_state;
NfcConfigurationState config_state;
NfcMode mode;
uint32_t fdt_listen_fc;
uint32_t mask_rx_time_fc;
uint32_t fdt_poll_fc;
uint32_t fdt_poll_poll_us;
uint32_t guard_time_us;
NfcEventCallback callback;
void* context;
uint8_t tx_buffer[NFC_MAX_BUFFER_SIZE];
size_t tx_bits;
uint8_t rx_buffer[NFC_MAX_BUFFER_SIZE];
size_t rx_bits;
FuriThread* worker_thread;
};
typedef bool (*NfcWorkerPollerStateHandler)(Nfc* instance);
static const FuriHalNfcTech nfc_tech_table[NfcModeNum][NfcTechNum] = {
[NfcModePoller] =
{
[NfcTechIso14443a] = FuriHalNfcTechIso14443a,
[NfcTechIso14443b] = FuriHalNfcTechIso14443b,
[NfcTechIso15693] = FuriHalNfcTechIso15693,
[NfcTechFelica] = FuriHalNfcTechFelica,
},
[NfcModeListener] =
{
[NfcTechIso14443a] = FuriHalNfcTechIso14443a,
[NfcTechIso14443b] = FuriHalNfcTechInvalid,
[NfcTechIso15693] = FuriHalNfcTechIso15693,
[NfcTechFelica] = FuriHalNfcTechInvalid,
},
};
static NfcError nfc_process_hal_error(FuriHalNfcError error) {
NfcError ret = NfcErrorNone;
switch(error) {
case FuriHalNfcErrorNone:
ret = NfcErrorNone;
break;
case FuriHalNfcErrorIncompleteFrame:
ret = NfcErrorIncompleteFrame;
break;
case FuriHalNfcErrorDataFormat:
ret = NfcErrorDataFormat;
break;
default:
ret = NfcErrorInternal;
}
return ret;
}
static int32_t nfc_worker_listener(void* context) {
furi_assert(context);
Nfc* instance = context;
furi_assert(instance->callback);
furi_assert(instance->config_state == NfcConfigurationStateDone);
instance->state = NfcStateRunning;
furi_hal_nfc_event_start();
NfcEventData event_data = {};
event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
NfcEvent nfc_event = {.data = event_data};
NfcCommand command = NfcCommandContinue;
while(true) {
FuriHalNfcEvent event = furi_hal_nfc_listener_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
if(event & FuriHalNfcEventAbortRequest) {
nfc_event.type = NfcEventTypeUserAbort;
instance->callback(nfc_event, instance->context);
break;
}
if(event & FuriHalNfcEventFieldOn) {
nfc_event.type = NfcEventTypeFieldOn;
instance->callback(nfc_event, instance->context);
}
if(event & FuriHalNfcEventFieldOff) {
nfc_event.type = NfcEventTypeFieldOff;
instance->callback(nfc_event, instance->context);
furi_hal_nfc_listener_idle();
}
if(event & FuriHalNfcEventListenerActive) {
nfc_event.type = NfcEventTypeListenerActivated;
instance->callback(nfc_event, instance->context);
}
if(event & FuriHalNfcEventRxEnd) {
furi_hal_nfc_timer_block_tx_start(instance->fdt_listen_fc);
nfc_event.type = NfcEventTypeRxEnd;
furi_hal_nfc_listener_rx(
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
bit_buffer_copy_bits(event_data.buffer, instance->rx_buffer, instance->rx_bits);
command = instance->callback(nfc_event, instance->context);
if(command == NfcCommandStop) {
break;
} else if(command == NfcCommandReset) {
furi_hal_nfc_listener_enable_rx();
} else if(command == NfcCommandSleep) {
furi_hal_nfc_listener_sleep();
}
}
}
furi_hal_nfc_reset_mode();
instance->config_state = NfcConfigurationStateIdle;
bit_buffer_free(event_data.buffer);
furi_hal_nfc_low_power_mode_start();
return 0;
}
bool nfc_worker_poller_start_handler(Nfc* instance) {
furi_hal_nfc_poller_field_on();
if(instance->guard_time_us) {
furi_hal_nfc_timer_block_tx_start_us(instance->guard_time_us);
FuriHalNfcEvent event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
furi_assert(event & FuriHalNfcEventTimerBlockTxExpired);
}
instance->poller_state = NfcPollerStateReady;
return false;
}
bool nfc_worker_poller_ready_handler(Nfc* instance) {
NfcCommand command = NfcCommandContinue;
NfcEvent event = {.type = NfcEventTypePollerReady};
command = instance->callback(event, instance->context);
if(command == NfcCommandReset) {
instance->poller_state = NfcPollerStateReset;
} else if(command == NfcCommandStop) {
instance->poller_state = NfcPollerStateStop;
}
return false;
}
bool nfc_worker_poller_reset_handler(Nfc* instance) {
furi_hal_nfc_low_power_mode_start();
furi_delay_ms(100);
furi_hal_nfc_low_power_mode_stop();
instance->poller_state = NfcPollerStateStart;
return false;
}
bool nfc_worker_poller_stop_handler(Nfc* instance) {
furi_hal_nfc_reset_mode();
instance->config_state = NfcConfigurationStateIdle;
furi_hal_nfc_low_power_mode_start();
// Wait after field is off some time to reset tag power
furi_delay_ms(10);
instance->poller_state = NfcPollerStateStart;
return true;
}
static const NfcWorkerPollerStateHandler nfc_worker_poller_state_handlers[NfcPollerStateNum] = {
[NfcPollerStateStart] = nfc_worker_poller_start_handler,
[NfcPollerStateReady] = nfc_worker_poller_ready_handler,
[NfcPollerStateReset] = nfc_worker_poller_reset_handler,
[NfcPollerStateStop] = nfc_worker_poller_stop_handler,
};
static int32_t nfc_worker_poller(void* context) {
furi_assert(context);
Nfc* instance = context;
furi_assert(instance->callback);
instance->state = NfcStateRunning;
instance->poller_state = NfcPollerStateStart;
furi_hal_nfc_event_start();
bool exit = false;
while(!exit) {
exit = nfc_worker_poller_state_handlers[instance->poller_state](instance);
}
return 0;
}
Nfc* nfc_alloc() {
furi_check(furi_hal_nfc_acquire() == FuriHalNfcErrorNone);
Nfc* instance = malloc(sizeof(Nfc));
instance->state = NfcStateIdle;
instance->comm_state = NfcCommStateIdle;
instance->config_state = NfcConfigurationStateIdle;
instance->worker_thread = furi_thread_alloc();
furi_thread_set_name(instance->worker_thread, "NfcWorker");
furi_thread_set_context(instance->worker_thread, instance);
furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHighest);
furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);
return instance;
}
void nfc_free(Nfc* instance) {
furi_assert(instance);
furi_assert(instance->state == NfcStateIdle);
furi_thread_free(instance->worker_thread);
free(instance);
furi_hal_nfc_release();
}
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {
furi_assert(instance);
furi_assert(mode < NfcModeNum);
furi_assert(tech < NfcTechNum);
furi_assert(instance->config_state == NfcConfigurationStateIdle);
FuriHalNfcTech hal_tech = nfc_tech_table[mode][tech];
if(hal_tech == FuriHalNfcTechInvalid) {
furi_crash("Unsupported mode for given tech");
}
FuriHalNfcMode hal_mode = (mode == NfcModePoller) ? FuriHalNfcModePoller :
FuriHalNfcModeListener;
furi_hal_nfc_low_power_mode_stop();
furi_hal_nfc_set_mode(hal_mode, hal_tech);
instance->mode = mode;
instance->config_state = NfcConfigurationStateDone;
}
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {
furi_assert(instance);
instance->fdt_poll_fc = fdt_poll_fc;
}
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
furi_assert(instance);
instance->fdt_listen_fc = fdt_listen_fc;
}
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
furi_assert(instance);
instance->fdt_poll_poll_us = fdt_poll_poll_us;
}
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
furi_assert(instance);
instance->guard_time_us = guard_time_us;
}
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
furi_assert(instance);
instance->mask_rx_time_fc = mask_rx_time_fc;
}
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
furi_assert(instance);
furi_assert(instance->worker_thread);
furi_assert(callback);
furi_assert(instance->config_state == NfcConfigurationStateDone);
instance->callback = callback;
instance->context = context;
if(instance->mode == NfcModePoller) {
furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);
} else {
furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);
}
instance->comm_state = NfcCommStateIdle;
furi_thread_start(instance->worker_thread);
}
void nfc_stop(Nfc* instance) {
furi_assert(instance);
furi_assert(instance->state == NfcStateRunning);
if(instance->mode == NfcModeListener) {
furi_hal_nfc_abort();
}
furi_thread_join(instance->worker_thread);
instance->state = NfcStateIdle;
}
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
NfcError ret = NfcErrorNone;
while(furi_hal_nfc_timer_block_tx_is_running()) {
}
FuriHalNfcError error =
furi_hal_nfc_listener_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in listener TX");
ret = nfc_process_hal_error(error);
}
return ret;
}
static NfcError nfc_poller_trx_state_machine(Nfc* instance, uint32_t fwt_fc) {
FuriHalNfcEvent event = 0;
NfcError error = NfcErrorNone;
while(true) {
event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
if(event & FuriHalNfcEventTimerBlockTxExpired) {
if(instance->comm_state == NfcCommStateWaitBlockTxTimer) {
instance->comm_state = NfcCommStateReadyTx;
}
}
if(event & FuriHalNfcEventTxEnd) {
if(instance->comm_state == NfcCommStateWaitTxEnd) {
if(fwt_fc) {
furi_hal_nfc_timer_fwt_start(fwt_fc);
}
furi_hal_nfc_timer_block_tx_start_us(instance->fdt_poll_poll_us);
instance->comm_state = NfcCommStateWaitRxStart;
}
}
if(event & FuriHalNfcEventRxStart) {
if(instance->comm_state == NfcCommStateWaitRxStart) {
furi_hal_nfc_timer_block_tx_stop();
furi_hal_nfc_timer_fwt_stop();
instance->comm_state = NfcCommStateWaitRxEnd;
}
}
if(event & FuriHalNfcEventRxEnd) {
furi_hal_nfc_timer_block_tx_start(instance->fdt_poll_fc);
furi_hal_nfc_timer_fwt_stop();
instance->comm_state = NfcCommStateWaitBlockTxTimer;
break;
}
if(event & FuriHalNfcEventTimerFwtExpired) {
if(instance->comm_state == NfcCommStateWaitRxStart) {
error = NfcErrorTimeout;
FURI_LOG_D(TAG, "FWT Timeout");
if(furi_hal_nfc_timer_block_tx_is_running()) {
instance->comm_state = NfcCommStateWaitBlockTxTimer;
} else {
instance->comm_state = NfcCommStateReadyTx;
}
break;
}
}
}
return error;
}
NfcError nfc_iso14443a_poller_trx_custom_parity(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
furi_assert(instance->poller_state == NfcPollerStateReady);
NfcError ret = NfcErrorNone;
FuriHalNfcError error = FuriHalNfcErrorNone;
do {
furi_hal_nfc_trx_reset();
while(furi_hal_nfc_timer_block_tx_is_running()) {
FuriHalNfcEvent event =
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
}
bit_buffer_write_bytes_with_parity(
tx_buffer, instance->tx_buffer, sizeof(instance->tx_buffer), &instance->tx_bits);
error =
furi_hal_nfc_iso14443a_poller_tx_custom_parity(instance->tx_buffer, instance->tx_bits);
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller TX");
ret = nfc_process_hal_error(error);
break;
}
instance->comm_state = NfcCommStateWaitTxEnd;
ret = nfc_poller_trx_state_machine(instance, fwt);
if(ret != NfcErrorNone) {
FURI_LOG_T(TAG, "Failed TRX state machine");
break;
}
error = furi_hal_nfc_poller_rx(
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller RX");
ret = nfc_process_hal_error(error);
break;
}
if(instance->rx_bits >= 9) {
if((instance->rx_bits % 9) != 0) {
ret = NfcErrorDataFormat;
break;
}
}
bit_buffer_copy_bytes_with_parity(rx_buffer, instance->rx_buffer, instance->rx_bits);
} while(false);
return ret;
}
NfcError
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
furi_assert(instance->poller_state == NfcPollerStateReady);
NfcError ret = NfcErrorNone;
FuriHalNfcError error = FuriHalNfcErrorNone;
do {
furi_hal_nfc_trx_reset();
while(furi_hal_nfc_timer_block_tx_is_running()) {
FuriHalNfcEvent event =
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
}
error =
furi_hal_nfc_poller_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller TX");
ret = nfc_process_hal_error(error);
break;
}
instance->comm_state = NfcCommStateWaitTxEnd;
ret = nfc_poller_trx_state_machine(instance, fwt);
if(ret != NfcErrorNone) {
FURI_LOG_T(TAG, "Failed TRX state machine");
break;
}
error = furi_hal_nfc_poller_rx(
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller RX");
ret = nfc_process_hal_error(error);
break;
}
bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);
} while(false);
return ret;
}
NfcError nfc_iso14443a_listener_set_col_res_data(
Nfc* instance,
uint8_t* uid,
uint8_t uid_len,
uint8_t* atqa,
uint8_t sak) {
furi_assert(instance);
FuriHalNfcError error =
furi_hal_nfc_iso14443a_listener_set_col_res_data(uid, uid_len, atqa, sak);
instance->comm_state = NfcCommStateIdle;
return nfc_process_hal_error(error);
}
NfcError nfc_iso14443a_poller_trx_short_frame(
Nfc* instance,
NfcIso14443aShortFrame frame,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(rx_buffer);
FuriHalNfcaShortFrame short_frame = (frame == NfcIso14443aShortFrameAllReqa) ?
FuriHalNfcaShortFrameAllReq :
FuriHalNfcaShortFrameSensReq;
furi_assert(instance->poller_state == NfcPollerStateReady);
NfcError ret = NfcErrorNone;
FuriHalNfcError error = FuriHalNfcErrorNone;
do {
furi_hal_nfc_trx_reset();
while(furi_hal_nfc_timer_block_tx_is_running()) {
FuriHalNfcEvent event =
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
}
error = furi_hal_nfc_iso14443a_poller_trx_short_frame(short_frame);
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller TX");
ret = nfc_process_hal_error(error);
break;
}
instance->comm_state = NfcCommStateWaitTxEnd;
ret = nfc_poller_trx_state_machine(instance, fwt);
if(ret != NfcErrorNone) {
FURI_LOG_T(TAG, "Failed TRX state machine");
break;
}
error = furi_hal_nfc_poller_rx(
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller RX");
ret = nfc_process_hal_error(error);
break;
}
bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);
} while(false);
return ret;
}
NfcError nfc_iso14443a_poller_trx_sdd_frame(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
furi_assert(instance->poller_state == NfcPollerStateReady);
NfcError ret = NfcErrorNone;
FuriHalNfcError error = FuriHalNfcErrorNone;
do {
furi_hal_nfc_trx_reset();
while(furi_hal_nfc_timer_block_tx_is_running()) {
FuriHalNfcEvent event =
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
}
error = furi_hal_nfc_iso14443a_tx_sdd_frame(
bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller TX");
ret = nfc_process_hal_error(error);
break;
}
instance->comm_state = NfcCommStateWaitTxEnd;
ret = nfc_poller_trx_state_machine(instance, fwt);
if(ret != NfcErrorNone) {
FURI_LOG_T(TAG, "Failed TRX state machine");
break;
}
error = furi_hal_nfc_poller_rx(
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
if(error != FuriHalNfcErrorNone) {
FURI_LOG_D(TAG, "Failed in poller RX");
ret = nfc_process_hal_error(error);
break;
}
bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);
} while(false);
return ret;
}
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
NfcError ret = NfcErrorNone;
FuriHalNfcError error = FuriHalNfcErrorNone;
const uint8_t* tx_data = bit_buffer_get_data(tx_buffer);
const uint8_t* tx_parity = bit_buffer_get_parity(tx_buffer);
size_t tx_bits = bit_buffer_get_size(tx_buffer);
error = furi_hal_nfc_iso14443a_listener_tx_custom_parity(tx_data, tx_parity, tx_bits);
ret = nfc_process_hal_error(error);
return ret;
}
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
furi_assert(instance);
while(furi_hal_nfc_timer_block_tx_is_running()) {
}
FuriHalNfcError error = furi_hal_nfc_iso15693_listener_tx_sof();
NfcError ret = nfc_process_hal_error(error);
return ret;
}
#endif // APP_UNIT_TESTS
+364
View File
@@ -0,0 +1,364 @@
/**
* @file nfc.h
* @brief Transport layer Nfc library.
*
* The Nfc layer is responsible for setting the operating mode (poller or listener)
* and technology (ISO14443-3A/B, ISO15693, ...), data exchange between higher
* protocol-specific levels and underlying NFC hardware, as well as timings handling.
*
* In applications using the NFC protocol system there is no need to neiter explicitly
* create an Nfc instance nor call any of its functions, as it is all handled
* automatically under the hood.
*
* If the NFC protocol system is not suitable for the application's intended purpose
* or there is need of having direct access to the NFC transport layer, then an Nfc
* instance must be allocated and the below functions shall be used to implement
* the required logic.
*/
#pragma once
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Nfc opaque type definition.
*/
typedef struct Nfc Nfc;
/**
* @brief Enumeration of possible Nfc event types.
*
* Not all technologies implement all events (this is due to hardware limitations).
*/
typedef enum {
NfcEventTypeUserAbort, /**< User code explicitly aborted the current operation. */
NfcEventTypeFieldOn, /**< Reader's field was detected by the NFC hardware. */
NfcEventTypeFieldOff, /**< Reader's field was lost. */
NfcEventTypeTxStart, /**< Data transmission has started. */
NfcEventTypeTxEnd, /**< Data transmission has ended. */
NfcEventTypeRxStart, /**< Data reception has started. */
NfcEventTypeRxEnd, /**< Data reception has ended. */
NfcEventTypeListenerActivated, /**< The listener has been activated by the reader. */
NfcEventTypePollerReady, /**< The card has been activated by the poller. */
} NfcEventType;
/**
* @brief Nfc event data structure.
*/
typedef struct {
BitBuffer* buffer; /**< Pointer to the received data buffer. */
} NfcEventData;
/**
* @brief Nfc event structure.
*
* Upon emission of an event, an instance of this struct will be passed to the callback.
*/
typedef struct {
NfcEventType type; /**< Type of the emitted event. */
NfcEventData data; /**< Event-specific data. */
} NfcEvent;
/**
* @brief Enumeration of possible Nfc commands.
*
* The event callback must return one of these to determine the next action.
*/
typedef enum {
NfcCommandContinue, /**< Continue operation normally. */
NfcCommandReset, /**< Reset the current state. */
NfcCommandStop, /**< Stop the current operation. */
NfcCommandSleep, /**< Switch Nfc hardware to low-power mode. */
} NfcCommand;
/**
* @brief Nfc event callback type.
*
* A function of this type must be passed as the callback parameter upon start of a an Nfc instance.
*
* @param [in] event Nfc event, passed by value, complete with protocol type and data.
* @param [in,out] context pointer to the user-specific context (set when starting an Nfc instance).
* @returns command which the event producer must execute.
*/
typedef NfcCommand (*NfcEventCallback)(NfcEvent event, void* context);
/**
* @brief Enumeration of possible operating modes.
*
* Not all technologies implement the listener operating mode.
*/
typedef enum {
NfcModePoller, /**< Configure the Nfc instance as a poller. */
NfcModeListener, /**< Configure the Nfc instance as a listener. */
NfcModeNum, /**< Operating mode count. Internal use. */
} NfcMode;
/**
* @brief Enumeration of available technologies.
*/
typedef enum {
NfcTechIso14443a, /**< Configure the Nfc instance to use the ISO14443-3A technology. */
NfcTechIso14443b, /**< Configure the Nfc instance to use the ISO14443-3B technology. */
NfcTechIso15693, /**< Configure the Nfc instance to use the ISO15693 technology. */
NfcTechFelica, /**< Configure the Nfc instance to use the FeliCa technology. */
NfcTechNum, /**< Technologies count. Internal use. */
} NfcTech;
/**
* @brief Enumeration of possible Nfc error codes.
*/
typedef enum {
NfcErrorNone, /**< No error has occurred. */
NfcErrorInternal, /**< An unknown error has occured on the lower level. */
NfcErrorTimeout, /**< Operation is taking too long (e.g. card does not respond). */
NfcErrorIncompleteFrame, /**< An incomplete data frame has been received. */
NfcErrorDataFormat, /**< Data has not been parsed due to wrong/unknown format. */
} NfcError;
/**
* @brief Allocate an Nfc instance.
*
* Will exclusively take over the NFC HAL until deleted.
*
* @returns pointer to the allocated Nfc instance.
*/
Nfc* nfc_alloc();
/**
* @brief Delete an Nfc instance.
*
* Will release the NFC HAL lock, making it available for use by others.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void nfc_free(Nfc* instance);
/**
* @brief Configure the Nfc instance to work in a particular mode.
*
* Not all technologies implement the listener operating mode.
*
* @param[in,out] instance pointer to the instance to be configured.
* @param[in] mode required operating mode.
* @param[in] tech required technology configuration.
*/
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech);
/**
* @brief Set poller frame delay time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] fdt_poll_fc frame delay time, in carrier cycles.
*/
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc);
/**
* @brief Set listener frame delay time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] fdt_listen_fc frame delay time, in carrier cycles.
*/
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc);
/**
* @brief Set mask receive time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] mask_rx_time mask receive time, in carrier cycles.
*/
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc);
/**
* @brief Set frame delay time.
*
* Frame delay time is the minimum time between two consecutive poll frames.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] fdt_poll_poll_us frame delay time, in microseconds.
*/
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us);
/**
* @brief Set guard time.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] guard_time_us guard time, in microseconds.
*/
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us);
/**
* @brief Start the Nfc instance.
*
* The instance must be configured to work with a specific technology
* in a specific operating mode with a nfc_config() call before starting.
*
* Once started, the user code will be receiving events through the provided
* callback which must handle them according to the logic required.
*
* @param[in,out] instance pointer to the instance to be started.
* @param[in] callback pointer to a user-defined callback function which will receive events.
* @param[in] context pointer to a user-specific context (will be passed to the callback).
*/
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context);
/**
* @brief Stop Nfc instance.
*
* The instance can only be stopped if it is running.
*
* @param[in,out] instance pointer to the instance to be stopped.
*/
void nfc_stop(Nfc* instance);
/**
* @brief Transmit and receive a data frame in poller mode.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer, with a timeout defined by the fwt parameter.
*
* The data being transmitted and received may be either bit- or byte-oriented.
* It shall not contain any technology-specific sequences as start or stop bits
* and/or other special symbols, as this is handled on the underlying HAL level.
*
* Must ONLY be used inside the callback function.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt);
/**
* @brief Transmit a data frame in listener mode.
*
* Used to transmit a response to the reader request in listener mode.
*
* The data being transmitted may be either bit- or byte-oriented.
* It shall not contain any technology-specific sequences as start or stop bits
* and/or other special symbols, as this is handled on the underlying HAL level.
*
* Must ONLY be used inside the callback function.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer);
/*
* Technology-specific functions.
*
* In a perfect world, this would not be necessary.
* However, the current implementation employs NFC hardware that partially implements
* certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features.
*/
/******************* ISO14443-3A specific API *******************/
/**
* @brief Enumeration of possible ISO14443-3A short frame types.
*/
typedef enum {
NfcIso14443aShortFrameSensReq,
NfcIso14443aShortFrameAllReqa,
} NfcIso14443aShortFrame;
/**
* @brief Transmit an ISO14443-3A short frame and receive the response in poller mode.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] frame type of short frame to be sent.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_poller_trx_short_frame(
Nfc* instance,
NfcIso14443aShortFrame frame,
BitBuffer* rx_buffer,
uint32_t fwt);
/**
* @brief Transmit an ISO14443-3A SDD frame and receive the response in poller mode.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_poller_trx_sdd_frame(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
/**
* @brief Transmit an ISO14443-3A data frame with custom parity bits and receive the response in poller mode.
*
* Same as nfc_poller_trx(), but uses the parity bits provided by the user code
* instead of calculating them automatically.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_poller_trx_custom_parity(
Nfc* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
/**
* @brief Transmit an ISO14443-3A frame with custom parity bits in listener mode.
*
* Same as nfc_listener_tx(), but uses the parity bits provided by the user code
* instead of calculating them automatically.
*
* @param[in,out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer);
/**
* @brief Set ISO14443-3A collision resolution parameters in listener mode.
*
* Configures the NFC hardware for automatic collision resolution.
*
* @param[in,out] instance pointer to the instance to be configured.
* @param[in] uid pointer to a byte array containing the UID.
* @param[in] uid_len UID length in bytes (must be supported by the protocol).
* @param[in] atqa ATQA byte value.
* @param[in] sak SAK byte value.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso14443a_listener_set_col_res_data(
Nfc* instance,
uint8_t* uid,
uint8_t uid_len,
uint8_t* atqa,
uint8_t sak);
/**
* @brief Send ISO15693 Start of Frame pattern in listener mode
*
* @param[in,out] instance pointer to the instance to be configured.
* @returns NfcErrorNone on success, any other error code on failure.
*/
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance);
#ifdef __cplusplus
}
#endif
+22
View File
@@ -0,0 +1,22 @@
/**
* @file nfc_common.h
* @brief Various common NFC-related macros.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* NFC file format version which changed ATQA format. Deprecated. */
#define NFC_LSB_ATQA_FORMAT_VERSION (2)
/* NFC file format version which is still supported as backwards compatible. */
#define NFC_MINIMUM_SUPPORTED_FORMAT_VERSION NFC_LSB_ATQA_FORMAT_VERSION
/* NFC file format version which implemented the unified loading process. */
#define NFC_UNIFIED_FORMAT_VERSION (4)
/* Current NFC file format version. */
#define NFC_CURRENT_FORMAT_VERSION NFC_UNIFIED_FORMAT_VERSION
#ifdef __cplusplus
}
#endif
+287 -1666
View File
File diff suppressed because it is too large Load Diff
+236 -97
View File
@@ -1,126 +1,265 @@
/**
* @file nfc_device.h
* @brief Abstract interface for managing NFC device data.
*
* Under the hood, it makes use of the protocol-specific functions that each one of them provides
* and abstracts it with a protocol-independent API.
*
* It does not perform any signal processing, but merely serves as a container with some handy
* operations such as loading and saving from and to a file.
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <storage/storage.h>
#include <dialogs/dialogs.h>
#include <furi_hal_nfc.h>
#include <lib/nfc/helpers/mf_classic_dict.h>
#include <lib/nfc/protocols/emv.h>
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfcv.h>
#include "protocols/nfc_device_base.h"
#include "protocols/nfc_protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NFC_DEV_NAME_MAX_LEN 22
#define NFC_READER_DATA_MAX_SIZE 64
#define NFC_DICT_KEY_BATCH_SIZE 10
#define NFC_APP_FILENAME_PREFIX "NFC"
#define NFC_APP_FILENAME_EXTENSION ".nfc"
#define NFC_APP_SHADOW_EXTENSION ".shd"
/**
* @brief NfcDevice opaque type definition.
*/
typedef struct NfcDevice NfcDevice;
/**
* @brief Loading callback function signature.
*
* A function with such signature can be set as a callback to indicate
* the completion (or a failure) of nfc_device_load() and nfc_device_save() functions.
*
* This facility is commonly used to control GUI elements, such as progress dialogs.
*
* @param[in] context user-defined context that was passed in nfc_device_set_loading_callback().
* @param[in] state true if the data was loaded successfully, false otherwise.
*/
typedef void (*NfcLoadingCallback)(void* context, bool state);
typedef enum {
NfcDeviceProtocolUnknown,
NfcDeviceProtocolEMV,
NfcDeviceProtocolMifareUl,
NfcDeviceProtocolMifareClassic,
NfcDeviceProtocolMifareDesfire,
NfcDeviceProtocolNfcV
} NfcProtocol;
typedef enum {
NfcDeviceSaveFormatUid,
NfcDeviceSaveFormatBankCard,
NfcDeviceSaveFormatMifareUl,
NfcDeviceSaveFormatMifareClassic,
NfcDeviceSaveFormatMifareDesfire,
NfcDeviceSaveFormatNfcV,
} NfcDeviceSaveFormat;
typedef struct {
uint8_t data[NFC_READER_DATA_MAX_SIZE];
uint16_t size;
} NfcReaderRequestData;
typedef struct {
MfClassicDict* dict;
uint8_t current_sector;
} NfcMfClassicDictAttackData;
typedef enum {
NfcReadModeAuto,
NfcReadModeMfClassic,
NfcReadModeMfUltralight,
NfcReadModeMfDesfire,
NfcReadModeNFCA,
} NfcReadMode;
typedef struct {
FuriHalNfcDevData nfc_data;
NfcProtocol protocol;
NfcReadMode read_mode;
union {
NfcReaderRequestData reader_data;
NfcMfClassicDictAttackData mf_classic_dict_attack_data;
MfUltralightAuth mf_ul_auth;
};
union {
EmvData emv_data;
MfUltralightData mf_ul_data;
MfClassicData mf_classic_data;
MifareDesfireData mf_df_data;
NfcVData nfcv_data;
};
FuriString* parsed_data;
} NfcDeviceData;
typedef struct {
Storage* storage;
DialogsApp* dialogs;
NfcDeviceData dev_data;
char dev_name[NFC_DEV_NAME_MAX_LEN + 1];
FuriString* load_path;
FuriString* folder;
NfcDeviceSaveFormat format;
bool shadow_file_exist;
NfcLoadingCallback loading_cb;
void* loading_cb_ctx;
} NfcDevice;
/**
* @brief Allocate an NfcDevice instance.
*
* A newly created instance does not hold any data and thus is considered invalid. The most common
* use case would be to set its data by calling nfc_device_set_data() right afterwards.
*
* @returns pointer to the allocated instance.
*/
NfcDevice* nfc_device_alloc();
void nfc_device_free(NfcDevice* nfc_dev);
/**
* @brief Delete an NfcDevice instance.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void nfc_device_free(NfcDevice* instance);
void nfc_device_set_name(NfcDevice* dev, const char* name);
/**
* @brief Clear an NfcDevice instance.
*
* All data contained in the instance will be deleted and the instance itself will become invalid
* as if it was just allocated.
*
* @param[in,out] instance pointer to the instance to be cleared.
*/
void nfc_device_clear(NfcDevice* instance);
bool nfc_device_save(NfcDevice* dev, const char* dev_name);
/**
* @brief Reset an NfcDevice instance.
*
* The data contained in the instance will be reset according to the protocol-defined procedure.
* Unlike the nfc_device_clear() function, the instance will remain valid.
*
* @param[in,out] instance pointer to the instance to be reset.
*/
void nfc_device_reset(NfcDevice* instance);
bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name);
/**
* @brief Get the protocol identifier from an NfcDevice instance.
*
* If the instance is invalid, the return value will be NfcProtocolInvalid.
*
* @param[in] instance pointer to the instance to be queried.
* @returns protocol identifier contained in the instance.
*/
NfcProtocol nfc_device_get_protocol(const NfcDevice* instance);
bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog);
/**
* @brief Get the protocol-specific data from an NfcDevice instance.
*
* The protocol parameter's behaviour is a bit tricky. The function will check
* whether there is such a protocol somewhere in the protocol hierarchy and return
* the data exactly from that level.
*
* Example: Call nfc_device_get_data() on an instance with Mf DESFire protocol.
* The protocol hierarchy will look like the following:
*
* `Mf DESFire --> ISO14443-4A --> ISO14443-3A`
*
* Thus, the following values of the protocol parameter are valid:
*
* * NfcProtocolIso14443_3a
* * NfcProtocolIso14443_4a
* * NfcProtocolMfDesfire
*
* and passing them to the call would result in the respective data being returned.
*
* However, supplying a protocol identifier which is not in the hierarchy will
* result in a crash. This is to improve type safety.
*
* @param instance pointer to the instance to be queried
* @param protocol protocol identifier of the data to be retrieved.
* @returns pointer to the instance's data.
*/
const NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol);
bool nfc_device_load_key_cache(NfcDevice* dev);
/**
* @brief Get the protocol name by its identifier.
*
* This function does not require an instance as its return result depends only
* the protocol identifier.
*
* @param[in] protocol numeric identifier of the protocol in question.
* @returns pointer to a statically allocated string containing the protocol name.
*/
const char* nfc_device_get_protocol_name(NfcProtocol protocol);
bool nfc_file_select(NfcDevice* dev);
/**
* @brief Get the name of an NfcDevice instance.
*
* The return value may change depending on the instance's internal state and the name_type parameter.
*
* @param[in] instance pointer to the instance to be queried.
* @param[in] name_type type of the name to be displayed.
* @returns pointer to a statically allocated string containing the device name.
*/
const char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type);
void nfc_device_data_clear(NfcDeviceData* dev);
/**
* @brief Get the unique identifier (UID) of an NfcDevice instance.
*
* The UID length is protocol-dependent. Additionally, a particular protocol might support
* several UID lengths.
*
* @param[in] instance pointer to the instance to be queried.
* @param[out] uid_len pointer to the variable to contain the UID length.
* @returns pointer to the byte array containing the instance's UID.
*/
const uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len);
void nfc_device_clear(NfcDevice* dev);
/**
* @brief Set the unique identifier (UID) of an NfcDevice instance.
*
* The UID length must be supported by the instance's protocol.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] uid pointer to the byte array containing the new UID.
* @param[in] uid_len length of the UID.
* @return true if the UID was valid and set, false otherwise.
*/
bool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len);
bool nfc_device_delete(NfcDevice* dev, bool use_load_path);
/**
* @brief Set the data and protocol of an NfcDevice instance.
*
* Any data previously contained in the instance will be deleted.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] protocol numeric identifier of the data's protocol.
* @param[in] protocol_data pointer to the protocol-specific data.
*/
void nfc_device_set_data(
NfcDevice* instance,
NfcProtocol protocol,
const NfcDeviceData* protocol_data);
bool nfc_device_restore(NfcDevice* dev, bool use_load_path);
/**
* @brief Copy (export) the data contained in an NfcDevice instance to an outside NfcDeviceData instance.
*
* This function does the inverse of nfc_device_set_data().
void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context);
* The protocol identifier passed as the protocol parameter MUST match the one
* stored in the instance, otherwise a crash will occur.
* This is to improve type safety.
*
* @param[in] instance pointer to the instance to be copied from.
* @param[in] protocol numeric identifier of the instance's protocol.
* @param[out] protocol_data pointer to the destination data.
*/
void nfc_device_copy_data(
const NfcDevice* instance,
NfcProtocol protocol,
NfcDeviceData* protocol_data);
/**
* @brief Check whether an NfcDevice instance holds certain data.
*
* This function's behaviour is similar to nfc_device_is_equal(), with the difference
* that it takes NfcProtocol and NfcDeviceData* instead of the second NfcDevice*.
*
* The following code snippets [1] and [2] are equivalent:
*
* [1]
* ```c
* bool is_equal = nfc_device_is_equal(device1, device2);
* ```
* [2]
* ```c
* NfcProtocol protocol = nfc_device_get_protocol(device2);
* const NfcDeviceData* data = nfc_device_get_data(device2, protocol);
* bool is_equal = nfc_device_is_equal_data(device1, protocol, data);
* ```
*
* @param[in] instance pointer to the instance to be compared.
* @param[in] protocol protocol identifier of the data to be compared.
* @param[in] protocol_data pointer to the NFC device data to be compared.
* @returns true if the instance is of the right type and the data matches, false otherwise.
*/
bool nfc_device_is_equal_data(
const NfcDevice* instance,
NfcProtocol protocol,
const NfcDeviceData* protocol_data);
/**
* @brief Compare two NfcDevice instances to determine whether they are equal.
*
* @param[in] instance pointer to the first instance to be compared.
* @param[in] other pointer to the second instance to be compared.
* @returns true if both instances are considered equal, false otherwise.
*/
bool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other);
/**
* @brief Set the loading callback function.
*
* @param[in,out] instance pointer to the instance to be modified.
* @param[in] callback pointer to a function to be called when the load operation completes.
* @param[in] context pointer to a user-specific context (will be passed to the callback).
*/
void nfc_device_set_loading_callback(
NfcDevice* instance,
NfcLoadingCallback callback,
void* context);
/**
* @brief Save NFC device data form an NfcDevice instance to a file.
*
* @param[in] instance pointer to the instance to be saved.
* @param[in] path pointer to a character string with a full file path.
* @returns true if the data was successfully saved, false otherwise.
*/
bool nfc_device_save(NfcDevice* instance, const char* path);
/**
* @brief Load NFC device data to an NfcDevice instance from a file.
*
* @param[in,out] instance pointer to the instance to be loaded into.
* @param[in] path pointer to a character string with a full file path.
* @returns true if the data was successfully loaded, false otherwise.
*/
bool nfc_device_load(NfcDevice* instance, const char* path);
#ifdef __cplusplus
}
+37
View File
@@ -0,0 +1,37 @@
#include "nfc_device_i.h"
#include "protocols/nfc_device_defs.h"
#include <furi/furi.h>
static NfcDeviceData*
nfc_device_search_base_protocol_data(const NfcDevice* instance, NfcProtocol protocol) {
NfcProtocol protocol_tmp = instance->protocol;
NfcDeviceData* dev_data_tmp = instance->protocol_data;
while(true) {
dev_data_tmp = nfc_devices[protocol_tmp]->get_base_data(dev_data_tmp);
protocol_tmp = nfc_protocol_get_parent(protocol_tmp);
if(protocol_tmp == protocol) {
break;
}
}
return dev_data_tmp;
}
NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol) {
furi_assert(instance);
furi_assert(protocol < NfcProtocolNum);
NfcDeviceData* dev_data = NULL;
if(instance->protocol == protocol) {
dev_data = instance->protocol_data;
} else if(nfc_protocol_has_parent(instance->protocol, protocol)) {
dev_data = nfc_device_search_base_protocol_data(instance, protocol);
} else {
furi_crash("Incorrect protocol");
}
return dev_data;
}
+46
View File
@@ -0,0 +1,46 @@
/**
* @file nfc_device_i.h
* @brief NfcDevice private types and definitions.
*
* This file is an implementation detail. It must not be included in
* any public API-related headers.
*/
#pragma once
#include "nfc_device.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief NfcDevice structure definition.
*/
struct NfcDevice {
NfcProtocol protocol; /**< Numeric identifier of the data's protocol*/
NfcDeviceData* protocol_data; /**< Pointer to the NFC device data. */
NfcLoadingCallback
loading_callback; /**< Pointer to the function to be called upon loading completion. */
void* loading_callback_context; /**< Pointer to the context to be passed to the loading callback. */
};
/**
* @brief Get the mutable (non-const) data from an NfcDevice instance.
*
* The behaviour is the same as with nfc_device_get_data(), but the
* return pointer is non-const, allowing for changing data it is pointing to.
*
* @see nfc_device.h
*
* Under the hood, nfc_device_get_data() calls this and then adds const-ness to the return value.
*
* @param instance pointer to the instance to be queried
* @param protocol protocol identifier of the data to be retrieved.
* @returns pointer to the instance's (mutable) data.
*/
NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol);
#ifdef __cplusplus
}
#endif
+144
View File
@@ -0,0 +1,144 @@
#include "nfc_listener.h"
#include <nfc/protocols/nfc_listener_defs.h>
#include <nfc/nfc_device_i.h>
#include <furi.h>
typedef struct NfcListenerListElement {
NfcProtocol protocol;
NfcGenericInstance* listener;
const NfcListenerBase* listener_api;
struct NfcListenerListElement* child;
} NfcListenerListElement;
typedef struct {
NfcListenerListElement* head;
NfcListenerListElement* tail;
} NfcListenerList;
struct NfcListener {
NfcProtocol protocol;
Nfc* nfc;
NfcListenerList list;
NfcDevice* nfc_dev;
};
static void nfc_listener_list_alloc(NfcListener* instance) {
instance->list.head = malloc(sizeof(NfcListenerListElement));
instance->list.head->protocol = instance->protocol;
instance->list.head->listener_api = nfc_listeners_api[instance->protocol];
instance->list.head->child = NULL;
instance->list.tail = instance->list.head;
// Build linked list
do {
NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol);
if(parent_protocol == NfcProtocolInvalid) break;
NfcListenerListElement* parent = malloc(sizeof(NfcListenerListElement));
parent->protocol = parent_protocol;
parent->listener_api = nfc_listeners_api[parent_protocol];
parent->child = instance->list.head;
instance->list.head = parent;
} while(true);
// Allocate listener instances
NfcListenerListElement* iter = instance->list.head;
NfcDeviceData* data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->protocol);
iter->listener = iter->listener_api->alloc(instance->nfc, data_tmp);
do {
if(iter->child == NULL) break;
data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->child->protocol);
iter->child->listener = iter->child->listener_api->alloc(iter->listener, data_tmp);
iter->listener_api->set_callback(
iter->listener, iter->child->listener_api->run, iter->child->listener);
iter = iter->child;
} while(true);
}
static void nfc_listener_list_free(NfcListener* instance) {
// Free listener instances
do {
instance->list.head->listener_api->free(instance->list.head->listener);
NfcListenerListElement* child = instance->list.head->child;
free(instance->list.head);
if(child == NULL) break;
instance->list.head = child;
} while(true);
}
NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data) {
furi_assert(nfc);
furi_assert(protocol < NfcProtocolNum);
furi_assert(data);
furi_assert(nfc_listeners_api[protocol]);
NfcListener* instance = malloc(sizeof(NfcListener));
instance->nfc = nfc;
instance->protocol = protocol;
instance->nfc_dev = nfc_device_alloc();
nfc_device_set_data(instance->nfc_dev, protocol, data);
nfc_listener_list_alloc(instance);
return instance;
}
void nfc_listener_free(NfcListener* instance) {
furi_assert(instance);
nfc_listener_list_free(instance);
nfc_device_free(instance->nfc_dev);
free(instance);
}
NfcCommand nfc_listener_start_callback(NfcEvent event, void* context) {
furi_assert(context);
NfcListener* instance = context;
furi_assert(instance->list.head);
NfcCommand command = NfcCommandContinue;
NfcGenericEvent generic_event = {
.protocol = NfcProtocolInvalid,
.instance = instance->nfc,
.event_data = &event,
};
NfcListenerListElement* head_listener = instance->list.head;
command = head_listener->listener_api->run(generic_event, head_listener->listener);
return command;
}
void nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
NfcListenerListElement* tail_element = instance->list.tail;
tail_element->listener_api->set_callback(tail_element->listener, callback, context);
nfc_start(instance->nfc, nfc_listener_start_callback, instance);
}
void nfc_listener_stop(NfcListener* instance) {
furi_assert(instance);
nfc_stop(instance->nfc);
}
NfcProtocol nfc_listener_get_protocol(const NfcListener* instance) {
furi_assert(instance);
return instance->protocol;
}
const NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol) {
furi_assert(instance);
furi_assert(instance->protocol == protocol);
NfcListenerListElement* tail_element = instance->list.tail;
return tail_element->listener_api->get_data(tail_element->listener);
}
+94
View File
@@ -0,0 +1,94 @@
/**
* @file nfc_listener.h
* @brief NFC card emulation library.
*
* Once started, it will respond to supported commands from an NFC reader, thus imitating
* (or emulating) an NFC card. The responses will depend on the data that was supplied to
* the listener, so various card types and different cards of the same type can be emulated.
*
* It will also make any changes necessary to the emulated data in response to the
* reader commands if the protocol supports it.
*
* When running, NfcListener will generate events that the calling code must handle
* by providing a callback function. The events passed to the callback are protocol-specific
* and may include errors, state changes, data reception, special function requests and more.
*/
#pragma once
#include <nfc/protocols/nfc_generic_event.h>
#include <nfc/protocols/nfc_device_base.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief NfcListener opaque type definition.
*/
typedef struct NfcListener NfcListener;
/**
* @brief Allocate an NfcListener instance.
*
* @param[in] nfc pointer to an Nfc instance.
* @param[in] protocol identifier of the protocol to be used.
* @param[in] data pointer to the data to use during emulation.
* @returns pointer to an allocated instance.
*
* @see nfc.h
*/
NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data);
/**
* @brief Delete an NfcListener instance.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void nfc_listener_free(NfcListener* instance);
/**
* @brief Start an NfcListener instance.
*
* The callback logic is protocol-specific, so it cannot be described here in detail.
* However, the callback return value ALWAYS determines what the listener should do next:
* to continue whatever it was doing prior to the callback run or to stop.
*
* @param[in,out] instance pointer to the instance to be started.
* @param[in] callback pointer to a user-defined callback function which will receive events.
* @param[in] context pointer to a user-specific context (will be passed to the callback).
*/
void nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context);
/**
* @brief Stop an NfcListener instance.
*
* The emulation process can be stopped explicitly (the other way is via the callback return value).
*
* @param[in,out] instance pointer to the instance to be stopped.
*/
void nfc_listener_stop(NfcListener* instance);
/**
* @brief Get the protocol identifier an NfcListener instance was created with.
*
* @param[in] instance pointer to the instance to be queried.
* @returns identifier of the protocol used by the instance.
*/
NfcProtocol nfc_listener_get_protocol(const NfcListener* instance);
/**
* @brief Get the data that was that was provided for emulation.
*
* The protocol identifier passed as the protocol parameter MUST match the one
* stored in the instance, otherwise a crash will occur.
* This is to improve type safety.
*
* @param[in] instance pointer to the instance to be queried.
* @param[in] protocol assumed protocol identifier of the data to be retrieved.
* @returns pointer to the NFC device data.
*/
const NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol);
#ifdef __cplusplus
}
#endif
+211
View File
@@ -0,0 +1,211 @@
#include "nfc_poller.h"
#include <nfc/protocols/nfc_poller_defs.h>
#include <furi.h>
typedef enum {
NfcPollerSessionStateIdle,
NfcPollerSessionStateActive,
NfcPollerSessionStateStopRequest,
} NfcPollerSessionState;
typedef struct NfcPollerListElement {
NfcProtocol protocol;
NfcGenericInstance* poller;
const NfcPollerBase* poller_api;
struct NfcPollerListElement* child;
} NfcPollerListElement;
typedef struct {
NfcPollerListElement* head;
NfcPollerListElement* tail;
} NfcPollerList;
struct NfcPoller {
NfcProtocol protocol;
Nfc* nfc;
NfcPollerList list;
NfcPollerSessionState session_state;
bool protocol_detected;
};
static void nfc_poller_list_alloc(NfcPoller* instance) {
instance->list.head = malloc(sizeof(NfcPollerListElement));
instance->list.head->protocol = instance->protocol;
instance->list.head->poller_api = nfc_pollers_api[instance->protocol];
instance->list.head->child = NULL;
instance->list.tail = instance->list.head;
do {
NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol);
if(parent_protocol == NfcProtocolInvalid) break;
NfcPollerListElement* parent = malloc(sizeof(NfcPollerListElement));
parent->protocol = parent_protocol;
parent->poller_api = nfc_pollers_api[parent_protocol];
parent->child = instance->list.head;
instance->list.head = parent;
} while(true);
NfcPollerListElement* iter = instance->list.head;
iter->poller = iter->poller_api->alloc(instance->nfc);
do {
if(iter->child == NULL) break;
iter->child->poller = iter->child->poller_api->alloc(iter->poller);
iter->poller_api->set_callback(
iter->poller, iter->child->poller_api->run, iter->child->poller);
iter = iter->child;
} while(true);
}
static void nfc_poller_list_free(NfcPoller* instance) {
do {
instance->list.head->poller_api->free(instance->list.head->poller);
NfcPollerListElement* child = instance->list.head->child;
free(instance->list.head);
if(child == NULL) break;
instance->list.head = child;
} while(true);
}
NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol) {
furi_assert(nfc);
furi_assert(protocol < NfcProtocolNum);
NfcPoller* instance = malloc(sizeof(NfcPoller));
instance->session_state = NfcPollerSessionStateIdle;
instance->nfc = nfc;
instance->protocol = protocol;
nfc_poller_list_alloc(instance);
return instance;
}
void nfc_poller_free(NfcPoller* instance) {
furi_assert(instance);
nfc_poller_list_free(instance);
free(instance);
}
static NfcCommand nfc_poller_start_callback(NfcEvent event, void* context) {
furi_assert(context);
NfcPoller* instance = context;
NfcCommand command = NfcCommandContinue;
NfcGenericEvent poller_event = {
.protocol = NfcProtocolInvalid,
.instance = instance->nfc,
.event_data = &event,
};
if(event.type == NfcEventTypePollerReady) {
NfcPollerListElement* head_poller = instance->list.head;
command = head_poller->poller_api->run(poller_event, head_poller->poller);
}
if(instance->session_state == NfcPollerSessionStateStopRequest) {
command = NfcCommandStop;
}
return command;
}
void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
furi_assert(instance->session_state == NfcPollerSessionStateIdle);
NfcPollerListElement* tail_poller = instance->list.tail;
tail_poller->poller_api->set_callback(tail_poller->poller, callback, context);
instance->session_state = NfcPollerSessionStateActive;
nfc_start(instance->nfc, nfc_poller_start_callback, instance);
}
void nfc_poller_stop(NfcPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
instance->session_state = NfcPollerSessionStateStopRequest;
nfc_stop(instance->nfc);
instance->session_state = NfcPollerSessionStateIdle;
}
static NfcCommand nfc_poller_detect_tail_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
NfcPoller* instance = context;
NfcPollerListElement* tail_poller = instance->list.tail;
instance->protocol_detected = tail_poller->poller_api->detect(event, tail_poller->poller);
return NfcCommandStop;
}
static NfcCommand nfc_poller_detect_head_callback(NfcEvent event, void* context) {
furi_assert(context);
NfcPoller* instance = context;
NfcPollerListElement* tail_poller = instance->list.tail;
NfcPollerListElement* head_poller = instance->list.head;
NfcCommand command = NfcCommandContinue;
NfcGenericEvent poller_event = {
.protocol = NfcProtocolInvalid,
.instance = instance->nfc,
.event_data = &event,
};
if(event.type == NfcEventTypePollerReady) {
if(tail_poller == head_poller) {
instance->protocol_detected =
tail_poller->poller_api->detect(poller_event, tail_poller->poller);
command = NfcCommandStop;
} else {
command = head_poller->poller_api->run(poller_event, head_poller->poller);
}
}
return command;
}
bool nfc_poller_detect(NfcPoller* instance) {
furi_assert(instance);
furi_assert(instance->session_state == NfcPollerSessionStateIdle);
instance->session_state = NfcPollerSessionStateActive;
NfcPollerListElement* tail_poller = instance->list.tail;
NfcPollerListElement* iter = instance->list.head;
if(tail_poller != instance->list.head) {
while(iter->child != tail_poller) iter = iter->child;
iter->poller_api->set_callback(iter->poller, nfc_poller_detect_tail_callback, instance);
}
nfc_start(instance->nfc, nfc_poller_detect_head_callback, instance);
nfc_stop(instance->nfc);
if(tail_poller != instance->list.head) {
iter->poller_api->set_callback(
iter->poller, tail_poller->poller_api->run, tail_poller->poller);
}
return instance->protocol_detected;
}
NfcProtocol nfc_poller_get_protocol(const NfcPoller* instance) {
furi_assert(instance);
return instance->protocol;
}
const NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance) {
furi_assert(instance);
NfcPollerListElement* tail_poller = instance->list.tail;
return tail_poller->poller_api->get_data(tail_poller->poller);
}
+104
View File
@@ -0,0 +1,104 @@
/**
* @file nfc_poller.h
* @brief NFC card reading library.
*
* Once started, it will try to activate and read a card using the designated protocol,
* which is usually obtained by creating and starting an NfcScanner first.
*
* @see nfc_scanner.h
*
* When running, NfcPoller will generate events that the calling code must handle
* by providing a callback function. The events passed to the callback are protocol-specific
* and may include errors, state changes, data reception, special function requests and more.
*
*/
#pragma once
#include <nfc/protocols/nfc_generic_event.h>
#include <nfc/protocols/nfc_device_base.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief NfcPoller opaque type definition.
*/
typedef struct NfcPoller NfcPoller;
/**
* @brief Allocate an NfcPoller instance.
*
* @param[in] nfc pointer to an Nfc instance.
* @param[in] protocol identifier of the protocol to be used.
* @returns pointer to an allocated instance.
*
* @see nfc.h
*/
NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol);
/**
* @brief Delete an NfcPoller instance.
*
* @param[in,out] instance pointer to the instance to be deleted.
*/
void nfc_poller_free(NfcPoller* instance);
/**
* @brief Start an NfcPoller instance.
*
* The callback logic is protocol-specific, so it cannot be described here in detail.
* However, the callback return value ALWAYS determines what the poller should do next:
* to continue whatever it was doing prior to the callback run or to stop.
*
* @param[in,out] instance pointer to the instance to be started.
* @param[in] callback pointer to a user-defined callback function which will receive events.
* @param[in] context pointer to a user-specific context (will be passed to the callback).
*/
void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context);
/**
* @brief Stop an NfcPoller instance.
*
* The reading process can be stopped explicitly (the other way is via the callback return value).
*
* @param[in,out] instance pointer to the instance to be stopped.
*/
void nfc_poller_stop(NfcPoller* instance);
/**
* @brief Detect whether there is a card supporting a particular protocol in the vicinity.
*
* The behaviour of this function is protocol-defined, in general, it will do whatever is
* necessary to determine whether a card supporting the current protocol is in the vicinity
* and whether it is functioning normally.
*
* It is used automatically inside NfcScanner, so there is usually no need
* to call it explicitly.
*
* @see nfc_scanner.h
*
* @param[in,out] instance pointer to the instance to perform the detection with.
* @returns true if a supported card was detected, false otherwise.
*/
bool nfc_poller_detect(NfcPoller* instance);
/**
* @brief Get the protocol identifier an NfcPoller instance was created with.
*
* @param[in] instance pointer to the instance to be queried.
* @returns identifier of the protocol used by the instance.
*/
NfcProtocol nfc_poller_get_protocol(const NfcPoller* instance);
/**
* @brief Get the data that was that was gathered during the reading process.
*
* @param[in] instance pointer to the instance to be queried.
* @returns pointer to the NFC device data.
*/
const NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance);
#ifdef __cplusplus
}
#endif
+267
View File
@@ -0,0 +1,267 @@
#include "nfc_scanner.h"
#include "nfc_poller.h"
#include <nfc/protocols/nfc_poller_defs.h>
#include <furi/furi.h>
#define TAG "NfcScanner"
typedef enum {
NfcScannerStateIdle,
NfcScannerStateTryBasePollers,
NfcScannerStateFindChildrenProtocols,
NfcScannerStateDetectChildrenProtocols,
NfcScannerStateComplete,
NfcScannerStateNum,
} NfcScannerState;
typedef enum {
NfcScannerSessionStateIdle,
NfcScannerSessionStateActive,
NfcScannerSessionStateStopRequest,
} NfcScannerSessionState;
struct NfcScanner {
Nfc* nfc;
NfcScannerState state;
NfcScannerSessionState session_state;
NfcScannerCallback callback;
void* context;
NfcEvent nfc_event;
NfcProtocol first_detected_protocol;
size_t base_protocols_num;
size_t base_protocols_idx;
NfcProtocol base_protocols[NfcProtocolNum];
size_t detected_base_protocols_num;
NfcProtocol detected_base_protocols[NfcProtocolNum];
size_t children_protocols_num;
size_t children_protocols_idx;
NfcProtocol children_protocols[NfcProtocolNum];
size_t detected_protocols_num;
NfcProtocol detected_protocols[NfcProtocolNum];
NfcProtocol current_protocol;
FuriThread* scan_worker;
};
static void nfc_scanner_reset(NfcScanner* instance) {
instance->base_protocols_idx = 0;
instance->base_protocols_num = 0;
instance->children_protocols_idx = 0;
instance->children_protocols_num = 0;
instance->detected_protocols_num = 0;
instance->detected_base_protocols_num = 0;
instance->current_protocol = 0;
}
typedef void (*NfcScannerStateHandler)(NfcScanner* instance);
void nfc_scanner_state_handler_idle(NfcScanner* instance) {
for(size_t i = 0; i < NfcProtocolNum; i++) {
NfcProtocol parent_protocol = nfc_protocol_get_parent(i);
if(parent_protocol == NfcProtocolInvalid) {
instance->base_protocols[instance->base_protocols_num] = i;
instance->base_protocols_num++;
}
}
FURI_LOG_D(TAG, "Found %zu base protocols", instance->base_protocols_num);
instance->first_detected_protocol = NfcProtocolInvalid;
instance->state = NfcScannerStateTryBasePollers;
}
void nfc_scanner_state_handler_try_base_pollers(NfcScanner* instance) {
do {
instance->current_protocol = instance->base_protocols[instance->base_protocols_idx];
if(instance->first_detected_protocol == instance->current_protocol) {
instance->state = NfcScannerStateFindChildrenProtocols;
break;
}
NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol);
bool protocol_detected = nfc_poller_detect(poller);
nfc_poller_free(poller);
if(protocol_detected) {
instance->detected_protocols[instance->detected_protocols_num] =
instance->current_protocol;
instance->detected_protocols_num++;
instance->detected_base_protocols[instance->detected_base_protocols_num] =
instance->current_protocol;
instance->detected_base_protocols_num++;
if(instance->first_detected_protocol == NfcProtocolInvalid) {
instance->first_detected_protocol = instance->current_protocol;
instance->current_protocol = NfcProtocolInvalid;
}
}
instance->base_protocols_idx =
(instance->base_protocols_idx + 1) % instance->base_protocols_num;
} while(false);
}
void nfc_scanner_state_handler_find_children_protocols(NfcScanner* instance) {
for(size_t i = 0; i < NfcProtocolNum; i++) {
for(size_t j = 0; j < instance->detected_base_protocols_num; j++) {
if(nfc_protocol_has_parent(i, instance->detected_base_protocols[j])) {
instance->children_protocols[instance->children_protocols_num] = i;
instance->children_protocols_num++;
}
}
}
if(instance->children_protocols_num > 0) {
instance->state = NfcScannerStateDetectChildrenProtocols;
} else {
instance->state = NfcScannerStateComplete;
}
FURI_LOG_D(TAG, "Found %zu children", instance->children_protocols_num);
}
void nfc_scanner_state_handler_detect_children_protocols(NfcScanner* instance) {
furi_assert(instance->children_protocols_num);
instance->current_protocol = instance->children_protocols[instance->children_protocols_idx];
NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol);
bool protocol_detected = nfc_poller_detect(poller);
nfc_poller_free(poller);
if(protocol_detected) {
instance->detected_protocols[instance->detected_protocols_num] =
instance->current_protocol;
instance->detected_protocols_num++;
}
instance->children_protocols_idx++;
if(instance->children_protocols_idx == instance->children_protocols_num) {
instance->state = NfcScannerStateComplete;
}
}
static void nfc_scanner_filter_detected_protocols(NfcScanner* instance) {
size_t filtered_protocols_num = 0;
NfcProtocol filtered_protocols[NfcProtocolNum] = {};
for(size_t i = 0; i < instance->detected_protocols_num; i++) {
bool is_parent = false;
for(size_t j = i; j < instance->detected_protocols_num; j++) {
is_parent = nfc_protocol_has_parent(
instance->detected_protocols[j], instance->detected_protocols[i]);
if(is_parent) break;
}
if(!is_parent) {
filtered_protocols[filtered_protocols_num] = instance->detected_protocols[i];
filtered_protocols_num++;
}
}
instance->detected_protocols_num = filtered_protocols_num;
memcpy(instance->detected_protocols, filtered_protocols, filtered_protocols_num);
}
void nfc_scanner_state_handler_complete(NfcScanner* instance) {
if(instance->detected_protocols_num > 1) {
nfc_scanner_filter_detected_protocols(instance);
}
FURI_LOG_I(TAG, "Detected %zu protocols", instance->detected_protocols_num);
NfcScannerEvent event = {
.type = NfcScannerEventTypeDetected,
.data =
{
.protocol_num = instance->detected_protocols_num,
.protocols = instance->detected_protocols,
},
};
instance->callback(event, instance->context);
furi_delay_ms(100);
}
static NfcScannerStateHandler nfc_scanner_state_handlers[NfcScannerStateNum] = {
[NfcScannerStateIdle] = nfc_scanner_state_handler_idle,
[NfcScannerStateTryBasePollers] = nfc_scanner_state_handler_try_base_pollers,
[NfcScannerStateFindChildrenProtocols] = nfc_scanner_state_handler_find_children_protocols,
[NfcScannerStateDetectChildrenProtocols] = nfc_scanner_state_handler_detect_children_protocols,
[NfcScannerStateComplete] = nfc_scanner_state_handler_complete,
};
static int32_t nfc_scanner_worker(void* context) {
furi_assert(context);
NfcScanner* instance = context;
while(instance->session_state == NfcScannerSessionStateActive) {
nfc_scanner_state_handlers[instance->state](instance);
}
nfc_scanner_reset(instance);
return 0;
}
NfcScanner* nfc_scanner_alloc(Nfc* nfc) {
furi_assert(nfc);
NfcScanner* instance = malloc(sizeof(NfcScanner));
instance->nfc = nfc;
return instance;
}
void nfc_scanner_free(NfcScanner* instance) {
furi_assert(instance);
free(instance);
}
void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
furi_assert(instance->session_state == NfcScannerSessionStateIdle);
furi_assert(instance->scan_worker == NULL);
instance->callback = callback;
instance->context = context;
instance->session_state = NfcScannerSessionStateActive;
instance->scan_worker = furi_thread_alloc();
furi_thread_set_name(instance->scan_worker, "NfcScanWorker");
furi_thread_set_context(instance->scan_worker, instance);
furi_thread_set_stack_size(instance->scan_worker, 4 * 1024);
furi_thread_set_callback(instance->scan_worker, nfc_scanner_worker);
furi_thread_start(instance->scan_worker);
}
void nfc_scanner_stop(NfcScanner* instance) {
furi_assert(instance);
furi_assert(instance->scan_worker);
instance->session_state = NfcScannerSessionStateStopRequest;
furi_thread_join(instance->scan_worker);
instance->session_state = NfcScannerSessionStateIdle;
furi_thread_free(instance->scan_worker);
instance->scan_worker = NULL;
instance->callback = NULL;
instance->context = NULL;
instance->state = NfcScannerStateIdle;
}
+97
View File
@@ -0,0 +1,97 @@
/**
* @file nfc_scanner.h
* @brief NFC card detection library.
*
* Once started, a NfcScanner instance will iterate over all available protocols
* and return a list of one or more detected protocol identifiers via a user-provided callback.
*
* The NfcScanner behaviour is greedy, i.e. it will not stop scanning upon detection of
* a just one protocol and will try others as well until all possibilities are exhausted.
* This is to allow for multi-protocol card support.
*
* If no supported cards are in the vicinity, the scanning process will continue
* until stopped explicitly.
*/
#pragma once
#include <nfc/nfc.h>
#include <nfc/protocols/nfc_protocol.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief NfcScanner opaque type definition.
*/
typedef struct NfcScanner NfcScanner;
/**
* @brief Event type passed to the user callback.
*/
typedef enum {
NfcScannerEventTypeDetected, /**< One or more protocols have been detected. */
} NfcScannerEventType;
/**
* @brief Event data passed to the user callback.
*/
typedef struct {
size_t protocol_num; /**< Number of detected protocols (one or more). */
NfcProtocol* protocols; /**< Pointer to the array of detected protocol identifiers. */
} NfcScannerEventData;
/**
* @brief Event passed to the user callback.
*/
typedef struct {
NfcScannerEventType type; /**< Type of event. Determines how the data must be handled. */
NfcScannerEventData data; /**< Event-specific data. Handled accordingly to the even type. */
} NfcScannerEvent;
/**
* @brief User callback function signature.
*
* A function with such signature must be provided by the user upon calling nfc_scanner_start().
*
* @param[in] event occurred event, complete with type and data.
* @param[in] context pointer to the context data provided in nfc_scanner_start() call.
*/
typedef void (*NfcScannerCallback)(NfcScannerEvent event, void* context);
/**
* @brief Allocate an NfcScanner instance.
*
* @param[in] nfc pointer to an Nfc instance.
* @returns pointer to the allocated NfcScanner instance.
*
* @see nfc.h
*/
NfcScanner* nfc_scanner_alloc(Nfc* nfc);
/**
* @brief Delete an NfcScanner instance.
*
* @param[in,out] pointer to the instance to be deleted.
*/
void nfc_scanner_free(NfcScanner* instance);
/**
* @brief Start an NfcScanner.
*
* @param[in,out] pointer to the instance to be started.
* @param[in] callback pointer to the callback function (will be called upon a detection event).
* @param[in] context pointer to the caller-specific context (will be passed to the callback).
*/
void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context);
/**
* @brief Stop an NfcScanner.
*
* @param[in,out] pointer to the instance to be stopped.
*/
void nfc_scanner_stop(NfcScanner* instance);
#ifdef __cplusplus
}
#endif
-69
View File
@@ -1,69 +0,0 @@
#include "nfc_types.h"
const char* nfc_get_dev_type(FuriHalNfcType type) {
if(type == FuriHalNfcTypeA) {
return "NFC-A";
} else if(type == FuriHalNfcTypeB) {
return "NFC-B";
} else if(type == FuriHalNfcTypeF) {
return "NFC-F";
} else if(type == FuriHalNfcTypeV) {
return "NFC-V";
} else {
return "Unknown";
}
}
const char* nfc_guess_protocol(NfcProtocol protocol) {
if(protocol == NfcDeviceProtocolEMV) {
return "EMV bank card";
} else if(protocol == NfcDeviceProtocolMifareUl) {
return "Mifare Ultral/NTAG";
} else if(protocol == NfcDeviceProtocolMifareClassic) {
return "Mifare Classic";
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
return "Mifare DESFire";
} else {
return "Unrecognized";
}
}
const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) {
if(type == MfUltralightTypeNTAG213) {
return "NTAG213";
} else if(type == MfUltralightTypeNTAG215) {
return "NTAG215";
} else if(type == MfUltralightTypeNTAG216) {
return "NTAG216";
} else if(type == MfUltralightTypeNTAGI2C1K) {
return "NTAG I2C 1K";
} else if(type == MfUltralightTypeNTAGI2C2K) {
return "NTAG I2C 2K";
} else if(type == MfUltralightTypeNTAGI2CPlus1K) {
return "NTAG I2C Plus 1K";
} else if(type == MfUltralightTypeNTAGI2CPlus2K) {
return "NTAG I2C Plus 2K";
} else if(type == MfUltralightTypeNTAG203) {
return "NTAG203";
} else if(type == MfUltralightTypeULC) {
return "Mifare Ultralight C";
} else if(type == MfUltralightTypeUL11 && full_name) {
return "Mifare Ultralight 11";
} else if(type == MfUltralightTypeUL21 && full_name) {
return "Mifare Ultralight 21";
} else {
return "Mifare Ultralight";
}
}
const char* nfc_mf_classic_type(MfClassicType type) {
if(type == MfClassicTypeMini) {
return "Mifare Mini 0.3K";
} else if(type == MfClassicType1k) {
return "Mifare Classic 1K";
} else if(type == MfClassicType4k) {
return "Mifare Classic 4K";
} else {
return "Mifare Classic";
}
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include "nfc_device.h"
#ifdef __cplusplus
extern "C" {
#endif
const char* nfc_get_dev_type(FuriHalNfcType type);
const char* nfc_guess_protocol(NfcProtocol protocol);
const char* nfc_mf_ul_type(MfUltralightType type, bool full_name);
const char* nfc_mf_classic_type(MfClassicType type);
#ifdef __cplusplus
}
#endif
-1325
View File
File diff suppressed because it is too large Load Diff
-108
View File
@@ -1,108 +0,0 @@
#pragma once
#include "nfc_device.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct NfcWorker NfcWorker;
typedef enum {
// Init states
NfcWorkerStateNone,
NfcWorkerStateReady,
// Main worker states
NfcWorkerStateRead,
NfcWorkerStateUidEmulate,
NfcWorkerStateMfUltralightEmulate,
NfcWorkerStateMfClassicEmulate,
NfcWorkerStateMfClassicWrite,
NfcWorkerStateMfClassicUpdate,
NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader,
NfcWorkerStateNfcVEmulate,
NfcWorkerStateNfcVUnlock,
NfcWorkerStateNfcVUnlockAndSave,
NfcWorkerStateNfcVSniff,
// Debug
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
// Transition
NfcWorkerStateStop,
} NfcWorkerState;
typedef enum {
// Reserve first 50 events for application events
NfcWorkerEventReserved = 50,
// Nfc read events
NfcWorkerEventReadUidNfcB,
NfcWorkerEventReadUidNfcV,
NfcWorkerEventReadUidNfcF,
NfcWorkerEventReadUidNfcA,
NfcWorkerEventReadMfUltralight,
NfcWorkerEventReadMfDesfire,
NfcWorkerEventReadMfClassicDone,
NfcWorkerEventReadMfClassicLoadKeyCache,
NfcWorkerEventReadMfClassicDictAttackRequired,
NfcWorkerEventReadNfcV,
// Nfc worker common events
NfcWorkerEventSuccess,
NfcWorkerEventFail,
NfcWorkerEventAborted,
NfcWorkerEventCardDetected,
NfcWorkerEventNoCardDetected,
NfcWorkerEventWrongCardDetected,
// Read Mifare Classic events
NfcWorkerEventNoDictFound,
NfcWorkerEventNewSector,
NfcWorkerEventNewDictKeyBatch,
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,
NfcWorkerEventKeyAttackStart,
NfcWorkerEventKeyAttackStop,
NfcWorkerEventKeyAttackNextSector,
// Write Mifare Classic events
NfcWorkerEventWrongCard,
// Detect Reader events
NfcWorkerEventDetectReaderDetected,
NfcWorkerEventDetectReaderLost,
NfcWorkerEventDetectReaderMfkeyCollected,
// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key
NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command
NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key
NfcWorkerEventNfcVCommandExecuted,
NfcWorkerEventNfcVContentChanged,
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
NfcWorker* nfc_worker_alloc();
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
void nfc_worker_free(NfcWorker* nfc_worker);
void nfc_worker_start(
NfcWorker* nfc_worker,
NfcWorkerState state,
NfcDeviceData* dev_data,
NfcWorkerCallback callback,
void* context);
void nfc_worker_stop(NfcWorker* nfc_worker);
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker);
void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker);
void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker);
#ifdef __cplusplus
}
#endif
-59
View File
@@ -1,59 +0,0 @@
#pragma once
#include "nfc_worker.h"
#include <furi.h>
#include <lib/toolbox/stream/file_stream.h>
#include <lib/nfc/protocols/nfc_util.h>
#include <lib/nfc/protocols/mifare_common.h>
#include <lib/nfc/protocols/mifare_ultralight.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfca.h>
#include <lib/nfc/protocols/nfcv.h>
#include <lib/nfc/protocols/slix.h>
#include <lib/nfc/helpers/reader_analyzer.h>
struct NfcWorker {
FuriThread* thread;
Storage* storage;
Stream* dict_stream;
NfcDeviceData* dev_data;
NfcWorkerCallback callback;
void* context;
NfcWorkerState state;
ReaderAnalyzer* reader_analyzer;
};
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
int32_t nfc_worker_task(void* context);
void nfc_worker_read(NfcWorker* nfc_worker);
void nfc_worker_read_type(NfcWorker* nfc_worker);
void nfc_worker_emulate_uid(NfcWorker* nfc_worker);
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker);
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_write_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_update_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker);
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);
void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker);
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
void nfc_worker_analyze_reader(NfcWorker* nfc_worker);
-103
View File
@@ -1,103 +0,0 @@
#include "nfc_supported_card.h"
#include "all_in_one.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include <furi_hal.h>
#define ALL_IN_ONE_LAYOUT_UNKNOWN 0
#define ALL_IN_ONE_LAYOUT_A 1
#define ALL_IN_ONE_LAYOUT_D 2
#define ALL_IN_ONE_LAYOUT_E2 3
#define ALL_IN_ONE_LAYOUT_E3 4
#define ALL_IN_ONE_LAYOUT_E5 5
#define ALL_IN_ONE_LAYOUT_2 6
uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) {
// I absolutely hate what's about to happen here.
// Switch on the second half of the third byte of page 5
FURI_LOG_I("all_in_one", "Layout byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 2]);
FURI_LOG_I(
"all_in_one", "Layout half-byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 3] & 0x0F);
switch(dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F) {
// If it is A, the layout type is a type A layout
case 0x0A:
return ALL_IN_ONE_LAYOUT_A;
case 0x0D:
return ALL_IN_ONE_LAYOUT_D;
case 0x02:
return ALL_IN_ONE_LAYOUT_2;
default:
FURI_LOG_I(
"all_in_one",
"Unknown layout type: %d",
dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F);
return ALL_IN_ONE_LAYOUT_UNKNOWN;
}
}
bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
UNUSED(nfc_worker);
// If this is a all_in_one pass, first 2 bytes of page 4 are 0x45 0xD9
MfUltralightReader reader = {};
MfUltralightData data = {};
if(!mf_ul_read_card(tx_rx, &reader, &data)) {
return false;
} else {
if(data.data[4 * 4] == 0x45 && data.data[4 * 4 + 1] == 0xD9) {
FURI_LOG_I("all_in_one", "Pass verified");
return true;
}
}
return false;
}
bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
MfUltralightReader reader = {};
MfUltralightData data = {};
if(!mf_ul_read_card(tx_rx, &reader, &data)) {
return false;
} else {
memcpy(&nfc_worker->dev_data->mf_ul_data, &data, sizeof(data));
FURI_LOG_I("all_in_one", "Card read");
return true;
}
}
bool all_in_one_parser_parse(NfcDeviceData* dev_data) {
if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) {
FURI_LOG_I("all_in_one", "Pass not verified");
return false;
}
uint8_t ride_count = 0;
uint32_t serial = 0;
if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) {
// If the layout is A then the ride count is stored in the first byte of page 8
ride_count = dev_data->mf_ul_data.data[4 * 8];
} else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) {
// If the layout is D, the ride count is stored in the second byte of page 9
ride_count = dev_data->mf_ul_data.data[4 * 9 + 1];
} else {
FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data));
ride_count = 137;
}
// I hate this with a burning passion.
// The number starts at the second half of the third byte on page 4, and is 32 bits long
// So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
// B8 17 A2 A4 BD becomes 81 7A 2A 4B
serial =
(dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 |
dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | dev_data->mf_ul_data.data[4 * 4 + 4] << 12 |
dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4);
// Format string for rides count
furi_string_printf(
dev_data->parsed_data, "\e#All-In-One\nNumber: %lu\nRides left: %u", serial, ride_count);
return true;
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool all_in_one_parser_parse(NfcDeviceData* dev_data);
-82
View File
@@ -1,82 +0,0 @@
#include "nfc_supported_card.h"
#include "plantain_parser.h"
#include "troika_parser.h"
#include "plantain_4k_parser.h"
#include "troika_4k_parser.h"
#include "two_cities.h"
#include "all_in_one.h"
#include "opal.h"
NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
[NfcSupportedCardTypePlantain] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = plantain_parser_verify,
.read = plantain_parser_read,
.parse = plantain_parser_parse,
},
[NfcSupportedCardTypeTroika] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = troika_parser_verify,
.read = troika_parser_read,
.parse = troika_parser_parse,
},
[NfcSupportedCardTypePlantain4K] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = plantain_4k_parser_verify,
.read = plantain_4k_parser_read,
.parse = plantain_4k_parser_parse,
},
[NfcSupportedCardTypeTroika4K] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = troika_4k_parser_verify,
.read = troika_4k_parser_read,
.parse = troika_4k_parser_parse,
},
[NfcSupportedCardTypeTwoCities] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = two_cities_parser_verify,
.read = two_cities_parser_read,
.parse = two_cities_parser_parse,
},
[NfcSupportedCardTypeAllInOne] =
{
.protocol = NfcDeviceProtocolMifareUl,
.verify = all_in_one_parser_verify,
.read = all_in_one_parser_read,
.parse = all_in_one_parser_parse,
},
[NfcSupportedCardTypeOpal] =
{
.protocol = NfcDeviceProtocolMifareDesfire,
.verify = stub_parser_verify_read,
.read = stub_parser_verify_read,
.parse = opal_parser_parse,
},
};
bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data) {
furi_assert(dev_data);
bool card_parsed = false;
for(size_t i = 0; i < COUNT_OF(nfc_supported_card); i++) {
if(nfc_supported_card[i].parse(dev_data)) {
card_parsed = true;
break;
}
}
return card_parsed;
}
bool stub_parser_verify_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
UNUSED(nfc_worker);
UNUSED(tx_rx);
return false;
}
-47
View File
@@ -1,47 +0,0 @@
#pragma once
#include <furi_hal_nfc.h>
#include "../nfc_worker.h"
#include "../nfc_device.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
NfcSupportedCardTypePlantain,
NfcSupportedCardTypeTroika,
NfcSupportedCardTypePlantain4K,
NfcSupportedCardTypeTroika4K,
NfcSupportedCardTypeTwoCities,
NfcSupportedCardTypeAllInOne,
NfcSupportedCardTypeOpal,
NfcSupportedCardTypeEnd,
} NfcSupportedCardType;
typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
typedef bool (*NfcSupportedCardParse)(NfcDeviceData* dev_data);
typedef struct {
NfcProtocol protocol;
NfcSupportedCardVerify verify;
NfcSupportedCardRead read;
NfcSupportedCardParse parse;
} NfcSupportedCard;
extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd];
bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data);
// stub_parser_verify_read does nothing, and always reports that it does not
// support the card. This is needed for DESFire card parsers which can't
// provide keys, and only use NfcSupportedCard->parse.
bool stub_parser_verify_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
#ifdef __cplusplus
}
#endif
-204
View File
@@ -1,204 +0,0 @@
/*
* opal.c - Parser for Opal card (Sydney, Australia).
*
* Copyright 2023 Michael Farrell <micolous+git@gmail.com>
*
* This will only read "standard" MIFARE DESFire-based Opal cards. Free travel
* cards (including School Opal cards, veteran, vision-impaired persons and
* TfNSW employees' cards) and single-trip tickets are MIFARE Ultralight C
* cards and not supported.
*
* Reference: https://github.com/metrodroid/metrodroid/wiki/Opal
*
* Note: The card values are all little-endian (like Flipper), but the above
* reference was originally written based on Java APIs, which are big-endian.
* This implementation presumes a little-endian system.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nfc_supported_card.h"
#include "opal.h"
#include <applications/services/locale/locale.h>
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include <furi_hal.h>
static const uint8_t opal_aid[3] = {0x31, 0x45, 0x53};
static const char* opal_modes[5] =
{"Rail / Metro", "Ferry / Light Rail", "Bus", "Unknown mode", "Manly Ferry"};
static const char* opal_usages[14] = {
"New / Unused",
"Tap on: new journey",
"Tap on: transfer from same mode",
"Tap on: transfer from other mode",
"", // Manly Ferry: new journey
"", // Manly Ferry: transfer from ferry
"", // Manly Ferry: transfer from other
"Tap off: distance fare",
"Tap off: flat fare",
"Automated tap off: failed to tap off",
"Tap off: end of trip without start",
"Tap off: reversal",
"Tap on: rejected",
"Unknown usage",
};
// Opal file 0x7 structure. Assumes a little-endian CPU.
typedef struct __attribute__((__packed__)) {
uint32_t serial : 32;
uint8_t check_digit : 4;
bool blocked : 1;
uint16_t txn_number : 16;
int32_t balance : 21;
uint16_t days : 15;
uint16_t minutes : 11;
uint8_t mode : 3;
uint16_t usage : 4;
bool auto_topup : 1;
uint8_t weekly_journeys : 4;
uint16_t checksum : 16;
} OpalFile;
static_assert(sizeof(OpalFile) == 16);
// Converts an Opal timestamp to FuriHalRtcDateTime.
//
// Opal measures days since 1980-01-01 and minutes since midnight, and presumes
// all days are 1440 minutes.
void opal_date_time_to_furi(uint16_t days, uint16_t minutes, FuriHalRtcDateTime* out) {
if(!out) return;
uint16_t diy;
out->year = 1980;
out->month = 1;
// 1980-01-01 is a Tuesday
out->weekday = ((days + 1) % 7) + 1;
out->hour = minutes / 60;
out->minute = minutes % 60;
out->second = 0;
// What year is it?
for(;;) {
diy = furi_hal_rtc_get_days_per_year(out->year);
if(days < diy) break;
days -= diy;
out->year++;
}
// 1-index the day of the year
days++;
// What month is it?
bool is_leap = furi_hal_rtc_is_leap_year(out->year);
for(;;) {
uint8_t dim = furi_hal_rtc_get_days_per_month(is_leap, out->month);
if(days <= dim) break;
days -= dim;
out->month++;
}
out->day = days;
}
bool opal_parser_parse(NfcDeviceData* dev_data) {
if(dev_data->protocol != NfcDeviceProtocolMifareDesfire) {
return false;
}
MifareDesfireApplication* app = mf_df_get_application(&dev_data->mf_df_data, &opal_aid);
if(app == NULL) {
return false;
}
MifareDesfireFile* f = mf_df_get_file(app, 0x07);
if(f == NULL || f->type != MifareDesfireFileTypeStandard || f->settings.data.size != 16 ||
!f->contents) {
return false;
}
OpalFile* o = (OpalFile*)f->contents;
uint8_t serial2 = o->serial / 10000000;
uint16_t serial3 = (o->serial / 1000) % 10000;
uint16_t serial4 = (o->serial % 1000);
if(o->check_digit > 9) {
return false;
}
char* sign = "";
if(o->balance < 0) {
// Negative balance. Make this a positive value again and record the
// sign separately, because then we can handle balances of -99..-1
// cents, as the "dollars" division below would result in a positive
// zero value.
o->balance = abs(o->balance); //-V1081
sign = "-";
}
uint8_t cents = o->balance % 100;
int32_t dollars = o->balance / 100;
FuriHalRtcDateTime timestamp;
opal_date_time_to_furi(o->days, o->minutes, &timestamp);
if(o->mode >= 3) {
// 3..7 are "reserved", but we use 4 to indicate the Manly Ferry.
o->mode = 3;
}
if(o->usage >= 4 && o->usage <= 6) {
// Usages 4..6 associated with the Manly Ferry, which correspond to
// usages 1..3 for other modes.
o->usage -= 3;
o->mode = 4;
}
const char* mode_str = (o->mode <= 4 ? opal_modes[o->mode] : opal_modes[3]); //-V547
const char* usage_str = (o->usage <= 12 ? opal_usages[o->usage] : opal_usages[13]);
furi_string_printf(
dev_data->parsed_data,
"\e#Opal: $%s%ld.%02hu\n3085 22%02hhu %04hu %03hu%01hhu\n%s, %s\n",
sign,
dollars,
cents,
serial2,
serial3,
serial4,
o->check_digit,
mode_str,
usage_str);
FuriString* timestamp_str = furi_string_alloc();
locale_format_date(timestamp_str, &timestamp, locale_get_date_format(), "-");
furi_string_cat(dev_data->parsed_data, timestamp_str);
furi_string_cat_str(dev_data->parsed_data, " at ");
locale_format_time(timestamp_str, &timestamp, locale_get_time_format(), false);
furi_string_cat(dev_data->parsed_data, timestamp_str);
furi_string_free(timestamp_str);
furi_string_cat_printf(
dev_data->parsed_data,
"\nWeekly journeys: %hhu, Txn #%hu\n",
o->weekly_journeys,
o->txn_number);
if(o->auto_topup) {
furi_string_cat_str(dev_data->parsed_data, "Auto-topup enabled\n");
}
if(o->blocked) {
furi_string_cat_str(dev_data->parsed_data, "Card blocked\n");
}
return true;
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool opal_parser_parse(NfcDeviceData* dev_data);
-124
View File
@@ -1,124 +0,0 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include <furi_hal.h>
static const MfClassicAuthContext plantain_keys_4k[] = {
{.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF},
{.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b},
{.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a},
{.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7},
{.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3},
{.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb},
{.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56},
{.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26},
{.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf},
{.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a},
{.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b},
{.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3},
{.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22},
{.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056},
{.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510},
{.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf},
{.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439},
{.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406},
{.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3},
{.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA},
{.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f},
{.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3},
{.sector = 30, .key_a = 0x0eb23cc8110b, .key_b = 0x04dc35277635},
{.sector = 31, .key_a = 0xbc4580b7f20b, .key_b = 0xd0a4131fb290},
{.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023},
{.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9},
{.sector = 34, .key_a = 0xfd8705e721b0, .key_b = 0x296fc317a513},
{.sector = 35, .key_a = 0x22052b480d11, .key_b = 0xe19504c39461},
{.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7},
{.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a},
{.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7},
{.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085},
};
bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
return false;
}
uint8_t sector = 8;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Plant4K", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) {
FURI_LOG_D("Plant4K", "Sector %d verified", sector);
return true;
}
return false;
}
bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(plantain_keys_4k); i++) {
mf_classic_reader_add_sector(
&reader,
plantain_keys_4k[i].sector,
plantain_keys_4k[i].key_a,
plantain_keys_4k[i].key_b);
FURI_LOG_T("plant4k", "Added sector %d", plantain_keys_4k[i].sector);
}
for(int i = 0; i < 5; i++) {
if(mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40) {
return true;
}
}
return false;
}
bool plantain_4k_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != plantain_keys_4k[8].key_a) return false;
// Point to block 0 of sector 4, value 0
uint8_t* temp_ptr = &data->block[4 * 4].value[0];
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = &data->block[0 * 4].value[0];
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
}
// Copy card number to uint64_t
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
}
furi_string_printf(
dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance);
return true;
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_4k_parser_parse(NfcDeviceData* dev_data);
-97
View File
@@ -1,97 +0,0 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include <furi_hal.h>
static const MfClassicAuthContext plantain_keys[] = {
{.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b},
{.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a},
{.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7},
{.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3},
{.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb},
{.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56},
{.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26},
{.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
};
bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) {
return false;
}
uint8_t sector = 8;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Plant", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) {
FURI_LOG_D("Plant", "Sector %d verified", sector);
return true;
}
return false;
}
bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(plantain_keys); i++) {
mf_classic_reader_add_sector(
&reader, plantain_keys[i].sector, plantain_keys[i].key_a, plantain_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
}
uint8_t plantain_calculate_luhn(uint64_t number) {
// No.
UNUSED(number);
return 0;
}
bool plantain_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != plantain_keys[8].key_a) return false;
// Point to block 0 of sector 4, value 0
uint8_t* temp_ptr = &data->block[4 * 4].value[0];
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = &data->block[0 * 4].value[0];
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
}
// Copy card number to uint64_t
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
}
furi_string_printf(
dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance);
return true;
}
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_parser_parse(NfcDeviceData* dev_data);
uint8_t plantain_calculate_luhn(uint64_t number);
-106
View File
@@ -1,106 +0,0 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
static const MfClassicAuthContext troika_4k_keys[] = {
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
{.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
{.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698},
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb},
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
{.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d},
{.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b},
{.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0},
{.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75},
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408},
{.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df},
{.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9},
{.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4},
{.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216},
{.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba},
{.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252},
{.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01},
{.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994},
{.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6},
{.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32},
{.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3},
{.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5},
{.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a},
{.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80},
{.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d},
{.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2},
{.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324},
{.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae},
{.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda},
{.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d},
{.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465},
{.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4},
{.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70},
};
bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
return false;
}
uint8_t sector = 11;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Troika", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) {
FURI_LOG_D("Troika", "Sector %d verified", sector);
return true;
}
return false;
}
bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) {
mf_classic_reader_add_sector(
&reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40;
}
bool troika_4k_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != troika_4k_keys[4].key_a) return false;
// Verify card type
if(data->type != MfClassicType4k) return false;
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
temp_ptr = &data->block[8 * 4].value[2];
uint32_t number = 0;
for(size_t i = 1; i < 5; i++) {
number <<= 8;
number |= temp_ptr[i];
}
number >>= 4;
number |= (temp_ptr[0] & 0xf) << 28;
furi_string_printf(
dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance);
return true;
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_4k_parser_parse(NfcDeviceData* dev_data);
-88
View File
@@ -1,88 +0,0 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
static const MfClassicAuthContext troika_keys[] = {
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
{.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
{.sector = 5, .key_a = 0xfbc2793d540b, .key_b = 0xd3a297dc2698},
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dba},
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
{.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d},
{.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b},
{.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0},
{.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75},
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
};
bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) {
return false;
}
uint8_t sector = 11;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Troika", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) {
FURI_LOG_D("Troika", "Sector %d verified", sector);
return true;
}
return false;
}
bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(troika_keys); i++) {
mf_classic_reader_add_sector(
&reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
}
bool troika_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
bool troika_parsed = false;
do {
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != troika_keys[8].key_a) break;
// Verify card type
if(data->type != MfClassicType1k) break;
// Parse data
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
temp_ptr = &data->block[8 * 4].value[2];
uint32_t number = 0;
for(size_t i = 1; i < 5; i++) {
number <<= 8;
number |= temp_ptr[i];
}
number >>= 4;
number |= (temp_ptr[0] & 0xf) << 28;
furi_string_printf(
dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance);
troika_parsed = true;
} while(false);
return troika_parsed;
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_parser_parse(NfcDeviceData* dev_data);
-146
View File
@@ -1,146 +0,0 @@
#include "nfc_supported_card.h"
#include "plantain_parser.h" // For plantain-specific stuff
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include <furi_hal.h>
static const MfClassicAuthContext two_cities_keys_4k[] = {
{.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b},
{.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a},
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
{.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb},
{.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56},
{.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26},
{.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf},
{.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a},
{.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b},
{.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3},
{.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22},
{.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056},
{.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510},
{.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf},
{.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439},
{.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406},
{.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3},
{.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA},
{.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f},
{.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3},
{.sector = 30, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7},
{.sector = 31, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3},
{.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023},
{.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9},
{.sector = 34, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 35, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7},
{.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a},
{.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7},
{.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085},
};
bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
return false;
}
uint8_t sector = 4;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("2cities", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) {
FURI_LOG_D("2cities", "Sector %d verified", sector);
return true;
}
return false;
}
bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(two_cities_keys_4k); i++) {
mf_classic_reader_add_sector(
&reader,
two_cities_keys_4k[i].sector,
two_cities_keys_4k[i].key_a,
two_cities_keys_4k[i].key_b);
FURI_LOG_T("2cities", "Added sector %d", two_cities_keys_4k[i].sector);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40;
}
bool two_cities_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != two_cities_keys_4k[4].key_a) return false;
// =====
// PLANTAIN
// =====
// Point to block 0 of sector 4, value 0
uint8_t* temp_ptr = &data->block[4 * 4].value[0];
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = &data->block[0 * 4].value[0];
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
}
// Copy card number to uint64_t
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
}
// =====
// --PLANTAIN--
// =====
// TROIKA
// =====
uint8_t* troika_temp_ptr = &data->block[8 * 4 + 1].value[5];
uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25;
troika_temp_ptr = &data->block[8 * 4].value[3];
uint32_t troika_number = 0;
for(size_t i = 0; i < 4; i++) {
troika_number <<= 8;
troika_number |= troika_temp_ptr[i];
}
troika_number >>= 4;
furi_string_printf(
dev_data->parsed_data,
"\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n",
card_number,
balance,
troika_number,
troika_balance);
return true;
}
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool two_cities_parser_parse(NfcDeviceData* dev_data);
-128
View File
@@ -1,128 +0,0 @@
#include "crypto1.h"
#include "nfc_util.h"
#include <furi.h>
// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git
#define SWAPENDIAN(x) \
((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
#define LF_POLY_ODD (0x29CE5C)
#define LF_POLY_EVEN (0x870804)
#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24)
void crypto1_reset(Crypto1* crypto1) {
furi_assert(crypto1);
crypto1->even = 0;
crypto1->odd = 0;
}
void crypto1_init(Crypto1* crypto1, uint64_t key) {
furi_assert(crypto1);
crypto1->even = 0;
crypto1->odd = 0;
for(int8_t i = 47; i > 0; i -= 2) {
crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7);
crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7);
}
}
uint32_t crypto1_filter(uint32_t in) {
uint32_t out = 0;
out = 0xf22c0 >> (in & 0xf) & 16;
out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8;
out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4;
out |= 0x1e458 >> (in >> 12 & 0xf) & 2;
out |= 0x0d938 >> (in >> 16 & 0xf) & 1;
return FURI_BIT(0xEC57E80A, out);
}
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
furi_assert(crypto1);
uint8_t out = crypto1_filter(crypto1->odd);
uint32_t feed = out & (!!is_encrypted);
feed ^= !!in;
feed ^= LF_POLY_ODD & crypto1->odd;
feed ^= LF_POLY_EVEN & crypto1->even;
crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed));
FURI_SWAP(crypto1->odd, crypto1->even);
return out;
}
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {
furi_assert(crypto1);
uint8_t out = 0;
for(uint8_t i = 0; i < 8; i++) {
out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;
}
return out;
}
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
furi_assert(crypto1);
uint32_t out = 0;
for(uint8_t i = 0; i < 32; i++) {
out |= crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i);
}
return out;
}
uint32_t prng_successor(uint32_t x, uint32_t n) {
SWAPENDIAN(x);
while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
return SWAPENDIAN(x);
}
void crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
uint16_t encrypted_data_bits,
uint8_t* decrypted_data) {
furi_assert(crypto);
furi_assert(encrypted_data);
furi_assert(decrypted_data);
if(encrypted_data_bits < 8) {
uint8_t decrypted_byte = 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
decrypted_data[0] = decrypted_byte;
} else {
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
}
}
}
void crypto1_encrypt(
Crypto1* crypto,
uint8_t* keystream,
uint8_t* plain_data,
uint16_t plain_data_bits,
uint8_t* encrypted_data,
uint8_t* encrypted_parity) {
furi_assert(crypto);
furi_assert(plain_data);
furi_assert(encrypted_data);
furi_assert(encrypted_parity);
if(plain_data_bits < 8) {
encrypted_data[0] = 0;
for(size_t i = 0; i < plain_data_bits; i++) {
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
}
} else {
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
plain_data[i];
encrypted_parity[i / 8] |=
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
<< (7 - (i & 0x0007)));
}
}
}
-444
View File
@@ -1,444 +0,0 @@
#include "emv.h"
#include <core/common_defines.h>
#define TAG "Emv"
const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information
const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type
const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicator
const PDOLValue pdol_term_trans_qualifies = {
0x9F66,
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
const PDOLValue pdol_addtnl_term_qualifies = {
0x9F40,
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
const PDOLValue pdol_amount_authorise = {
0x9F02,
{0x00, 0x00, 0x00, 0x10, 0x00, 0x00}}; // Amount, authorised
const PDOLValue pdol_amount = {0x9F03, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Amount
const PDOLValue pdol_country_code = {0x9F1A, {0x01, 0x24}}; // Terminal country code
const PDOLValue pdol_currency_code = {0x5F2A, {0x01, 0x24}}; // Transaction currency code
const PDOLValue pdol_term_verification = {
0x95,
{0x00, 0x00, 0x00, 0x00, 0x00}}; // Terminal verification results
const PDOLValue pdol_transaction_date = {0x9A, {0x19, 0x01, 0x01}}; // Transaction date
const PDOLValue pdol_transaction_type = {0x9C, {0x00}}; // Transaction type
const PDOLValue pdol_transaction_cert = {0x98, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; // Transaction cert
const PDOLValue pdol_unpredict_number = {0x9F37, {0x82, 0x3D, 0xDE, 0x7A}}; // Unpredictable number
const PDOLValue* const pdol_values[] = {
&pdol_term_info,
&pdol_term_type,
&pdol_merchant_type,
&pdol_term_trans_qualifies,
&pdol_addtnl_term_qualifies,
&pdol_amount_authorise,
&pdol_amount,
&pdol_country_code,
&pdol_currency_code,
&pdol_term_verification,
&pdol_transaction_date,
&pdol_transaction_type,
&pdol_transaction_cert,
&pdol_unpredict_number,
};
static const uint8_t select_ppse_ans[] = {0x6F, 0x29, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E,
0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31,
0xA5, 0x17, 0xBF, 0x0C, 0x14, 0x61, 0x12, 0x4F, 0x07,
0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x04,
0x56, 0x49, 0x53, 0x41, 0x87, 0x01, 0x01, 0x90, 0x00};
static const uint8_t select_app_ans[] = {0x6F, 0x20, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03,
0x10, 0x10, 0xA5, 0x15, 0x50, 0x04, 0x56, 0x49, 0x53,
0x41, 0x9F, 0x38, 0x0C, 0x9F, 0x66, 0x04, 0x9F, 0x02,
0x06, 0x9F, 0x37, 0x04, 0x5F, 0x2A, 0x02, 0x90, 0x00};
static const uint8_t pdol_ans[] = {0x77, 0x40, 0x82, 0x02, 0x20, 0x00, 0x57, 0x13, 0x55, 0x70,
0x73, 0x83, 0x85, 0x87, 0x73, 0x31, 0xD1, 0x80, 0x22, 0x01,
0x38, 0x84, 0x77, 0x94, 0x00, 0x00, 0x1F, 0x5F, 0x34, 0x01,
0x00, 0x9F, 0x10, 0x07, 0x06, 0x01, 0x11, 0x03, 0x80, 0x00,
0x00, 0x9F, 0x26, 0x08, 0x7A, 0x65, 0x7F, 0xD3, 0x52, 0x96,
0xC9, 0x85, 0x9F, 0x27, 0x01, 0x00, 0x9F, 0x36, 0x02, 0x06,
0x0C, 0x9F, 0x6C, 0x02, 0x10, 0x00, 0x90, 0x00};
static void emv_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) {
if(furi_log_get_level() == FuriLogLevelTrace) {
FURI_LOG_T(TAG, "%s", message);
printf("TX: ");
for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) {
printf("%02X ", tx_rx->tx_data[i]);
}
printf("\r\nRX: ");
for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) {
printf("%02X ", tx_rx->rx_data[i]);
}
printf("\r\n");
}
}
static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
uint16_t i = 0;
uint16_t tag = 0, first_byte = 0;
uint16_t tlen = 0;
bool success = false;
while(i < len) {
first_byte = buff[i];
if((first_byte & 31) == 31) { // 2-byte tag
tag = buff[i] << 8 | buff[i + 1];
i++;
FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag);
} else {
tag = buff[i];
FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag);
}
i++;
tlen = buff[i];
if((tlen & 128) == 128) { // long length value
i++;
tlen = buff[i];
FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen);
} else {
FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen);
}
i++;
if((first_byte & 32) == 32) { // "Constructed" -- contains more TLV data to parse
FURI_LOG_T(TAG, "Constructed TLV %x", tag);
if(!emv_decode_response(&buff[i], tlen, app)) {
FURI_LOG_T(TAG, "Failed to decode response for %x", tag);
// return false;
} else {
success = true;
}
} else {
switch(tag) {
case EMV_TAG_AID:
app->aid_len = tlen;
memcpy(app->aid, &buff[i], tlen);
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag);
break;
case EMV_TAG_PRIORITY:
memcpy(&app->priority, &buff[i], tlen);
success = true;
break;
case EMV_TAG_CARD_NAME:
memcpy(app->name, &buff[i], tlen);
app->name[tlen] = '\0';
app->name_found = true;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name);
break;
case EMV_TAG_PDOL:
memcpy(app->pdol.data, &buff[i], tlen);
app->pdol.size = tlen;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen);
break;
case EMV_TAG_AFL:
memcpy(app->afl.data, &buff[i], tlen);
app->afl.size = tlen;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen);
break;
case EMV_TAG_TRACK_1_EQUIV: {
char track_1_equiv[80];
memcpy(track_1_equiv, &buff[i], tlen);
track_1_equiv[tlen] = '\0';
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv);
break;
}
case EMV_TAG_TRACK_2_EQUIV: {
// 0xD0 delimits PAN from expiry (YYMM)
for(int x = 1; x < tlen; x++) {
if(buff[i + x + 1] > 0xD0) {
memcpy(app->card_number, &buff[i], x + 1);
app->card_number_len = x + 1;
app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4);
app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4);
break;
}
}
// Convert 4-bit to ASCII representation
char track_2_equiv[41];
uint8_t track_2_equiv_len = 0;
for(int x = 0; x < tlen; x++) {
char top = (buff[i + x] >> 4) + '0';
char bottom = (buff[i + x] & 0x0F) + '0';
track_2_equiv[x * 2] = top;
track_2_equiv_len++;
if(top == '?') break;
track_2_equiv[x * 2 + 1] = bottom;
track_2_equiv_len++;
if(bottom == '?') break;
}
track_2_equiv[track_2_equiv_len] = '\0';
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv);
break;
}
case EMV_TAG_PAN:
memcpy(app->card_number, &buff[i], tlen);
app->card_number_len = tlen;
success = true;
break;
case EMV_TAG_EXP_DATE:
app->exp_year = buff[i];
app->exp_month = buff[i + 1];
success = true;
break;
case EMV_TAG_CURRENCY_CODE:
app->currency_code = (buff[i] << 8 | buff[i + 1]);
success = true;
break;
case EMV_TAG_COUNTRY_CODE:
app->country_code = (buff[i] << 8 | buff[i + 1]);
success = true;
break;
}
}
i += tlen;
}
return success;
}
static bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool app_aid_found = false;
const uint8_t emv_select_ppse_cmd[] = {
0x00, 0xA4, // SELECT ppse
0x04, 0x00, // P1:By name, P2: empty
0x0e, // Lc: Data length
0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string:
0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE)
0x00 // Le
};
memcpy(tx_rx->tx_data, emv_select_ppse_cmd, sizeof(emv_select_ppse_cmd));
tx_rx->tx_bits = sizeof(emv_select_ppse_cmd) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select PPSE");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "Select PPSE answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
app_aid_found = true;
} else {
FURI_LOG_E(TAG, "Failed to parse application");
}
} else {
FURI_LOG_E(TAG, "Failed select PPSE");
}
return app_aid_found;
}
static bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
app->app_started = false;
const uint8_t emv_select_header[] = {
0x00,
0xA4, // SELECT application
0x04,
0x00 // P1:By name, P2:First or only occurence
};
uint16_t size = sizeof(emv_select_header);
// Copy header
memcpy(tx_rx->tx_data, emv_select_header, size);
// Copy AID
tx_rx->tx_data[size++] = app->aid_len;
memcpy(&tx_rx->tx_data[size], app->aid, app->aid_len);
size += app->aid_len;
tx_rx->tx_data[size++] = 0x00;
tx_rx->tx_bits = size * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Start application");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "Start application answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
app->app_started = true;
} else {
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
}
} else {
FURI_LOG_E(TAG, "Failed to start application");
}
return app->app_started;
}
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
bool tag_found;
for(uint16_t i = 0; i < src->size; i++) {
tag_found = false;
for(uint8_t j = 0; j < sizeof(pdol_values) / sizeof(PDOLValue*); j++) {
if(src->data[i] == pdol_values[j]->tag) {
// Found tag with 1 byte length
uint8_t len = src->data[++i];
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
dest->size += len;
tag_found = true;
break;
} else if(((src->data[i] << 8) | src->data[i + 1]) == pdol_values[j]->tag) {
// Found tag with 2 byte length
i += 2;
uint8_t len = src->data[i];
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
dest->size += len;
tag_found = true;
break;
}
}
if(!tag_found) {
// Unknown tag, fill zeros
i += 2;
uint8_t len = src->data[i];
memset(dest->data + dest->size, 0, len);
dest->size += len;
}
}
return dest->size;
}
static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool card_num_read = false;
const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00};
uint16_t size = sizeof(emv_gpo_header);
// Copy header
memcpy(tx_rx->tx_data, emv_gpo_header, size);
APDU pdol_data = {0, {0}};
// Prepare and copy pdol parameters
emv_prepare_pdol(&pdol_data, &app->pdol);
tx_rx->tx_data[size++] = 0x02 + pdol_data.size;
tx_rx->tx_data[size++] = 0x83;
tx_rx->tx_data[size++] = pdol_data.size;
memcpy(tx_rx->tx_data + size, pdol_data.data, pdol_data.size);
size += pdol_data.size;
tx_rx->tx_data[size++] = 0;
tx_rx->tx_bits = size * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Get proccessing options");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "Get processing options answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
if(app->card_number_len > 0) {
card_num_read = true;
}
}
} else {
FURI_LOG_E(TAG, "Failed to get processing options");
}
return card_num_read;
}
static bool emv_read_sfi_record(
FuriHalNfcTxRxContext* tx_rx,
EmvApplication* app,
uint8_t sfi,
uint8_t record_num) {
bool card_num_read = false;
uint8_t sfi_param = (sfi << 3) | (1 << 2);
uint8_t emv_sfi_header[] = {
0x00,
0xB2, // READ RECORD
record_num, // P1:record_number
sfi_param, // P2:SFI
0x00 // Le
};
memcpy(tx_rx->tx_data, emv_sfi_header, sizeof(emv_sfi_header));
tx_rx->tx_bits = sizeof(emv_sfi_header) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "SFI record:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
card_num_read = true;
}
} else {
FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num);
}
return card_num_read;
}
static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool card_num_read = false;
if(app->afl.size == 0) {
return false;
}
FURI_LOG_D(TAG, "Search PAN in SFI");
// Iterate through all files
for(size_t i = 0; i < app->afl.size; i += 4) {
uint8_t sfi = app->afl.data[i] >> 3;
uint8_t record_start = app->afl.data[i + 1];
uint8_t record_end = app->afl.data[i + 2];
// Iterate through all records in file
for(uint8_t record = record_start; record <= record_end; ++record) {
card_num_read |= emv_read_sfi_record(tx_rx, app, sfi, record);
}
}
return card_num_read;
}
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
furi_assert(tx_rx);
furi_assert(emv_app);
bool card_num_read = false;
memset(emv_app, 0, sizeof(EmvApplication));
do {
if(!emv_select_ppse(tx_rx, emv_app)) break;
if(!emv_select_app(tx_rx, emv_app)) break;
if(emv_get_processing_options(tx_rx, emv_app)) {
card_num_read = true;
} else {
card_num_read = emv_read_files(tx_rx, emv_app);
}
} while(false);
return card_num_read;
}
bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) {
furi_assert(tx_rx);
bool emulation_complete = false;
tx_rx->tx_bits = 0;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
do {
FURI_LOG_D(TAG, "Read select PPSE command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, select_ppse_ans, sizeof(select_ppse_ans));
tx_rx->tx_bits = sizeof(select_ppse_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select PPSE answer and read select App command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, select_app_ans, sizeof(select_app_ans));
tx_rx->tx_bits = sizeof(select_app_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select App answer and read get PDOL command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, pdol_ans, sizeof(pdol_ans));
tx_rx->tx_bits = sizeof(pdol_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send get PDOL answer");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
emulation_complete = true;
} while(false);
return emulation_complete;
}
-80
View File
@@ -1,80 +0,0 @@
#pragma once
#include <furi_hal_nfc.h>
#define MAX_APDU_LEN 255
#define EMV_TAG_APP_TEMPLATE 0x61
#define EMV_TAG_AID 0x4F
#define EMV_TAG_PRIORITY 0x87
#define EMV_TAG_PDOL 0x9F38
#define EMV_TAG_CARD_NAME 0x50
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_LOG_CTRL 0x9F4D
#define EMV_TAG_TRACK_1_EQUIV 0x56
#define EMV_TAG_TRACK_2_EQUIV 0x57
#define EMV_TAG_PAN 0x5A
#define EMV_TAG_AFL 0x94
#define EMV_TAG_EXP_DATE 0x5F24
#define EMV_TAG_COUNTRY_CODE 0x5F28
#define EMV_TAG_CURRENCY_CODE 0x9F42
#define EMV_TAG_CARDHOLDER_NAME 0x5F20
typedef struct {
char name[32];
uint8_t aid[16];
uint16_t aid_len;
uint8_t number[10];
uint8_t number_len;
uint8_t exp_mon;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
} EmvData;
typedef struct {
uint16_t tag;
uint8_t data[];
} PDOLValue;
typedef struct {
uint8_t size;
uint8_t data[MAX_APDU_LEN];
} APDU;
typedef struct {
uint8_t priority;
uint8_t aid[16];
uint8_t aid_len;
bool app_started;
char name[32];
bool name_found;
uint8_t card_number[10];
uint8_t card_number_len;
uint8_t exp_month;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
APDU pdol;
APDU afl;
} EmvApplication;
/** Read bank card data
* @note Search EMV Application, start it, try to read AID, PAN, card name,
* expiration date, currency and country codes
*
* @param tx_rx FuriHalNfcTxRxContext instance
* @param emv_app EmvApplication instance
*
* @return true on success
*/
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
/** Emulate bank card
* @note Answer to application selection and PDOL
*
* @param tx_rx FuriHalNfcTxRxContext instance
*
* @return true on success
*/
bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx);
+147
View File
@@ -0,0 +1,147 @@
#include "felica.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define FELICA_PROTOCOL_NAME "FeliCa"
#define FELICA_DEVICE_NAME "FeliCa"
#define FELICA_DATA_FORMAT_VERSION "Data format version"
#define FELICA_MANUFACTURE_ID "Manufacture id"
#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter"
static const uint32_t felica_data_format_version = 1;
const NfcDeviceBase nfc_device_felica = {
.protocol_name = FELICA_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)felica_alloc,
.free = (NfcDeviceFree)felica_free,
.reset = (NfcDeviceReset)felica_reset,
.copy = (NfcDeviceCopy)felica_copy,
.verify = (NfcDeviceVerify)felica_verify,
.load = (NfcDeviceLoad)felica_load,
.save = (NfcDeviceSave)felica_save,
.is_equal = (NfcDeviceEqual)felica_is_equal,
.get_name = (NfcDeviceGetName)felica_get_device_name,
.get_uid = (NfcDeviceGetUid)felica_get_uid,
.set_uid = (NfcDeviceSetUid)felica_set_uid,
.get_base_data = (NfcDeviceGetBaseData)felica_get_base_data,
};
FelicaData* felica_alloc() {
FelicaData* data = malloc(sizeof(FelicaData));
return data;
}
void felica_free(FelicaData* data) {
furi_assert(data);
free(data);
}
void felica_reset(FelicaData* data) {
memset(data, 0, sizeof(FelicaData));
}
void felica_copy(FelicaData* data, const FelicaData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool felica_verify(FelicaData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
return false;
}
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
uint32_t data_format_version = 0;
if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))
break;
if(data_format_version != felica_data_format_version) break;
if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
break;
if(!flipper_format_read_hex(
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break;
parsed = true;
} while(false);
return parsed;
}
bool felica_save(const FelicaData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break;
if(!flipper_format_write_uint32(
ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1))
break;
if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
break;
if(!flipper_format_write_hex(
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break;
saved = true;
} while(false);
return saved;
}
bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(FelicaData)) == 0;
}
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return FELICA_DEVICE_NAME;
}
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
furi_assert(data);
// Consider Manufacturer ID as UID
if(uid_len) {
*uid_len = FELICA_IDM_SIZE;
}
return data->idm.data;
}
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
// Consider Manufacturer ID as UID
const bool uid_valid = uid_len == FELICA_IDM_SIZE;
if(uid_valid) {
memcpy(data->idm.data, uid, uid_len);
}
return uid_valid;
}
FelicaData* felica_get_base_data(const FelicaData* data) {
UNUSED(data);
furi_crash("No base data");
}
+77
View File
@@ -0,0 +1,77 @@
#pragma once
#include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FELICA_IDM_SIZE (8U)
#define FELICA_PMM_SIZE (8U)
#define FELICA_GUARD_TIME_US (20000U)
#define FELICA_FDT_POLL_FC (10000U)
#define FELICA_POLL_POLL_MIN_US (1280U)
#define FELICA_SYSTEM_CODE_CODE (0xFFFFU)
#define FELICA_TIME_SLOT_1 (0x00U)
#define FELICA_TIME_SLOT_2 (0x01U)
#define FELICA_TIME_SLOT_4 (0x03U)
#define FELICA_TIME_SLOT_8 (0x07U)
#define FELICA_TIME_SLOT_16 (0x0FU)
typedef enum {
FelicaErrorNone,
FelicaErrorNotPresent,
FelicaErrorColResFailed,
FelicaErrorBufferOverflow,
FelicaErrorCommunication,
FelicaErrorFieldOff,
FelicaErrorWrongCrc,
FelicaErrorProtocol,
FelicaErrorTimeout,
} FelicaError;
typedef struct {
uint8_t data[FELICA_IDM_SIZE];
} FelicaIDm;
typedef struct {
uint8_t data[FELICA_PMM_SIZE];
} FelicaPMm;
typedef struct {
FelicaIDm idm;
FelicaPMm pmm;
} FelicaData;
extern const NfcDeviceBase nfc_device_felica;
FelicaData* felica_alloc();
void felica_free(FelicaData* data);
void felica_reset(FelicaData* data);
void felica_copy(FelicaData* data, const FelicaData* other);
bool felica_verify(FelicaData* data, const FuriString* device_type);
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version);
bool felica_save(const FelicaData* data, FlipperFormat* ff);
bool felica_is_equal(const FelicaData* data, const FelicaData* other);
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type);
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len);
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);
FelicaData* felica_get_base_data(const FelicaData* data);
#ifdef __cplusplus
}
#endif
+117
View File
@@ -0,0 +1,117 @@
#include "felica_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
FelicaPoller* instance = malloc(sizeof(FelicaPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechFelica);
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
instance->data = felica_alloc();
instance->felica_event.data = &instance->felica_event_data;
instance->general_event.protocol = NfcProtocolFelica;
instance->general_event.event_data = &instance->felica_event;
instance->general_event.instance = instance;
return instance;
}
static void felica_poller_free(FelicaPoller* instance) {
furi_assert(instance);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
felica_free(instance->data);
free(instance);
}
static void
felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
FelicaPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != FelicaPollerStateActivated) {
FelicaError error = felica_poller_async_activate(instance, instance->data);
if(error == FelicaErrorNone) {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->felica_event.type = FelicaPollerEventTypeError;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = FelicaErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
static bool felica_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
FelicaPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == FelicaPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
FelicaError error = felica_poller_async_activate(instance, instance->data);
protocol_detected = (error == FelicaErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_felica = {
.alloc = (NfcPollerAlloc)felica_poller_alloc,
.free = (NfcPollerFree)felica_poller_free,
.set_callback = (NfcPollerSetCallback)felica_poller_set_callback,
.run = (NfcPollerRun)felica_poller_run,
.detect = (NfcPollerDetect)felica_poller_detect,
.get_data = (NfcPollerGetData)felica_poller_get_data,
};
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "felica.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct FelicaPoller FelicaPoller;
typedef enum {
FelicaPollerEventTypeError,
FelicaPollerEventTypeReady,
} FelicaPollerEventType;
typedef struct {
FelicaError error;
} FelicaPollerEventData;
typedef struct {
FelicaPollerEventType type;
FelicaPollerEventData* data;
} FelicaPollerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,13 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const NfcPollerBase nfc_poller_felica;
#ifdef __cplusplus
}
#endif
+128
View File
@@ -0,0 +1,128 @@
#include "felica_poller_i.h"
#include <nfc/helpers/felica_crc.h>
#define TAG "FelicaPoller"
static FelicaError felica_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return FelicaErrorNone;
case NfcErrorTimeout:
return FelicaErrorTimeout;
default:
return FelicaErrorNotPresent;
}
}
static FelicaError felica_poller_frame_exchange(
FelicaPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE);
felica_crc_append(instance->tx_buffer);
FelicaError ret = FelicaErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = felica_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!felica_crc_check(instance->rx_buffer)) {
ret = FelicaErrorWrongCrc;
break;
}
felica_crc_trim(rx_buffer);
} while(false);
return ret;
}
FelicaError felica_poller_async_polling(
FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp) {
furi_assert(instance);
furi_assert(cmd);
furi_assert(resp);
FelicaError error = FelicaErrorNone;
do {
bit_buffer_set_size_bytes(instance->tx_buffer, 2);
// Set frame len
bit_buffer_set_byte(
instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + FELICA_CRC_SIZE);
// Set command code
bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_REQ_CODE);
// Set other data
bit_buffer_append_bytes(
instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand));
error = felica_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
if(error != FelicaErrorNone) break;
if(bit_buffer_get_byte(instance->rx_buffer, 1) != FELICA_POLLER_CMD_POLLING_RESP_CODE) {
error = FelicaErrorProtocol;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) <
sizeof(FelicaIDm) + sizeof(FelicaPMm) + 1) {
error = FelicaErrorProtocol;
break;
}
bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm));
bit_buffer_write_bytes_mid(
instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm));
} while(false);
return error;
}
FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data) {
furi_assert(instance);
felica_reset(data);
FelicaError ret;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send Polling command
const FelicaPollerPollingCommand polling_cmd = {
.system_code = FELICA_SYSTEM_CODE_CODE,
.request_code = 0,
.time_slot = FELICA_TIME_SLOT_1,
};
FelicaPollerPollingResponse polling_resp = {};
ret = felica_poller_async_polling(instance, &polling_cmd, &polling_resp);
if(ret != FelicaErrorNone) {
FURI_LOG_T(TAG, "Activation failed error: %d", ret);
break;
}
data->idm = polling_resp.idm;
data->pmm = polling_resp.pmm;
instance->state = FelicaPollerStateActivated;
} while(false);
return ret;
}
@@ -0,0 +1,60 @@
#pragma once
#include "felica_poller.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FELICA_POLLER_MAX_BUFFER_SIZE (256U)
#define FELICA_POLLER_POLLING_FWT (200000U)
#define FELICA_POLLER_CMD_POLLING_REQ_CODE (0x00U)
#define FELICA_POLLER_CMD_POLLING_RESP_CODE (0x01U)
typedef enum {
FelicaPollerStateIdle,
FelicaPollerStateActivated,
} FelicaPollerState;
struct FelicaPoller {
Nfc* nfc;
FelicaPollerState state;
FelicaData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
FelicaPollerEvent felica_event;
FelicaPollerEventData felica_event_data;
NfcGenericCallback callback;
void* context;
};
typedef struct {
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
} FelicaPollerPollingCommand;
typedef struct {
FelicaIDm idm;
FelicaPMm pmm;
uint8_t request_data[2];
} FelicaPollerPollingResponse;
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
FelicaError felica_poller_async_polling(
FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp);
FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data);
#ifdef __cplusplus
}
#endif
+186
View File
@@ -0,0 +1,186 @@
#include "iso14443_3a.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define ISO14443A_ATS_BIT (1U << 5)
#define ISO14443_3A_PROTOCOL_NAME_LEGACY "UID"
#define ISO14443_3A_PROTOCOL_NAME "ISO14443-3A"
#define ISO14443_3A_DEVICE_NAME "ISO14443-3A (Unknown)"
#define ISO14443_3A_ATQA_KEY "ATQA"
#define ISO14443_3A_SAK_KEY "SAK"
const NfcDeviceBase nfc_device_iso14443_3a = {
.protocol_name = ISO14443_3A_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_3a_alloc,
.free = (NfcDeviceFree)iso14443_3a_free,
.reset = (NfcDeviceReset)iso14443_3a_reset,
.copy = (NfcDeviceCopy)iso14443_3a_copy,
.verify = (NfcDeviceVerify)iso14443_3a_verify,
.load = (NfcDeviceLoad)iso14443_3a_load,
.save = (NfcDeviceSave)iso14443_3a_save,
.is_equal = (NfcDeviceEqual)iso14443_3a_is_equal,
.get_name = (NfcDeviceGetName)iso14443_3a_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_3a_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_3a_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data,
};
Iso14443_3aData* iso14443_3a_alloc() {
Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData));
return data;
}
void iso14443_3a_free(Iso14443_3aData* data) {
furi_assert(data);
free(data);
}
void iso14443_3a_reset(Iso14443_3aData* data) {
furi_assert(data);
memset(data, 0, sizeof(Iso14443_3aData));
}
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY);
}
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
// Common to all format versions
if(!flipper_format_read_hex(ff, ISO14443_3A_ATQA_KEY, data->atqa, 2)) break;
if(!flipper_format_read_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;
if(version > NFC_LSB_ATQA_FORMAT_VERSION) {
// Swap ATQA bytes for newer versions
FURI_SWAP(data->atqa[0], data->atqa[1]);
}
parsed = true;
} while(false);
return parsed;
}
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
// Save ATQA in MSB order for correct companion apps display
const uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_comment_cstr(ff, ISO14443_3A_PROTOCOL_NAME " specific data"))
break;
// Write ATQA and SAK
if(!flipper_format_write_hex(ff, ISO14443_3A_ATQA_KEY, atqa, 2)) break;
if(!flipper_format_write_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;
saved = true;
} while(false);
return saved;
}
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(Iso14443_3aData)) == 0;
}
const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_3A_DEVICE_NAME;
}
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) {
furi_assert(data);
if(uid_len) {
*uid_len = data->uid_len;
}
return data->uid;
}
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES ||
uid_len == ISO14443_3A_UID_7_BYTES ||
uid_len == ISO14443_3A_UID_10_BYTES;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
data->uid_len = uid_len;
}
return uid_valid;
}
Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) {
UNUSED(data);
furi_crash("No base data");
}
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) {
furi_assert(data);
uint32_t cuid = 0;
const uint8_t* cuid_start = data->uid;
if(data->uid_len == ISO14443_3A_UID_7_BYTES) {
cuid_start = &data->uid[3];
}
cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | (cuid_start[3]);
return cuid;
}
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data) {
furi_assert(data);
return data->sak & ISO14443A_ATS_BIT;
}
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data) {
furi_assert(data);
return data->sak;
}
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]) {
furi_assert(data);
furi_assert(atqa);
memcpy(atqa, data->atqa, sizeof(data->atqa));
}
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak) {
furi_assert(data);
data->sak = sak;
}
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]) {
furi_assert(data);
furi_assert(atqa);
memcpy(data->atqa, atqa, sizeof(data->atqa));
}
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3A_UID_4_BYTES (4U)
#define ISO14443_3A_UID_7_BYTES (7U)
#define ISO14443_3A_UID_10_BYTES (10U)
#define ISO14443_3A_MAX_UID_SIZE ISO14443_3A_UID_10_BYTES
#define ISO14443_3A_GUARD_TIME_US (5000)
#define ISO14443_3A_FDT_POLL_FC (1620)
#define ISO14443_3A_FDT_LISTEN_FC (1172)
#define ISO14443_3A_POLLER_MASK_RX_FS ((ISO14443_3A_FDT_LISTEN_FC) / 2)
#define ISO14443_3A_POLL_POLL_MIN_US (1100)
typedef enum {
Iso14443_3aErrorNone,
Iso14443_3aErrorNotPresent,
Iso14443_3aErrorColResFailed,
Iso14443_3aErrorBufferOverflow,
Iso14443_3aErrorCommunication,
Iso14443_3aErrorFieldOff,
Iso14443_3aErrorWrongCrc,
Iso14443_3aErrorTimeout,
} Iso14443_3aError;
typedef struct {
uint8_t sens_resp[2];
} Iso14443_3aSensResp;
typedef struct {
uint8_t sel_cmd;
uint8_t sel_par;
uint8_t data[4]; // max data bit is 32
} Iso14443_3aSddReq;
typedef struct {
uint8_t nfcid[4];
uint8_t bss;
} Iso14443_3aSddResp;
typedef struct {
uint8_t sel_cmd;
uint8_t sel_par;
uint8_t nfcid[4];
uint8_t bcc;
} Iso14443_3aSelReq;
typedef struct {
uint8_t sak;
} Iso14443_3aSelResp;
typedef struct {
uint8_t uid[ISO14443_3A_MAX_UID_SIZE];
uint8_t uid_len;
uint8_t atqa[2];
uint8_t sak;
} Iso14443_3aData;
Iso14443_3aData* iso14443_3a_alloc();
void iso14443_3a_free(Iso14443_3aData* data);
void iso14443_3a_reset(Iso14443_3aData* data);
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other);
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type);
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff);
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other);
const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len);
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data);
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data);
// Getters and tests
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data);
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data);
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]);
// Setters
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak);
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_3a;
@@ -0,0 +1,127 @@
#include "iso14443_3a_listener_i.h"
#include "nfc/protocols/nfc_listener_base.h"
#include "nfc/helpers/iso14443_crc.h"
#include <furi.h>
#include <lib/nfc/nfc.h>
#define TAG "Iso14443_3aListener"
#define ISO14443_3A_LISTENER_MAX_BUFFER_SIZE (256)
static bool iso14443_3a_listener_halt_received(BitBuffer* buf) {
bool halt_cmd_received = false;
do {
if(bit_buffer_get_size_bytes(buf) != 4) break;
if(!iso14443_crc_check(Iso14443CrcTypeA, buf)) break;
if(bit_buffer_get_byte(buf, 0) != 0x50) break;
if(bit_buffer_get_byte(buf, 1) != 0x00) break;
halt_cmd_received = true;
} while(false);
return halt_cmd_received;
}
Iso14443_3aListener* iso14443_3a_listener_alloc(Nfc* nfc, Iso14443_3aData* data) {
furi_assert(nfc);
Iso14443_3aListener* instance = malloc(sizeof(Iso14443_3aListener));
instance->nfc = nfc;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_LISTENER_MAX_BUFFER_SIZE);
instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;
instance->generic_event.protocol = NfcProtocolIso14443_3a;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->iso14443_3a_event;
nfc_set_fdt_listen_fc(instance->nfc, ISO14443_3A_FDT_LISTEN_FC);
nfc_config(instance->nfc, NfcModeListener, NfcTechIso14443a);
nfc_iso14443a_listener_set_col_res_data(
instance->nfc,
instance->data->uid,
instance->data->uid_len,
instance->data->atqa,
instance->data->sak);
return instance;
}
void iso14443_3a_listener_free(Iso14443_3aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
void iso14443_3a_listener_set_callback(
Iso14443_3aListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
const Iso14443_3aData* iso14443_3a_listener_get_data(Iso14443_3aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
NfcCommand iso14443_3a_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3aListener* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypeListenerActivated) {
instance->state = Iso14443_3aListenerStateActive;
} else if(nfc_event->type == NfcEventTypeFieldOff) {
instance->state = Iso14443_3aListenerStateIdle;
if(instance->callback) {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeFieldOff;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
} else if(nfc_event->type == NfcEventTypeRxEnd) {
if(iso14443_3a_listener_halt_received(nfc_event->data.buffer)) {
if(instance->callback) {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeHalted;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
} else {
if(iso14443_crc_check(Iso14443CrcTypeA, nfc_event->data.buffer)) {
instance->iso14443_3a_event.type =
Iso14443_3aListenerEventTypeReceivedStandardFrame;
iso14443_crc_trim(nfc_event->data.buffer);
} else {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeReceivedData;
}
instance->iso14443_3a_event_data.buffer = nfc_event->data.buffer;
if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context);
}
}
}
return command;
}
const NfcListenerBase nfc_listener_iso14443_3a = {
.alloc = (NfcListenerAlloc)iso14443_3a_listener_alloc,
.free = (NfcListenerFree)iso14443_3a_listener_free,
.set_callback = (NfcListenerSetCallback)iso14443_3a_listener_set_callback,
.get_data = (NfcListenerGetData)iso14443_3a_listener_get_data,
.run = (NfcListenerRun)iso14443_3a_listener_run,
};
@@ -0,0 +1,31 @@
#pragma once
#include "iso14443_3a.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aListener Iso14443_3aListener;
typedef enum {
Iso14443_3aListenerEventTypeFieldOff,
Iso14443_3aListenerEventTypeHalted,
Iso14443_3aListenerEventTypeReceivedStandardFrame,
Iso14443_3aListenerEventTypeReceivedData,
} Iso14443_3aListenerEventType;
typedef struct {
BitBuffer* buffer;
} Iso14443_3aListenerEventData;
typedef struct {
Iso14443_3aListenerEventType type;
Iso14443_3aListenerEventData* data;
} Iso14443_3aListenerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_iso14443_3a;
@@ -0,0 +1,73 @@
#include "iso14443_3a_listener_i.h"
#include "nfc/helpers/iso14443_crc.h"
#define TAG "Iso14443_3aListener"
static Iso14443_3aError iso14443_3a_listener_process_nfc_error(NfcError error) {
Iso14443_3aError ret = Iso14443_3aErrorNone;
if(error == NfcErrorNone) {
ret = Iso14443_3aErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso14443_3aErrorTimeout;
} else {
ret = Iso14443_3aErrorFieldOff;
}
return ret;
}
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_listener_tx(instance->nfc, tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_iso14443a_listener_tx_custom_parity(instance->nfc, tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
}
return ret;
};
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(instance->tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
break;
}
} while(false);
return ret;
}
@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_3a_listener.h"
#include <nfc/protocols/nfc_generic_event.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_3aListenerStateIdle,
Iso14443_3aListenerStateActive,
} Iso14443_3aListenerState;
struct Iso14443_3aListener {
Nfc* nfc;
Iso14443_3aData* data;
Iso14443_3aListenerState state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Iso14443_3aListenerEvent iso14443_3a_event;
Iso14443_3aListenerEventData iso14443_3a_event_data;
NfcGenericCallback callback;
void* context;
};
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,128 @@
#include "iso14443_3a_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "ISO14443_3A"
const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static Iso14443_3aPoller* iso14443_3a_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
Iso14443_3aPoller* instance = malloc(sizeof(Iso14443_3aPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443a);
nfc_set_guard_time_us(instance->nfc, ISO14443_3A_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3A_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3A_POLL_POLL_MIN_US);
instance->data = iso14443_3a_alloc();
instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;
instance->general_event.protocol = NfcProtocolIso14443_3a;
instance->general_event.event_data = &instance->iso14443_3a_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_3a_poller_free_new(Iso14443_3aPoller* iso14443_3a_poller) {
furi_assert(iso14443_3a_poller);
Iso14443_3aPoller* instance = iso14443_3a_poller;
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
iso14443_3a_free(instance->data);
free(instance);
}
static void iso14443_3a_poller_set_callback(
Iso14443_3aPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_3a_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3aPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != Iso14443_3aPollerStateActivated) {
Iso14443_3aData data = {};
Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, &data);
if(error == Iso14443_3aErrorNone) {
instance->state = Iso14443_3aPollerStateActivated;
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;
instance->iso14443_3a_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeError;
instance->iso14443_3a_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;
instance->iso14443_3a_event_data.error = Iso14443_3aErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
if(command == NfcCommandReset) {
instance->state = Iso14443_3aPollerStateIdle;
}
return command;
}
static bool iso14443_3a_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
Iso14443_3aPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == Iso14443_3aPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, NULL);
protocol_detected = (error == Iso14443_3aErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_3a = {
.alloc = (NfcPollerAlloc)iso14443_3a_poller_alloc,
.free = (NfcPollerFree)iso14443_3a_poller_free_new,
.set_callback = (NfcPollerSetCallback)iso14443_3a_poller_set_callback,
.run = (NfcPollerRun)iso14443_3a_poller_run,
.detect = (NfcPollerDetect)iso14443_3a_poller_detect,
.get_data = (NfcPollerGetData)iso14443_3a_poller_get_data,
};
@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_3a.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aPoller Iso14443_3aPoller;
typedef enum {
Iso14443_3aPollerEventTypeError,
Iso14443_3aPollerEventTypeReady,
} Iso14443_3aPollerEventType;
typedef struct {
Iso14443_3aError error;
} Iso14443_3aPollerEventData;
typedef struct {
Iso14443_3aPollerEventType type;
Iso14443_3aPollerEventData* data;
} Iso14443_3aPollerEvent;
Iso14443_3aError iso14443_3a_poller_txrx(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
Iso14443_3aError iso14443_3a_poller_send_standard_frame(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_3a;
@@ -0,0 +1,293 @@
#include "iso14443_3a_poller_i.h"
#include <furi.h>
#include "nfc/helpers/iso14443_crc.h"
#define TAG "ISO14443_3A"
static Iso14443_3aError iso14443_3a_poller_process_error(NfcError error) {
Iso14443_3aError ret = Iso14443_3aErrorNone;
if(error == NfcErrorNone) {
ret = Iso14443_3aErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso14443_3aErrorTimeout;
} else {
ret = Iso14443_3aErrorNotPresent;
}
return ret;
}
static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
uint16_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - 2);
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) {
ret = Iso14443_3aErrorWrongCrc;
break;
}
iso14443_crc_trim(rx_buffer);
} while(false);
return ret;
}
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
NfcError error = NfcErrorNone;
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
error = nfc_iso14443a_poller_trx_short_frame(
instance->nfc,
NfcIso14443aShortFrameSensReq,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {
ret = Iso14443_3aErrorCommunication;
break;
}
} while(false);
return ret;
}
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(instance->tx_buffer);
uint8_t halt_cmd[2] = {0x50, 0x00};
bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd));
iso14443_3a_poller_standard_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);
instance->state = Iso14443_3aPollerStateIdle;
return Iso14443_3aErrorNone;
}
Iso14443_3aError iso14443_3a_poller_async_activate(
Iso14443_3aPoller* instance,
Iso14443_3aData* iso14443_3a_data) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
// Reset Iso14443_3a poller state
memset(&instance->col_res, 0, sizeof(instance->col_res));
memset(instance->data, 0, sizeof(Iso14443_3aData));
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Halt if necessary
if(instance->state != Iso14443_3aPollerStateIdle) {
iso14443_3a_poller_halt(instance);
instance->state = Iso14443_3aPollerStateIdle;
}
NfcError error = NfcErrorNone;
Iso14443_3aError ret = Iso14443_3aErrorNone;
bool activated = false;
do {
error = nfc_iso14443a_poller_trx_short_frame(
instance->nfc,
NfcIso14443aShortFrameSensReq,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
ret = Iso14443_3aErrorNotPresent;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {
FURI_LOG_W(TAG, "Wrong sens response size");
ret = Iso14443_3aErrorCommunication;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer,
&instance->col_res.sens_resp,
sizeof(instance->col_res.sens_resp));
memcpy(
instance->data->atqa,
&instance->col_res.sens_resp,
sizeof(instance->col_res.sens_resp));
instance->state = Iso14443_3aPollerStateColResInProgress;
instance->col_res.cascade_level = 0;
instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;
while(instance->state == Iso14443_3aPollerStateColResInProgress) {
if(instance->col_res.state == Iso14443_3aPollerColResStateStateNewCascade) {
bit_buffer_set_size_bytes(instance->tx_buffer, 2);
bit_buffer_set_byte(
instance->tx_buffer,
0,
ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level));
bit_buffer_set_byte(instance->tx_buffer, 1, ISO14443_3A_POLLER_SEL_PAR(2, 0));
error = nfc_iso14443a_poller_trx_sdd_frame(
instance->nfc,
instance->tx_buffer,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
FURI_LOG_E(TAG, "Sdd request failed: %d", error);
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != 5) {
FURI_LOG_E(TAG, "Sdd response wrong length");
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer, &instance->col_res.sdd_resp, sizeof(Iso14443_3aSddResp));
instance->col_res.state = Iso14443_3aPollerColResStateStateSelectCascade;
} else if(instance->col_res.state == Iso14443_3aPollerColResStateStateSelectCascade) {
instance->col_res.sel_req.sel_cmd =
ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level);
instance->col_res.sel_req.sel_par = ISO14443_3A_POLLER_SEL_PAR(7, 0);
memcpy(
instance->col_res.sel_req.nfcid,
instance->col_res.sdd_resp.nfcid,
sizeof(instance->col_res.sdd_resp.nfcid));
instance->col_res.sel_req.bcc = instance->col_res.sdd_resp.bss;
bit_buffer_copy_bytes(
instance->tx_buffer,
(uint8_t*)&instance->col_res.sel_req,
sizeof(instance->col_res.sel_req));
ret = iso14443_3a_poller_send_standard_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);
if(ret != Iso14443_3aErrorNone) {
FURI_LOG_E(TAG, "Sel request failed: %d", ret);
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) !=
sizeof(instance->col_res.sel_resp)) {
FURI_LOG_E(TAG, "Sel response wrong length");
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer,
&instance->col_res.sel_resp,
sizeof(instance->col_res.sel_resp));
FURI_LOG_T(TAG, "Sel resp: %02X", instance->col_res.sel_resp.sak);
if(instance->col_res.sel_req.nfcid[0] == ISO14443_3A_POLLER_SDD_CL) {
// Copy part of UID
memcpy(
&instance->data->uid[instance->data->uid_len],
&instance->col_res.sel_req.nfcid[1],
3);
instance->data->uid_len += 3;
instance->col_res.cascade_level++;
instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;
} else {
FURI_LOG_T(TAG, "Col resolution complete");
instance->data->sak = instance->col_res.sel_resp.sak;
memcpy(
&instance->data->uid[instance->data->uid_len],
&instance->col_res.sel_req.nfcid[0],
4);
instance->data->uid_len += 4;
instance->col_res.state = Iso14443_3aPollerColResStateStateSuccess;
instance->state = Iso14443_3aPollerStateActivated;
}
}
}
activated = (instance->state == Iso14443_3aPollerStateActivated);
} while(false);
if(activated && iso14443_3a_data) {
*iso14443_3a_data = *instance->data;
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error =
nfc_iso14443a_poller_trx_custom_parity(instance->nfc, tx_buffer, rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_txrx(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_send_standard_frame(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret =
iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
return ret;
}
@@ -0,0 +1,81 @@
#pragma once
#include "iso14443_3a_poller.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U)
#define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl))
#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits)&0x0fU))
#define ISO14443_3A_POLLER_SDD_CL (0x88U)
typedef enum {
Iso14443_3aPollerColResStateStateIdle,
Iso14443_3aPollerColResStateStateNewCascade,
Iso14443_3aPollerColResStateStateSelectCascade,
Iso14443_3aPollerColResStateStateSuccess,
Iso14443_3aPollerColResStateStateFail,
} Iso14443_3aPollerColResState;
typedef struct {
Iso14443_3aPollerColResState state;
Iso14443_3aSensResp sens_resp;
Iso14443_3aSddReq sdd_req;
Iso14443_3aSddResp sdd_resp;
Iso14443_3aSelReq sel_req;
Iso14443_3aSelResp sel_resp;
uint8_t cascade_level;
} Iso14443_3aPollerColRes;
typedef enum {
Iso14443_3aPollerStateIdle,
Iso14443_3aPollerStateColResInProgress,
Iso14443_3aPollerStateColResFailed,
Iso14443_3aPollerStateActivated,
} Iso14443_3aPollerState;
typedef enum {
Iso14443_3aPollerConfigStateIdle,
Iso14443_3aPollerConfigStateDone,
} Iso14443_3aPollerConfigState;
struct Iso14443_3aPoller {
Nfc* nfc;
Iso14443_3aPollerState state;
Iso14443_3aPollerConfigState config_state;
Iso14443_3aPollerColRes col_res;
Iso14443_3aData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
Iso14443_3aPollerEvent iso14443_3a_event;
Iso14443_3aPollerEventData iso14443_3a_event_data;
NfcGenericCallback callback;
void* context;
};
const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_async_activate(
Iso14443_3aPoller* instance,
Iso14443_3aData* iso14443_3a_data);
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,58 @@
#include "iso14443_3a_poller_sync_api.h"
#include "iso14443_3a_poller_i.h"
#include <nfc/nfc_poller.h>
#include <furi/furi.h>
#define ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)
typedef struct {
Iso14443_3aPoller* instance;
FuriThreadId thread_id;
Iso14443_3aError error;
Iso14443_3aData data;
} Iso14443_3aPollerContext;
NfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
Iso14443_3aPollerContext* poller_context = context;
Iso14443_3aPoller* iso14443_3a_poller = event.instance;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
iso14443_3a_copy(&poller_context->data, iso14443_3a_poller->data);
}
poller_context->error = iso14443_3a_event->data->error;
furi_thread_flags_set(poller_context->thread_id, ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);
return NfcCommandStop;
}
Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) {
furi_assert(nfc);
furi_assert(iso14443_3a_data);
Iso14443_3aPollerContext poller_context = {};
poller_context.thread_id = furi_thread_get_current_id();
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a);
nfc_poller_start(poller, iso14443_3a_poller_read_callback, &poller_context);
furi_thread_flags_wait(
ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
furi_thread_flags_clear(ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);
nfc_poller_stop(poller);
nfc_poller_free(poller);
if(poller_context.error == Iso14443_3aErrorNone) {
*iso14443_3a_data = poller_context.data;
}
return poller_context.error;
}
@@ -0,0 +1,14 @@
#pragma once
#include "iso14443_3a.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data);
#ifdef __cplusplus
}
#endif
+223
View File
@@ -0,0 +1,223 @@
#include "iso14443_3b_i.h"
#include <furi.h>
#include <nfc/protocols/nfc_device_base_i.h>
#include <nfc/nfc_common.h>
#include <nfc/helpers/iso14443_crc.h>
#define ISO14443_3B_PROTOCOL_NAME "ISO14443-3B"
#define ISO14443_3B_DEVICE_NAME "ISO14443-3B (Unknown)"
#define ISO14443_3B_APP_DATA_KEY "Application data"
#define ISO14443_3B_PROTOCOL_INFO_KEY "Protocol info"
#define ISO14443_3B_FDT_POLL_DEFAULT_FC (ISO14443_3B_FDT_POLL_FC)
const NfcDeviceBase nfc_device_iso14443_3b = {
.protocol_name = ISO14443_3B_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_3b_alloc,
.free = (NfcDeviceFree)iso14443_3b_free,
.reset = (NfcDeviceReset)iso14443_3b_reset,
.copy = (NfcDeviceCopy)iso14443_3b_copy,
.verify = (NfcDeviceVerify)iso14443_3b_verify,
.load = (NfcDeviceLoad)iso14443_3b_load,
.save = (NfcDeviceSave)iso14443_3b_save,
.is_equal = (NfcDeviceEqual)iso14443_3b_is_equal,
.get_name = (NfcDeviceGetName)iso14443_3b_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_3b_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_3b_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_3b_get_base_data,
};
Iso14443_3bData* iso14443_3b_alloc() {
Iso14443_3bData* data = malloc(sizeof(Iso14443_3bData));
return data;
}
void iso14443_3b_free(Iso14443_3bData* data) {
furi_assert(data);
free(data);
}
void iso14443_3b_reset(Iso14443_3bData* data) {
memset(data, 0, sizeof(Iso14443_3bData));
}
void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// No support for old ISO14443-3B
return false;
}
bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
if(!flipper_format_read_hex(
ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE))
break;
if(!flipper_format_read_hex(
ff,
ISO14443_3B_PROTOCOL_INFO_KEY,
(uint8_t*)&data->protocol_info,
sizeof(Iso14443_3bProtocolInfo)))
break;
parsed = true;
} while(false);
return parsed;
}
bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, ISO14443_3B_PROTOCOL_NAME " specific data"))
break;
if(!flipper_format_write_hex(
ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE))
break;
if(!flipper_format_write_hex(
ff,
ISO14443_3B_PROTOCOL_INFO_KEY,
(uint8_t*)&data->protocol_info,
sizeof(Iso14443_3bProtocolInfo)))
break;
saved = true;
} while(false);
return saved;
}
bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(Iso14443_3bData)) == 0;
}
const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_3B_DEVICE_NAME;
}
const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len) {
furi_assert(data);
furi_assert(uid_len);
*uid_len = ISO14443_3B_UID_SIZE;
return data->uid;
}
bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
const bool uid_valid = uid_len == ISO14443_3B_UID_SIZE;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
}
return uid_valid;
}
Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data) {
UNUSED(data);
furi_crash("No base data");
}
bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data) {
furi_assert(data);
return data->protocol_info.protocol_type == 0x01;
}
bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate) {
furi_assert(data);
const uint8_t capability = data->protocol_info.bit_rate_capability;
switch(bit_rate) {
case Iso14443_3bBitRateBoth106Kbit:
return capability == ISO14443_3B_BIT_RATE_BOTH_106KBIT;
case Iso14443_3bBitRatePiccToPcd212Kbit:
return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT;
case Iso14443_3bBitRatePiccToPcd424Kbit:
return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT;
case Iso14443_3bBitRatePiccToPcd848Kbit:
return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT;
case Iso14443_3bBitRatePcdToPicc212Kbit:
return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT;
case Iso14443_3bBitRatePcdToPicc424Kbit:
return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT;
case Iso14443_3bBitRatePcdToPicc848Kbit:
return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT;
default:
return false;
}
}
bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option) {
furi_assert(data);
switch(option) {
case Iso14443_3bFrameOptionNad:
return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_NAD;
case Iso14443_3bFrameOptionCid:
return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_CID;
default:
return false;
}
}
const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size) {
furi_assert(data);
furi_assert(data_size);
*data_size = ISO14443_3B_APP_DATA_SIZE;
return data->app_data;
}
uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data) {
furi_assert(data);
const uint8_t fs_bits = data->protocol_info.max_frame_size;
if(fs_bits < 5) {
return fs_bits * 8 + 16;
} else if(fs_bits == 5) {
return 64;
} else if(fs_bits == 6) {
return 96;
} else if(fs_bits < 13) {
return 128U << (fs_bits - 7);
} else {
return 0;
}
}
uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data) {
furi_assert(data);
const uint8_t fwi = data->protocol_info.fwi;
return fwi < 0x0F ? 4096UL << fwi : ISO14443_3B_FDT_POLL_DEFAULT_FC;
}
@@ -0,0 +1,83 @@
#pragma once
#include <nfc/protocols/nfc_device_base.h>
#include <core/string.h>
#include <toolbox/bit_buffer.h>
#include <flipper_format/flipper_format.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_3bErrorNone,
Iso14443_3bErrorNotPresent,
Iso14443_3bErrorColResFailed,
Iso14443_3bErrorBufferOverflow,
Iso14443_3bErrorCommunication,
Iso14443_3bErrorFieldOff,
Iso14443_3bErrorWrongCrc,
Iso14443_3bErrorTimeout,
} Iso14443_3bError;
typedef enum {
Iso14443_3bBitRateBoth106Kbit,
Iso14443_3bBitRatePiccToPcd212Kbit,
Iso14443_3bBitRatePiccToPcd424Kbit,
Iso14443_3bBitRatePiccToPcd848Kbit,
Iso14443_3bBitRatePcdToPicc212Kbit,
Iso14443_3bBitRatePcdToPicc424Kbit,
Iso14443_3bBitRatePcdToPicc848Kbit,
} Iso14443_3bBitRate;
typedef enum {
Iso14443_3bFrameOptionNad,
Iso14443_3bFrameOptionCid,
} Iso14443_3bFrameOption;
typedef struct Iso14443_3bData Iso14443_3bData;
// Virtual methods
Iso14443_3bData* iso14443_3b_alloc();
void iso14443_3b_free(Iso14443_3bData* data);
void iso14443_3b_reset(Iso14443_3bData* data);
void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other);
bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type);
bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff);
bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other);
const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len);
bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data);
// Getters and tests
bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data);
bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate);
bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option);
const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size);
uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data);
uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_3b;
@@ -0,0 +1 @@
#include "iso14443_3b_i.h"
@@ -0,0 +1,37 @@
#pragma once
#include "iso14443_3b.h"
#define ISO14443_3B_UID_SIZE (4U)
#define ISO14443_3B_APP_DATA_SIZE (4U)
#define ISO14443_3B_GUARD_TIME_US (5000U)
#define ISO14443_3B_FDT_POLL_FC (9000U)
#define ISO14443_3B_POLL_POLL_MIN_US (1280U)
#define ISO14443_3B_BIT_RATE_BOTH_106KBIT (0U << 0)
#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT (1U << 0)
#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT (1U << 1)
#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT (1U << 2)
#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT (1U << 4)
#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT (1U << 5)
#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT (1U << 6)
#define ISO14443_3B_BIT_RATE_BOTH_SAME_COMPULSORY (1U << 7)
#define ISO14443_3B_FRAME_OPTION_NAD (1U << 1)
#define ISO14443_3B_FRAME_OPTION_CID (1U << 0)
typedef struct {
uint8_t bit_rate_capability;
uint8_t protocol_type : 4;
uint8_t max_frame_size : 4;
uint8_t fo : 2;
uint8_t adc : 2;
uint8_t fwi : 4;
} Iso14443_3bProtocolInfo;
struct Iso14443_3bData {
uint8_t uid[ISO14443_3B_UID_SIZE];
uint8_t app_data[ISO14443_3B_APP_DATA_SIZE];
Iso14443_3bProtocolInfo protocol_info;
};
@@ -0,0 +1,121 @@
#include "iso14443_3b_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "ISO14443_3bPoller"
const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static Iso14443_3bPoller* iso14443_3b_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
Iso14443_3bPoller* instance = malloc(sizeof(Iso14443_3bPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b);
nfc_set_guard_time_us(instance->nfc, ISO14443_3B_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3B_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3B_POLL_POLL_MIN_US);
instance->data = iso14443_3b_alloc();
instance->iso14443_3b_event.data = &instance->iso14443_3b_event_data;
instance->general_event.protocol = NfcProtocolIso14443_3b;
instance->general_event.event_data = &instance->iso14443_3b_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_3b_poller_free(Iso14443_3bPoller* instance) {
furi_assert(instance);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
iso14443_3b_free(instance->data);
free(instance);
}
static void iso14443_3b_poller_set_callback(
Iso14443_3bPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_3b_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3bPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != Iso14443_3bPollerStateActivated) {
Iso14443_3bError error = iso14443_3b_poller_async_activate(instance, instance->data);
if(error == Iso14443_3bErrorNone) {
instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady;
instance->iso14443_3b_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeError;
instance->iso14443_3b_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady;
instance->iso14443_3b_event_data.error = Iso14443_3bErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
static bool iso14443_3b_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
Iso14443_3bPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == Iso14443_3bPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
Iso14443_3bError error = iso14443_3b_poller_async_activate(instance, instance->data);
protocol_detected = (error == Iso14443_3bErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_3b = {
.alloc = (NfcPollerAlloc)iso14443_3b_poller_alloc,
.free = (NfcPollerFree)iso14443_3b_poller_free,
.set_callback = (NfcPollerSetCallback)iso14443_3b_poller_set_callback,
.run = (NfcPollerRun)iso14443_3b_poller_run,
.detect = (NfcPollerDetect)iso14443_3b_poller_detect,
.get_data = (NfcPollerGetData)iso14443_3b_poller_get_data,
};
@@ -0,0 +1,30 @@
#pragma once
#include "iso14443_3b.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3bPoller Iso14443_3bPoller;
typedef enum {
Iso14443_3bPollerEventTypeError,
Iso14443_3bPollerEventTypeReady,
} Iso14443_3bPollerEventType;
typedef struct {
Iso14443_3bError error;
} Iso14443_3bPollerEventData;
typedef struct {
Iso14443_3bPollerEventType type;
Iso14443_3bPollerEventData* data;
} Iso14443_3bPollerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_3b;
@@ -0,0 +1,194 @@
#include "iso14443_3b_poller_i.h"
#include <nfc/helpers/iso14443_crc.h>
#define TAG "Iso14443_3bPoller"
#define ISO14443_3B_ATTRIB_FRAME_SIZE_256 (0x08)
static Iso14443_3bError iso14443_3b_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso14443_3bErrorNone;
case NfcErrorTimeout:
return Iso14443_3bErrorTimeout;
default:
return Iso14443_3bErrorNotPresent;
}
}
static Iso14443_3bError iso14443_3b_poller_prepare_trx(Iso14443_3bPoller* instance) {
furi_assert(instance);
if(instance->state == Iso14443_3bPollerStateIdle) {
return iso14443_3b_poller_async_activate(instance, NULL);
}
return Iso14443_3bErrorNone;
}
static Iso14443_3bError iso14443_3b_poller_frame_exchange(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(
tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE);
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer);
Iso14443_3bError ret = Iso14443_3bErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3b_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) {
ret = Iso14443_3bErrorWrongCrc;
break;
}
iso14443_crc_trim(rx_buffer);
} while(false);
return ret;
}
Iso14443_3bError
iso14443_3b_poller_async_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data) {
furi_assert(instance);
furi_assert(instance->nfc);
iso14443_3b_reset(data);
Iso14443_3bError ret;
do {
instance->state = Iso14443_3bPollerStateColResInProgress;
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send REQB
bit_buffer_append_byte(instance->tx_buffer, 0x05);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
bit_buffer_append_byte(instance->tx_buffer, 0x08);
ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC);
if(ret != Iso14443_3bErrorNone) {
instance->state = Iso14443_3bPollerStateColResFailed;
break;
}
typedef struct {
uint8_t flag;
uint8_t uid[ISO14443_3B_UID_SIZE];
uint8_t app_data[ISO14443_3B_APP_DATA_SIZE];
Iso14443_3bProtocolInfo protocol_info;
} Iso14443_3bAtqBLayout;
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(Iso14443_3bAtqBLayout)) {
FURI_LOG_D(TAG, "Unexpected REQB response");
instance->state = Iso14443_3bPollerStateColResFailed;
ret = Iso14443_3bErrorCommunication;
break;
}
instance->state = Iso14443_3bPollerStateActivationInProgress;
const Iso14443_3bAtqBLayout* atqb =
(const Iso14443_3bAtqBLayout*)bit_buffer_get_data(instance->rx_buffer);
memcpy(data->uid, atqb->uid, ISO14443_3B_UID_SIZE);
memcpy(data->app_data, atqb->app_data, ISO14443_3B_APP_DATA_SIZE);
data->protocol_info = atqb->protocol_info;
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send ATTRIB
bit_buffer_append_byte(instance->tx_buffer, 0x1d);
bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256);
bit_buffer_append_byte(instance->tx_buffer, 0x01);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data));
if(ret != Iso14443_3bErrorNone) {
instance->state = Iso14443_3bPollerStateActivationFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1 ||
bit_buffer_get_byte(instance->rx_buffer, 0) != 0) {
FURI_LOG_D(TAG, "Unexpected ATTRIB response");
instance->state = Iso14443_3bPollerStateActivationFailed;
ret = Iso14443_3bErrorCommunication;
break;
}
instance->state = Iso14443_3bPollerStateActivated;
} while(false);
return ret;
}
Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
bit_buffer_append_byte(instance->tx_buffer, 0x50);
bit_buffer_append_bytes(instance->tx_buffer, instance->data->uid, ISO14443_3B_UID_SIZE);
Iso14443_3bError ret;
do {
ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC);
if(ret != Iso14443_3bErrorNone) {
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(uint8_t) ||
bit_buffer_get_byte(instance->rx_buffer, 0) != 0) {
ret = Iso14443_3bErrorCommunication;
break;
}
instance->state = Iso14443_3bPollerStateIdle;
} while(false);
return ret;
}
Iso14443_3bError iso14443_3b_poller_send_frame(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
Iso14443_3bError ret;
do {
ret = iso14443_3b_poller_prepare_trx(instance);
if(ret != Iso14443_3bErrorNone) break;
ret = iso14443_3b_poller_frame_exchange(
instance, tx_buffer, rx_buffer, iso14443_3b_get_fwt_fc_max(instance->data));
} while(false);
return ret;
}
@@ -0,0 +1,49 @@
#pragma once
#include "iso14443_3b_poller.h"
#include "iso14443_3b_i.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3B_POLLER_MAX_BUFFER_SIZE (256U)
typedef enum {
Iso14443_3bPollerStateIdle,
Iso14443_3bPollerStateColResInProgress,
Iso14443_3bPollerStateColResFailed,
Iso14443_3bPollerStateActivationInProgress,
Iso14443_3bPollerStateActivationFailed,
Iso14443_3bPollerStateActivated,
} Iso14443_3bPollerState;
struct Iso14443_3bPoller {
Nfc* nfc;
Iso14443_3bPollerState state;
Iso14443_3bData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
Iso14443_3bPollerEvent iso14443_3b_event;
Iso14443_3bPollerEventData iso14443_3b_event_data;
NfcGenericCallback callback;
void* context;
};
const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance);
Iso14443_3bError
iso14443_3b_poller_async_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data);
Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance);
Iso14443_3bError iso14443_3b_poller_send_frame(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif
+300
View File
@@ -0,0 +1,300 @@
#include "iso14443_4a_i.h"
#include <furi.h>
#define ISO14443_4A_PROTOCOL_NAME "ISO14443-4A"
#define ISO14443_4A_DEVICE_NAME "ISO14443-4A (Unknown)"
#define ISO14443_4A_T0_KEY "T0"
#define ISO14443_4A_TA1_KEY "TA(1)"
#define ISO14443_4A_TB1_KEY "TB(1)"
#define ISO14443_4A_TC1_KEY "TC(1)"
#define ISO14443_4A_T1_TK_KEY "T1...Tk"
#define ISO14443_4A_FDT_DEFAULT_FC ISO14443_3A_FDT_POLL_FC
typedef enum {
Iso14443_4aInterfaceByteTA1,
Iso14443_4aInterfaceByteTB1,
Iso14443_4aInterfaceByteTC1,
} Iso14443_4aInterfaceByte;
const NfcDeviceBase nfc_device_iso14443_4a = {
.protocol_name = ISO14443_4A_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_4a_alloc,
.free = (NfcDeviceFree)iso14443_4a_free,
.reset = (NfcDeviceReset)iso14443_4a_reset,
.copy = (NfcDeviceCopy)iso14443_4a_copy,
.verify = (NfcDeviceVerify)iso14443_4a_verify,
.load = (NfcDeviceLoad)iso14443_4a_load,
.save = (NfcDeviceSave)iso14443_4a_save,
.is_equal = (NfcDeviceEqual)iso14443_4a_is_equal,
.get_name = (NfcDeviceGetName)iso14443_4a_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_4a_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_4a_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data,
};
Iso14443_4aData* iso14443_4a_alloc() {
Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData));
data->iso14443_3a_data = iso14443_3a_alloc();
data->ats_data.t1_tk = simple_array_alloc(&simple_array_config_uint8_t);
return data;
}
void iso14443_4a_free(Iso14443_4aData* data) {
furi_assert(data);
simple_array_free(data->ats_data.t1_tk);
iso14443_3a_free(data->iso14443_3a_data);
free(data);
}
void iso14443_4a_reset(Iso14443_4aData* data) {
furi_assert(data);
iso14443_3a_reset(data->iso14443_3a_data);
data->ats_data.tl = 1;
data->ats_data.t0 = 0;
data->ats_data.ta_1 = 0;
data->ats_data.tb_1 = 0;
data->ats_data.tc_1 = 0;
simple_array_reset(data->ats_data.t1_tk);
}
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) {
furi_assert(data);
furi_assert(other);
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
data->ats_data.tl = other->ats_data.tl;
data->ats_data.t0 = other->ats_data.t0;
data->ats_data.ta_1 = other->ats_data.ta_1;
data->ats_data.tb_1 = other->ats_data.tb_1;
data->ats_data.tc_1 = other->ats_data.tc_1;
simple_array_copy(data->ats_data.t1_tk, other->ats_data.t1_tk);
}
bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// Empty, unified file format only
return false;
}
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;
Iso14443_4aAtsData* ats_data = &data->ats_data;
ats_data->tl = 1;
if(flipper_format_key_exist(ff, ISO14443_4A_T0_KEY)) {
if(!flipper_format_read_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TA1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TB1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TC1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;
++ats_data->tl;
}
if(flipper_format_key_exist(ff, ISO14443_4A_T1_TK_KEY)) {
uint32_t t1_tk_size;
if(!flipper_format_get_value_count(ff, ISO14443_4A_T1_TK_KEY, &t1_tk_size)) break;
if(t1_tk_size > 0) {
simple_array_init(ats_data->t1_tk, t1_tk_size);
if(!flipper_format_read_hex(
ff,
ISO14443_4A_T1_TK_KEY,
simple_array_get_data(ats_data->t1_tk),
t1_tk_size))
break;
ats_data->tl += t1_tk_size;
}
}
parsed = true;
} while(false);
return parsed;
}
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, ISO14443_4A_PROTOCOL_NAME " specific data"))
break;
const Iso14443_4aAtsData* ats_data = &data->ats_data;
if(ats_data->tl > 1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;
if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;
}
const uint32_t t1_tk_size = simple_array_get_count(ats_data->t1_tk);
if(t1_tk_size > 0) {
if(!flipper_format_write_hex(
ff,
ISO14443_4A_T1_TK_KEY,
simple_array_cget_data(ats_data->t1_tk),
t1_tk_size))
break;
}
}
saved = true;
} while(false);
return saved;
}
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) {
return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data);
}
const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_4A_DEVICE_NAME;
}
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) {
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
}
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
}
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) {
furi_assert(data);
return data->iso14443_3a_data;
}
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {
furi_assert(data);
const uint8_t fsci = data->ats_data.t0 & 0x0F;
if(fsci < 5) {
return fsci * 8 + 16;
} else if(fsci == 5) {
return 64;
} else if(fsci == 6) {
return 96;
} else if(fsci < 13) {
return 128U << (fsci - 7);
} else {
return 0;
}
}
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {
furi_assert(data);
uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC;
do {
if(!(data->ats_data.tl > 1)) break;
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) break;
const uint8_t fwi = data->ats_data.tb_1 >> 4;
if(fwi == 0x0F) break;
fwt_fc_max = 4096UL << fwi;
} while(false);
return fwt_fc_max;
}
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) {
furi_assert(data);
furi_assert(count);
*count = simple_array_get_count(data->ats_data.t1_tk);
return simple_array_cget_data(data->ats_data.t1_tk);
}
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) {
furi_assert(data);
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1))
return bit_rate == Iso14443_4aBitRateBoth106Kbit;
const uint8_t ta_1 = data->ats_data.ta_1;
switch(bit_rate) {
case Iso14443_4aBitRateBoth106Kbit:
return ta_1 == ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY;
case Iso14443_4aBitRatePiccToPcd212Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT;
case Iso14443_4aBitRatePiccToPcd424Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT;
case Iso14443_4aBitRatePiccToPcd848Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT;
case Iso14443_4aBitRatePcdToPicc212Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT;
case Iso14443_4aBitRatePcdToPicc424Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT;
case Iso14443_4aBitRatePcdToPicc848Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT;
default:
return false;
}
}
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option) {
furi_assert(data);
const Iso14443_4aAtsData* ats_data = &data->ats_data;
if(!(ats_data->t0 & ISO14443_4A_ATS_T0_TC1)) return false;
switch(option) {
case Iso14443_4aFrameOptionNad:
return ats_data->tc_1 & ISO14443_4A_ATS_TC1_NAD;
case Iso14443_4aFrameOptionCid:
return ats_data->tc_1 & ISO14443_4A_ATS_TC1_CID;
default:
return false;
}
}
@@ -0,0 +1,73 @@
#pragma once
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4aErrorNone,
Iso14443_4aErrorNotPresent,
Iso14443_4aErrorProtocol,
Iso14443_4aErrorTimeout,
} Iso14443_4aError;
typedef enum {
Iso14443_4aBitRateBoth106Kbit,
Iso14443_4aBitRatePiccToPcd212Kbit,
Iso14443_4aBitRatePiccToPcd424Kbit,
Iso14443_4aBitRatePiccToPcd848Kbit,
Iso14443_4aBitRatePcdToPicc212Kbit,
Iso14443_4aBitRatePcdToPicc424Kbit,
Iso14443_4aBitRatePcdToPicc848Kbit,
} Iso14443_4aBitRate;
typedef enum {
Iso14443_4aFrameOptionNad,
Iso14443_4aFrameOptionCid,
} Iso14443_4aFrameOption;
typedef struct Iso14443_4aData Iso14443_4aData;
// Virtual methods
Iso14443_4aData* iso14443_4a_alloc();
void iso14443_4a_free(Iso14443_4aData* data);
void iso14443_4a_reset(Iso14443_4aData* data);
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other);
bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type);
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff);
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other);
const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len);
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data);
// Getters & Tests
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data);
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data);
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count);
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate);
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_4a;
@@ -0,0 +1,71 @@
#include "iso14443_4a_i.h"
bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf) {
bool can_parse = false;
do {
const size_t buf_size = bit_buffer_get_size_bytes(buf);
if(buf_size == 0) break;
size_t current_index = 0;
const uint8_t tl = bit_buffer_get_byte(buf, current_index++);
if(tl != buf_size) break;
data->tl = tl;
if(tl > 1) {
const uint8_t t0 = bit_buffer_get_byte(buf, current_index++);
const bool has_ta_1 = t0 & ISO14443_4A_ATS_T0_TA1;
const bool has_tb_1 = t0 & ISO14443_4A_ATS_T0_TB1;
const bool has_tc_1 = t0 & ISO14443_4A_ATS_T0_TC1;
const uint8_t buf_size_min =
2 + (has_ta_1 ? 1 : 0) + (has_tb_1 ? 1 : 0) + (has_tc_1 ? 1 : 0);
if(buf_size < buf_size_min) break;
data->t0 = t0;
if(has_ta_1) {
data->ta_1 = bit_buffer_get_byte(buf, current_index++);
}
if(has_tb_1) {
data->tb_1 = bit_buffer_get_byte(buf, current_index++);
}
if(has_tc_1) {
data->tc_1 = bit_buffer_get_byte(buf, current_index++);
}
const uint8_t t1_tk_size = buf_size - buf_size_min;
if(t1_tk_size > 0) {
simple_array_init(data->t1_tk, t1_tk_size);
bit_buffer_write_bytes_mid(
buf, simple_array_get_data(data->t1_tk), current_index, t1_tk_size);
}
}
can_parse = true;
} while(false);
return can_parse;
}
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error) {
switch(error) {
case Iso14443_3aErrorNone:
return Iso14443_4aErrorNone;
case Iso14443_3aErrorNotPresent:
return Iso14443_4aErrorNotPresent;
case Iso14443_3aErrorColResFailed:
case Iso14443_3aErrorCommunication:
case Iso14443_3aErrorWrongCrc:
return Iso14443_4aErrorProtocol;
case Iso14443_3aErrorTimeout:
return Iso14443_4aErrorTimeout;
default:
return Iso14443_4aErrorProtocol;
}
}

Some files were not shown because too many files have changed in this diff Show More