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,433 @@
#include "slix_i.h"
#include "slix_device_defs.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define SLIX_PROTOCOL_NAME "SLIX"
#define SLIX_DEVICE_NAME "SLIX"
#define SLIX_TYPE_SLIX_SLIX2 (0x01U)
#define SLIX_TYPE_SLIX_S (0x02U)
#define SLIX_TYPE_SLIX_L (0x03U)
#define SLIX_TYPE_INDICATOR_SLIX (0x02U)
#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U)
#define SLIX_PASSWORD_READ_KEY "Password Read"
#define SLIX_PASSWORD_WRITE_KEY "Password Write"
#define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy"
#define SLIX_PASSWORD_DESTROY_KEY "Password Destroy"
#define SLIX_PASSWORD_EAS_KEY "Password EAS"
#define SLIX_SIGNATURE_KEY "Signature"
#define SLIX_PRIVACY_MODE_KEY "Privacy Mode"
#define SLIX_PROTECTION_POINTER_KEY "Protection Pointer"
#define SLIX_PROTECTION_CONDITION_KEY "Protection Condition"
#define SLIX_LOCK_EAS_KEY "Lock EAS"
#define SLIX_LOCK_PPL_KEY "Lock PPL"
typedef struct {
uint8_t iso15693_3[2];
uint8_t icode_type;
union {
struct {
uint8_t unused_1 : 3;
uint8_t type_indicator : 2;
uint8_t unused_2 : 3;
};
uint8_t serial_num[5];
};
} SlixUidLayout;
const NfcDeviceBase nfc_device_slix = {
.protocol_name = SLIX_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)slix_alloc,
.free = (NfcDeviceFree)slix_free,
.reset = (NfcDeviceReset)slix_reset,
.copy = (NfcDeviceCopy)slix_copy,
.verify = (NfcDeviceVerify)slix_verify,
.load = (NfcDeviceLoad)slix_load,
.save = (NfcDeviceSave)slix_save,
.is_equal = (NfcDeviceEqual)slix_is_equal,
.get_name = (NfcDeviceGetName)slix_get_device_name,
.get_uid = (NfcDeviceGetUid)slix_get_uid,
.set_uid = (NfcDeviceSetUid)slix_set_uid,
.get_base_data = (NfcDeviceGetBaseData)slix_get_base_data,
};
static const char* slix_nfc_device_name[] = {
[SlixTypeSlix] = SLIX_DEVICE_NAME,
[SlixTypeSlixS] = SLIX_DEVICE_NAME "-S",
[SlixTypeSlixL] = SLIX_DEVICE_NAME "-L",
[SlixTypeSlix2] = SLIX_DEVICE_NAME "2",
};
static const SlixTypeFeatures slix_type_features[] = {
[SlixTypeSlix] = SLIX_TYPE_FEATURES_SLIX,
[SlixTypeSlixS] = SLIX_TYPE_FEATURES_SLIX_S,
[SlixTypeSlixL] = SLIX_TYPE_FEATURES_SLIX_L,
[SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2,
};
typedef struct {
const char* key;
SlixTypeFeatures feature_flag;
SlixPassword default_value;
} SlixPasswordConfig;
static const SlixPasswordConfig slix_password_configs[] = {
[SlixPasswordTypeRead] = {SLIX_PASSWORD_READ_KEY, SLIX_TYPE_FEATURE_READ, 0x00000000U},
[SlixPasswordTypeWrite] = {SLIX_PASSWORD_WRITE_KEY, SLIX_TYPE_FEATURE_WRITE, 0x00000000U},
[SlixPasswordTypePrivacy] = {SLIX_PASSWORD_PRIVACY_KEY, SLIX_TYPE_FEATURE_PRIVACY, 0xFFFFFFFFU},
[SlixPasswordTypeDestroy] = {SLIX_PASSWORD_DESTROY_KEY, SLIX_TYPE_FEATURE_DESTROY, 0xFFFFFFFFU},
[SlixPasswordTypeEasAfi] = {SLIX_PASSWORD_EAS_KEY, SLIX_TYPE_FEATURE_EAS, 0x00000000U},
};
static void slix_password_set_defaults(SlixPassword* passwords) {
for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) {
passwords[i] = slix_password_configs[i].default_value;
}
}
SlixData* slix_alloc() {
SlixData* data = malloc(sizeof(SlixData));
data->iso15693_3_data = iso15693_3_alloc();
slix_password_set_defaults(data->passwords);
return data;
}
void slix_free(SlixData* data) {
furi_assert(data);
iso15693_3_free(data->iso15693_3_data);
free(data);
}
void slix_reset(SlixData* data) {
furi_assert(data);
iso15693_3_reset(data->iso15693_3_data);
slix_password_set_defaults(data->passwords);
memset(&data->system_info, 0, sizeof(SlixSystemInfo));
memset(data->signature, 0, sizeof(SlixSignature));
data->privacy = false;
}
void slix_copy(SlixData* data, const SlixData* other) {
furi_assert(data);
furi_assert(other);
iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data);
memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount);
memcpy(data->signature, other->signature, sizeof(SlixSignature));
data->system_info = other->system_info;
data->privacy = other->privacy;
}
bool slix_verify(SlixData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// No backward compatibility, unified format only
return false;
}
static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
bool ret = true;
for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) {
const SlixPasswordConfig* password_config = &slix_password_configs[i];
if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue;
if(!flipper_format_key_exist(ff, password_config->key)) {
passwords[i] = password_config->default_value;
continue;
}
if(!flipper_format_read_hex(
ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) {
ret = false;
break;
}
}
return ret;
}
bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool loaded = false;
do {
if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break;
const SlixType slix_type = slix_get_type(data);
if(slix_type >= SlixTypeCount) break;
if(!slix_load_passwords(data->passwords, slix_type, ff)) break;
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {
if(flipper_format_key_exist(ff, SLIX_SIGNATURE_KEY)) {
if(!flipper_format_read_hex(
ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE))
break;
}
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) {
if(flipper_format_key_exist(ff, SLIX_PRIVACY_MODE_KEY)) {
if(!flipper_format_read_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break;
}
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {
SlixProtection* protection = &data->system_info.protection;
if(flipper_format_key_exist(ff, SLIX_PROTECTION_POINTER_KEY) &&
flipper_format_key_exist(ff, SLIX_PROTECTION_CONDITION_KEY)) {
if(!flipper_format_read_hex(
ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, 1))
break;
if(!flipper_format_read_hex(
ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, 1))
break;
}
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) {
if(flipper_format_key_exist(ff, SLIX_LOCK_EAS_KEY)) {
SlixLockBits* lock_bits = &data->system_info.lock_bits;
if(!flipper_format_read_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break;
}
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {
if(flipper_format_key_exist(ff, SLIX_LOCK_PPL_KEY)) {
SlixLockBits* lock_bits = &data->system_info.lock_bits;
if(!flipper_format_read_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break;
}
}
loaded = true;
} while(false);
return loaded;
}
static bool
slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
bool ret = true;
for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) {
const SlixPasswordConfig* password_config = &slix_password_configs[i];
if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue;
if(!flipper_format_write_hex(
ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) {
ret = false;
break;
}
}
return ret;
}
bool slix_save(const SlixData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
const SlixType slix_type = slix_get_type(data);
if(slix_type >= SlixTypeCount) break;
if(!iso15693_3_save(data->iso15693_3_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME " specific data")) break;
if(!flipper_format_write_comment_cstr(
ff,
"Passwords are optional. If a password is omitted, a default value will be used"))
break;
if(!slix_save_passwords(data->passwords, slix_type, ff)) break;
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {
if(!flipper_format_write_comment_cstr(
ff,
"This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key."))
break;
if(!flipper_format_write_hex(
ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE))
break;
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) {
if(!flipper_format_write_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break;
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {
const SlixProtection* protection = &data->system_info.protection;
if(!flipper_format_write_comment_cstr(ff, "Protection pointer configuration")) break;
if(!flipper_format_write_hex(
ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, sizeof(uint8_t)))
break;
if(!flipper_format_write_hex(
ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, sizeof(uint8_t)))
break;
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS) ||
slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {
if(!flipper_format_write_comment_cstr(ff, "SLIX Lock Bits")) break;
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) {
const SlixLockBits* lock_bits = &data->system_info.lock_bits;
if(!flipper_format_write_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break;
}
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) {
const SlixLockBits* lock_bits = &data->system_info.lock_bits;
if(!flipper_format_write_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break;
}
saved = true;
} while(false);
return saved;
}
bool slix_is_equal(const SlixData* data, const SlixData* other) {
return iso15693_3_is_equal(data->iso15693_3_data, other->iso15693_3_data) &&
memcmp(&data->system_info, &other->system_info, sizeof(SlixSystemInfo)) == 0 &&
memcmp(
data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount) ==
0 &&
memcmp(&data->signature, &other->signature, sizeof(SlixSignature)) == 0 &&
data->privacy == other->privacy;
}
const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type) {
UNUSED(name_type);
const SlixType slix_type = slix_get_type(data);
furi_assert(slix_type < SlixTypeCount);
return slix_nfc_device_name[slix_type];
}
const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len) {
return iso15693_3_get_uid(data->iso15693_3_data, uid_len);
}
bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso15693_3_set_uid(data->iso15693_3_data, uid, uid_len);
}
const Iso15693_3Data* slix_get_base_data(const SlixData* data) {
furi_assert(data);
return data->iso15693_3_data;
}
SlixType slix_get_type(const SlixData* data) {
SlixType type = SlixTypeCount;
do {
if(iso15693_3_get_manufacturer_id(data->iso15693_3_data) != SLIX_NXP_MANUFACTURER_CODE)
break;
const SlixUidLayout* uid = (const SlixUidLayout*)data->iso15693_3_data->uid;
if(uid->icode_type == SLIX_TYPE_SLIX_SLIX2) {
if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX) {
type = SlixTypeSlix;
} else if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX2) {
type = SlixTypeSlix2;
}
} else if(uid->icode_type == SLIX_TYPE_SLIX_S) {
type = SlixTypeSlixS;
} else if(uid->icode_type == SLIX_TYPE_SLIX_L) {
type = SlixTypeSlixL;
}
} while(false);
return type;
}
SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type) {
furi_assert(data);
furi_assert(password_type < SlixPasswordTypeCount);
return data->passwords[password_type];
}
uint16_t slix_get_counter(const SlixData* data) {
furi_assert(data);
const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data(
data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);
return counter->value;
}
bool slix_is_privacy_mode(const SlixData* data) {
furi_assert(data);
return data->privacy;
}
bool slix_is_block_protected(
const SlixData* data,
SlixPasswordType password_type,
uint8_t block_num) {
furi_assert(data);
furi_assert(password_type < SlixPasswordTypeCount);
bool ret = false;
do {
if(password_type != SlixPasswordTypeRead && password_type != SlixPasswordTypeWrite) break;
if(block_num >= iso15693_3_get_block_count(data->iso15693_3_data)) break;
if(block_num == SLIX_COUNTER_BLOCK_NUM) break;
const bool high = block_num >= data->system_info.protection.pointer;
const bool read = password_type == SlixPasswordTypeRead;
const uint8_t condition = high ? (read ? SLIX_PP_CONDITION_RH : SLIX_PP_CONDITION_WH) :
(read ? SLIX_PP_CONDITION_RL : SLIX_PP_CONDITION_WL);
ret = data->system_info.protection.condition & condition;
} while(false);
return ret;
}
bool slix_is_counter_increment_protected(const SlixData* data) {
furi_assert(data);
const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data(
data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);
return counter->protection != 0;
}
bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features) {
furi_assert(slix_type < SlixTypeCount);
return (slix_type_features[slix_type] & features) == features;
}
bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type) {
furi_assert(slix_type < SlixTypeCount);
furi_assert(password_type < SlixPasswordTypeCount);
return slix_type_features[slix_type] & slix_password_configs[password_type].feature_flag;
}

View File

@@ -0,0 +1,146 @@
#pragma once
#include <nfc/protocols/iso15693_3/iso15693_3.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SLIX_BLOCK_SIZE (4U)
#define SLIX_SIGNATURE_SIZE (32U)
#define SLIX_COUNTER_BLOCK_NUM (79U)
#define SLIX_PP_CONDITION_RL (1U << 0)
#define SLIX_PP_CONDITION_WL (1U << 1)
#define SLIX_PP_CONDITION_RH (1U << 4)
#define SLIX_PP_CONDITION_WH (1U << 5)
#define SLIX_FEATURE_FLAG_UM_PP (1UL << 0)
#define SLIX_FEATURE_FLAG_COUNTER (1UL << 1)
#define SLIX_FEATURE_FLAG_EAS_ID (1UL << 2)
#define SLIX_FEATURE_FLAG_EAS_PP (1UL << 3)
#define SLIX_FEATURE_FLAG_AFI_PP (1UL << 4)
#define SLIX_FEATURE_FLAG_INVENTORY_READ_EXT (1UL << 5)
#define SLIX_FEATURE_FLAG_EAS_IR (1UL << 6)
#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG (1UL << 8)
#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG_PP (1UL << 9)
#define SLIX_FEATURE_FLAG_PERSISTENT_QUIET (1UL << 10)
#define SLIX_FEATURE_FLAG_PRIVACY (1UL << 12)
#define SLIX_FEATURE_FLAG_DESTROY (1UL << 13)
#define SLIX_FEATURE_EXT (1UL << 31)
#define SLIX_TYPE_FEATURE_READ (1U << 0)
#define SLIX_TYPE_FEATURE_WRITE (1U << 1)
#define SLIX_TYPE_FEATURE_PRIVACY (1U << 2)
#define SLIX_TYPE_FEATURE_DESTROY (1U << 3)
#define SLIX_TYPE_FEATURE_EAS (1U << 4)
#define SLIX_TYPE_FEATURE_SIGNATURE (1U << 5)
#define SLIX_TYPE_FEATURE_PROTECTION (1U << 6)
typedef uint32_t SlixTypeFeatures;
typedef enum {
SlixErrorNone,
SlixErrorTimeout,
SlixErrorFormat,
SlixErrorNotSupported,
SlixErrorInternal,
SlixErrorWrongPassword,
SlixErrorUidMismatch,
SlixErrorUnknown,
} SlixError;
typedef enum {
SlixTypeSlix,
SlixTypeSlixS,
SlixTypeSlixL,
SlixTypeSlix2,
SlixTypeCount,
} SlixType;
typedef enum {
SlixPasswordTypeRead,
SlixPasswordTypeWrite,
SlixPasswordTypePrivacy,
SlixPasswordTypeDestroy,
SlixPasswordTypeEasAfi,
SlixPasswordTypeCount,
} SlixPasswordType;
typedef uint32_t SlixPassword;
typedef uint8_t SlixSignature[SLIX_SIGNATURE_SIZE];
typedef bool SlixPrivacy;
typedef struct {
uint8_t pointer;
uint8_t condition;
} SlixProtection;
typedef struct {
bool eas;
bool ppl;
} SlixLockBits;
typedef struct {
SlixProtection protection;
SlixLockBits lock_bits;
} SlixSystemInfo;
typedef struct {
Iso15693_3Data* iso15693_3_data;
SlixSystemInfo system_info;
SlixSignature signature;
SlixPassword passwords[SlixPasswordTypeCount];
SlixPrivacy privacy;
} SlixData;
SlixData* slix_alloc();
void slix_free(SlixData* data);
void slix_reset(SlixData* data);
void slix_copy(SlixData* data, const SlixData* other);
bool slix_verify(SlixData* data, const FuriString* device_type);
bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version);
bool slix_save(const SlixData* data, FlipperFormat* ff);
bool slix_is_equal(const SlixData* data, const SlixData* other);
const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type);
const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len);
bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len);
const Iso15693_3Data* slix_get_base_data(const SlixData* data);
// Getters and tests
SlixType slix_get_type(const SlixData* data);
SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type);
uint16_t slix_get_counter(const SlixData* data);
bool slix_is_privacy_mode(const SlixData* data);
bool slix_is_block_protected(
const SlixData* data,
SlixPasswordType password_type,
uint8_t block_num);
bool slix_is_counter_increment_protected(const SlixData* data);
// Static methods
bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features);
bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type);
#ifdef __cplusplus
}
#endif

View File

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

View File

@@ -0,0 +1,127 @@
#include "slix_i.h"
#include <nfc/protocols/iso15693_3/iso15693_3_i.h>
bool slix_error_response_parse(SlixError* error, const BitBuffer* buf) {
Iso15693_3Error iso15693_3_error;
const bool ret = iso15693_3_error_response_parse(&iso15693_3_error, buf);
if(ret) {
*error = slix_process_iso15693_3_error(iso15693_3_error);
}
return ret;
}
SlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error) {
switch(iso15693_3_error) {
case Iso15693_3ErrorNone:
return SlixErrorNone;
case Iso15693_3ErrorTimeout:
return SlixErrorTimeout;
case Iso15693_3ErrorFormat:
return SlixErrorFormat;
case Iso15693_3ErrorInternal:
return SlixErrorInternal;
default:
return SlixErrorUnknown;
}
}
SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf) {
furi_assert(data);
SlixError error = SlixErrorNone;
do {
if(slix_error_response_parse(&error, buf)) break;
typedef struct {
uint8_t flags;
uint8_t pp_pointer;
uint8_t pp_condition;
uint8_t lock_bits;
uint32_t feature_flags;
} SlixGetNxpSystemInfoResponseLayout;
const size_t size_received = bit_buffer_get_size_bytes(buf);
const size_t size_required = sizeof(SlixGetNxpSystemInfoResponseLayout);
if(size_received != size_required) {
error = SlixErrorFormat;
break;
}
const SlixGetNxpSystemInfoResponseLayout* response =
(const SlixGetNxpSystemInfoResponseLayout*)bit_buffer_get_data(buf);
SlixProtection* protection = &data->system_info.protection;
protection->pointer = response->pp_pointer;
protection->condition = response->pp_condition;
Iso15693_3LockBits* iso15693_3_lock_bits = &data->iso15693_3_data->settings.lock_bits;
iso15693_3_lock_bits->dsfid = response->lock_bits & SLIX_LOCK_BITS_DSFID;
iso15693_3_lock_bits->afi = response->lock_bits & SLIX_LOCK_BITS_AFI;
SlixLockBits* slix_lock_bits = &data->system_info.lock_bits;
slix_lock_bits->eas = response->lock_bits & SLIX_LOCK_BITS_EAS;
slix_lock_bits->ppl = response->lock_bits & SLIX_LOCK_BITS_PPL;
} while(false);
return error;
}
SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf) {
SlixError error = SlixErrorNone;
do {
if(slix_error_response_parse(&error, buf)) break;
typedef struct {
uint8_t flags;
uint8_t signature[SLIX_SIGNATURE_SIZE];
} SlixReadSignatureResponseLayout;
const size_t size_received = bit_buffer_get_size_bytes(buf);
const size_t size_required = sizeof(SlixReadSignatureResponseLayout);
if(size_received != size_required) {
error = SlixErrorFormat;
break;
}
const SlixReadSignatureResponseLayout* response =
(const SlixReadSignatureResponseLayout*)bit_buffer_get_data(buf);
memcpy(data, response->signature, sizeof(SlixSignature));
} while(false);
return error;
}
void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password) {
furi_assert(data);
furi_assert(password_type < SlixPasswordTypeCount);
data->passwords[password_type] = password;
}
void slix_set_privacy_mode(SlixData* data, bool set) {
furi_assert(data);
data->privacy = set;
}
void slix_increment_counter(SlixData* data) {
furi_assert(data);
const uint8_t* block_data =
iso15693_3_get_block_data(data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);
SlixCounter counter;
memcpy(counter.bytes, block_data, SLIX_BLOCK_SIZE);
counter.value += 1;
iso15693_3_set_block_data(
data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM, counter.bytes, sizeof(SlixCounter));
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include "slix.h"
#include <nfc/protocols/iso15693_3/iso15693_3_i.h>
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SLIX_NXP_MANUFACTURER_CODE (0x04U)
#define SLIX_LOCK_BITS_AFI (1U << 0)
#define SLIX_LOCK_BITS_EAS (1U << 1)
#define SLIX_LOCK_BITS_DSFID (1U << 2)
#define SLIX_LOCK_BITS_PPL (1U << 3)
#define SLIX_CMD_CUSTOM_START (0xA2U)
#define SLIX_CMD_SET_EAS (0xA2U)
#define SLIX_CMD_RESET_EAS (0xA3U)
#define SLIX_CMD_LOCK_EAS (0xA4U)
#define SLIX_CMD_EAS_ALARM (0xA5U)
#define SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U)
#define SLIX_CMD_WRITE_EAS_ID (0xA7U)
#define SLIX_CMD_GET_NXP_SYSTEM_INFORMATION (0xABU)
#define SLIX_CMD_INVENTORY_PAGE_READ (0xB0U)
#define SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U)
#define SLIX_CMD_GET_RANDOM_NUMBER (0xB2U)
#define SLIX_CMD_SET_PASSWORD (0xB3U)
#define SLIX_CMD_WRITE_PASSWORD (0xB4U)
#define SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U)
#define SLIX_CMD_PROTECT_PAGE (0xB6U)
#define SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U)
#define SLIX_CMD_DESTROY (0xB9U)
#define SLIX_CMD_ENABLE_PRIVACY (0xBAU)
#define SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU)
#define SLIX_CMD_READ_SIGNATURE (0xBDU)
#define SLIX_CMD_CUSTOM_END (0xBEU)
#define SLIX_CMD_CUSTOM_COUNT (SLIX_CMD_CUSTOM_END - SLIX_CMD_CUSTOM_START)
#define SLIX_TYPE_FEATURES_SLIX (SLIX_TYPE_FEATURE_EAS)
#define SLIX_TYPE_FEATURES_SLIX_S \
(SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \
SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS)
#define SLIX_TYPE_FEATURES_SLIX_L \
(SLIX_TYPE_FEATURE_PRIVACY | SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS)
#define SLIX_TYPE_FEATURES_SLIX2 \
(SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \
SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS | SLIX_TYPE_FEATURE_SIGNATURE | \
SLIX_TYPE_FEATURE_PROTECTION)
#define SLIX2_FEATURE_FLAGS \
(SLIX_FEATURE_FLAG_UM_PP | SLIX_FEATURE_FLAG_COUNTER | SLIX_FEATURE_FLAG_EAS_ID | \
SLIX_FEATURE_FLAG_EAS_PP | SLIX_FEATURE_FLAG_AFI_PP | SLIX_FEATURE_FLAG_INVENTORY_READ_EXT | \
SLIX_FEATURE_FLAG_EAS_IR | SLIX_FEATURE_FLAG_ORIGINALITY_SIG | \
SLIX_FEATURE_FLAG_PERSISTENT_QUIET | SLIX_FEATURE_FLAG_PRIVACY | SLIX_FEATURE_FLAG_DESTROY)
typedef union {
struct {
uint16_t value;
uint8_t reserved;
uint8_t protection;
};
uint8_t bytes[SLIX_BLOCK_SIZE];
} SlixCounter;
// Same behaviour as iso15693_3_error_response_parse
bool slix_error_response_parse(SlixError* error, const BitBuffer* buf);
SlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error);
SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf);
SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf);
// Setters
void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password);
void slix_set_privacy_mode(SlixData* data, bool set);
void slix_increment_counter(SlixData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,79 @@
#include "slix_listener_i.h"
#include <furi.h>
#include <nfc/protocols/nfc_listener_base.h>
#define TAG "SlixListener"
#define SLIX_LISTENER_BUF_SIZE (64U)
static SlixListener* slix_listener_alloc(Iso15693_3Listener* iso15693_3_listener, SlixData* data) {
furi_assert(iso15693_3_listener);
SlixListener* instance = malloc(sizeof(SlixListener));
instance->iso15693_3_listener = iso15693_3_listener;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(SLIX_LISTENER_BUF_SIZE);
instance->slix_event.data = &instance->slix_event_data;
instance->generic_event.protocol = NfcProtocolSlix;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->slix_event;
slix_listener_init_iso15693_3_extensions(instance);
return instance;
}
static void slix_listener_free(SlixListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
static void
slix_listener_set_callback(SlixListener* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
static const SlixData* slix_listener_get_data(SlixListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static NfcCommand slix_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolIso15693_3);
furi_assert(event.event_data);
SlixListener* instance = context;
Iso15693_3ListenerEvent* iso15693_3_event = event.event_data;
BitBuffer* rx_buffer = iso15693_3_event->data->buffer;
NfcCommand command = NfcCommandContinue;
if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) {
const SlixError error = slix_listener_process_request(instance, rx_buffer);
if(error == SlixErrorWrongPassword) {
command = NfcCommandStop;
}
}
return command;
}
const NfcListenerBase nfc_listener_slix = {
.alloc = (NfcListenerAlloc)slix_listener_alloc,
.free = (NfcListenerFree)slix_listener_free,
.set_callback = (NfcListenerSetCallback)slix_listener_set_callback,
.get_data = (NfcListenerGetData)slix_listener_get_data,
.run = (NfcListenerRun)slix_listener_run,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <lib/nfc/protocols/iso15693_3/iso15693_3_listener.h>
#include "slix.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SlixListener SlixListener;
typedef enum {
SlixListenerEventTypeFieldOff,
SlixListenerEventTypeCustomCommand,
} SlixListenerEventType;
typedef struct {
BitBuffer* buffer;
} SlixListenerEventData;
typedef struct {
SlixListenerEventType type;
SlixListenerEventData* data;
} SlixListenerEvent;
#ifdef __cplusplus
}
#endif

View File

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

View File

@@ -0,0 +1,617 @@
#include "slix_listener_i.h"
#include <nfc/protocols/iso15693_3/iso15693_3_listener_i.h>
#include <furi_hal_random.h>
#define TAG "SlixListener"
typedef SlixError (*SlixRequestHandler)(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags);
// Helper functions
static bool
slix_listener_is_password_lock_enabled(SlixListener* instance, SlixPasswordType password_type) {
return !instance->session_state.password_match[password_type];
}
static SlixPasswordType slix_listener_get_password_type_by_id(uint8_t id) {
uint32_t type;
for(type = 0; type < SlixPasswordTypeCount; ++type) {
if(id >> type == 0x01U) break;
}
return type;
}
static SlixPassword
slix_listener_unxor_password(const SlixPassword password_xored, uint16_t random) {
return password_xored ^ ((SlixPassword)random << 16 | random);
}
static SlixError slix_listener_set_password(
SlixListener* instance,
SlixPasswordType password_type,
SlixPassword password) {
SlixError error = SlixErrorNone;
do {
if(password_type >= SlixPasswordTypeCount) {
error = SlixErrorInternal;
break;
}
SlixData* slix_data = instance->data;
if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) {
error = SlixErrorNotSupported;
break;
}
SlixListenerSessionState* session_state = &instance->session_state;
session_state->password_match[password_type] =
(password == slix_get_password(slix_data, password_type));
if(!session_state->password_match[password_type]) {
error = SlixErrorWrongPassword;
break;
}
} while(false);
return error;
}
static SlixError slix_listener_write_password(
SlixListener* instance,
SlixPasswordType password_type,
SlixPassword password) {
SlixError error = SlixErrorNone;
do {
if(password_type >= SlixPasswordTypeCount) {
error = SlixErrorInternal;
break;
}
SlixData* slix_data = instance->data;
if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) {
error = SlixErrorNotSupported;
break;
}
SlixListenerSessionState* session_state = &instance->session_state;
if(session_state->password_match[password_type]) {
// TODO FL-3634: check for password lock
slix_set_password(slix_data, password_type, password);
// Require another SET_PASSWORD command with the new password
session_state->password_match[password_type] = false;
} else {
error = SlixErrorWrongPassword;
break;
}
} while(false);
return error;
}
// Custom SLIX request handlers
static SlixError slix_listener_default_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(instance);
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
// Empty placeholder handler
return SlixErrorNotSupported;
}
static SlixError slix_listener_get_nxp_system_info_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
const SlixData* slix_data = instance->data;
const Iso15693_3Data* iso15693_data = instance->data->iso15693_3_data;
const SlixProtection* protection = &slix_data->system_info.protection;
bit_buffer_append_byte(instance->tx_buffer, protection->pointer);
bit_buffer_append_byte(instance->tx_buffer, protection->condition);
uint8_t lock_bits = 0;
if(iso15693_data->settings.lock_bits.dsfid) {
lock_bits |= SLIX_LOCK_BITS_DSFID;
}
if(iso15693_data->settings.lock_bits.afi) {
lock_bits |= SLIX_LOCK_BITS_AFI;
}
if(slix_data->system_info.lock_bits.eas) {
lock_bits |= SLIX_LOCK_BITS_EAS;
}
if(slix_data->system_info.lock_bits.ppl) {
lock_bits |= SLIX_LOCK_BITS_PPL;
}
bit_buffer_append_byte(instance->tx_buffer, lock_bits);
const uint32_t feature_flags = SLIX2_FEATURE_FLAGS;
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&feature_flags, sizeof(uint32_t));
return SlixErrorNone;
}
static SlixError slix_listener_get_random_number_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
SlixListenerSessionState* session_state = &instance->session_state;
session_state->random = furi_hal_random_get();
bit_buffer_append_bytes(
instance->tx_buffer, (uint8_t*)&session_state->random, sizeof(uint16_t));
return SlixErrorNone;
}
static SlixError slix_listener_set_password_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(flags);
SlixError error = SlixErrorNone;
do {
#pragma pack(push, 1)
typedef struct {
uint8_t password_id;
SlixPassword password_xored;
} SlixSetPasswordRequestLayout;
#pragma pack(pop)
if(data_size != sizeof(SlixSetPasswordRequestLayout)) {
error = SlixErrorFormat;
break;
}
const SlixSetPasswordRequestLayout* request = (const SlixSetPasswordRequestLayout*)data;
const SlixPasswordType password_type =
slix_listener_get_password_type_by_id(request->password_id);
const SlixPassword password_received =
slix_listener_unxor_password(request->password_xored, instance->session_state.random);
error = slix_listener_set_password(instance, password_type, password_received);
if(error != SlixErrorNone) break;
if(password_type == SlixPasswordTypePrivacy) {
slix_set_privacy_mode(instance->data, false);
}
} while(false);
return error;
}
static SlixError slix_listener_write_password_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(flags);
SlixError error = SlixErrorNone;
do {
#pragma pack(push, 1)
typedef struct {
uint8_t password_id;
SlixPassword password;
} SlixWritePasswordRequestLayout;
#pragma pack(pop)
if(data_size != sizeof(SlixWritePasswordRequestLayout)) {
error = SlixErrorFormat;
break;
}
const SlixWritePasswordRequestLayout* request =
(const SlixWritePasswordRequestLayout*)data;
const SlixPasswordType password_type =
slix_listener_get_password_type_by_id(request->password_id);
error = slix_listener_write_password(instance, password_type, request->password);
if(error != SlixErrorNone) break;
} while(false);
return error;
}
static SlixError slix_listener_protect_page_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(flags);
SlixError error = SlixErrorNone;
do {
typedef struct {
uint8_t pointer;
uint8_t condition;
} SlixProtectPageRequestLayout;
if(data_size != sizeof(SlixProtectPageRequestLayout)) {
error = SlixErrorFormat;
break;
}
SlixData* slix_data = instance->data;
if(slix_data->system_info.lock_bits.ppl) {
error = SlixErrorInternal;
break;
}
const SlixListenerSessionState* session_state = &instance->session_state;
if(!session_state->password_match[SlixPasswordTypeRead] ||
!session_state->password_match[SlixPasswordTypeWrite]) {
error = SlixErrorInternal;
break;
}
const SlixProtectPageRequestLayout* request = (const SlixProtectPageRequestLayout*)data;
if(request->pointer >= SLIX_COUNTER_BLOCK_NUM) {
error = SlixErrorInternal;
break;
}
SlixProtection* protection = &slix_data->system_info.protection;
protection->pointer = request->pointer;
protection->condition = request->condition;
} while(false);
return error;
}
static SlixError slix_listener_enable_privacy_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(flags);
SlixError error = SlixErrorNone;
do {
typedef struct {
SlixPassword password_xored;
} SlixEnablePrivacyRequestLayout;
if(data_size != sizeof(SlixEnablePrivacyRequestLayout)) {
error = SlixErrorFormat;
break;
}
const SlixEnablePrivacyRequestLayout* request =
(const SlixEnablePrivacyRequestLayout*)data;
const SlixPassword password_received =
slix_listener_unxor_password(request->password_xored, instance->session_state.random);
error = slix_listener_set_password(instance, SlixPasswordTypePrivacy, password_received);
if(error != SlixErrorNone) break;
slix_set_privacy_mode(instance->data, true);
} while(false);
return error;
}
static SlixError slix_listener_read_signature_handler(
SlixListener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
bit_buffer_append_bytes(instance->tx_buffer, instance->data->signature, sizeof(SlixSignature));
return SlixErrorNone;
}
// Custom SLIX commands handler table
static const SlixRequestHandler slix_request_handler_table[SLIX_CMD_CUSTOM_COUNT] = {
slix_listener_default_handler, // SLIX_CMD_SET_EAS (0xA2U)
slix_listener_default_handler, // SLIX_CMD_RESET_EAS (0xA3U)
slix_listener_default_handler, // SLIX_CMD_LOCK_EAS (0xA4U)
slix_listener_default_handler, // SLIX_CMD_EAS_ALARM (0xA5U)
slix_listener_default_handler, // SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U)
slix_listener_default_handler, // SLIX_CMD_WRITE_EAS_ID (0xA7U)
slix_listener_default_handler, // UNUSED (0xA8U)
slix_listener_default_handler, // UNUSED (0xA9U)
slix_listener_default_handler, // UNUSED (0xAAU)
slix_listener_get_nxp_system_info_handler,
slix_listener_default_handler, // UNUSED (0xACU)
slix_listener_default_handler, // UNUSED (0xADU)
slix_listener_default_handler, // UNUSED (0xAEU)
slix_listener_default_handler, // UNUSED (0xAFU)
slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ (0xB0U)
slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U)
slix_listener_get_random_number_handler,
slix_listener_set_password_handler,
slix_listener_write_password_handler,
slix_listener_default_handler, // SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U)
slix_listener_protect_page_handler,
slix_listener_default_handler, // SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U)
slix_listener_default_handler, // UNUSED (0xB8U)
slix_listener_default_handler, // SLIX_CMD_DESTROY (0xB9U)
slix_listener_enable_privacy_handler,
slix_listener_default_handler, // UNUSED (0xBBU)
slix_listener_default_handler, // SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU)
slix_listener_read_signature_handler,
};
// ISO15693-3 Protocol extension handlers
static Iso15693_3Error
slix_listener_iso15693_3_inventory_extension_handler(SlixListener* instance, va_list args) {
UNUSED(args);
return instance->data->privacy ? Iso15693_3ErrorIgnore : Iso15693_3ErrorNone;
}
static Iso15693_3Error
slix_iso15693_3_read_block_extension_handler(SlixListener* instance, va_list args) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
const uint32_t block_num = va_arg(args, uint32_t);
// SLIX Counter has no read protection
if(block_num == SLIX_COUNTER_BLOCK_NUM) break;
if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) {
if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {
error = Iso15693_3ErrorInternal;
break;
}
}
} while(false);
return error;
}
static Iso15693_3Error
slix_listener_iso15693_3_write_block_extension_handler(SlixListener* instance, va_list args) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
const uint32_t block_num = va_arg(args, uint32_t);
if(block_num == SLIX_COUNTER_BLOCK_NUM) {
const uint32_t counter = *(va_arg(args, uint32_t*));
if(counter == 0x00000001U) {
if(slix_is_counter_increment_protected(instance->data) &&
slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {
error = Iso15693_3ErrorInternal;
break;
}
slix_increment_counter(instance->data);
error = Iso15693_3ErrorFullyHandled;
break;
}
} else if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) {
if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {
error = Iso15693_3ErrorInternal;
break;
}
}
if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) {
if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) {
error = Iso15693_3ErrorInternal;
break;
}
}
} while(false);
return error;
}
static Iso15693_3Error
slix_listener_iso15693_3_lock_block_extension_handler(SlixListener* instance, va_list args) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
const uint32_t block_num = va_arg(args, uint32_t);
// SLIX counter cannot be locked
if(block_num == SLIX_COUNTER_BLOCK_NUM) {
error = Iso15693_3ErrorInternal;
break;
}
if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) {
if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {
error = Iso15693_3ErrorInternal;
break;
}
}
if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) {
if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) {
error = Iso15693_3ErrorInternal;
break;
}
}
} while(false);
return error;
}
static Iso15693_3Error slix_listener_iso15693_3_read_multi_block_extension_handler(
SlixListener* instance,
va_list args) {
Iso15693_3Error error = Iso15693_3ErrorNone;
const uint32_t block_index_start = va_arg(args, uint32_t);
const uint32_t block_index_end = va_arg(args, uint32_t);
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
// SLIX Counter has no read protection
if(i == SLIX_COUNTER_BLOCK_NUM) continue;
if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, i)) {
if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) {
error = Iso15693_3ErrorInternal;
break;
}
}
}
return error;
}
static Iso15693_3Error slix_listener_iso15693_3_write_multi_block_extension_handler(
SlixListener* instance,
va_list args) {
UNUSED(instance);
UNUSED(args);
// No mention of this command in SLIX docs, assuming not supported
return Iso15693_3ErrorNotSupported;
}
static Iso15693_3Error slix_listener_iso15693_3_write_lock_afi_extension_handler(
SlixListener* instance,
va_list args) {
UNUSED(args);
return slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeEasAfi) ?
Iso15693_3ErrorInternal :
Iso15693_3ErrorNone;
}
// Extended ISO15693-3 standard commands handler table (NULL = no extension)
static const Iso15693_3ExtensionHandlerTable slix_iso15693_extension_handler_table = {
.mandatory =
{
(Iso15693_3ExtensionHandler)slix_listener_iso15693_3_inventory_extension_handler,
(Iso15693_3ExtensionHandler)NULL // ISO15693_3_CMD_STAY_QUIET (0x02U)
},
.optional =
{
(Iso15693_3ExtensionHandler)slix_iso15693_3_read_block_extension_handler,
(Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_block_extension_handler,
(Iso15693_3ExtensionHandler)slix_listener_iso15693_3_lock_block_extension_handler,
(Iso15693_3ExtensionHandler)slix_listener_iso15693_3_read_multi_block_extension_handler,
(Iso15693_3ExtensionHandler)
slix_listener_iso15693_3_write_multi_block_extension_handler,
(Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_SELECT (0x25U)
(Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_RESET_TO_READY (0x26U)
(Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler,
(Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler,
(Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_WRITE_DSFID (0x29U)
(Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_LOCK_DSFID (0x2AU)
(Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_SYS_INFO (0x2BU)
(Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU)
},
};
SlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance) {
iso15693_3_listener_set_extension_handler_table(
instance->iso15693_3_listener, &slix_iso15693_extension_handler_table, instance);
return SlixErrorNone;
}
SlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer) {
SlixError error = SlixErrorNone;
do {
typedef struct {
uint8_t flags;
uint8_t command;
uint8_t manufacturer;
uint8_t data[];
} SlixRequestLayout;
const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer);
if(buf_size < sizeof(SlixRequestLayout)) {
error = SlixErrorFormat;
break;
}
const SlixRequestLayout* request =
(const SlixRequestLayout*)bit_buffer_get_data(rx_buffer);
const bool addressed_mode = instance->iso15693_3_listener->session_state.addressed;
const size_t uid_field_size = addressed_mode ? ISO15693_3_UID_SIZE : 0;
const size_t buf_size_min = sizeof(SlixRequestLayout) + uid_field_size;
if(buf_size < buf_size_min) {
error = SlixErrorFormat;
break;
}
if(addressed_mode) {
if(!iso15693_3_is_equal_uid(instance->data->iso15693_3_data, request->data)) {
error = SlixErrorUidMismatch;
break;
}
}
const uint8_t command = request->command;
const bool is_valid_slix_command = command >= SLIX_CMD_CUSTOM_START &&
command < SLIX_CMD_CUSTOM_END;
if(!is_valid_slix_command) {
error = SlixErrorNotSupported;
break;
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE);
const uint8_t* request_data = &request->data[uid_field_size];
const size_t request_data_size = buf_size - buf_size_min;
SlixRequestHandler handler = slix_request_handler_table[command - SLIX_CMD_CUSTOM_START];
error = handler(instance, request_data, request_data_size, request->flags);
// It's a trick! Send no reply.
if(error == SlixErrorFormat || error == SlixErrorWrongPassword ||
error == SlixErrorNotSupported)
break;
if(error != SlixErrorNone) {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN);
}
const Iso15693_3Error iso15693_error =
iso15693_3_listener_send_frame(instance->iso15693_3_listener, instance->tx_buffer);
error = slix_process_iso15693_3_error(iso15693_error);
} while(false);
return error;
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <nfc/protocols/nfc_generic_event.h>
#include "slix_listener.h"
#include "slix_i.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint16_t random;
bool password_match[SlixPasswordTypeCount];
} SlixListenerSessionState;
struct SlixListener {
Iso15693_3Listener* iso15693_3_listener;
SlixData* data;
SlixListenerSessionState session_state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
SlixListenerEvent slix_event;
SlixListenerEventData slix_event_data;
NfcGenericCallback callback;
void* context;
};
SlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance);
SlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,159 @@
#include "slix_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "SlixPoller"
#define SLIX_POLLER_BUF_SIZE (64U)
typedef NfcCommand (*SlixPollerStateHandler)(SlixPoller* instance);
const SlixData* slix_poller_get_data(SlixPoller* instance) {
furi_assert(instance);
return instance->data;
}
static SlixPoller* slix_poller_alloc(Iso15693_3Poller* iso15693_3_poller) {
SlixPoller* instance = malloc(sizeof(SlixPoller));
instance->iso15693_3_poller = iso15693_3_poller;
instance->data = slix_alloc();
instance->tx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE);
instance->rx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE);
instance->slix_event.data = &instance->slix_event_data;
instance->general_event.protocol = NfcProtocolSlix;
instance->general_event.event_data = &instance->slix_event;
instance->general_event.instance = instance;
return instance;
}
static void slix_poller_free(SlixPoller* instance) {
furi_assert(instance);
slix_free(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
free(instance);
}
static NfcCommand slix_poller_handler_idle(SlixPoller* instance) {
iso15693_3_copy(
instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller));
instance->poller_state = SlixPollerStateGetNxpSysInfo;
return NfcCommandContinue;
}
static NfcCommand slix_poller_handler_get_nfc_system_info(SlixPoller* instance) {
instance->error =
slix_poller_async_get_nxp_system_info(instance, &instance->data->system_info);
if(instance->error == SlixErrorNone) {
instance->poller_state = SlixPollerStateReadSignature;
} else {
instance->poller_state = SlixPollerStateError;
}
return NfcCommandContinue;
}
static NfcCommand slix_poller_handler_read_signature(SlixPoller* instance) {
instance->error = slix_poller_async_read_signature(instance, &instance->data->signature);
if(instance->error == SlixErrorNone) {
instance->poller_state = SlixPollerStateReady;
} else {
instance->poller_state = SlixPollerStateError;
}
return NfcCommandContinue;
}
static NfcCommand slix_poller_handler_error(SlixPoller* instance) {
instance->slix_event_data.error = instance->error;
instance->slix_event.type = SlixPollerEventTypeError;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->poller_state = SlixPollerStateIdle;
return command;
}
static NfcCommand slix_poller_handler_ready(SlixPoller* instance) {
instance->slix_event.type = SlixPollerEventTypeReady;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const SlixPollerStateHandler slix_poller_state_handler[SlixPollerStateNum] = {
[SlixPollerStateIdle] = slix_poller_handler_idle,
[SlixPollerStateError] = slix_poller_handler_error,
[SlixPollerStateGetNxpSysInfo] = slix_poller_handler_get_nfc_system_info,
[SlixPollerStateReadSignature] = slix_poller_handler_read_signature,
[SlixPollerStateReady] = slix_poller_handler_ready,
};
static void
slix_poller_set_callback(SlixPoller* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand slix_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso15693_3);
SlixPoller* instance = context;
furi_assert(instance);
furi_assert(instance->callback);
Iso15693_3PollerEvent* iso15693_3_event = event.event_data;
furi_assert(iso15693_3_event);
NfcCommand command = NfcCommandContinue;
if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {
command = slix_poller_state_handler[instance->poller_state](instance);
} else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) {
instance->slix_event.type = SlixPollerEventTypeError;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
static bool slix_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso15693_3);
SlixPoller* instance = context;
furi_assert(instance);
const Iso15693_3PollerEvent* iso15693_3_event = event.event_data;
furi_assert(iso15693_3_event);
iso15693_3_copy(
instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller));
bool protocol_detected = false;
if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) {
if(slix_get_type(instance->data) < SlixTypeCount) {
SlixSystemInfo system_info = {};
SlixError error = slix_poller_async_get_nxp_system_info(instance, &system_info);
protocol_detected = (error == SlixErrorNone);
}
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_slix = {
.alloc = (NfcPollerAlloc)slix_poller_alloc,
.free = (NfcPollerFree)slix_poller_free,
.set_callback = (NfcPollerSetCallback)slix_poller_set_callback,
.run = (NfcPollerRun)slix_poller_run,
.detect = (NfcPollerDetect)slix_poller_detect,
.get_data = (NfcPollerGetData)slix_poller_get_data,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include "slix.h"
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SlixPoller SlixPoller;
typedef enum {
SlixPollerEventTypeError,
SlixPollerEventTypeReady,
} SlixPollerEventType;
typedef struct {
SlixError error;
} SlixPollerEventData;
typedef struct {
SlixPollerEventType type;
SlixPollerEventData* data;
} SlixPollerEvent;
#ifdef __cplusplus
}
#endif

View File

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

View File

@@ -0,0 +1,69 @@
#include "slix_poller_i.h"
#include <furi.h>
#include "slix_i.h"
#define TAG "SlixPoller"
static void slix_poller_prepare_request(SlixPoller* instance, uint8_t command) {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
bit_buffer_append_byte(
instance->tx_buffer,
ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI |
ISO15693_3_REQ_FLAG_T4_ADDRESSED);
bit_buffer_append_byte(instance->tx_buffer, command);
bit_buffer_append_byte(instance->tx_buffer, SLIX_NXP_MANUFACTURER_CODE);
iso15693_3_append_uid(instance->data->iso15693_3_data, instance->tx_buffer);
}
SlixError slix_poller_send_frame(
SlixPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
const Iso15693_3Error iso15693_3_error =
iso15693_3_poller_send_frame(instance->iso15693_3_poller, tx_buffer, rx_buffer, fwt);
return slix_process_iso15693_3_error(iso15693_3_error);
}
SlixError slix_poller_async_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data) {
furi_assert(instance);
furi_assert(data);
slix_poller_prepare_request(instance, SLIX_CMD_GET_NXP_SYSTEM_INFORMATION);
SlixError error = SlixErrorNone;
do {
error = slix_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
if(error != SlixErrorNone) break;
error = slix_get_nxp_system_info_response_parse(instance->data, instance->rx_buffer);
} while(false);
return error;
}
SlixError slix_poller_async_read_signature(SlixPoller* instance, SlixSignature* data) {
furi_assert(instance);
furi_assert(data);
slix_poller_prepare_request(instance, SLIX_CMD_READ_SIGNATURE);
SlixError error = SlixErrorNone;
do {
error = slix_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC * 2);
if(error != SlixErrorNone) break;
error = slix_read_signature_response_parse(instance->data->signature, instance->rx_buffer);
} while(false);
return error;
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>
#include "slix_poller.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SlixPollerStateIdle,
SlixPollerStateGetNxpSysInfo,
SlixPollerStateReadSignature,
SlixPollerStateReady,
SlixPollerStateError,
SlixPollerStateNum,
} SlixPollerState;
struct SlixPoller {
Iso15693_3Poller* iso15693_3_poller;
SlixData* data;
SlixPollerState poller_state;
SlixError error;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
SlixPollerEventData slix_event_data;
SlixPollerEvent slix_event;
NfcGenericEvent general_event;
NfcGenericCallback callback;
void* context;
};
SlixError slix_poller_send_frame(
SlixPoller* instance,
const BitBuffer* tx_data,
BitBuffer* rx_data,
uint32_t fwt);
SlixError slix_poller_async_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data);
SlixError slix_poller_async_read_signature(SlixPoller* instance, SlixSignature* data);
#ifdef __cplusplus
}
#endif