NFC: add Slix capabilities (#3652)

* iso15693 listener: fix inventory cmd and buffer overflow
* iso15 listener: fix read multiple blocks command
* slix: print password
* slix: add capabilities field
* slix listener: skip password validation for special capability
* slix: fix capability name
* slix: add capabilities handler to verify and reset
* nfc test: introduce slix tests
* fbt: change toolchain back to 33 version
* slix: fix saving capablities comment
* unit tests: add slix files to resources
* slix: fix set passwrd signature
* nfc tests: add set correct password test
* nfc test: complete slix password tests
* nfc test: add slix file test
* nfc test: handle errors in worker callback
* iso15693_3: code clean up
* iso15693_listener: fix incorrect afi handling
* slix: chage capabilities format to one word camel case
* unit tests: update nfc files with new slix format

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
gornekich 2024-05-17 12:58:32 +01:00 committed by GitHub
parent 603a86dbe6
commit 217bfac2fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 375 additions and 24 deletions

View File

@ -13,6 +13,12 @@
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h> #include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h> #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller.h> #include <nfc/protocols/mf_classic/mf_classic_poller.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller.h>
#include <nfc/protocols/slix/slix.h>
#include <nfc/protocols/slix/slix_i.h>
#include <nfc/protocols/slix/slix_poller.h>
#include <nfc/protocols/slix/slix_poller_i.h>
#include <nfc/nfc_poller.h> #include <nfc/nfc_poller.h>
#include <toolbox/keys_dict.h> #include <toolbox/keys_dict.h>
@ -42,6 +48,19 @@ typedef struct {
FuriThreadId thread_id; FuriThreadId thread_id;
} NfcTestMfClassicSendFrameTest; } NfcTestMfClassicSendFrameTest;
typedef enum {
NfcTestSlixPollerSetPasswordStateGetRandomNumber,
NfcTestSlixPollerSetPasswordStateSetPassword,
} NfcTestSlixPollerSetPasswordState;
typedef struct {
FuriThreadId thread_id;
NfcTestSlixPollerSetPasswordState state;
SlixRandomNumber random_number;
SlixPassword password;
SlixError error;
} NfcTestSlixPollerSetPasswordContext;
typedef struct { typedef struct {
Storage* storage; Storage* storage;
} NfcTest; } NfcTest;
@ -627,6 +646,127 @@ MU_TEST(mf_classic_dict_test) {
"Remove test dict failed"); "Remove test dict failed");
} }
MU_TEST(slix_file_with_capabilities_test) {
NfcDevice* nfc_device_missed_cap = nfc_device_alloc();
mu_assert(
nfc_device_load(nfc_device_missed_cap, EXT_PATH("unit_tests/nfc/Slix_cap_missed.nfc")),
"nfc_device_load() failed\r\n");
NfcDevice* nfc_device_default_cap = nfc_device_alloc();
mu_assert(
nfc_device_load(nfc_device_default_cap, EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc")),
"nfc_device_load() failed\r\n");
mu_assert(
nfc_device_is_equal(nfc_device_missed_cap, nfc_device_default_cap),
"nfc_device_is_equal() failed\r\n");
nfc_device_free(nfc_device_default_cap);
nfc_device_free(nfc_device_missed_cap);
}
NfcCommand slix_poller_set_password_callback(NfcGenericEventEx event, void* context) {
furi_check(event.poller);
furi_check(event.parent_event_data);
furi_check(context);
NfcCommand command = NfcCommandContinue;
Iso15693_3PollerEvent* iso15_event = event.parent_event_data;
SlixPoller* poller = event.poller;
NfcTestSlixPollerSetPasswordContext* slix_ctx = context;
if(iso15_event->type == Iso15693_3PollerEventTypeReady) {
iso15693_3_copy(
poller->data->iso15693_3_data, iso15693_3_poller_get_data(poller->iso15693_3_poller));
if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateGetRandomNumber) {
slix_ctx->error = slix_poller_get_random_number(poller, &slix_ctx->random_number);
if(slix_ctx->error != SlixErrorNone) {
furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);
command = NfcCommandStop;
} else {
slix_ctx->state = NfcTestSlixPollerSetPasswordStateSetPassword;
}
} else if(slix_ctx->state == NfcTestSlixPollerSetPasswordStateSetPassword) {
slix_ctx->error = slix_poller_set_password(
poller, SlixPasswordTypeRead, slix_ctx->password, slix_ctx->random_number);
furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);
command = NfcCommandStop;
}
} else {
slix_ctx->error = slix_process_iso15693_3_error(iso15_event->data->error);
furi_thread_flags_set(slix_ctx->thread_id, NFC_TEST_FLAG_WORKER_DONE);
command = NfcCommandStop;
}
return command;
}
static void slix_set_password_test(const char* file_path, SlixPassword pass, bool correct_pass) {
FURI_LOG_I(TAG, "Testing file: %s", file_path);
Nfc* poller = nfc_alloc();
Nfc* listener = nfc_alloc();
NfcDevice* nfc_device = nfc_device_alloc();
mu_assert(nfc_device_load(nfc_device, file_path), "nfc_device_load() failed\r\n");
const SlixData* slix_data = nfc_device_get_data(nfc_device, NfcProtocolSlix);
NfcListener* slix_listener = nfc_listener_alloc(listener, NfcProtocolSlix, slix_data);
nfc_listener_start(slix_listener, NULL, NULL);
SlixCapabilities slix_capabilities = slix_data->capabilities;
NfcPoller* slix_poller = nfc_poller_alloc(poller, NfcProtocolSlix);
NfcTestSlixPollerSetPasswordContext slix_poller_context = {
.thread_id = furi_thread_get_current_id(),
.state = NfcTestSlixPollerSetPasswordStateGetRandomNumber,
.password = pass,
.error = SlixErrorNone,
};
nfc_poller_start_ex(slix_poller, slix_poller_set_password_callback, &slix_poller_context);
uint32_t flag =
furi_thread_flags_wait(NFC_TEST_FLAG_WORKER_DONE, FuriFlagWaitAny, FuriWaitForever);
mu_assert(flag == NFC_TEST_FLAG_WORKER_DONE, "Wrong thread flag\r\n");
nfc_poller_stop(slix_poller);
nfc_poller_free(slix_poller);
nfc_listener_stop(slix_listener);
nfc_listener_free(slix_listener);
mu_assert(
slix_poller_context.state == NfcTestSlixPollerSetPasswordStateSetPassword,
"Poller failed before setting password\r\n");
if((slix_capabilities == SlixCapabilitiesAcceptAllPasswords) || (correct_pass)) {
mu_assert(slix_poller_context.error == SlixErrorNone, "Failed to set password\r\n");
} else {
mu_assert(
slix_poller_context.error == SlixErrorTimeout,
"Must have received SlixErrorTimeout\r\n");
}
nfc_device_free(nfc_device);
nfc_free(listener);
nfc_free(poller);
}
MU_TEST(slix_set_password_default_cap_correct_pass) {
slix_set_password_test(EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc"), 0x00000000, true);
}
MU_TEST(slix_set_password_default_cap_incorrect_pass) {
slix_set_password_test(EXT_PATH("unit_tests/nfc/Slix_cap_default.nfc"), 0x12341234, false);
}
MU_TEST(slix_set_password_access_all_passwords_cap) {
slix_set_password_test(
EXT_PATH("unit_tests/nfc/Slix_cap_accept_all_pass.nfc"), 0x12341234, false);
}
MU_TEST_SUITE(nfc) { MU_TEST_SUITE(nfc) {
nfc_test_alloc(); nfc_test_alloc();
@ -668,6 +808,11 @@ MU_TEST_SUITE(nfc) {
MU_RUN_TEST(mf_classic_send_frame_test); MU_RUN_TEST(mf_classic_send_frame_test);
MU_RUN_TEST(mf_classic_dict_test); MU_RUN_TEST(mf_classic_dict_test);
MU_RUN_TEST(slix_file_with_capabilities_test);
MU_RUN_TEST(slix_set_password_default_cap_correct_pass);
MU_RUN_TEST(slix_set_password_default_cap_incorrect_pass);
MU_RUN_TEST(slix_set_password_access_all_passwords_cap);
nfc_test_free(); nfc_test_free();
} }

View File

@ -55,6 +55,7 @@ struct Nfc {
Iso14443_3aColResStatus col_res_status; Iso14443_3aColResStatus col_res_status;
Iso14443_3aColResData col_res_data; Iso14443_3aColResData col_res_data;
bool software_col_res_required;
NfcEventCallback callback; NfcEventCallback callback;
void* context; void* context;
@ -170,6 +171,7 @@ NfcError nfc_iso14443a_listener_set_col_res_data(
furi_check(atqa); furi_check(atqa);
nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak); nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
instance->software_col_res_required = true;
return NfcErrorNone; return NfcErrorNone;
} }
@ -275,7 +277,8 @@ static int32_t nfc_worker_listener(void* context) {
} else if(message.type == NfcMessageTypeTx) { } else if(message.type == NfcMessageTypeTx) {
nfc_test_print( nfc_test_print(
NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits); NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits);
if(instance->col_res_status != Iso14443_3aColResStatusDone) { if(instance->software_col_res_required &&
(instance->col_res_status != Iso14443_3aColResStatusDone)) {
nfc_worker_listener_pass_col_res( nfc_worker_listener_pass_col_res(
instance, message.data.data, message.data.data_bits); instance, message.data.data, message.data.data_bits);
} else { } else {

View File

@ -0,0 +1,41 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: SLIX
# UID is common for all formats
UID: E0 04 01 08 49 D0 DC 81
# ISO15693-3 specific data
# Data Storage Format Identifier
DSFID: 01
# Application Family Identifier
AFI: 3D
# IC Reference - Vendor specific meaning
IC Reference: 01
# Lock Bits
Lock DSFID: true
Lock AFI: true
# Number of memory blocks, valid range = 1..256
Block Count: 80
# Size of a single memory block, valid range = 01...20 (hex)
Block Size: 04
Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01
# Block Security Status: 01 = locked, 00 = not locked
Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# SLIX specific data
# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords
Capabilities: AcceptAllPasswords
# Passwords are optional. If a password is omitted, a default value will be used
Password Read: 00 00 00 00
Password Write: 00 00 00 00
Password Privacy: 0F 0F 0F 0F
Password Destroy: 0F 0F 0F 0F
Password EAS: 00 00 00 00
# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.
Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D
Privacy Mode: false
# Protection pointer configuration
Protection Pointer: 32
Protection Condition: 02
# SLIX Lock Bits
Lock EAS: true
Lock PPL: true

View File

@ -0,0 +1,41 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: SLIX
# UID is common for all formats
UID: E0 04 01 08 49 D0 DC 81
# ISO15693-3 specific data
# Data Storage Format Identifier
DSFID: 01
# Application Family Identifier
AFI: 3D
# IC Reference - Vendor specific meaning
IC Reference: 01
# Lock Bits
Lock DSFID: true
Lock AFI: true
# Number of memory blocks, valid range = 1..256
Block Count: 80
# Size of a single memory block, valid range = 01...20 (hex)
Block Size: 04
Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01
# Block Security Status: 01 = locked, 00 = not locked
Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# SLIX specific data
# SLIX capabilities field affects emulation modes. Possible options: Default, AcceptAllPasswords
Capabilities: Default
# Passwords are optional. If a password is omitted, a default value will be used
Password Read: 00 00 00 00
Password Write: 00 00 00 00
Password Privacy: 0F 0F 0F 0F
Password Destroy: 0F 0F 0F 0F
Password EAS: 00 00 00 00
# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.
Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D
Privacy Mode: false
# Protection pointer configuration
Protection Pointer: 32
Protection Condition: 02
# SLIX Lock Bits
Lock EAS: true
Lock PPL: true

View File

@ -0,0 +1,39 @@
Filetype: Flipper NFC device
Version: 4
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
Device type: SLIX
# UID is common for all formats
UID: E0 04 01 08 49 D0 DC 81
# ISO15693-3 specific data
# Data Storage Format Identifier
DSFID: 01
# Application Family Identifier
AFI: 3D
# IC Reference - Vendor specific meaning
IC Reference: 01
# Lock Bits
Lock DSFID: true
Lock AFI: true
# Number of memory blocks, valid range = 1..256
Block Count: 80
# Size of a single memory block, valid range = 01...20 (hex)
Block Size: 04
Data Content: 03 0A 82 ED 86 39 61 D2 03 14 1E 32 B6 CA 00 3C 36 42 0C 33 53 30 37 32 32 34 30 30 00 00 00 00 00 FF 04 01 01 00 00 00 A3 03 1E 00 26 00 00 00 00 00 0F 00 76 03 65 01 00 00 00 00 85 01 34 00 75 09 05 00 01 00 00 00 00 00 00 00 00 00 00 00 D7 FA 00 1C 9E 1C 67 27 00 30 30 30 30 30 30 30 30 30 30 00 00 00 97 25 55 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 8C 00 30 53 48 80 DE 00 00 00 00 F4 C3 58 2B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 F3 00 2C DD C3 3E 91 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E5 FF 00 01
# Block Security Status: 01 = locked, 00 = not locked
Security Status: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# SLIX specific data
# Passwords are optional. If a password is omitted, a default value will be used
Password Read: 00 00 00 00
Password Write: 00 00 00 00
Password Privacy: 0F 0F 0F 0F
Password Destroy: 0F 0F 0F 0F
Password EAS: 00 00 00 00
# This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.
Signature: A6 25 54 03 74 24 C4 38 36 F4 89 70 76 1A 72 27 54 D9 E7 3D 38 CB 4C 1B 3E FD 0E DF 8A F6 7E 3D
Privacy Mode: false
# Protection pointer configuration
Protection Pointer: 32
Protection Condition: 02
# SLIX Lock Bits
Lock EAS: true
Lock PPL: true

View File

@ -8,7 +8,7 @@
#define TAG "Iso15693_3Listener" #define TAG "Iso15693_3Listener"
#define ISO15693_3_LISTENER_BUFFER_SIZE (64U) #define ISO15693_3_LISTENER_BUFFER_SIZE (256U)
Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) { Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) {
furi_assert(nfc); furi_assert(nfc);
@ -67,6 +67,7 @@ NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) {
if(nfc_event->type == NfcEventTypeRxEnd) { if(nfc_event->type == NfcEventTypeRxEnd) {
BitBuffer* rx_buffer = nfc_event->data.buffer; BitBuffer* rx_buffer = nfc_event->data.buffer;
bit_buffer_reset(instance->tx_buffer);
if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) { if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
iso13239_crc_trim(rx_buffer); iso13239_crc_trim(rx_buffer);

View File

@ -64,7 +64,9 @@ static Iso15693_3Error iso15693_3_listener_inventory_handler(
if(afi_flag) { if(afi_flag) {
const uint8_t afi = *data++; const uint8_t afi = *data++;
// When AFI flag is set, ignore non-matching requests // When AFI flag is set, ignore non-matching requests
if(afi != instance->data->system_info.afi) break; if(afi != 0) {
if(afi != instance->data->system_info.afi) break;
}
} }
const uint8_t mask_len = *data++; const uint8_t mask_len = *data++;
@ -260,16 +262,9 @@ static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler(
} }
const uint32_t block_index_start = request->first_block_num; const uint32_t block_index_start = request->first_block_num;
const uint32_t block_index_end = block_index_start + request->block_count; const uint32_t block_index_end =
MIN((block_index_start + request->block_count + 1),
const uint32_t block_count = request->block_count + 1; ((uint32_t)instance->data->system_info.block_count - 1));
const uint32_t block_count_max = instance->data->system_info.block_count;
const uint32_t block_count_available = block_count_max - block_index_start;
if(block_count > block_count_available) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler( error = iso15693_3_listener_extension_handler(
instance, instance,

View File

@ -14,6 +14,7 @@
#define SLIX_TYPE_INDICATOR_SLIX (0x02U) #define SLIX_TYPE_INDICATOR_SLIX (0x02U)
#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U) #define SLIX_TYPE_INDICATOR_SLIX2 (0x01U)
#define SLIX_CAPABILITIES_KEY "Capabilities"
#define SLIX_PASSWORD_READ_KEY "Password Read" #define SLIX_PASSWORD_READ_KEY "Password Read"
#define SLIX_PASSWORD_WRITE_KEY "Password Write" #define SLIX_PASSWORD_WRITE_KEY "Password Write"
#define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy" #define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy"
@ -69,6 +70,11 @@ static const SlixTypeFeatures slix_type_features[] = {
[SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2, [SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2,
}; };
static const char* slix_capabilities_names[SlixCapabilitiesCount] = {
[SlixCapabilitiesDefault] = "Default",
[SlixCapabilitiesAcceptAllPasswords] = "AcceptAllPasswords",
};
typedef struct { typedef struct {
const char* key; const char* key;
SlixTypeFeatures feature_flag; SlixTypeFeatures feature_flag;
@ -110,6 +116,7 @@ void slix_reset(SlixData* data) {
furi_check(data); furi_check(data);
iso15693_3_reset(data->iso15693_3_data); iso15693_3_reset(data->iso15693_3_data);
data->capabilities = SlixCapabilitiesDefault;
slix_password_set_defaults(data->passwords); slix_password_set_defaults(data->passwords);
memset(&data->system_info, 0, sizeof(SlixSystemInfo)); memset(&data->system_info, 0, sizeof(SlixSystemInfo));
@ -123,6 +130,7 @@ void slix_copy(SlixData* data, const SlixData* other) {
furi_check(other); furi_check(other);
iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data); iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data);
data->capabilities = other->capabilities;
memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount); memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount);
memcpy(data->signature, other->signature, sizeof(SlixSignature)); memcpy(data->signature, other->signature, sizeof(SlixSignature));
@ -138,6 +146,30 @@ bool slix_verify(SlixData* data, const FuriString* device_type) {
return false; return false;
} }
static bool slix_load_capabilities(SlixData* data, FlipperFormat* ff) {
bool capabilities_loaded = false;
FuriString* capabilities_str = furi_string_alloc();
if(!flipper_format_read_string(ff, SLIX_CAPABILITIES_KEY, capabilities_str)) {
if(flipper_format_rewind(ff)) {
data->capabilities = SlixCapabilitiesDefault;
capabilities_loaded = true;
}
} else {
for(size_t i = 0; i < COUNT_OF(slix_capabilities_names); i++) {
if(furi_string_cmp_str(capabilities_str, slix_capabilities_names[i]) == 0) {
data->capabilities = i;
capabilities_loaded = true;
break;
}
}
}
furi_string_free(capabilities_str);
return capabilities_loaded;
}
static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
bool ret = true; bool ret = true;
@ -164,13 +196,14 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
furi_check(ff); furi_check(ff);
bool loaded = false; bool loaded = false;
do { do {
if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break; if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break;
const SlixType slix_type = slix_get_type(data); const SlixType slix_type = slix_get_type(data);
if(slix_type >= SlixTypeCount) break; if(slix_type >= SlixTypeCount) break;
if(!slix_load_capabilities(data, ff)) break;
if(!slix_load_passwords(data->passwords, slix_type, ff)) break; if(!slix_load_passwords(data->passwords, slix_type, ff)) break;
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {
@ -220,6 +253,33 @@ bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
return loaded; return loaded;
} }
static bool slix_save_capabilities(const SlixData* data, FlipperFormat* ff) {
bool save_success = false;
FuriString* tmp_str = furi_string_alloc();
do {
furi_string_set_str(
tmp_str, "SLIX capabilities field affects emulation modes. Possible options: ");
for(size_t i = 0; i < SlixCapabilitiesCount; i++) {
furi_string_cat_str(tmp_str, slix_capabilities_names[i]);
if(i < SlixCapabilitiesCount - 1) {
furi_string_cat(tmp_str, ", ");
}
}
if(!flipper_format_write_comment_cstr(ff, furi_string_get_cstr(tmp_str))) break;
if(!flipper_format_write_string_cstr(
ff, SLIX_CAPABILITIES_KEY, slix_capabilities_names[data->capabilities]))
break;
save_success = true;
} while(false);
furi_string_free(tmp_str);
return save_success;
}
static bool static bool
slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) {
bool ret = true; bool ret = true;
@ -251,6 +311,8 @@ bool slix_save(const SlixData* data, FlipperFormat* ff) {
if(!iso15693_3_save(data->iso15693_3_data, ff)) 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, SLIX_PROTOCOL_NAME " specific data")) break;
if(!slix_save_capabilities(data, ff)) break;
if(!flipper_format_write_comment_cstr( if(!flipper_format_write_comment_cstr(
ff, ff,
"Passwords are optional. If a password is omitted, a default value will be used")) "Passwords are optional. If a password is omitted, a default value will be used"))

View File

@ -91,12 +91,20 @@ typedef struct {
SlixLockBits lock_bits; SlixLockBits lock_bits;
} SlixSystemInfo; } SlixSystemInfo;
typedef enum {
SlixCapabilitiesDefault,
SlixCapabilitiesAcceptAllPasswords,
SlixCapabilitiesCount,
} SlixCapabilities;
typedef struct { typedef struct {
Iso15693_3Data* iso15693_3_data; Iso15693_3Data* iso15693_3_data;
SlixSystemInfo system_info; SlixSystemInfo system_info;
SlixSignature signature; SlixSignature signature;
SlixPassword passwords[SlixPasswordTypeCount]; SlixPassword passwords[SlixPasswordTypeCount];
SlixPrivacy privacy; SlixPrivacy privacy;
SlixCapabilities capabilities;
} SlixData; } SlixData;
SlixData* slix_alloc(void); SlixData* slix_alloc(void);

View File

@ -54,6 +54,13 @@ static SlixError slix_listener_set_password(
} }
SlixListenerSessionState* session_state = &instance->session_state; SlixListenerSessionState* session_state = &instance->session_state;
// With AcceptAllPassword capability set skip password validation
if(instance->data->capabilities == SlixCapabilitiesAcceptAllPasswords) {
session_state->password_match[password_type] = true;
break;
}
session_state->password_match[password_type] = session_state->password_match[password_type] =
(password == slix_get_password(slix_data, password_type)); (password == slix_get_password(slix_data, password_type));

View File

@ -114,7 +114,8 @@ static NfcCommand slix_poller_handler_check_privacy_password(SlixPoller* instanc
break; break;
} }
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd); instance->error = slix_poller_set_password(
instance, SlixPasswordTypePrivacy, pwd, instance->random_number);
if(instance->error != SlixErrorNone) { if(instance->error != SlixErrorNone) {
command = NfcCommandReset; command = NfcCommandReset;
break; break;
@ -145,7 +146,8 @@ static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) {
instance->error = slix_poller_get_random_number(instance, &instance->random_number); instance->error = slix_poller_get_random_number(instance, &instance->random_number);
if(instance->error != SlixErrorNone) break; if(instance->error != SlixErrorNone) break;
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd); instance->error = slix_poller_set_password(
instance, SlixPasswordTypePrivacy, pwd, instance->random_number);
if(instance->error != SlixErrorNone) { if(instance->error != SlixErrorNone) {
command = NfcCommandReset; command = NfcCommandReset;
break; break;

View File

@ -107,12 +107,16 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber*
* Must ONLY be used inside the callback function. * Must ONLY be used inside the callback function.
* *
* @param[in, out] instance pointer to the instance to be used in the transaction. * @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[out] type SlixPasswordType instance. * @param[in] type SlixPasswordType instance.
* @param[out] password SlixPassword instance. * @param[in] password SlixPassword instance.
* @param[in] random_number SlixRandomNumber instance.
* @return SlixErrorNone on success, an error code on failure. * @return SlixErrorNone on success, an error code on failure.
*/ */
SlixError SlixError slix_poller_set_password(
slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password); SlixPoller* instance,
SlixPasswordType type,
SlixPassword password,
SlixRandomNumber random_number);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -92,8 +92,11 @@ SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber*
return error; return error;
} }
SlixError SlixError slix_poller_set_password(
slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password) { SlixPoller* instance,
SlixPasswordType type,
SlixPassword password,
SlixRandomNumber random_number) {
furi_assert(instance); furi_assert(instance);
bool skip_uid = (type == SlixPasswordTypePrivacy); bool skip_uid = (type == SlixPasswordTypePrivacy);
@ -102,8 +105,8 @@ SlixError
uint8_t password_type = (0x01 << type); uint8_t password_type = (0x01 << type);
bit_buffer_append_byte(instance->tx_buffer, password_type); bit_buffer_append_byte(instance->tx_buffer, password_type);
uint8_t rn_l = instance->random_number >> 8; uint8_t rn_l = random_number >> 8;
uint8_t rn_h = instance->random_number; uint8_t rn_h = random_number;
uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l; uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l;
uint32_t xored_password = double_rand_num ^ password; uint32_t xored_password = double_rand_num ^ password;
uint8_t xored_password_arr[4] = {}; uint8_t xored_password_arr[4] = {};