mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-11-23 10:01:58 +03:00
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:
parent
603a86dbe6
commit
217bfac2fc
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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);
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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"))
|
||||||
|
@ -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);
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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] = {};
|
||||||
|
Loading…
Reference in New Issue
Block a user