NFC: Implement Type 4 Tag saving

This commit is contained in:
Willy-JL
2025-03-06 02:21:47 +00:00
parent d5161f0806
commit aa38025977
7 changed files with 107 additions and 28 deletions

View File

@@ -2,8 +2,6 @@
#include "../iso14443_4a/iso14443_4a_render.h" #include "../iso14443_4a/iso14443_4a_render.h"
#define TYPE_4_TAG_RENDER_MAX_RECORD_SIZE (256U)
void nfc_render_type_4_tag_info( void nfc_render_type_4_tag_info(
const Type4TagData* data, const Type4TagData* data,
NfcProtocolFormatType format_type, NfcProtocolFormatType format_type,
@@ -11,26 +9,27 @@ void nfc_render_type_4_tag_info(
nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str); nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str);
furi_string_cat(str, "\n:::::::::::::::[Stored NDEF]:::::::::::::::\n"); furi_string_cat(str, "\n:::::::::::::::[Stored NDEF]:::::::::::::::\n");
furi_string_cat_printf( furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data));
str, "Current NDEF Size: %lu\n", simple_array_get_count(data->ndef_data));
furi_string_cat(str, "::::::::::::::::::[Tag Specs]::::::::::::::::::\n"); if(data->is_tag_specific) {
furi_string_cat_printf( furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n");
str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor); furi_string_cat_printf(
furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id); str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor);
furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len); furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id);
furi_string_cat_printf( furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len);
str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write); furi_string_cat_printf(
furi_string_cat_printf( str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write);
str, furi_string_cat_printf(
"Read Lock: 0x%02X%s\n", str,
data->ndef_read_lock, "Read Lock: 0x%02X%s\n",
data->ndef_read_lock == 0 ? " (unlocked)" : ""); data->ndef_read_lock,
furi_string_cat_printf( data->ndef_read_lock == 0 ? " (unlocked)" : "");
str, furi_string_cat_printf(
"Write Lock: 0x%02X%s", str,
data->ndef_write_lock, "Write Lock: 0x%02X%s",
data->ndef_write_lock == 0 ? " (unlocked)" : ""); data->ndef_write_lock,
data->ndef_write_lock == 0 ? " (unlocked)" : "");
}
if(format_type != NfcProtocolFormatTypeFull) return; if(format_type != NfcProtocolFormatTypeFull) return;

View File

@@ -4,7 +4,7 @@
#include "../nfc_protocol_support_render_common.h" #include "../nfc_protocol_support_render_common.h"
#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4) #define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4U)
void nfc_render_type_4_tag_info( void nfc_render_type_4_tag_info(
const Type4TagData* data, const Type4TagData* data,

View File

@@ -41,6 +41,7 @@ void type_4_tag_reset(Type4TagData* data) {
iso14443_4a_reset(data->iso14443_4a_data); iso14443_4a_reset(data->iso14443_4a_data);
data->is_tag_specific = false;
data->t4t_version.value = 0; data->t4t_version.value = 0;
data->chunk_max_read = 0; data->chunk_max_read = 0;
data->chunk_max_write = 0; data->chunk_max_write = 0;
@@ -60,6 +61,7 @@ void type_4_tag_copy(Type4TagData* data, const Type4TagData* other) {
iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);
data->is_tag_specific = other->is_tag_specific;
data->t4t_version.value = other->t4t_version.value; data->t4t_version.value = other->t4t_version.value;
data->chunk_max_read = other->chunk_max_read; data->chunk_max_read = other->chunk_max_read;
data->chunk_max_write = other->chunk_max_write; data->chunk_max_write = other->chunk_max_write;
@@ -89,6 +91,8 @@ bool type_4_tag_load(Type4TagData* data, FlipperFormat* ff, uint32_t version) {
do { do {
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
if(!type_4_tag_ndef_data_load(data, ff)) break;
success = true; success = true;
} while(false); } while(false);
@@ -109,6 +113,7 @@ bool type_4_tag_save(const Type4TagData* data, FlipperFormat* ff) {
if(!flipper_format_write_comment_cstr(ff, TYPE_4_TAG_PROTOCOL_NAME " specific data")) if(!flipper_format_write_comment_cstr(ff, TYPE_4_TAG_PROTOCOL_NAME " specific data"))
break; break;
if(!type_4_tag_ndef_data_save(data, ff)) break;
success = true; success = true;
} while(false); } while(false);
@@ -122,6 +127,7 @@ bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other) {
furi_check(other); furi_check(other);
return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&
data->is_tag_specific == other->is_tag_specific &&
data->t4t_version.value == other->t4t_version.value && data->t4t_version.value == other->t4t_version.value &&
data->chunk_max_read == other->chunk_max_read && data->chunk_max_read == other->chunk_max_read &&
data->chunk_max_write == other->chunk_max_write && data->chunk_max_write == other->chunk_max_write &&

View File

@@ -17,7 +17,8 @@ typedef enum {
typedef struct { typedef struct {
Iso14443_4aData* iso14443_4a_data; Iso14443_4aData* iso14443_4a_data;
// Tag specific // Tag specific data
bool is_tag_specific;
union { union {
struct { struct {
uint8_t minor : 4; uint8_t minor : 4;

View File

@@ -1,3 +1,63 @@
#include "type_4_tag_i.h" #include "type_4_tag_i.h"
#define TAG "Type4Tag" #define TAG "Type4Tag"
#define TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY "NDEF Data Size"
#define TYPE_4_TAG_FFF_NDEF_DATA_KEY "NDEF Data"
#define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U)
bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff) {
uint32_t ndef_data_size;
if(!flipper_format_read_uint32(ff, TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY, &ndef_data_size, 1)) {
return false;
}
if(ndef_data_size == 0) {
return true;
}
simple_array_init(data->ndef_data, ndef_data_size);
uint32_t ndef_data_pos = 0;
uint8_t* ndef_data = simple_array_get_data(data->ndef_data);
while(ndef_data_size > 0) {
uint8_t ndef_line_size = MIN(ndef_data_size, TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE);
if(!flipper_format_read_hex(
ff, TYPE_4_TAG_FFF_NDEF_DATA_KEY, &ndef_data[ndef_data_pos], ndef_line_size)) {
simple_array_reset(data->ndef_data);
return false;
}
ndef_data_pos += ndef_line_size;
ndef_data_size -= ndef_line_size;
}
return true;
}
bool type_4_tag_ndef_data_save(const Type4TagData* data, FlipperFormat* ff) {
uint32_t ndef_data_size = simple_array_get_count(data->ndef_data);
if(!flipper_format_write_uint32(ff, TYPE_4_TAG_FFF_NDEF_DATA_SIZE_KEY, &ndef_data_size, 1)) {
return false;
}
if(ndef_data_size == 0) {
return true;
}
uint32_t ndef_data_pos = 0;
uint8_t* ndef_data = simple_array_get_data(data->ndef_data);
while(ndef_data_size > 0) {
uint8_t ndef_line_size = MIN(ndef_data_size, TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE);
if(!flipper_format_write_hex(
ff, TYPE_4_TAG_FFF_NDEF_DATA_KEY, &ndef_data[ndef_data_pos], ndef_line_size)) {
return false;
}
ndef_data_pos += ndef_line_size;
ndef_data_size -= ndef_line_size;
}
return true;
}

View File

@@ -2,26 +2,30 @@
#include "type_4_tag.h" #include "type_4_tag.h"
// ISO SELECT FILE command and parameters
#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4 #define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4
#define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04) #define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04)
#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00) #define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00)
#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C) #define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C)
#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00) #define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00)
// ISO READ BINARY command and parameters
#define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0 #define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0
#define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00) #define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00)
#define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00) #define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00)
#define TYPE_4_TAG_ISO_READ_LE_FULL (0x00) #define TYPE_4_TAG_ISO_READ_LE_FULL (0x00)
#define TYPE_4_TAG_ISO_STATUS_LEN (2) // Common APDU parameters and values
#define TYPE_4_TAG_ISO_STATUS_LEN (2U)
#define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00 #define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00
#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255) #define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U)
#define TYPE_4_TAG_ISO_APP_NAME_LEN (7) #define TYPE_4_TAG_ISO_APP_NAME_LEN (7U)
#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 #define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01
#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2) #define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U)
#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03 #define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03
#define TYPE_4_TAG_T4T_CC_VNO (0x20)
#define TYPE_4_TAG_T4T_CC_VNO 0x20 // Capability Container parsing structures
typedef enum FURI_PACKED { typedef enum FURI_PACKED {
Type4TagCcTlvTypeNdefFileCtrl = 0x04, Type4TagCcTlvTypeNdefFileCtrl = 0x04,
@@ -52,3 +56,11 @@ typedef struct FURI_PACKED {
uint16_t mlc; uint16_t mlc;
Type4TagCcTlv tlv[]; Type4TagCcTlv tlv[];
} Type4TagCc; } Type4TagCc;
// Load internal Type4Tag structures
bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff);
// Save internal Type4Tag structures
bool type_4_tag_ndef_data_save(const Type4TagData* data, FlipperFormat* ff);

View File

@@ -137,6 +137,7 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) {
break; break;
} }
instance->data->is_tag_specific = true;
instance->data->t4t_version.value = cc->t4t_vno; instance->data->t4t_version.value = cc->t4t_vno;
instance->data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle)); instance->data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle));
instance->data->chunk_max_write = instance->data->chunk_max_write =