Refactor ISO APDUs in T4T poller

This commit is contained in:
Willy-JL
2025-03-17 06:16:31 +00:00
parent 967eb06082
commit 22a6c37e20
5 changed files with 135 additions and 142 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ typedef enum {
Type4TagErrorTimeout,
Type4TagErrorWrongFormat,
Type4TagErrorNotSupported,
Type4TagErrorUnknown,
Type4TagErrorApduFailed,
Type4TagErrorCustomCommand,
} Type4TagError;
+7 -5
View File
@@ -9,6 +9,9 @@
#define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U)
const uint8_t type_4_tag_iso_picc_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_PICC_NAME};
const uint8_t type_4_tag_iso_app_name[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME};
Type4TagError type_4_tag_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
@@ -56,14 +59,13 @@ void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len) {
data->is_tag_specific ? data->ndef_write_lock : TYPE_4_TAG_T4T_CC_RW_LOCK_NONE;
}
Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf) {
const size_t cc_len = bit_buffer_get_size_bytes(buf);
if(cc_len < TYPE_4_TAG_T4T_CC_MIN_SIZE) {
Type4TagError type_4_tag_cc_parse(Type4TagData* data, const uint8_t* buf, size_t len) {
if(len < TYPE_4_TAG_T4T_CC_MIN_SIZE) {
FURI_LOG_E(TAG, "Unsupported T4T version");
return Type4TagErrorWrongFormat;
}
const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(buf);
const Type4TagCc* cc = (const Type4TagCc*)buf;
if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) {
FURI_LOG_E(TAG, "Unsupported T4T version");
return Type4TagErrorNotSupported;
@@ -71,7 +73,7 @@ Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf) {
const Type4TagCcTlv* tlv = cc->tlv;
const Type4TagCcTlvNdefFileCtrl* ndef_file_ctrl = NULL;
const void* end = MIN((void*)cc + cc->len, (void*)cc + cc_len);
const void* end = MIN((void*)cc + cc->len, (void*)cc + len);
while((void*)tlv < end) {
if(tlv->type == Type4TagCcTlvTypeNdefFileCtrl) {
ndef_file_ctrl = &tlv->value.ndef_file_ctrl;
+6 -4
View File
@@ -33,10 +33,9 @@
#define TYPE_4_TAG_ISO_STATUS_NO_CMD 0x68, 0x00
#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U)
#define TYPE_4_TAG_ISO_NAME_LEN (7U)
#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01
#define TYPE_4_TAG_ISO_PICC_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00
#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2U)
#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03
#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01
#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE103
#define TYPE_4_TAG_T4T_CC_VNO (0x20)
#define TYPE_4_TAG_T4T_DEFAULT_FILE_ID 0xE104
#define TYPE_4_TAG_T4T_CC_RW_LOCK_NONE 0x00
@@ -48,6 +47,9 @@
#define TYPE_4_TAG_CHUNK_LEN MIN(TYPE_4_TAG_BUF_SIZE - 5U, TYPE_4_TAG_ISO_RW_CHUNK_LEN)
#define TYPE_4_TAG_DEFAULT_SIZE (2048U)
extern const uint8_t type_4_tag_iso_picc_name[TYPE_4_TAG_ISO_NAME_LEN];
extern const uint8_t type_4_tag_iso_app_name[TYPE_4_TAG_ISO_NAME_LEN];
// Capability Container parsing structures
typedef enum FURI_PACKED {
@@ -86,7 +88,7 @@ Type4TagError type_4_tag_process_error(Iso14443_4aError error);
void type_4_tag_cc_dump(const Type4TagData* data, uint8_t* buf, size_t len);
Type4TagError type_4_tag_cc_parse(Type4TagData* data, const BitBuffer* buf);
Type4TagError type_4_tag_cc_parse(Type4TagData* data, const uint8_t* buf, size_t len);
// Load internal Type4Tag structures
@@ -36,16 +36,16 @@ static Type4TagError type_4_tag_listener_iso_select(
UNUSED(le);
if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME) {
static const uint8_t t4t_picc[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_PICC_NAME};
if(lc == sizeof(t4t_picc) && memcmp(t4t_picc, data, sizeof(t4t_picc)) == 0) {
if(lc == sizeof(type_4_tag_iso_picc_name) &&
memcmp(type_4_tag_iso_picc_name, data, sizeof(type_4_tag_iso_picc_name)) == 0) {
instance->state = Type4TagListenerStateSelectedPicc;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
}
static const uint8_t t4t_app[TYPE_4_TAG_ISO_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME};
if(lc == sizeof(t4t_app) && memcmp(t4t_app, data, sizeof(t4t_app)) == 0) {
if(lc == sizeof(type_4_tag_iso_app_name) &&
memcmp(type_4_tag_iso_app_name, data, sizeof(type_4_tag_iso_app_name)) == 0) {
instance->state = Type4TagListenerStateSelectedApplication;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
@@ -54,23 +54,18 @@ static Type4TagError type_4_tag_listener_iso_select(
} else if(
instance->state >= Type4TagListenerStateSelectedApplication &&
(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID)) {
static const uint8_t cc_id[TYPE_4_TAG_T4T_CC_FILE_ID_LEN] = {TYPE_4_TAG_T4T_CC_FILE_ID};
if(lc == sizeof(cc_id) && memcmp(cc_id, data, sizeof(cc_id)) == 0) {
(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_ID || p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID) &&
lc == sizeof(uint16_t)) {
uint16_t file_id = bit_lib_bytes_to_num_be(data, sizeof(uint16_t));
if(file_id == TYPE_4_TAG_T4T_CC_FILE_ID) {
instance->state = Type4TagListenerStateSelectedCapabilityContainer;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
}
uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)];
bit_lib_num_to_bytes_be(
instance->data->is_tag_specific ? instance->data->ndef_file_id :
TYPE_4_TAG_T4T_DEFAULT_FILE_ID,
sizeof(ndef_file_id_be),
ndef_file_id_be);
if(lc == sizeof(ndef_file_id_be) &&
memcmp(ndef_file_id_be, data, sizeof(ndef_file_id_be)) == 0) {
if(file_id == (instance->data->is_tag_specific ? instance->data->ndef_file_id :
TYPE_4_TAG_T4T_DEFAULT_FILE_ID)) {
instance->state = Type4TagListenerStateSelectedNdefMessage;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
+111 -117
View File
@@ -35,27 +35,106 @@ Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, B
return Type4TagErrorNone;
} else {
FURI_LOG_E(TAG, "APDU failed: 0x%02X%02X", status[0], status[1]);
return Type4TagErrorUnknown;
return Type4TagErrorApduFailed;
}
}
static Type4TagError type_5_tag_poller_iso_select_name(
Type4TagPoller* instance,
const uint8_t* df_name,
uint8_t df_name_len) {
const uint8_t type_4_tag_iso_select_name_apdu[] = {
TYPE_4_TAG_ISO_SELECT_CMD,
TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME,
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
};
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_iso_select_name_apdu,
sizeof(type_4_tag_iso_select_name_apdu));
bit_buffer_append_byte(instance->tx_buffer, df_name_len);
bit_buffer_append_bytes(instance->tx_buffer, df_name, df_name_len);
return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
}
static Type4TagError
type_5_tag_poller_iso_select_file(Type4TagPoller* instance, uint16_t file_id) {
const uint8_t type_4_tag_iso_select_file_apdu[] = {
TYPE_4_TAG_ISO_SELECT_CMD,
TYPE_4_TAG_ISO_SELECT_P1_BY_ID,
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
sizeof(file_id),
};
uint8_t file_id_be[sizeof(file_id)];
bit_lib_num_to_bytes_be(file_id, sizeof(file_id), file_id_be);
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_iso_select_file_apdu,
sizeof(type_4_tag_iso_select_file_apdu));
bit_buffer_append_bytes(instance->tx_buffer, file_id_be, sizeof(file_id_be));
return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
}
static Type4TagError type_5_tag_poller_iso_read(
Type4TagPoller* instance,
uint16_t offset,
uint16_t length,
uint8_t* buffer) {
const uint8_t chunk_max = instance->data->is_tag_specific ?
MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN) :
TYPE_4_TAG_CHUNK_LEN;
if(offset + length > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(length)) {
FURI_LOG_E(TAG, "File too large: %zu bytes", length);
return Type4TagErrorNotSupported;
}
const uint8_t type_4_tag_iso_read_apdu[] = {
TYPE_4_TAG_ISO_READ_CMD,
};
while(length > 0) {
uint8_t chunk_len = MIN(length, chunk_max);
uint8_t offset_be[sizeof(offset)];
bit_lib_num_to_bytes_be(offset, sizeof(offset_be), offset_be);
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_iso_read_apdu, sizeof(type_4_tag_iso_read_apdu));
bit_buffer_append_bytes(instance->tx_buffer, offset_be, sizeof(offset_be));
bit_buffer_append_byte(instance->tx_buffer, chunk_len);
Type4TagError error =
type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
if(error != Type4TagErrorNone) {
return error;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != chunk_len) {
FURI_LOG_E(
TAG,
"Wrong chunk len: %zu != %zu",
bit_buffer_get_size_bytes(instance->rx_buffer),
chunk_len);
return Type4TagErrorWrongFormat;
}
memcpy(buffer, bit_buffer_get_data(instance->rx_buffer), chunk_len);
buffer += chunk_len;
offset += chunk_len;
length -= chunk_len;
}
return Type4TagErrorNone;
}
Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) {
furi_check(instance);
FURI_LOG_D(TAG, "Select application");
const uint8_t type_4_tag_select_app_apdu[] = {
TYPE_4_TAG_ISO_SELECT_CMD,
TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME,
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
TYPE_4_TAG_ISO_NAME_LEN,
TYPE_4_TAG_ISO_APP_NAME,
TYPE_4_TAG_ISO_SELECT_LE_EMPTY,
};
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_select_app_apdu, sizeof(type_4_tag_select_app_apdu));
return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
return type_5_tag_poller_iso_select_name(
instance, type_4_tag_iso_app_name, sizeof(type_4_tag_iso_app_name));
}
Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) {
@@ -65,35 +144,22 @@ Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) {
do {
FURI_LOG_D(TAG, "Select CC");
const uint8_t type_4_tag_select_cc_apdu[] = {
TYPE_4_TAG_ISO_SELECT_CMD,
TYPE_4_TAG_ISO_SELECT_P1_BY_ID,
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
TYPE_4_TAG_T4T_CC_FILE_ID_LEN,
TYPE_4_TAG_T4T_CC_FILE_ID,
TYPE_4_TAG_ISO_SELECT_LE_EMPTY,
};
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_select_cc_apdu, sizeof(type_4_tag_select_cc_apdu));
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
error = type_5_tag_poller_iso_select_file(instance, TYPE_4_TAG_T4T_CC_FILE_ID);
if(error != Type4TagErrorNone) break;
FURI_LOG_D(TAG, "Read CC len");
uint16_t cc_len;
uint8_t cc_len_be[sizeof(cc_len)];
error = type_5_tag_poller_iso_read(instance, 0, sizeof(cc_len_be), cc_len_be);
if(error != Type4TagErrorNone) break;
cc_len = bit_lib_bytes_to_num_be(cc_len_be, sizeof(cc_len_be));
FURI_LOG_D(TAG, "Read CC");
const uint8_t type_4_tag_read_cc_apdu[] = {
TYPE_4_TAG_ISO_READ_CMD,
TYPE_4_TAG_ISO_READ_P_BEGINNING,
TYPE_4_TAG_ISO_READ_LE_FULL,
};
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_read_cc_apdu, sizeof(type_4_tag_read_cc_apdu));
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
uint8_t cc_buf[cc_len];
error = type_5_tag_poller_iso_read(instance, 0, sizeof(cc_buf), cc_buf);
if(error != Type4TagErrorNone) break;
error = type_4_tag_cc_parse(instance->data, instance->rx_buffer);
error = type_4_tag_cc_parse(instance->data, cc_buf, sizeof(cc_buf));
if(error != Type4TagErrorNone) break;
instance->data->is_tag_specific = true;
@@ -110,97 +176,25 @@ Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) {
do {
FURI_LOG_D(TAG, "Select NDEF");
const uint8_t type_4_tag_select_ndef_apdu_1[] = {
TYPE_4_TAG_ISO_SELECT_CMD,
TYPE_4_TAG_ISO_SELECT_P1_BY_ID,
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
sizeof(instance->data->ndef_file_id),
};
uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)];
bit_lib_num_to_bytes_be(
instance->data->ndef_file_id, sizeof(instance->data->ndef_file_id), ndef_file_id_be);
const uint8_t type_4_tag_select_ndef_apdu_2[] = {
TYPE_4_TAG_ISO_SELECT_LE_EMPTY,
};
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_select_ndef_apdu_1,
sizeof(type_4_tag_select_ndef_apdu_1));
bit_buffer_append_bytes(instance->tx_buffer, ndef_file_id_be, sizeof(ndef_file_id_be));
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_select_ndef_apdu_2,
sizeof(type_4_tag_select_ndef_apdu_2));
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
error = type_5_tag_poller_iso_select_file(instance, instance->data->ndef_file_id);
if(error != Type4TagErrorNone) break;
FURI_LOG_D(TAG, "Read NDEF len");
uint16_t ndef_len;
const uint8_t type_4_tag_read_ndef_len_apdu[] = {
TYPE_4_TAG_ISO_READ_CMD,
TYPE_4_TAG_ISO_READ_P_BEGINNING,
sizeof(ndef_len),
};
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_read_ndef_len_apdu,
sizeof(type_4_tag_read_ndef_len_apdu));
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
uint8_t ndef_len_be[sizeof(ndef_len)];
error = type_5_tag_poller_iso_read(instance, 0, sizeof(ndef_len_be), ndef_len_be);
if(error != Type4TagErrorNone) break;
ndef_len = bit_lib_bytes_to_num_be(ndef_len_be, sizeof(ndef_len_be));
ndef_len =
bit_lib_bytes_to_num_be(bit_buffer_get_data(instance->rx_buffer), sizeof(ndef_len));
if(ndef_len == 0) {
FURI_LOG_D(TAG, "NDEF file is empty");
break;
}
uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_CHUNK_LEN);
if(ndef_len > TYPE_4_TAG_ISO_READ_P_OFFSET_MAX + chunk_max - sizeof(ndef_len)) {
FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len);
error = Type4TagErrorNotSupported;
break;
}
simple_array_init(instance->data->ndef_data, ndef_len);
FURI_LOG_D(TAG, "Read NDEF");
const uint8_t type_4_tag_read_ndef_apdu_1[] = {
TYPE_4_TAG_ISO_READ_CMD,
};
uint16_t ndef_pos = 0;
uint8_t* ndef_data = simple_array_get_data(instance->data->ndef_data);
while(ndef_len > 0) {
uint8_t chunk_len = MIN(ndef_len, chunk_max);
uint8_t ndef_pos_be[sizeof(ndef_pos)];
bit_lib_num_to_bytes_be(sizeof(ndef_len) + ndef_pos, sizeof(ndef_pos_be), ndef_pos_be);
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_read_ndef_apdu_1,
sizeof(type_4_tag_read_ndef_apdu_1));
bit_buffer_append_bytes(instance->tx_buffer, ndef_pos_be, sizeof(ndef_pos_be));
bit_buffer_append_byte(instance->tx_buffer, chunk_len);
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
if(error != Type4TagErrorNone) break;
if(bit_buffer_get_size_bytes(instance->rx_buffer) != chunk_len) {
FURI_LOG_E(
TAG,
"Wrong NDEF chunk len: %zu != %zu",
bit_buffer_get_size_bytes(instance->rx_buffer),
ndef_len);
error = Type4TagErrorWrongFormat;
break;
}
memcpy(&ndef_data[ndef_pos], bit_buffer_get_data(instance->rx_buffer), chunk_len);
ndef_pos += chunk_len;
ndef_len -= chunk_len;
}
simple_array_init(instance->data->ndef_data, ndef_len);
uint8_t* ndef_buf = simple_array_get_data(instance->data->ndef_data);
error = type_5_tag_poller_iso_read(instance, sizeof(ndef_len), ndef_len, ndef_buf);
if(error != Type4TagErrorNone) break;
FURI_LOG_D(