NFC refactoring (#3050)

"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring.

Starring:

- @gornekich - NFC refactoring project lead, architect, senior developer
- @gsurkov - architect, senior developer
- @RebornedBrain - senior developer

Supporting roles:

- @skotopes, @DrZlo13, @hedger - general architecture advisors, code review
- @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance

Special thanks:

@bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
gornekich
2023-10-24 07:08:09 +04:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41488 additions and 68125 deletions

View File

@@ -0,0 +1,186 @@
#include "iso14443_3a.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define ISO14443A_ATS_BIT (1U << 5)
#define ISO14443_3A_PROTOCOL_NAME_LEGACY "UID"
#define ISO14443_3A_PROTOCOL_NAME "ISO14443-3A"
#define ISO14443_3A_DEVICE_NAME "ISO14443-3A (Unknown)"
#define ISO14443_3A_ATQA_KEY "ATQA"
#define ISO14443_3A_SAK_KEY "SAK"
const NfcDeviceBase nfc_device_iso14443_3a = {
.protocol_name = ISO14443_3A_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_3a_alloc,
.free = (NfcDeviceFree)iso14443_3a_free,
.reset = (NfcDeviceReset)iso14443_3a_reset,
.copy = (NfcDeviceCopy)iso14443_3a_copy,
.verify = (NfcDeviceVerify)iso14443_3a_verify,
.load = (NfcDeviceLoad)iso14443_3a_load,
.save = (NfcDeviceSave)iso14443_3a_save,
.is_equal = (NfcDeviceEqual)iso14443_3a_is_equal,
.get_name = (NfcDeviceGetName)iso14443_3a_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_3a_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_3a_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data,
};
Iso14443_3aData* iso14443_3a_alloc() {
Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData));
return data;
}
void iso14443_3a_free(Iso14443_3aData* data) {
furi_assert(data);
free(data);
}
void iso14443_3a_reset(Iso14443_3aData* data) {
furi_assert(data);
memset(data, 0, sizeof(Iso14443_3aData));
}
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY);
}
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
// Common to all format versions
if(!flipper_format_read_hex(ff, ISO14443_3A_ATQA_KEY, data->atqa, 2)) break;
if(!flipper_format_read_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;
if(version > NFC_LSB_ATQA_FORMAT_VERSION) {
// Swap ATQA bytes for newer versions
FURI_SWAP(data->atqa[0], data->atqa[1]);
}
parsed = true;
} while(false);
return parsed;
}
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
// Save ATQA in MSB order for correct companion apps display
const uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_comment_cstr(ff, ISO14443_3A_PROTOCOL_NAME " specific data"))
break;
// Write ATQA and SAK
if(!flipper_format_write_hex(ff, ISO14443_3A_ATQA_KEY, atqa, 2)) break;
if(!flipper_format_write_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;
saved = true;
} while(false);
return saved;
}
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(Iso14443_3aData)) == 0;
}
const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_3A_DEVICE_NAME;
}
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) {
furi_assert(data);
if(uid_len) {
*uid_len = data->uid_len;
}
return data->uid;
}
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES ||
uid_len == ISO14443_3A_UID_7_BYTES ||
uid_len == ISO14443_3A_UID_10_BYTES;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
data->uid_len = uid_len;
}
return uid_valid;
}
Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) {
UNUSED(data);
furi_crash("No base data");
}
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) {
furi_assert(data);
uint32_t cuid = 0;
const uint8_t* cuid_start = data->uid;
if(data->uid_len == ISO14443_3A_UID_7_BYTES) {
cuid_start = &data->uid[3];
}
cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | (cuid_start[3]);
return cuid;
}
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data) {
furi_assert(data);
return data->sak & ISO14443A_ATS_BIT;
}
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data) {
furi_assert(data);
return data->sak;
}
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]) {
furi_assert(data);
furi_assert(atqa);
memcpy(atqa, data->atqa, sizeof(data->atqa));
}
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak) {
furi_assert(data);
data->sak = sak;
}
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]) {
furi_assert(data);
furi_assert(atqa);
memcpy(data->atqa, atqa, sizeof(data->atqa));
}

View File

@@ -0,0 +1,107 @@
#pragma once
#include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3A_UID_4_BYTES (4U)
#define ISO14443_3A_UID_7_BYTES (7U)
#define ISO14443_3A_UID_10_BYTES (10U)
#define ISO14443_3A_MAX_UID_SIZE ISO14443_3A_UID_10_BYTES
#define ISO14443_3A_GUARD_TIME_US (5000)
#define ISO14443_3A_FDT_POLL_FC (1620)
#define ISO14443_3A_FDT_LISTEN_FC (1172)
#define ISO14443_3A_POLLER_MASK_RX_FS ((ISO14443_3A_FDT_LISTEN_FC) / 2)
#define ISO14443_3A_POLL_POLL_MIN_US (1100)
typedef enum {
Iso14443_3aErrorNone,
Iso14443_3aErrorNotPresent,
Iso14443_3aErrorColResFailed,
Iso14443_3aErrorBufferOverflow,
Iso14443_3aErrorCommunication,
Iso14443_3aErrorFieldOff,
Iso14443_3aErrorWrongCrc,
Iso14443_3aErrorTimeout,
} Iso14443_3aError;
typedef struct {
uint8_t sens_resp[2];
} Iso14443_3aSensResp;
typedef struct {
uint8_t sel_cmd;
uint8_t sel_par;
uint8_t data[4]; // max data bit is 32
} Iso14443_3aSddReq;
typedef struct {
uint8_t nfcid[4];
uint8_t bss;
} Iso14443_3aSddResp;
typedef struct {
uint8_t sel_cmd;
uint8_t sel_par;
uint8_t nfcid[4];
uint8_t bcc;
} Iso14443_3aSelReq;
typedef struct {
uint8_t sak;
} Iso14443_3aSelResp;
typedef struct {
uint8_t uid[ISO14443_3A_MAX_UID_SIZE];
uint8_t uid_len;
uint8_t atqa[2];
uint8_t sak;
} Iso14443_3aData;
Iso14443_3aData* iso14443_3a_alloc();
void iso14443_3a_free(Iso14443_3aData* data);
void iso14443_3a_reset(Iso14443_3aData* data);
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other);
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type);
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff);
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other);
const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len);
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data);
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data);
// Getters and tests
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data);
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data);
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]);
// Setters
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak);
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_3a;

View File

@@ -0,0 +1,127 @@
#include "iso14443_3a_listener_i.h"
#include "nfc/protocols/nfc_listener_base.h"
#include "nfc/helpers/iso14443_crc.h"
#include <furi.h>
#include <lib/nfc/nfc.h>
#define TAG "Iso14443_3aListener"
#define ISO14443_3A_LISTENER_MAX_BUFFER_SIZE (256)
static bool iso14443_3a_listener_halt_received(BitBuffer* buf) {
bool halt_cmd_received = false;
do {
if(bit_buffer_get_size_bytes(buf) != 4) break;
if(!iso14443_crc_check(Iso14443CrcTypeA, buf)) break;
if(bit_buffer_get_byte(buf, 0) != 0x50) break;
if(bit_buffer_get_byte(buf, 1) != 0x00) break;
halt_cmd_received = true;
} while(false);
return halt_cmd_received;
}
Iso14443_3aListener* iso14443_3a_listener_alloc(Nfc* nfc, Iso14443_3aData* data) {
furi_assert(nfc);
Iso14443_3aListener* instance = malloc(sizeof(Iso14443_3aListener));
instance->nfc = nfc;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_LISTENER_MAX_BUFFER_SIZE);
instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;
instance->generic_event.protocol = NfcProtocolIso14443_3a;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->iso14443_3a_event;
nfc_set_fdt_listen_fc(instance->nfc, ISO14443_3A_FDT_LISTEN_FC);
nfc_config(instance->nfc, NfcModeListener, NfcTechIso14443a);
nfc_iso14443a_listener_set_col_res_data(
instance->nfc,
instance->data->uid,
instance->data->uid_len,
instance->data->atqa,
instance->data->sak);
return instance;
}
void iso14443_3a_listener_free(Iso14443_3aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
void iso14443_3a_listener_set_callback(
Iso14443_3aListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
const Iso14443_3aData* iso14443_3a_listener_get_data(Iso14443_3aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
NfcCommand iso14443_3a_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3aListener* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypeListenerActivated) {
instance->state = Iso14443_3aListenerStateActive;
} else if(nfc_event->type == NfcEventTypeFieldOff) {
instance->state = Iso14443_3aListenerStateIdle;
if(instance->callback) {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeFieldOff;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
} else if(nfc_event->type == NfcEventTypeRxEnd) {
if(iso14443_3a_listener_halt_received(nfc_event->data.buffer)) {
if(instance->callback) {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeHalted;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
} else {
if(iso14443_crc_check(Iso14443CrcTypeA, nfc_event->data.buffer)) {
instance->iso14443_3a_event.type =
Iso14443_3aListenerEventTypeReceivedStandardFrame;
iso14443_crc_trim(nfc_event->data.buffer);
} else {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeReceivedData;
}
instance->iso14443_3a_event_data.buffer = nfc_event->data.buffer;
if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context);
}
}
}
return command;
}
const NfcListenerBase nfc_listener_iso14443_3a = {
.alloc = (NfcListenerAlloc)iso14443_3a_listener_alloc,
.free = (NfcListenerFree)iso14443_3a_listener_free,
.set_callback = (NfcListenerSetCallback)iso14443_3a_listener_set_callback,
.get_data = (NfcListenerGetData)iso14443_3a_listener_get_data,
.run = (NfcListenerRun)iso14443_3a_listener_run,
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include "iso14443_3a.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aListener Iso14443_3aListener;
typedef enum {
Iso14443_3aListenerEventTypeFieldOff,
Iso14443_3aListenerEventTypeHalted,
Iso14443_3aListenerEventTypeReceivedStandardFrame,
Iso14443_3aListenerEventTypeReceivedData,
} Iso14443_3aListenerEventType;
typedef struct {
BitBuffer* buffer;
} Iso14443_3aListenerEventData;
typedef struct {
Iso14443_3aListenerEventType type;
Iso14443_3aListenerEventData* data;
} Iso14443_3aListenerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_iso14443_3a;

View File

@@ -0,0 +1,73 @@
#include "iso14443_3a_listener_i.h"
#include "nfc/helpers/iso14443_crc.h"
#define TAG "Iso14443_3aListener"
static Iso14443_3aError iso14443_3a_listener_process_nfc_error(NfcError error) {
Iso14443_3aError ret = Iso14443_3aErrorNone;
if(error == NfcErrorNone) {
ret = Iso14443_3aErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso14443_3aErrorTimeout;
} else {
ret = Iso14443_3aErrorFieldOff;
}
return ret;
}
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_listener_tx(instance->nfc, tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_iso14443a_listener_tx_custom_parity(instance->nfc, tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
}
return ret;
};
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(instance->tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
break;
}
} while(false);
return ret;
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_3a_listener.h"
#include <nfc/protocols/nfc_generic_event.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_3aListenerStateIdle,
Iso14443_3aListenerStateActive,
} Iso14443_3aListenerState;
struct Iso14443_3aListener {
Nfc* nfc;
Iso14443_3aData* data;
Iso14443_3aListenerState state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Iso14443_3aListenerEvent iso14443_3a_event;
Iso14443_3aListenerEventData iso14443_3a_event_data;
NfcGenericCallback callback;
void* context;
};
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,128 @@
#include "iso14443_3a_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "ISO14443_3A"
const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static Iso14443_3aPoller* iso14443_3a_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
Iso14443_3aPoller* instance = malloc(sizeof(Iso14443_3aPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443a);
nfc_set_guard_time_us(instance->nfc, ISO14443_3A_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3A_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3A_POLL_POLL_MIN_US);
instance->data = iso14443_3a_alloc();
instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;
instance->general_event.protocol = NfcProtocolIso14443_3a;
instance->general_event.event_data = &instance->iso14443_3a_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_3a_poller_free_new(Iso14443_3aPoller* iso14443_3a_poller) {
furi_assert(iso14443_3a_poller);
Iso14443_3aPoller* instance = iso14443_3a_poller;
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
iso14443_3a_free(instance->data);
free(instance);
}
static void iso14443_3a_poller_set_callback(
Iso14443_3aPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_3a_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3aPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != Iso14443_3aPollerStateActivated) {
Iso14443_3aData data = {};
Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, &data);
if(error == Iso14443_3aErrorNone) {
instance->state = Iso14443_3aPollerStateActivated;
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;
instance->iso14443_3a_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeError;
instance->iso14443_3a_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;
instance->iso14443_3a_event_data.error = Iso14443_3aErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
if(command == NfcCommandReset) {
instance->state = Iso14443_3aPollerStateIdle;
}
return command;
}
static bool iso14443_3a_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
Iso14443_3aPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == Iso14443_3aPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, NULL);
protocol_detected = (error == Iso14443_3aErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_3a = {
.alloc = (NfcPollerAlloc)iso14443_3a_poller_alloc,
.free = (NfcPollerFree)iso14443_3a_poller_free_new,
.set_callback = (NfcPollerSetCallback)iso14443_3a_poller_set_callback,
.run = (NfcPollerRun)iso14443_3a_poller_run,
.detect = (NfcPollerDetect)iso14443_3a_poller_detect,
.get_data = (NfcPollerGetData)iso14443_3a_poller_get_data,
};

View File

@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_3a.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aPoller Iso14443_3aPoller;
typedef enum {
Iso14443_3aPollerEventTypeError,
Iso14443_3aPollerEventTypeReady,
} Iso14443_3aPollerEventType;
typedef struct {
Iso14443_3aError error;
} Iso14443_3aPollerEventData;
typedef struct {
Iso14443_3aPollerEventType type;
Iso14443_3aPollerEventData* data;
} Iso14443_3aPollerEvent;
Iso14443_3aError iso14443_3a_poller_txrx(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
Iso14443_3aError iso14443_3a_poller_send_standard_frame(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_3a;

View File

@@ -0,0 +1,293 @@
#include "iso14443_3a_poller_i.h"
#include <furi.h>
#include "nfc/helpers/iso14443_crc.h"
#define TAG "ISO14443_3A"
static Iso14443_3aError iso14443_3a_poller_process_error(NfcError error) {
Iso14443_3aError ret = Iso14443_3aErrorNone;
if(error == NfcErrorNone) {
ret = Iso14443_3aErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso14443_3aErrorTimeout;
} else {
ret = Iso14443_3aErrorNotPresent;
}
return ret;
}
static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
uint16_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - 2);
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) {
ret = Iso14443_3aErrorWrongCrc;
break;
}
iso14443_crc_trim(rx_buffer);
} while(false);
return ret;
}
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
NfcError error = NfcErrorNone;
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
error = nfc_iso14443a_poller_trx_short_frame(
instance->nfc,
NfcIso14443aShortFrameSensReq,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {
ret = Iso14443_3aErrorCommunication;
break;
}
} while(false);
return ret;
}
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(instance->tx_buffer);
uint8_t halt_cmd[2] = {0x50, 0x00};
bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd));
iso14443_3a_poller_standard_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);
instance->state = Iso14443_3aPollerStateIdle;
return Iso14443_3aErrorNone;
}
Iso14443_3aError iso14443_3a_poller_async_activate(
Iso14443_3aPoller* instance,
Iso14443_3aData* iso14443_3a_data) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
// Reset Iso14443_3a poller state
memset(&instance->col_res, 0, sizeof(instance->col_res));
memset(instance->data, 0, sizeof(Iso14443_3aData));
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Halt if necessary
if(instance->state != Iso14443_3aPollerStateIdle) {
iso14443_3a_poller_halt(instance);
instance->state = Iso14443_3aPollerStateIdle;
}
NfcError error = NfcErrorNone;
Iso14443_3aError ret = Iso14443_3aErrorNone;
bool activated = false;
do {
error = nfc_iso14443a_poller_trx_short_frame(
instance->nfc,
NfcIso14443aShortFrameSensReq,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
ret = Iso14443_3aErrorNotPresent;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {
FURI_LOG_W(TAG, "Wrong sens response size");
ret = Iso14443_3aErrorCommunication;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer,
&instance->col_res.sens_resp,
sizeof(instance->col_res.sens_resp));
memcpy(
instance->data->atqa,
&instance->col_res.sens_resp,
sizeof(instance->col_res.sens_resp));
instance->state = Iso14443_3aPollerStateColResInProgress;
instance->col_res.cascade_level = 0;
instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;
while(instance->state == Iso14443_3aPollerStateColResInProgress) {
if(instance->col_res.state == Iso14443_3aPollerColResStateStateNewCascade) {
bit_buffer_set_size_bytes(instance->tx_buffer, 2);
bit_buffer_set_byte(
instance->tx_buffer,
0,
ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level));
bit_buffer_set_byte(instance->tx_buffer, 1, ISO14443_3A_POLLER_SEL_PAR(2, 0));
error = nfc_iso14443a_poller_trx_sdd_frame(
instance->nfc,
instance->tx_buffer,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
FURI_LOG_E(TAG, "Sdd request failed: %d", error);
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != 5) {
FURI_LOG_E(TAG, "Sdd response wrong length");
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer, &instance->col_res.sdd_resp, sizeof(Iso14443_3aSddResp));
instance->col_res.state = Iso14443_3aPollerColResStateStateSelectCascade;
} else if(instance->col_res.state == Iso14443_3aPollerColResStateStateSelectCascade) {
instance->col_res.sel_req.sel_cmd =
ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level);
instance->col_res.sel_req.sel_par = ISO14443_3A_POLLER_SEL_PAR(7, 0);
memcpy(
instance->col_res.sel_req.nfcid,
instance->col_res.sdd_resp.nfcid,
sizeof(instance->col_res.sdd_resp.nfcid));
instance->col_res.sel_req.bcc = instance->col_res.sdd_resp.bss;
bit_buffer_copy_bytes(
instance->tx_buffer,
(uint8_t*)&instance->col_res.sel_req,
sizeof(instance->col_res.sel_req));
ret = iso14443_3a_poller_send_standard_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);
if(ret != Iso14443_3aErrorNone) {
FURI_LOG_E(TAG, "Sel request failed: %d", ret);
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) !=
sizeof(instance->col_res.sel_resp)) {
FURI_LOG_E(TAG, "Sel response wrong length");
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer,
&instance->col_res.sel_resp,
sizeof(instance->col_res.sel_resp));
FURI_LOG_T(TAG, "Sel resp: %02X", instance->col_res.sel_resp.sak);
if(instance->col_res.sel_req.nfcid[0] == ISO14443_3A_POLLER_SDD_CL) {
// Copy part of UID
memcpy(
&instance->data->uid[instance->data->uid_len],
&instance->col_res.sel_req.nfcid[1],
3);
instance->data->uid_len += 3;
instance->col_res.cascade_level++;
instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;
} else {
FURI_LOG_T(TAG, "Col resolution complete");
instance->data->sak = instance->col_res.sel_resp.sak;
memcpy(
&instance->data->uid[instance->data->uid_len],
&instance->col_res.sel_req.nfcid[0],
4);
instance->data->uid_len += 4;
instance->col_res.state = Iso14443_3aPollerColResStateStateSuccess;
instance->state = Iso14443_3aPollerStateActivated;
}
}
}
activated = (instance->state == Iso14443_3aPollerStateActivated);
} while(false);
if(activated && iso14443_3a_data) {
*iso14443_3a_data = *instance->data;
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error =
nfc_iso14443a_poller_trx_custom_parity(instance->nfc, tx_buffer, rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_txrx(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_send_standard_frame(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret =
iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
return ret;
}

View File

@@ -0,0 +1,81 @@
#pragma once
#include "iso14443_3a_poller.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U)
#define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl))
#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits)&0x0fU))
#define ISO14443_3A_POLLER_SDD_CL (0x88U)
typedef enum {
Iso14443_3aPollerColResStateStateIdle,
Iso14443_3aPollerColResStateStateNewCascade,
Iso14443_3aPollerColResStateStateSelectCascade,
Iso14443_3aPollerColResStateStateSuccess,
Iso14443_3aPollerColResStateStateFail,
} Iso14443_3aPollerColResState;
typedef struct {
Iso14443_3aPollerColResState state;
Iso14443_3aSensResp sens_resp;
Iso14443_3aSddReq sdd_req;
Iso14443_3aSddResp sdd_resp;
Iso14443_3aSelReq sel_req;
Iso14443_3aSelResp sel_resp;
uint8_t cascade_level;
} Iso14443_3aPollerColRes;
typedef enum {
Iso14443_3aPollerStateIdle,
Iso14443_3aPollerStateColResInProgress,
Iso14443_3aPollerStateColResFailed,
Iso14443_3aPollerStateActivated,
} Iso14443_3aPollerState;
typedef enum {
Iso14443_3aPollerConfigStateIdle,
Iso14443_3aPollerConfigStateDone,
} Iso14443_3aPollerConfigState;
struct Iso14443_3aPoller {
Nfc* nfc;
Iso14443_3aPollerState state;
Iso14443_3aPollerConfigState config_state;
Iso14443_3aPollerColRes col_res;
Iso14443_3aData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
Iso14443_3aPollerEvent iso14443_3a_event;
Iso14443_3aPollerEventData iso14443_3a_event_data;
NfcGenericCallback callback;
void* context;
};
const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_async_activate(
Iso14443_3aPoller* instance,
Iso14443_3aData* iso14443_3a_data);
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,58 @@
#include "iso14443_3a_poller_sync_api.h"
#include "iso14443_3a_poller_i.h"
#include <nfc/nfc_poller.h>
#include <furi/furi.h>
#define ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)
typedef struct {
Iso14443_3aPoller* instance;
FuriThreadId thread_id;
Iso14443_3aError error;
Iso14443_3aData data;
} Iso14443_3aPollerContext;
NfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
Iso14443_3aPollerContext* poller_context = context;
Iso14443_3aPoller* iso14443_3a_poller = event.instance;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
iso14443_3a_copy(&poller_context->data, iso14443_3a_poller->data);
}
poller_context->error = iso14443_3a_event->data->error;
furi_thread_flags_set(poller_context->thread_id, ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);
return NfcCommandStop;
}
Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) {
furi_assert(nfc);
furi_assert(iso14443_3a_data);
Iso14443_3aPollerContext poller_context = {};
poller_context.thread_id = furi_thread_get_current_id();
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a);
nfc_poller_start(poller, iso14443_3a_poller_read_callback, &poller_context);
furi_thread_flags_wait(
ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
furi_thread_flags_clear(ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);
nfc_poller_stop(poller);
nfc_poller_free(poller);
if(poller_context.error == Iso14443_3aErrorNone) {
*iso14443_3a_data = poller_context.data;
}
return poller_context.error;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "iso14443_3a.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data);
#ifdef __cplusplus
}
#endif