From 22a6c37e20b09978911b3725d67070911151b0da Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:16:31 +0000 Subject: [PATCH] Refactor ISO APDUs in T4T poller --- lib/nfc/protocols/type_4_tag/type_4_tag.h | 2 +- lib/nfc/protocols/type_4_tag/type_4_tag_i.c | 12 +- lib/nfc/protocols/type_4_tag/type_4_tag_i.h | 10 +- .../type_4_tag/type_4_tag_listener_i.c | 25 +- .../type_4_tag/type_4_tag_poller_i.c | 228 +++++++++--------- 5 files changed, 135 insertions(+), 142 deletions(-) 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 250f34edc..220458e86 100644 --- a/lib/nfc/protocols/type_4_tag/type_4_tag.h +++ b/lib/nfc/protocols/type_4_tag/type_4_tag.h @@ -13,7 +13,7 @@ typedef enum { Type4TagErrorTimeout, Type4TagErrorWrongFormat, Type4TagErrorNotSupported, - Type4TagErrorUnknown, + Type4TagErrorApduFailed, Type4TagErrorCustomCommand, } Type4TagError; 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 d0b0bf22e..90b5fecc2 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 @@ -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; 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 687728fc4..00571f90f 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 @@ -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 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 index 940aca9e1..264771e9b 100644 --- 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 @@ -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)); 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 cf0d14861..55ba1b059 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 @@ -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(