NFC: Implement Type 4 Tag rudimentary emulation

This commit is contained in:
Willy-JL
2025-03-10 09:19:38 +00:00
parent fbc176eb84
commit d174fa9505
13 changed files with 546 additions and 42 deletions
@@ -2,13 +2,13 @@
#include "type_4_tag_render.h"
#include <nfc/protocols/type_4_tag/type_4_tag_poller.h>
#include <nfc/protocols/type_4_tag/type_4_tag_listener.h>
#include <toolbox/pretty_format.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
#include "../iso14443_4a/iso14443_4a_i.h"
enum {
SubmenuIndexWrite = SubmenuIndexCommonMax,
@@ -167,19 +167,42 @@ static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) {
furi_string_free(temp_str);
}
static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) {
// TODO: Implement full emulation
const Iso14443_4aData* iso14443_4a_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
static NfcCommand
nfc_scene_emulate_listener_callback_type_4_tag(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolType4Tag);
furi_assert(event.event_data);
instance->listener =
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
NfcApp* nfc = context;
Type4TagListenerEvent* type_4_tag_event = event.event_data;
if(type_4_tag_event->type == Type4TagListenerEventTypeCustomCommand) {
if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) {
furi_string_cat_printf(nfc->text_box_store, "R:");
for(size_t i = 0; i < bit_buffer_get_size_bytes(type_4_tag_event->data->buffer); i++) {
furi_string_cat_printf(
nfc->text_box_store,
" %02X",
bit_buffer_get_byte(type_4_tag_event->data->buffer, i));
}
furi_string_push_back(nfc->text_box_store, '\n');
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate);
}
}
return NfcCommandContinue;
}
static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) {
const Type4TagData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolType4Tag);
instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolType4Tag, data);
nfc_listener_start(
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
instance->listener, nfc_scene_emulate_listener_callback_type_4_tag, instance);
}
const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = {
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
.scene_info =
{
+2 -1
View File
@@ -7,6 +7,7 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h>
#include <nfc/protocols/mf_classic/mf_classic_listener_defs.h>
#include <nfc/protocols/slix/slix_listener_defs.h>
#include <nfc/protocols/type_4_tag/type_4_tag_listener_defs.h>
const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = {
[NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a,
@@ -21,5 +22,5 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = {
[NfcProtocolMfDesfire] = NULL,
[NfcProtocolSlix] = &nfc_listener_slix,
[NfcProtocolSt25tb] = NULL,
[NfcProtocolType4Tag] = NULL,
[NfcProtocolType4Tag] = &nfc_listener_type_4_tag,
};
@@ -14,6 +14,7 @@ typedef enum {
Type4TagErrorWrongFormat,
Type4TagErrorNotSupported,
Type4TagErrorUnknown,
Type4TagErrorCustomCommand,
} Type4TagError;
typedef struct {
@@ -7,6 +7,19 @@
#define TYPE_4_TAG_FFF_NDEF_DATA_PER_LINE (16U)
Type4TagError type_4_tag_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
return Type4TagErrorNone;
case Iso14443_4aErrorNotPresent:
return Type4TagErrorNotPresent;
case Iso14443_4aErrorTimeout:
return Type4TagErrorTimeout;
default:
return Type4TagErrorProtocol;
}
}
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)) {
+27 -13
View File
@@ -3,27 +3,37 @@
#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_P1_BY_NAME (0x04)
#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_LE_EMPTY (0x00)
#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4
#define TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME (0x04)
#define TYPE_4_TAG_ISO_SELECT_P1_BY_EF_ID (0x02)
#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_LE_EMPTY (0x00)
// ISO READ BINARY command and parameters
#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_ID_MASK (1 << 7)
#define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00)
#define TYPE_4_TAG_ISO_READ_LE_FULL (0x00)
// 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_RW_CHUNK_LEN (255U)
#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_T4T_CC_FILE_ID_LEN (2U)
#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03
#define TYPE_4_TAG_T4T_CC_VNO (0x20)
#define TYPE_4_TAG_ISO_STATUS_LEN (2U)
#define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00
#define TYPE_4_TAG_ISO_STATUS_OFFSET_ERR 0x6B, 0x00
#define TYPE_4_TAG_ISO_STATUS_NOT_FOUND 0x6A, 0x82
#define TYPE_4_TAG_ISO_STATUS_NO_SUPPORT 0x6A, 0x81
#define TYPE_4_TAG_ISO_STATUS_BAD_PARAMS 0x6A, 0x86
#define TYPE_4_TAG_ISO_STATUS_NO_CMD 0x68, 0x00
#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255U)
#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_T4T_CC_FILE_ID_LEN (2U)
#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03
#define TYPE_4_TAG_T4T_CC_VNO (0x20)
// Read returns 2 byte status trailer, write sends 5 byte command header
#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5)
// Capability Container parsing structures
@@ -57,6 +67,10 @@ typedef struct FURI_PACKED {
Type4TagCcTlv tlv[];
} Type4TagCc;
// Internal helpers
Type4TagError type_4_tag_process_error(Iso14443_4aError error);
// Load internal Type4Tag structures
bool type_4_tag_ndef_data_load(Type4TagData* data, FlipperFormat* ff);
@@ -0,0 +1,88 @@
#include "type_4_tag_listener_i.h"
#include "type_4_tag_listener_defs.h"
#include "type_4_tag_i.h"
#define TAG "Type4TagListener"
static void type_4_tag_listener_reset_state(Type4TagListener* instance) {
instance->state = Type4TagListenerStateIdle;
}
static Type4TagListener*
type_4_tag_listener_alloc(Iso14443_4aListener* iso14443_4a_listener, Type4TagData* data) {
furi_assert(iso14443_4a_listener);
Type4TagListener* instance = malloc(sizeof(Type4TagListener));
instance->iso14443_4a_listener = iso14443_4a_listener;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(TYPE_4_TAG_BUF_SIZE);
instance->type_4_tag_event.data = &instance->type_4_tag_event_data;
instance->generic_event.protocol = NfcProtocolType4Tag;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->type_4_tag_event;
return instance;
}
static void type_4_tag_listener_free(Type4TagListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
static void type_4_tag_listener_set_callback(
Type4TagListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
static const Type4TagData* type_4_tag_listener_get_data(Type4TagListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static NfcCommand type_4_tag_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolIso15693_3);
furi_assert(event.event_data);
Type4TagListener* instance = context;
Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data;
BitBuffer* rx_buffer = iso14443_4a_event->data->buffer;
NfcCommand command = NfcCommandContinue;
if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeFieldOff) {
type_4_tag_listener_reset_state(instance);
command = NfcCommandSleep;
} else if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeHalted) {
type_4_tag_listener_reset_state(instance);
} else if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) {
const Type4TagError error = type_4_tag_listener_handle_apdu(instance, rx_buffer);
if(error == Type4TagErrorCustomCommand && instance->callback) {
instance->type_4_tag_event.type = Type4TagListenerEventTypeCustomCommand;
instance->type_4_tag_event.data->buffer = rx_buffer;
command = instance->callback(instance->generic_event, instance->context);
}
}
return command;
}
const NfcListenerBase nfc_listener_type_4_tag = {
.alloc = (NfcListenerAlloc)type_4_tag_listener_alloc,
.free = (NfcListenerFree)type_4_tag_listener_free,
.set_callback = (NfcListenerSetCallback)type_4_tag_listener_set_callback,
.get_data = (NfcListenerGetData)type_4_tag_listener_get_data,
.run = (NfcListenerRun)type_4_tag_listener_run,
};
@@ -0,0 +1,26 @@
#pragma once
#include "type_4_tag.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Type4TagListener Type4TagListener;
typedef enum {
Type4TagListenerEventTypeCustomCommand,
} Type4TagListenerEventType;
typedef struct {
BitBuffer* buffer;
} Type4TagListenerEventData;
typedef struct {
Type4TagListenerEventType type;
Type4TagListenerEventData* data;
} Type4TagListenerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_type_4_tag;
@@ -0,0 +1,314 @@
#include "type_4_tag_listener_i.h"
#include "type_4_tag_i.h"
#include <bit_lib/bit_lib.h>
#define TAG "Type4TagListener"
typedef Type4TagError (*Type4TagListenerApduHandler)(
Type4TagListener* instance,
uint8_t p1,
uint8_t p2,
size_t lc,
const uint8_t* data,
size_t le);
typedef struct {
uint8_t cla_ins[2];
Type4TagListenerApduHandler handler;
} Type4TagListenerApduCommand;
static const uint8_t type_4_tag_success_apdu[] = {TYPE_4_TAG_ISO_STATUS_SUCCESS};
static const uint8_t type_4_tag_offset_error_apdu[] = {TYPE_4_TAG_ISO_STATUS_OFFSET_ERR};
static const uint8_t type_4_tag_not_found_apdu[] = {TYPE_4_TAG_ISO_STATUS_NOT_FOUND};
static const uint8_t type_4_tag_no_support_apdu[] = {TYPE_4_TAG_ISO_STATUS_NO_SUPPORT};
static const uint8_t type_4_tag_bad_params_apdu[] = {TYPE_4_TAG_ISO_STATUS_BAD_PARAMS};
static const uint8_t type_4_tag_no_cmd_apdu[] = {TYPE_4_TAG_ISO_STATUS_NO_CMD};
static Type4TagError type_4_tag_listener_iso_select(
Type4TagListener* instance,
uint8_t p1,
uint8_t p2,
size_t lc,
const uint8_t* data,
size_t le) {
UNUSED(p2);
UNUSED(le);
if(p1 == TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME) {
static const uint8_t t4t_app[TYPE_4_TAG_ISO_APP_NAME_LEN] = {TYPE_4_TAG_ISO_APP_NAME};
if(lc == sizeof(t4t_app) && memcmp(t4t_app, data, sizeof(t4t_app)) == 0) {
instance->state = Type4TagListenerStateSelectedApplication;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
}
instance->state = Type4TagListenerStateIdle;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu));
return Type4TagErrorCustomCommand;
}
if(instance->state >= Type4TagListenerStateSelectedApplication) {
if(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) {
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->ndef_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) {
instance->state = Type4TagListenerStateSelectedNdefMessage;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
}
}
}
instance->state = Type4TagListenerStateIdle;
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu));
return Type4TagErrorCustomCommand;
}
static Type4TagError type_4_tag_listener_iso_read(
Type4TagListener* instance,
uint8_t p1,
uint8_t p2,
size_t lc,
const uint8_t* data,
size_t le) {
UNUSED(lc);
UNUSED(data);
size_t offset;
if(p1 & TYPE_4_TAG_ISO_READ_P1_ID_MASK) {
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_no_support_apdu, sizeof(type_4_tag_no_support_apdu));
return Type4TagErrorCustomCommand;
} else {
offset = (p1 << 8) + p2;
}
if(instance->state == Type4TagListenerStateSelectedCapabilityContainer) {
uint8_t cc_buf[sizeof(Type4TagCc) + sizeof(Type4TagCcTlv)];
if(offset >= sizeof(cc_buf)) {
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_offset_error_apdu,
sizeof(type_4_tag_offset_error_apdu));
return Type4TagErrorWrongFormat;
}
Type4TagCc* cc = (Type4TagCc*)cc_buf;
bit_lib_num_to_bytes_be(sizeof(cc_buf), sizeof(cc->len), (void*)&cc->len);
cc->t4t_vno = TYPE_4_TAG_T4T_CC_VNO;
bit_lib_num_to_bytes_be(
instance->data->is_tag_specific ? instance->data->chunk_max_read :
TYPE_4_TAG_ISO_RW_CHUNK_LEN,
sizeof(cc->mle),
(void*)&cc->mle);
bit_lib_num_to_bytes_be(
instance->data->is_tag_specific ? instance->data->chunk_max_write :
TYPE_4_TAG_ISO_RW_CHUNK_LEN,
sizeof(cc->mlc),
(void*)&cc->mlc);
cc->tlv[0].type = Type4TagCcTlvTypeNdefFileCtrl;
cc->tlv[0].len = sizeof(cc->tlv[0].value.ndef_file_ctrl);
bit_lib_num_to_bytes_be(
instance->data->ndef_file_id,
sizeof(cc->tlv[0].value.ndef_file_ctrl.file_id),
(void*)&cc->tlv[0].value.ndef_file_ctrl.file_id);
bit_lib_num_to_bytes_be(
instance->data->is_tag_specific ? instance->data->ndef_max_len :
TYPE_4_TAG_ISO_RW_CHUNK_LEN,
sizeof(cc->tlv[0].value.ndef_file_ctrl.max_len),
(void*)&cc->tlv[0].value.ndef_file_ctrl.max_len);
cc->tlv[0].value.ndef_file_ctrl.read_perm =
instance->data->is_tag_specific ? instance->data->ndef_read_lock : 0x00;
cc->tlv[0].value.ndef_file_ctrl.write_perm =
instance->data->is_tag_specific ? instance->data->ndef_write_lock : 0x00;
bit_buffer_append_bytes(
instance->tx_buffer, cc_buf + offset, MIN(sizeof(cc_buf) - offset, le));
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
}
if(instance->state == Type4TagListenerStateSelectedNdefMessage) {
size_t ndef_file_len =
instance->data->ndef_data ? simple_array_get_count(instance->data->ndef_data) : 0;
bool included_len = false;
if(offset < sizeof(uint16_t)) {
uint8_t ndef_file_len_be[sizeof(uint16_t)];
bit_lib_num_to_bytes_be(ndef_file_len, sizeof(ndef_file_len_be), ndef_file_len_be);
uint8_t read_len = MIN(sizeof(ndef_file_len_be) - offset, le);
bit_buffer_append_bytes(instance->tx_buffer, &ndef_file_len_be[offset], read_len);
included_len = true;
offset = sizeof(uint16_t);
le -= read_len;
}
offset -= sizeof(uint16_t);
if(offset >= ndef_file_len) {
if(included_len) {
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
} else {
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_offset_error_apdu,
sizeof(type_4_tag_offset_error_apdu));
return Type4TagErrorWrongFormat;
}
}
const uint8_t* ndef_data = simple_array_cget_data(instance->data->ndef_data);
bit_buffer_append_bytes(
instance->tx_buffer, &ndef_data[offset], MIN(ndef_file_len - offset, le));
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_success_apdu, sizeof(type_4_tag_success_apdu));
return Type4TagErrorNone;
}
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_not_found_apdu, sizeof(type_4_tag_not_found_apdu));
return Type4TagErrorCustomCommand;
}
static const Type4TagListenerApduCommand type_4_tag_listener_commands[] = {
{
.cla_ins = {TYPE_4_TAG_ISO_SELECT_CMD},
.handler = type_4_tag_listener_iso_select,
},
{
.cla_ins = {TYPE_4_TAG_ISO_READ_CMD},
.handler = type_4_tag_listener_iso_read,
},
};
Type4TagError
type_4_tag_listener_handle_apdu(Type4TagListener* instance, const BitBuffer* rx_buffer) {
Type4TagError error = Type4TagErrorNone;
do {
typedef struct {
uint8_t cla_ins[2];
uint8_t p1;
uint8_t p2;
uint8_t body[];
} Type4TagApdu;
const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer);
if(buf_size < sizeof(Type4TagApdu)) {
error = Type4TagErrorWrongFormat;
break;
}
const Type4TagApdu* apdu = (const Type4TagApdu*)bit_buffer_get_data(rx_buffer);
Type4TagListenerApduHandler handler = NULL;
for(size_t i = 0; i < COUNT_OF(type_4_tag_listener_commands); i++) {
const Type4TagListenerApduCommand* command = &type_4_tag_listener_commands[i];
if(memcmp(apdu->cla_ins, command->cla_ins, sizeof(apdu->cla_ins)) == 0) {
handler = command->handler;
break;
}
}
if(!handler) {
bit_buffer_append_bytes(
instance->tx_buffer, type_4_tag_no_cmd_apdu, sizeof(type_4_tag_no_cmd_apdu));
error = Type4TagErrorCustomCommand;
break;
}
size_t body_size = buf_size - offsetof(Type4TagApdu, body);
size_t lc;
const uint8_t* data = apdu->body;
size_t le;
if(body_size == 0) {
lc = 0;
data = NULL;
le = 0;
} else if(body_size == 1) {
lc = 0;
data = NULL;
le = apdu->body[0];
if(le == 0) {
le = 256;
}
} else if(body_size == 3 && apdu->body[0] == 0) {
lc = 0;
data = NULL;
le = bit_lib_bytes_to_num_be(&apdu->body[1], sizeof(uint16_t));
if(le == 0) {
le = 65536;
}
} else {
bool extended_lc = false;
if(data[0] == 0) {
extended_lc = true;
lc = bit_lib_bytes_to_num_be(&data[1], sizeof(uint16_t));
data += 1 + sizeof(uint16_t);
body_size -= 1 + sizeof(uint16_t);
} else {
lc = data[0];
data++;
body_size--;
}
if(lc == 0 || body_size < lc) {
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_bad_params_apdu,
sizeof(type_4_tag_bad_params_apdu));
error = Type4TagErrorWrongFormat;
break;
}
if(body_size == lc) {
le = 0;
} else if(!extended_lc && body_size - lc == 1) {
le = data[lc];
if(le == 0) {
le = 256;
}
} else if(extended_lc && body_size - lc == 2) {
le = bit_lib_bytes_to_num_be(&data[lc], sizeof(uint16_t));
if(le == 0) {
le = 65536;
}
} else {
bit_buffer_append_bytes(
instance->tx_buffer,
type_4_tag_bad_params_apdu,
sizeof(type_4_tag_bad_params_apdu));
error = Type4TagErrorWrongFormat;
break;
}
}
bit_buffer_reset(instance->tx_buffer);
error = handler(instance, apdu->p1, apdu->p2, lc, data, le);
const Iso14443_4aError iso14443_4a_error =
iso14443_4a_listener_send_block(instance->iso14443_4a_listener, instance->tx_buffer);
// Show unknown command on screen
if(error == Type4TagErrorCustomCommand) break;
error = type_4_tag_process_error(iso14443_4a_error);
} while(false);
return error;
}
@@ -0,0 +1,37 @@
#pragma once
#include "type_4_tag_listener.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Type4TagListenerStateIdle,
Type4TagListenerStateSelectedApplication,
Type4TagListenerStateSelectedCapabilityContainer,
Type4TagListenerStateSelectedNdefMessage,
} Type4TagListenerState;
struct Type4TagListener {
Iso14443_4aListener* iso14443_4a_listener;
Type4TagData* data;
Type4TagListenerState state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Type4TagListenerEvent type_4_tag_event;
Type4TagListenerEventData type_4_tag_event_data;
NfcGenericCallback callback;
void* context;
};
Type4TagError
type_4_tag_listener_handle_apdu(Type4TagListener* instance, const BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif
@@ -4,9 +4,6 @@
#define TAG "Type4TagPoller"
// Read returns 2 byte status trailer, write sends 5 byte command header
#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5)
typedef NfcCommand (*Type4TagPollerReadHandler)(Type4TagPoller* instance);
const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) {
@@ -5,19 +5,6 @@
#define TAG "Type4TagPoller"
Type4TagError type_4_tag_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
return Type4TagErrorNone;
case Iso14443_4aErrorNotPresent:
return Type4TagErrorNotPresent;
case Iso14443_4aErrorTimeout:
return Type4TagErrorTimeout;
default:
return Type4TagErrorProtocol;
}
}
Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) {
furi_check(instance);
@@ -58,7 +45,7 @@ Type4TagError type_4_tag_poller_select_app(Type4TagPoller* 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_NAME,
TYPE_4_TAG_ISO_SELECT_P1_BY_DF_NAME,
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
TYPE_4_TAG_ISO_APP_NAME_LEN,
TYPE_4_TAG_ISO_APP_NAME,
@@ -33,8 +33,6 @@ struct Type4TagPoller {
void* context;
};
Type4TagError type_4_tag_process_error(Iso14443_4aError error);
const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance);
Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance);