mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-17 04:34:44 -07:00
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:
433
lib/nfc/protocols/slix/slix.c
Normal file
433
lib/nfc/protocols/slix/slix.c
Normal 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;
|
||||
}
|
||||
146
lib/nfc/protocols/slix/slix.h
Normal file
146
lib/nfc/protocols/slix/slix.h
Normal 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
|
||||
5
lib/nfc/protocols/slix/slix_device_defs.h
Normal file
5
lib/nfc/protocols/slix/slix_device_defs.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_device_base_i.h>
|
||||
|
||||
extern const NfcDeviceBase nfc_device_slix;
|
||||
127
lib/nfc/protocols/slix/slix_i.c
Normal file
127
lib/nfc/protocols/slix/slix_i.c
Normal 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));
|
||||
}
|
||||
86
lib/nfc/protocols/slix/slix_i.h
Normal file
86
lib/nfc/protocols/slix/slix_i.h
Normal 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
|
||||
79
lib/nfc/protocols/slix/slix_listener.c
Normal file
79
lib/nfc/protocols/slix/slix_listener.c
Normal 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,
|
||||
};
|
||||
29
lib/nfc/protocols/slix/slix_listener.h
Normal file
29
lib/nfc/protocols/slix/slix_listener.h
Normal 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
|
||||
5
lib/nfc/protocols/slix/slix_listener_defs.h
Normal file
5
lib/nfc/protocols/slix/slix_listener_defs.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_listener_base.h>
|
||||
|
||||
extern const NfcListenerBase nfc_listener_slix;
|
||||
617
lib/nfc/protocols/slix/slix_listener_i.c
Normal file
617
lib/nfc/protocols/slix/slix_listener_i.c
Normal 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;
|
||||
}
|
||||
37
lib/nfc/protocols/slix/slix_listener_i.h
Normal file
37
lib/nfc/protocols/slix/slix_listener_i.h
Normal 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
|
||||
159
lib/nfc/protocols/slix/slix_poller.c
Normal file
159
lib/nfc/protocols/slix/slix_poller.c
Normal 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,
|
||||
};
|
||||
29
lib/nfc/protocols/slix/slix_poller.h
Normal file
29
lib/nfc/protocols/slix/slix_poller.h
Normal 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
|
||||
5
lib/nfc/protocols/slix/slix_poller_defs.h
Normal file
5
lib/nfc/protocols/slix/slix_poller_defs.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
extern const NfcPollerBase nfc_poller_slix;
|
||||
69
lib/nfc/protocols/slix/slix_poller_i.c
Normal file
69
lib/nfc/protocols/slix/slix_poller_i.c
Normal 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;
|
||||
}
|
||||
48
lib/nfc/protocols/slix/slix_poller_i.h
Normal file
48
lib/nfc/protocols/slix/slix_poller_i.h
Normal 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
|
||||
Reference in New Issue
Block a user