NFC: Refactor DESFire poller to NxpNativeCommand helper

This commit is contained in:
Willy-JL
2025-03-27 04:40:03 +00:00
parent 54adc38b3a
commit 0b42027973
5 changed files with 235 additions and 99 deletions
+115
View File
@@ -0,0 +1,115 @@
#include "nxp_native_command.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
#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;
}
+92
View File
@@ -0,0 +1,92 @@
#pragma once
#include "nxp_native_command_mode.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
// 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);
+11
View File
@@ -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;
+2 -46
View File
@@ -2,55 +2,11 @@
#include "mf_desfire.h"
#include <nfc/helpers/nxp_native_command.h>
#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;
@@ -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) {