unleashed-firmware/lib/nfc/protocols/iso15693_3/iso15693_3.c
gornekich d92b0a82cc
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.
2023-10-24 12:08:09 +09:00

359 lines
12 KiB
C

#include "iso15693_3.h"
#include "iso15693_3_device_defs.h"
#include <nfc/nfc_common.h>
#define ISO15693_3_PROTOCOL_NAME "ISO15693-3"
#define ISO15693_3_PROTOCOL_NAME_LEGACY "ISO15693"
#define ISO15693_3_DEVICE_NAME "ISO15693-3 (Unknown)"
#define ISO15693_3_LOCK_DSFID_LEGACY (1U << 0)
#define ISO15693_3_LOCK_AFI_LEGACY (1U << 1)
#define ISO15693_3_DSFID_KEY "DSFID"
#define ISO15693_3_AFI_KEY "AFI"
#define ISO15693_3_IC_REF_KEY "IC Reference"
#define ISO15693_3_BLOCK_COUNT_KEY "Block Count"
#define ISO15693_3_BLOCK_SIZE_KEY "Block Size"
#define ISO15693_3_DATA_CONTENT_KEY "Data Content"
#define ISO15693_3_LOCK_DSFID_KEY "Lock DSFID"
#define ISO15693_3_LOCK_AFI_KEY "Lock AFI"
#define ISO15693_3_SECURITY_STATUS_KEY "Security Status"
const NfcDeviceBase nfc_device_iso15693_3 = {
.protocol_name = ISO15693_3_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso15693_3_alloc,
.free = (NfcDeviceFree)iso15693_3_free,
.reset = (NfcDeviceReset)iso15693_3_reset,
.copy = (NfcDeviceCopy)iso15693_3_copy,
.verify = (NfcDeviceVerify)iso15693_3_verify,
.load = (NfcDeviceLoad)iso15693_3_load,
.save = (NfcDeviceSave)iso15693_3_save,
.is_equal = (NfcDeviceEqual)iso15693_3_is_equal,
.get_name = (NfcDeviceGetName)iso15693_3_get_device_name,
.get_uid = (NfcDeviceGetUid)iso15693_3_get_uid,
.set_uid = (NfcDeviceSetUid)iso15693_3_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data,
};
Iso15693_3Data* iso15693_3_alloc() {
Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data));
data->block_data = simple_array_alloc(&simple_array_config_uint8_t);
data->block_security = simple_array_alloc(&simple_array_config_uint8_t);
return data;
}
void iso15693_3_free(Iso15693_3Data* data) {
furi_assert(data);
simple_array_free(data->block_data);
simple_array_free(data->block_security);
free(data);
}
void iso15693_3_reset(Iso15693_3Data* data) {
furi_assert(data);
memset(data->uid, 0, ISO15693_3_UID_SIZE);
memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo));
memset(&data->settings, 0, sizeof(Iso15693_3Settings));
simple_array_reset(data->block_data);
simple_array_reset(data->block_security);
}
void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) {
furi_assert(data);
furi_assert(other);
memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE);
data->system_info = other->system_info;
data->settings = other->settings;
simple_array_copy(data->block_data, other->block_data);
simple_array_copy(data->block_security, other->block_security);
}
bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY);
}
static inline bool iso15693_3_load_security_legacy(Iso15693_3Data* data, FlipperFormat* ff) {
bool loaded = false;
uint8_t* legacy_data = NULL;
do {
uint32_t value_count;
if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))
break;
if(simple_array_get_count(data->block_security) + 1 != value_count) break;
legacy_data = malloc(value_count);
if(!flipper_format_read_hex(ff, ISO15693_3_SECURITY_STATUS_KEY, legacy_data, value_count))
break;
// First legacy data byte is lock bits
data->settings.lock_bits.dsfid = legacy_data[0] & ISO15693_3_LOCK_DSFID_LEGACY;
data->settings.lock_bits.afi = legacy_data[0] & ISO15693_3_LOCK_AFI_LEGACY;
// The rest are block security
memcpy(
&legacy_data[1],
simple_array_get_data(data->block_security),
simple_array_get_count(data->block_security));
loaded = true;
} while(false);
if(legacy_data) free(legacy_data);
return loaded;
}
static inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat* ff) {
bool loaded = false;
do {
uint32_t value_count;
if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))
break;
if(simple_array_get_count(data->block_security) != value_count) break;
if(!flipper_format_read_hex(
ff,
ISO15693_3_SECURITY_STATUS_KEY,
simple_array_get_data(data->block_security),
simple_array_get_count(data->block_security)))
break;
loaded = true;
} while(false);
return loaded;
}
bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
UNUSED(version);
bool loaded = false;
do {
if(flipper_format_key_exist(ff, ISO15693_3_DSFID_KEY)) {
if(!flipper_format_read_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))
break;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_DSFID;
}
if(flipper_format_key_exist(ff, ISO15693_3_AFI_KEY)) {
if(!flipper_format_read_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_AFI;
}
if(flipper_format_key_exist(ff, ISO15693_3_IC_REF_KEY)) {
if(!flipper_format_read_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))
break;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_IC_REF;
}
const bool has_lock_bits = flipper_format_key_exist(ff, ISO15693_3_LOCK_DSFID_KEY) &&
flipper_format_key_exist(ff, ISO15693_3_LOCK_AFI_KEY);
if(has_lock_bits) {
Iso15693_3LockBits* lock_bits = &data->settings.lock_bits;
if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_DSFID_KEY, &lock_bits->dsfid, 1))
break;
if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_AFI_KEY, &lock_bits->afi, 1)) break;
}
if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) &&
flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) {
uint32_t block_count;
if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break;
data->system_info.block_count = block_count;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY;
if(!flipper_format_read_hex(
ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1))
break;
simple_array_init(
data->block_data, data->system_info.block_size * data->system_info.block_count);
if(!flipper_format_read_hex(
ff,
ISO15693_3_DATA_CONTENT_KEY,
simple_array_get_data(data->block_data),
simple_array_get_count(data->block_data)))
break;
if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) {
simple_array_init(data->block_security, data->system_info.block_count);
const bool security_loaded = has_lock_bits ?
iso15693_3_load_security(data, ff) :
iso15693_3_load_security_legacy(data, ff);
if(!security_loaded) break;
}
}
loaded = true;
} while(false);
return loaded;
}
bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, ISO15693_3_PROTOCOL_NAME " specific data"))
break;
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
if(!flipper_format_write_comment_cstr(ff, "Data Storage Format Identifier")) break;
if(!flipper_format_write_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))
break;
}
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
if(!flipper_format_write_comment_cstr(ff, "Application Family Identifier")) break;
if(!flipper_format_write_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;
}
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
if(!flipper_format_write_comment_cstr(ff, "IC Reference - Vendor specific meaning"))
break;
if(!flipper_format_write_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))
break;
}
if(!flipper_format_write_comment_cstr(ff, "Lock Bits")) break;
if(!flipper_format_write_bool(
ff, ISO15693_3_LOCK_DSFID_KEY, &data->settings.lock_bits.dsfid, 1))
break;
if(!flipper_format_write_bool(
ff, ISO15693_3_LOCK_AFI_KEY, &data->settings.lock_bits.afi, 1))
break;
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
const uint32_t block_count = data->system_info.block_count;
if(!flipper_format_write_comment_cstr(
ff, "Number of memory blocks, valid range = 1..256"))
break;
if(!flipper_format_write_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1))
break;
if(!flipper_format_write_comment_cstr(
ff, "Size of a single memory block, valid range = 01...20 (hex)"))
break;
if(!flipper_format_write_hex(
ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1))
break;
if(!flipper_format_write_hex(
ff,
ISO15693_3_DATA_CONTENT_KEY,
simple_array_cget_data(data->block_data),
simple_array_get_count(data->block_data)))
break;
if(!flipper_format_write_comment_cstr(
ff, "Block Security Status: 01 = locked, 00 = not locked"))
break;
if(!flipper_format_write_hex(
ff,
ISO15693_3_SECURITY_STATUS_KEY,
simple_array_cget_data(data->block_security),
simple_array_get_count(data->block_security)))
break;
}
saved = true;
} while(false);
return saved;
}
bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 &&
memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 &&
memcmp(&data->system_info, &other->system_info, sizeof(Iso15693_3SystemInfo)) == 0 &&
simple_array_is_equal(data->block_data, other->block_data) &&
simple_array_is_equal(data->block_security, other->block_security);
}
const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO15693_3_DEVICE_NAME;
}
const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) {
furi_assert(data);
if(uid_len) *uid_len = ISO15693_3_UID_SIZE;
return data->uid;
}
bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
furi_assert(uid);
bool uid_valid = uid_len == ISO15693_3_UID_SIZE;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
// All ISO15693-3 cards must have this as first UID byte
data->uid[0] = 0xe0;
}
return uid_valid;
}
Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) {
UNUSED(data);
furi_crash("No base data");
}
bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) {
furi_assert(data);
furi_assert(block_index < data->system_info.block_count);
return *(const uint8_t*)simple_array_cget(data->block_security, block_index);
}
uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) {
furi_assert(data);
return data->uid[1];
}
uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) {
furi_assert(data);
return data->system_info.block_count;
}
uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) {
furi_assert(data);
return data->system_info.block_size;
}
const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) {
furi_assert(data);
furi_assert(data->system_info.block_count > block_index);
return (const uint8_t*)simple_array_cget(
data->block_data, block_index * data->system_info.block_size);
}