mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
Add Saflok MFUL Parser Support
by aaronjamt
This commit is contained in:
@@ -369,8 +369,19 @@ App(
|
|||||||
)
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="saflok_parser",
|
appid="saflok_mfc_parser",
|
||||||
apptype=FlipperAppType.PLUGIN,
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
cdefines=[("SL_PROTO", "SL_PROTO_MFC")],
|
||||||
|
entry_point="saflok_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/saflok.c"],
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="saflok_ul_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
cdefines=[("SL_PROTO", "SL_PROTO_UL")],
|
||||||
entry_point="saflok_plugin_ep",
|
entry_point="saflok_plugin_ep",
|
||||||
targets=["f7"],
|
targets=["f7"],
|
||||||
requires=["nfc"],
|
requires=["nfc"],
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include <flipper_application.h>
|
#include <flipper_application.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_ultralight/mf_ultralight_poller_sync.h>
|
||||||
|
|
||||||
#include <nfc_app_i.h>
|
#include <nfc_app_i.h>
|
||||||
#include <bit_lib.h>
|
#include <bit_lib.h>
|
||||||
|
|
||||||
@@ -18,9 +20,24 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||||
|
|
||||||
#define TAG "Saflok"
|
#define TAG "Saflok"
|
||||||
|
|
||||||
#define MAGIC_TABLE_SIZE 192
|
#define MAGIC_TABLE_SIZE 192
|
||||||
|
#define SL_PROTO_INVALID (-1)
|
||||||
|
#define SL_PROTO_MFC (0)
|
||||||
|
#define SL_PROTO_UL (1)
|
||||||
|
#define SL_PROTO_TOTAL (2)
|
||||||
|
|
||||||
|
#ifndef SL_PROTO
|
||||||
|
#error Must specify what protocol to use with SL_PROTO define!
|
||||||
|
#endif
|
||||||
|
#if SL_PROTO <= SL_PROTO_INVALID || SL_PROTO >= SL_PROTO_TOTAL
|
||||||
|
#error Invalid SL_PROTO specified!
|
||||||
|
#endif
|
||||||
|
|
||||||
#define KEY_LENGTH 6
|
#define KEY_LENGTH 6
|
||||||
#define UID_LENGTH 4
|
#define UID_LENGTH 4
|
||||||
#define CHECK_SECTOR 1
|
#define CHECK_SECTOR 1
|
||||||
@@ -255,25 +272,40 @@ static bool saflok_read(Nfc* nfc, NfcDevice* device) {
|
|||||||
|
|
||||||
bool saflok_parse(const NfcDevice* device, FuriString* parsed_data) {
|
bool saflok_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
furi_assert(device);
|
furi_assert(device);
|
||||||
|
#if SL_PROTO == SL_PROTO_MFC
|
||||||
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
||||||
|
#elif SL_PROTO == SL_PROTO_UL
|
||||||
|
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
|
||||||
|
#endif
|
||||||
bool parsed = false;
|
bool parsed = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
#if SL_PROTO == SL_PROTO_MFC
|
||||||
// Check card type
|
// Check card type
|
||||||
if(data->type != MfClassicType1k) break;
|
if(data->type != MfClassicType1k) break;
|
||||||
|
|
||||||
// Verify key
|
// Verify key
|
||||||
const MfClassicSectorTrailer* sec_tr =
|
const MfClassicSectorTrailer* sec_tr =
|
||||||
mf_classic_get_sector_trailer_by_sector(data, CHECK_SECTOR);
|
mf_classic_get_sector_trailer_by_sector(data, CHECK_SECTOR);
|
||||||
|
|
||||||
const uint64_t key_a =
|
const uint64_t key_a =
|
||||||
bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
|
bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
|
||||||
if(key_a != saflok_1k_keys[CHECK_SECTOR].a) break;
|
if(key_a != saflok_1k_keys[CHECK_SECTOR].a) break;
|
||||||
|
// Init basic access
|
||||||
// Decrypt basic access
|
|
||||||
uint8_t basicAccess[BASIC_ACCESS_BYTE_NUM];
|
uint8_t basicAccess[BASIC_ACCESS_BYTE_NUM];
|
||||||
memcpy(&basicAccess, &data->block[1].data, 16);
|
memcpy(&basicAccess, &data->block[1].data, 16);
|
||||||
memcpy(&basicAccess[16], &data->block[2].data[0], 1);
|
memcpy(&basicAccess[16], &data->block[2].data[0], 1);
|
||||||
|
#elif SL_PROTO == SL_PROTO_UL
|
||||||
|
// Check card type
|
||||||
|
if(data->type != MfUltralightTypeMfulC) break;
|
||||||
|
// Init basic access
|
||||||
|
uint8_t basicAccess[BASIC_ACCESS_BYTE_NUM];
|
||||||
|
memcpy(&basicAccess[0 * 4], &data->page[34].data, 4);
|
||||||
|
memcpy(&basicAccess[1 * 4], &data->page[35].data, 4);
|
||||||
|
memcpy(&basicAccess[2 * 4], &data->page[36].data, 4);
|
||||||
|
memcpy(&basicAccess[3 * 4], &data->page[37].data, 4);
|
||||||
|
memcpy(&basicAccess[4 * 4], &data->page[38].data[0], 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Decrypt basic access
|
||||||
uint8_t decodedBA[BASIC_ACCESS_BYTE_NUM];
|
uint8_t decodedBA[BASIC_ACCESS_BYTE_NUM];
|
||||||
DecryptCard(basicAccess, BASIC_ACCESS_BYTE_NUM, decodedBA);
|
DecryptCard(basicAccess, BASIC_ACCESS_BYTE_NUM, decodedBA);
|
||||||
|
|
||||||
@@ -290,8 +322,7 @@ bool saflok_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
uint16_t key_record = (key_record_high << 8) | decodedBA[3];
|
uint16_t key_record = (key_record_high << 8) | decodedBA[3];
|
||||||
|
|
||||||
// Byte 4 & 5: Pass level in reversed binary
|
// Byte 4 & 5: Pass level in reversed binary
|
||||||
// This part is commented because the relevance of this info is still unknown
|
uint16_t pass_level = ((decodedBA[4] & 0xFF) << 8) | decodedBA[5];
|
||||||
// uint16_t pass_level = ((decodedBA[4] & 0xFF) << 8) | decodedBA[5];
|
|
||||||
// uint8_t pass_levels[12];
|
// uint8_t pass_levels[12];
|
||||||
// int pass_levels_count = 0;
|
// int pass_levels_count = 0;
|
||||||
|
|
||||||
@@ -390,26 +421,31 @@ bool saflok_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
uint8_t checksum = decodedBA[16];
|
uint8_t checksum = decodedBA[16];
|
||||||
uint8_t checksum_calculated = CalculateCheckSum(decodedBA);
|
uint8_t checksum_calculated = CalculateCheckSum(decodedBA);
|
||||||
bool checksum_valid = (checksum_calculated == checksum);
|
bool checksum_valid = (checksum_calculated == checksum);
|
||||||
for(int i = 0; i < 17; i++) {
|
for(int i = 0; i < BASIC_ACCESS_BYTE_NUM; i++) {
|
||||||
FURI_LOG_D(TAG, "%02X", decodedBA[i]);
|
FURI_LOG_D(TAG, "%02X", decodedBA[i]);
|
||||||
}
|
}
|
||||||
FURI_LOG_D(TAG, "CS decrypted: %02X", checksum);
|
FURI_LOG_D(TAG, "CS decrypted: %02X", checksum);
|
||||||
FURI_LOG_D(TAG, "CS calculated: %02X", checksum_calculated);
|
FURI_LOG_D(TAG, "CS calculated: %02X", checksum_calculated);
|
||||||
|
#if SL_PROTO == SL_PROTO_MFC
|
||||||
furi_string_cat_printf(parsed_data, "\e#Saflok Card\n");
|
furi_string_cat_printf(parsed_data, "\e#Saflok MFC 1K Card\n");
|
||||||
|
#elif SL_PROTO == SL_PROTO_UL
|
||||||
|
furi_string_cat_printf(parsed_data, "\e#Saflok UL-C Card\n");
|
||||||
|
#endif
|
||||||
|
furi_string_cat_printf(parsed_data, "Property Number: %u\n", property_id);
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
parsed_data,
|
parsed_data,
|
||||||
"Key Level: %u, %s\n",
|
"Key Level: %u, %s\n",
|
||||||
key_levels[key_level].level_num,
|
key_levels[key_level].level_num,
|
||||||
key_levels[key_level].level_name);
|
key_levels[key_level].level_name);
|
||||||
furi_string_cat_printf(parsed_data, "LED Exp. Warning: %s\n", led_warning ? "Yes" : "No");
|
|
||||||
furi_string_cat_printf(parsed_data, "Key ID: %02X\n", key_id);
|
furi_string_cat_printf(parsed_data, "Key ID: %02X\n", key_id);
|
||||||
furi_string_cat_printf(parsed_data, "Key Record: %04X\n", key_record);
|
furi_string_cat_printf(parsed_data, "Key Record: %04X\n", key_record);
|
||||||
furi_string_cat_printf(parsed_data, "Opening key: %s\n", opening_key ? "Yes" : "No");
|
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
parsed_data, "Seq. & Combination: %04X\n", sequence_combination_number);
|
parsed_data, "Seq. & Combination: %04X\n", sequence_combination_number);
|
||||||
|
furi_string_cat_printf(parsed_data, "Pass Level: %04X\n", pass_level);
|
||||||
|
furi_string_cat_printf(parsed_data, "Opening Key: %s\n", opening_key ? "Yes" : "No");
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
parsed_data, "Override Deadbolt: %s\n", override_deadbolt ? "Yes" : "No");
|
parsed_data, "Override Deadbolt: %s\n", override_deadbolt ? "Yes" : "No");
|
||||||
|
furi_string_cat_printf(parsed_data, "LED Exp. Warning: %s\n", led_warning ? "Yes" : "No");
|
||||||
furi_string_cat_printf(
|
furi_string_cat_printf(
|
||||||
parsed_data,
|
parsed_data,
|
||||||
"Restricted Weekday: %s\n",
|
"Restricted Weekday: %s\n",
|
||||||
@@ -430,19 +466,33 @@ bool saflok_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
expire_day,
|
expire_day,
|
||||||
expire_hour,
|
expire_hour,
|
||||||
expire_minute);
|
expire_minute);
|
||||||
furi_string_cat_printf(parsed_data, "Property Number: %u\n", property_id);
|
|
||||||
furi_string_cat_printf(parsed_data, "Checksum Valid: %s", checksum_valid ? "Yes" : "No");
|
furi_string_cat_printf(parsed_data, "Checksum Valid: %s", checksum_valid ? "Yes" : "No");
|
||||||
|
#if SL_PROTO == SL_PROTO_MFC
|
||||||
|
// MFC returns parsed = true since we have proper verify and read functions
|
||||||
parsed = true;
|
parsed = true;
|
||||||
|
#elif SL_PROTO == SL_PROTO_UL
|
||||||
|
// UL returns parsed = checksum_valid since we don't have proper verify and read functions
|
||||||
|
// TODO: change to true after verify and read are implemented
|
||||||
|
parsed = checksum_valid;
|
||||||
|
#endif
|
||||||
|
|
||||||
} while(false);
|
} while(false);
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actual implementation of app<>plugin interface */
|
/* Actual implementation of app<>plugin interface */
|
||||||
static const NfcSupportedCardsPlugin saflok_plugin = {
|
static const NfcSupportedCardsPlugin saflok_plugin = {
|
||||||
|
#if SL_PROTO == SL_PROTO_MFC
|
||||||
.protocol = NfcProtocolMfClassic,
|
.protocol = NfcProtocolMfClassic,
|
||||||
.verify = saflok_verify,
|
.verify = saflok_verify,
|
||||||
.read = saflok_read,
|
.read = saflok_read,
|
||||||
.parse = saflok_parse,
|
.parse = saflok_parse,
|
||||||
|
#elif SL_PROTO == SL_PROTO_UL
|
||||||
|
.protocol = NfcProtocolMfUltralight,
|
||||||
|
.verify = NULL,
|
||||||
|
.read = NULL,
|
||||||
|
.parse = saflok_parse,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Plugin descriptor to comply with basic plugin specification */
|
/* Plugin descriptor to comply with basic plugin specification */
|
||||||
@@ -456,3 +506,5 @@ static const FlipperAppPluginDescriptor saflok_plugin_descriptor = {
|
|||||||
const FlipperAppPluginDescriptor* saflok_plugin_ep(void) {
|
const FlipperAppPluginDescriptor* saflok_plugin_ep(void) {
|
||||||
return &saflok_plugin_descriptor;
|
return &saflok_plugin_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
Reference in New Issue
Block a user