diff --git a/lib/nfc/helpers/nxp_native_command.c b/lib/nfc/helpers/nxp_native_command.c new file mode 100644 index 000000000..d5d535891 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command.c @@ -0,0 +1,115 @@ +#include "nxp_native_command.h" + +#include + +#define TAG "NxpNativeCommand" + +Iso14443_4aError nxp_native_command_iso14443_4a_poller( + Iso14443_4aPoller* iso14443_4a_poller, + NxpNativeCommandStatus* status_code, + const BitBuffer* input_buffer, + BitBuffer* result_buffer, + NxpNativeCommandMode command_mode, + BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_check(iso14443_4a_poller); + furi_check(tx_buffer); + furi_check(rx_buffer); + furi_check(input_buffer); + furi_check(result_buffer); + furi_check(command_mode < NxpNativeCommandModeMAX); + + Iso14443_4aError error = Iso14443_4aErrorNone; + *status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + + do { + bit_buffer_reset(tx_buffer); + if(command_mode == NxpNativeCommandModePlain) { + bit_buffer_append(tx_buffer, input_buffer); + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA); + bit_buffer_append_byte(tx_buffer, bit_buffer_get_byte(input_buffer, 0)); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2); + if(bit_buffer_get_size_bytes(input_buffer) > 1) { + bit_buffer_append_byte(tx_buffer, bit_buffer_get_size_bytes(input_buffer) - 1); + bit_buffer_append_right(tx_buffer, input_buffer, 1); + } + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE); + } + + bit_buffer_reset(rx_buffer); + error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + + if(error != Iso14443_4aErrorNone) { + break; + } + + bit_buffer_reset(tx_buffer); + if(command_mode == NxpNativeCommandModePlain) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME); + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_CLA); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P1); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_P2); + bit_buffer_append_byte(tx_buffer, NXP_NATIVE_COMMAND_ISO_LE); + } + + size_t response_len = bit_buffer_get_size_bytes(rx_buffer); + *status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + bit_buffer_reset(result_buffer); + if(command_mode == NxpNativeCommandModePlain && response_len >= sizeof(uint8_t)) { + *status_code = bit_buffer_get_byte(rx_buffer, 0); + if(response_len > sizeof(uint8_t)) { + bit_buffer_copy_right(result_buffer, rx_buffer, sizeof(uint8_t)); + } + } else if( + command_mode == NxpNativeCommandModeIsoWrapped && + response_len >= 2 * sizeof(uint8_t) && + bit_buffer_get_byte(rx_buffer, response_len - 2) == NXP_NATIVE_COMMAND_ISO_SW1) { + *status_code = bit_buffer_get_byte(rx_buffer, response_len - 1); + if(response_len > 2 * sizeof(uint8_t)) { + bit_buffer_copy_left(result_buffer, rx_buffer, response_len - 2 * sizeof(uint8_t)); + } + } + + while(*status_code == NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME) { + bit_buffer_reset(rx_buffer); + error = iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + + if(error != Iso14443_4aErrorNone) { + break; + } + + const size_t rx_size = bit_buffer_get_size_bytes(rx_buffer); + const size_t rx_capacity_remaining = bit_buffer_get_capacity_bytes(result_buffer) - + bit_buffer_get_size_bytes(result_buffer); + + if(command_mode == NxpNativeCommandModePlain) { + *status_code = rx_size >= 1 ? bit_buffer_get_byte(rx_buffer, 0) : + NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + if(rx_size <= rx_capacity_remaining + 1) { + bit_buffer_append_right(result_buffer, rx_buffer, sizeof(uint8_t)); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1); + } + } else if(command_mode == NxpNativeCommandModeIsoWrapped) { + if(rx_size >= 2 && + bit_buffer_get_byte(rx_buffer, rx_size - 2) == NXP_NATIVE_COMMAND_ISO_SW1) { + *status_code = bit_buffer_get_byte(rx_buffer, rx_size - 1); + } else { + *status_code = NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR; + } + if(rx_size <= rx_capacity_remaining + 2) { + bit_buffer_set_size_bytes(rx_buffer, rx_size - 2); + bit_buffer_append(result_buffer, rx_buffer); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 2); + } + } + } + } while(false); + + return error; +} diff --git a/lib/nfc/helpers/nxp_native_command.h b/lib/nfc/helpers/nxp_native_command.h new file mode 100644 index 000000000..61677a3f6 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command.h @@ -0,0 +1,92 @@ +#pragma once + +#include "nxp_native_command_mode.h" + +#include + +// ISO 7816 command wrapping +#define NXP_NATIVE_COMMAND_ISO_CLA (0x90) +#define NXP_NATIVE_COMMAND_ISO_P1 (0x00) +#define NXP_NATIVE_COMMAND_ISO_P2 (0x00) +#define NXP_NATIVE_COMMAND_ISO_LE (0x00) +// ISO 7816 status wrapping +#define NXP_NATIVE_COMMAND_ISO_SW1 (0x91) + +// Successful operation +#define NXP_NATIVE_COMMAND_STATUS_OPERATION_OK (0x00) +// No changes done to backup files, CommitTransaction / AbortTransaction not necessary +#define NXP_NATIVE_COMMAND_STATUS_NO_CHANGES (0x0C) +// Insufficient NV-Memory to complete command +#define NXP_NATIVE_COMMAND_STATUS_OUT_OF_EEPROM_ERROR (0x0E) +// Command code not supported +#define NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE (0x1C) +// CRC or MAC does not match data Padding bytes not valid +#define NXP_NATIVE_COMMAND_STATUS_INTEGRITY_ERROR (0x1E) +// Invalid key number specified +#define NXP_NATIVE_COMMAND_STATUS_NO_SUCH_KEY (0x40) +// Length of command string invalid +#define NXP_NATIVE_COMMAND_STATUS_LENGTH_ERROR (0x7E) +// Current configuration / status does not allow the requested command +#define NXP_NATIVE_COMMAND_STATUS_PERMISSION_DENIED (0x9D) +// Value of the parameter(s) invalid +#define NXP_NATIVE_COMMAND_STATUS_PARAMETER_ERROR (0x9E) +// Requested AID not present on PICC +#define NXP_NATIVE_COMMAND_STATUS_APPLICATION_NOT_FOUND (0xA0) +// Unrecoverable error within application, application will be disabled +#define NXP_NATIVE_COMMAND_STATUS_APPL_INTEGRITY_ERROR (0xA1) +// Currently not allowed to authenticate. Keep trying until full delay is spent +#define NXP_NATIVE_COMMAND_STATUS_STATUS_AUTHENTICATION_DELAY (0xAD) +// Current authentication status does not allow the requested command +#define NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR (0xAE) +// Additional data frame is expected to be sent +#define NXP_NATIVE_COMMAND_STATUS_ADDITIONAL_FRAME (0xAF) +// Attempt to read/write data from/to beyond the file's/record's limits +// Attempt to exceed the limits of a value file. +#define NXP_NATIVE_COMMAND_STATUS_BOUNDARY_ERROR (0xBE) +// Unrecoverable error within PICC, PICC will be disabled +#define NXP_NATIVE_COMMAND_STATUS_PICC_INTEGRITY_ERROR (0xC1) +// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD +#define NXP_NATIVE_COMMAND_STATUS_COMMAND_ABORTED (0xCA) +// PICC was disabled by an unrecoverable error +#define NXP_NATIVE_COMMAND_STATUS_PICC_DISABLED_ERROR (0xCD) +// Number of Applications limited to 28, no additional CreateApplication possible +#define NXP_NATIVE_COMMAND_STATUS_COUNT_ERROR (0xCE) +// Creation of file/application failed because file/application with same number already exists +#define NXP_NATIVE_COMMAND_STATUS_DUBLICATE_ERROR (0xDE) +// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated +#define NXP_NATIVE_COMMAND_STATUS_EEPROM_ERROR (0xEE) +// Specified file number does not exist +#define NXP_NATIVE_COMMAND_STATUS_FILE_NOT_FOUND (0xF0) +// Unrecoverable error within file, file will be disabled +#define NXP_NATIVE_COMMAND_STATUS_FILE_INTEGRITY_ERROR (0xF1) + +typedef uint8_t NxpNativeCommandStatus; + +/** + * @brief Transmit and receive NXP Native Command chunks in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The result_buffer will be filled with any data received as a response to data + * sent from input_buffer, with a timeout defined by the fwt parameter. + * + * The tx_buffer and rx_buffer are used as working areas to handle individual + * command chunks and responses. + * + * @param[in, out] iso14443_4a_poller pointer to the instance to be used in the transaction. + * @param[out] status_code pointer to a status variable to hold the result of the operation. + * @param[in] input_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] result_buffer pointer to the buffer to be filled with received data. + * @param[in] command_mode what command formatting mode to use for the transaction. + * @param[in, out] tx_buffer pointer to the buffer for command construction. + * @param[in, out] rx_buffer pointer to the buffer for response handling. + * @return Iso14443_4aErrorNone and STATUS_OPERATION_OK on success, an error code on failure. + */ +Iso14443_4aError nxp_native_command_iso14443_4a_poller( + Iso14443_4aPoller* iso14443_4a_poller, + NxpNativeCommandStatus* status_code, + const BitBuffer* input_buffer, + BitBuffer* result_buffer, + NxpNativeCommandMode command_mode, + BitBuffer* tx_buffer, + BitBuffer* rx_buffer); diff --git a/lib/nfc/helpers/nxp_native_command_mode.h b/lib/nfc/helpers/nxp_native_command_mode.h new file mode 100644 index 000000000..7684b4d48 --- /dev/null +++ b/lib/nfc/helpers/nxp_native_command_mode.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * @brief Enumeration of possible command modes. + */ +typedef enum { + NxpNativeCommandModePlain, /**< Plain native commands. */ + NxpNativeCommandModeIsoWrapped, /**< ISO 7816-wrapped commands. */ + + NxpNativeCommandModeMAX, +} NxpNativeCommandMode; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h index 921bbb9de..9494120d2 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -2,55 +2,11 @@ #include "mf_desfire.h" +#include + #define MF_DESFIRE_FFF_PICC_PREFIX "PICC" #define MF_DESFIRE_FFF_APP_PREFIX "Application" -// Successful operation -#define MF_DESFIRE_STATUS_OPERATION_OK (0x00) -// No changes done to backup files, CommitTransaction / AbortTransaction not necessary -#define MF_DESFIRE_STATUS_NO_CHANGES (0x0C) -// Insufficient NV-Memory to complete command -#define MF_DESFIRE_STATUS_OUT_OF_EEPROM_ERROR (0x0E) -// Command code not supported -#define MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE (0x1C) -// CRC or MAC does not match data Padding bytes not valid -#define MF_DESFIRE_STATUS_INTEGRITY_ERROR (0x1E) -// Invalid key number specified -#define MF_DESFIRE_STATUS_NO_SUCH_KEY (0x40) -// Length of command string invalid -#define MF_DESFIRE_STATUS_LENGTH_ERROR (0x7E) -// Current configuration / status does not allow the requested command -#define MF_DESFIRE_STATUS_PERMISSION_DENIED (0x9D) -// Value of the parameter(s) invalid -#define MF_DESFIRE_STATUS_PARAMETER_ERROR (0x9E) -// Requested AID not present on PICC -#define MF_DESFIRE_STATUS_APPLICATION_NOT_FOUND (0xA0) -// Unrecoverable error within application, application will be disabled -#define MF_DESFIRE_STATUS_APPL_INTEGRITY_ERROR (0xA1) -// Current authentication status does not allow the requested command -#define MF_DESFIRE_STATUS_AUTHENTICATION_ERROR (0xAE) -// Additional data frame is expected to be sent -#define MF_DESFIRE_STATUS_ADDITIONAL_FRAME (0xAF) -// Attempt to read/write data from/to beyond the file's/record's limits -// Attempt to exceed the limits of a value file. -#define MF_DESFIRE_STATUS_BOUNDARY_ERROR (0xBE) -// Unrecoverable error within PICC, PICC will be disabled -#define MF_DESFIRE_STATUS_PICC_INTEGRITY_ERROR (0xC1) -// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD -#define MF_DESFIRE_STATUS_COMMAND_ABORTED (0xCA) -// PICC was disabled by an unrecoverable error -#define MF_DESFIRE_STATUS_PICC_DISABLED_ERROR (0xCD) -// Number of Applications limited to 28, no additional CreateApplication possible -#define MF_DESFIRE_STATUS_COUNT_ERROR (0xCE) -// Creation of file/application failed because file/application with same number already exists -#define MF_DESFIRE_STATUS_DUBLICATE_ERROR (0xDE) -// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated -#define MF_DESFIRE_STATUS_EEPROM_ERROR (0xEE) -// Specified file number does not exist -#define MF_DESFIRE_STATUS_FILE_NOT_FOUND (0xF0) -// Unrecoverable error within file, file will be disabled -#define MF_DESFIRE_STATUS_FILE_INTEGRITY_ERROR (0xF1) - // SimpleArray configurations extern const SimpleArrayConfig mf_desfire_key_version_array_config; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c index 6d8dfda16..f54133e3c 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -21,11 +21,11 @@ MfDesfireError mf_desfire_process_error(Iso14443_4aError error) { MfDesfireError mf_desfire_process_status_code(uint8_t status_code) { switch(status_code) { - case MF_DESFIRE_STATUS_OPERATION_OK: + case NXP_NATIVE_COMMAND_STATUS_OPERATION_OK: return MfDesfireErrorNone; - case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR: + case NXP_NATIVE_COMMAND_STATUS_AUTHENTICATION_ERROR: return MfDesfireErrorAuthentication; - case MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE: + case NXP_NATIVE_COMMAND_STATUS_ILLEGAL_COMMAND_CODE: return MfDesfireErrorCommandNotSupported; default: return MfDesfireErrorProtocol; @@ -37,60 +37,22 @@ MfDesfireError mf_desfire_send_chunks( const BitBuffer* tx_buffer, BitBuffer* rx_buffer) { furi_check(instance); - furi_check(instance->iso14443_4a_poller); - furi_check(instance->tx_buffer); - furi_check(instance->rx_buffer); - furi_check(tx_buffer); - furi_check(rx_buffer); - MfDesfireError error = MfDesfireErrorNone; + NxpNativeCommandStatus status_code = NXP_NATIVE_COMMAND_STATUS_OPERATION_OK; + Iso14443_4aError iso14443_4a_error = nxp_native_command_iso14443_4a_poller( + instance->iso14443_4a_poller, + &status_code, + tx_buffer, + rx_buffer, + NxpNativeCommandModePlain, + instance->tx_buffer, + instance->rx_buffer); - do { - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( - instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); - - if(iso14443_4a_error != Iso14443_4aErrorNone) { - error = mf_desfire_process_error(iso14443_4a_error); - break; - } - - bit_buffer_reset(instance->tx_buffer); - bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME); - - if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) { - bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); - } else { - bit_buffer_reset(rx_buffer); - } - - while( - bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) { - Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( - instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); - - if(iso14443_4a_error != Iso14443_4aErrorNone) { - error = mf_desfire_process_error(iso14443_4a_error); - break; - } - - const size_t rx_size = bit_buffer_get_size_bytes(instance->rx_buffer); - const size_t rx_capacity_remaining = - bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer); - - if(rx_size <= rx_capacity_remaining + 1) { - bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); - } else { - FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size - 1); - } - } - } while(false); - - if(error == MfDesfireErrorNone) { - uint8_t err_code = bit_buffer_get_byte(instance->rx_buffer, 0); - error = mf_desfire_process_status_code(err_code); + if(iso14443_4a_error != Iso14443_4aErrorNone) { + return mf_desfire_process_error(iso14443_4a_error); } - return error; + return mf_desfire_process_status_code(status_code); } MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {