diff --git a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c index 97a4872c7..043fd5f73 100644 --- a/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c +++ b/applications/main/nfc/helpers/protocol_support/type_4_tag/type_4_tag.c @@ -2,13 +2,13 @@ #include "type_4_tag_render.h" #include +#include #include #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 = { diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c index 017f6a571..a4f9e225a 100644 --- a/lib/nfc/protocols/nfc_listener_defs.c +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -7,6 +7,7 @@ #include #include #include +#include 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, }; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag.h b/lib/nfc/protocols/type_4_tag/type_4_tag.h index bfa9f9478..250f34edc 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -14,6 +14,7 @@ typedef enum { Type4TagErrorWrongFormat, Type4TagErrorNotSupported, Type4TagErrorUnknown, + Type4TagErrorCustomCommand, } Type4TagError; typedef struct { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c index ef252f9a0..d90911212 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.c @@ -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)) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h index 2fdd478ac..9a1add6ee 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_i.h @@ -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); diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.c new file mode 100644 index 000000000..58e94470d --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.c @@ -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, +}; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.h new file mode 100644 index 000000000..7e0301322 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener.h @@ -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 diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h new file mode 100644 index 000000000..87206fe6b --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcListenerBase nfc_listener_type_4_tag; diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c new file mode 100644 index 000000000..b18189f1e --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.c @@ -0,0 +1,314 @@ +#include "type_4_tag_listener_i.h" +#include "type_4_tag_i.h" + +#include + +#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; +} diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h new file mode 100644 index 000000000..c29eb54b0 --- /dev/null +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_listener_i.h @@ -0,0 +1,37 @@ +#pragma once + +#include "type_4_tag_listener.h" + +#include + +#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 diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c index 3ff58ea2e..8e321312b 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller.c @@ -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) { diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c index b356604c0..2e0bc3882 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.c @@ -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, diff --git a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h index c74135040..0301e21a3 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag_poller_i.h @@ -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);