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