mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-20 20:01:54 +03:00
359 lines
12 KiB
C
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);
|
||
|
}
|