"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.
		
			
				
	
	
		
			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);
 | 
						|
}
 |