mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-18 19:01:47 +03:00
Add Saflok and MyKey KDFs
This commit is contained in:
parent
d6fcb04aa8
commit
dc25bfb831
@ -119,6 +119,24 @@ App(
|
|||||||
sources=["plugins/supported_cards/aime.c"],
|
sources=["plugins/supported_cards/aime.c"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="saflok_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="saflok_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/saflok.c"],
|
||||||
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="mykey_parser",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="mykey_plugin_ep",
|
||||||
|
targets=["f7"],
|
||||||
|
requires=["nfc"],
|
||||||
|
sources=["plugins/supported_cards/mykey.c"],
|
||||||
|
)
|
||||||
|
|
||||||
App(
|
App(
|
||||||
appid="nfc_start",
|
appid="nfc_start",
|
||||||
targets=["f7"],
|
targets=["f7"],
|
||||||
|
137
applications/main/nfc/plugins/supported_cards/mykey.c
Normal file
137
applications/main/nfc/plugins/supported_cards/mykey.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include "nfc_supported_card_plugin.h"
|
||||||
|
#include <flipper_application/flipper_application.h>
|
||||||
|
#include <lib/nfc/protocols/st25tb/st25tb.h>
|
||||||
|
#include <nfc/nfc_device.h>
|
||||||
|
#include <nfc/helpers/nfc_util.h>
|
||||||
|
|
||||||
|
//Structures data of mykey card
|
||||||
|
enum {
|
||||||
|
MYKEY_BLOCK_KEY_ID = 0x07,
|
||||||
|
MYKEY_BLOCK_PRODUCTION_DATE = 0x08,
|
||||||
|
MYKEY_BLOCK_VENDOR_ID_1 = 0x18,
|
||||||
|
MYKEY_BLOCK_VENDOR_ID_2 = 0x19,
|
||||||
|
MYKEY_BLOCK_CURRENT_CREDIT = 0x21,
|
||||||
|
MYKEY_BLOCK_PREVIOUS_CREDIT = 0x23,
|
||||||
|
MYKEY_DEFAULT_VENDOR_ID = 0xFEDC0123,
|
||||||
|
MYKEY_DEFAULT_VENDOR_ID_1 = 0xFEDC,
|
||||||
|
MYKEY_DEFAULT_VENDOR_ID_2 = 0x0123,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LockIdStatusNone,
|
||||||
|
LockIdStatusActive,
|
||||||
|
} LockIdStatus;
|
||||||
|
|
||||||
|
/* Function to obtain the UID as a 32-bit */
|
||||||
|
uint32_t get_uid(const uint8_t uid[8]) {
|
||||||
|
return (uid[7] | (uid[6] << 8) | (uid[5] << 16) | (uid[4] << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OTP calculation (reverse block 6, incremental. 1,2,3, ecc.) */
|
||||||
|
uint32_t new_get_count_down_counter(uint32_t b6) {
|
||||||
|
return ~(b6 << 24 | (b6 & 0x0000FF00) << 8 | (b6 & 0x00FF0000) >> 8 | b6 >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function to check if the vendor is bound */
|
||||||
|
int get_is_bound(uint32_t vendor_id) {
|
||||||
|
return (vendor_id != MYKEY_DEFAULT_VENDOR_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MK = UID * VENDOR */
|
||||||
|
uint32_t get_master_key(uint32_t uid, uint32_t vendor_id) {
|
||||||
|
return uid * (vendor_id + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SK (Encryption key) = MK * OTP */
|
||||||
|
uint32_t get_encryption_key(uint32_t master_key, uint32_t count_down_counter) {
|
||||||
|
return master_key * (count_down_counter + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode or decode a MyKey block */
|
||||||
|
uint32_t encode_decode_block(uint32_t input) {
|
||||||
|
/*
|
||||||
|
* Swap all values using XOR
|
||||||
|
* 32 bit: 1111222233334444
|
||||||
|
*/
|
||||||
|
input ^= (input & 0x00C00000) << 6 | (input & 0x0000C000) << 12 | (input & 0x000000C0) << 18 |
|
||||||
|
(input & 0x000C0000) >> 6 | (input & 0x00030000) >> 12 | (input & 0x00000300) >> 6;
|
||||||
|
input ^= (input & 0x30000000) >> 6 | (input & 0x0C000000) >> 12 | (input & 0x03000000) >> 18 |
|
||||||
|
(input & 0x00003000) << 6 | (input & 0x00000030) << 12 | (input & 0x0000000C) << 6;
|
||||||
|
input ^= (input & 0x00C00000) << 6 | (input & 0x0000C000) << 12 | (input & 0x000000C0) << 18 |
|
||||||
|
(input & 0x000C0000) >> 6 | (input & 0x00030000) >> 12 | (input & 0x00000300) >> 6;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_block(uint32_t block) {
|
||||||
|
return encode_decode_block(__bswap32(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_xored_block(uint32_t block, uint32_t key) {
|
||||||
|
return encode_decode_block(__bswap32(block) ^ key);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_vendor(uint32_t b1, uint32_t b2) {
|
||||||
|
return b1 << 16 | (b2 & 0x0000FFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||||
|
furi_assert(device);
|
||||||
|
furi_assert(parsed_data);
|
||||||
|
|
||||||
|
bool parsed = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
//Get data
|
||||||
|
const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);
|
||||||
|
|
||||||
|
//Calc data
|
||||||
|
uint32_t _uid = get_uid(data->uid);
|
||||||
|
uint32_t _count_down_counter_new = new_get_count_down_counter(__bswap32(data->blocks[6]));
|
||||||
|
uint32_t _vendor_id = get_vendor(
|
||||||
|
get_block(data->blocks[MYKEY_BLOCK_VENDOR_ID_1]),
|
||||||
|
get_block(data->blocks[MYKEY_BLOCK_VENDOR_ID_2]));
|
||||||
|
uint32_t _master_key = get_master_key(_uid, _vendor_id);
|
||||||
|
uint32_t _encryption_key = get_encryption_key(_master_key, _count_down_counter_new);
|
||||||
|
uint16_t credit =
|
||||||
|
get_xored_block(data->blocks[MYKEY_BLOCK_CURRENT_CREDIT], _encryption_key);
|
||||||
|
uint16_t _previous_credit = get_block(data->blocks[MYKEY_BLOCK_PREVIOUS_CREDIT]);
|
||||||
|
bool _is_bound = get_is_bound(_vendor_id);
|
||||||
|
|
||||||
|
//parse data
|
||||||
|
furi_string_cat_printf(parsed_data, "\e#MyKey Card\n");
|
||||||
|
furi_string_cat_printf(parsed_data, "UID: %08lX\n", _uid);
|
||||||
|
furi_string_cat_printf(parsed_data, "Vendor ID: %08lX\n", _vendor_id);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data, "Current Credit: %d.%02d E \n", credit / 100, credit % 100);
|
||||||
|
furi_string_cat_printf(
|
||||||
|
parsed_data,
|
||||||
|
"Previus Credit: %d.%02d E \n",
|
||||||
|
_previous_credit / 100,
|
||||||
|
_previous_credit % 100);
|
||||||
|
furi_string_cat_printf(parsed_data, "Is Bound: %s\n", _is_bound ? "yes" : "no");
|
||||||
|
|
||||||
|
parsed = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual implementation of app<>plugin interface */
|
||||||
|
static const NfcSupportedCardsPlugin mykey_plugin = {
|
||||||
|
.protocol = NfcProtocolSt25tb,
|
||||||
|
.verify = NULL,
|
||||||
|
.read = NULL,
|
||||||
|
.parse = mykey_parse,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin descriptor to comply with basic plugin specification */
|
||||||
|
static const FlipperAppPluginDescriptor mykey_plugin_descriptor = {
|
||||||
|
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||||
|
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||||
|
.entry_point = &mykey_plugin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
|
const FlipperAppPluginDescriptor* mykey_plugin_ep() {
|
||||||
|
return &mykey_plugin_descriptor;
|
||||||
|
}
|
174
applications/main/nfc/plugins/supported_cards/saflok.c
Normal file
174
applications/main/nfc/plugins/supported_cards/saflok.c
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// From: https://gitee.com/jadenwu/Saflok_KDF/blob/master/saflok.c
|
||||||
|
// KDF published and reverse engineered by Jaden Wu
|
||||||
|
// FZ plugin by @noproto
|
||||||
|
|
||||||
|
#include "nfc_supported_card_plugin.h"
|
||||||
|
#include <flipper_application/flipper_application.h>
|
||||||
|
#include <nfc/nfc_device.h>
|
||||||
|
#include <nfc/helpers/nfc_util.h>
|
||||||
|
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define TAG "Saflok"
|
||||||
|
#define MAGIC_TABLE_SIZE 192
|
||||||
|
#define KEY_LENGTH 6
|
||||||
|
#define UID_LENGTH 4
|
||||||
|
#define CHECK_SECTOR 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t a;
|
||||||
|
uint64_t b;
|
||||||
|
} MfClassicKeyPair;
|
||||||
|
|
||||||
|
static MfClassicKeyPair saflok_1k_keys[] = {
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 000
|
||||||
|
{.a = 0x2a2c13cc242a, .b = 0xffffffffffff}, // 001
|
||||||
|
{.a = 0xffffffffffff, .b = 0xffffffffffff}, // 002
|
||||||
|
{.a = 0xffffffffffff, .b = 0xffffffffffff}, // 003
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 004
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 005
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 006
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 007
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 008
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 009
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 010
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 011
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 012
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 013
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 014
|
||||||
|
{.a = 0x000000000000, .b = 0xffffffffffff}, // 015
|
||||||
|
};
|
||||||
|
|
||||||
|
void generate_saflok_key(const uint8_t* uid, uint8_t* key) {
|
||||||
|
static const uint8_t magic_table[MAGIC_TABLE_SIZE] = {
|
||||||
|
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xF0, 0x57, 0xB3, 0x9E, 0xE3, 0xD8, 0x00, 0x00, 0xAA,
|
||||||
|
0x00, 0x00, 0x00, 0x96, 0x9D, 0x95, 0x4A, 0xC1, 0x57, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
|
||||||
|
0x8F, 0x43, 0x58, 0x0D, 0x2C, 0x9D, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xFF, 0xCC, 0xE0,
|
||||||
|
0x05, 0x0C, 0x43, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x34, 0x1B, 0x15, 0xA6, 0x90, 0xCC,
|
||||||
|
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x89, 0x58, 0x56, 0x12, 0xE7, 0x1B, 0x00, 0x00, 0xAA,
|
||||||
|
0x00, 0x00, 0x00, 0xBB, 0x74, 0xB0, 0x95, 0x36, 0x58, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
|
||||||
|
0xFB, 0x97, 0xF8, 0x4B, 0x5B, 0x74, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xC9, 0xD1, 0x88,
|
||||||
|
0x35, 0x9F, 0x92, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x8F, 0x92, 0xE9, 0x7F, 0x58, 0x97,
|
||||||
|
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x16, 0x6C, 0xA2, 0xB0, 0x9F, 0xD1, 0x00, 0x00, 0xAA,
|
||||||
|
0x00, 0x00, 0x00, 0x27, 0xDD, 0x93, 0x10, 0x1C, 0x6C, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00,
|
||||||
|
0xDA, 0x3E, 0x3F, 0xD6, 0x49, 0xDD, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x58, 0xDD, 0xED,
|
||||||
|
0x07, 0x8E, 0x3E, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x5C, 0xD0, 0x05, 0xCF, 0xD9, 0x07,
|
||||||
|
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x11, 0x8D, 0xD0, 0x01, 0x87, 0xD0};
|
||||||
|
|
||||||
|
uint8_t magic_byte = (uid[3] >> 4) + (uid[2] >> 4) + (uid[0] & 0x0F);
|
||||||
|
uint8_t magickal_index = (magic_byte & 0x0F) * 12 + 11;
|
||||||
|
|
||||||
|
uint8_t temp_key[KEY_LENGTH] = {magic_byte, uid[0], uid[1], uid[2], uid[3], magic_byte};
|
||||||
|
uint8_t carry_sum = 0;
|
||||||
|
|
||||||
|
for(int i = KEY_LENGTH - 1; i >= 0; i--, magickal_index--) {
|
||||||
|
uint16_t keysum = temp_key[i] + magic_table[magickal_index];
|
||||||
|
temp_key[i] = (keysum & 0xFF) + carry_sum;
|
||||||
|
carry_sum = keysum >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key, temp_key, KEY_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool saflok_verify(Nfc* nfc) {
|
||||||
|
bool verified = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const uint8_t block_num = mf_classic_get_first_block_num_of_sector(CHECK_SECTOR);
|
||||||
|
FURI_LOG_D(TAG, "Saflok: Verifying sector %i", CHECK_SECTOR);
|
||||||
|
|
||||||
|
MfClassicKey key = {0};
|
||||||
|
nfc_util_num2bytes(saflok_1k_keys[CHECK_SECTOR].a, COUNT_OF(key.data), key.data);
|
||||||
|
|
||||||
|
MfClassicAuthContext auth_context;
|
||||||
|
MfClassicError error =
|
||||||
|
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
|
||||||
|
if(error != MfClassicErrorNone) {
|
||||||
|
FURI_LOG_D(TAG, "Saflok: Failed to read block %u: %d", block_num, error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
verified = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool saflok_read(Nfc* nfc, NfcDevice* device) {
|
||||||
|
FURI_LOG_D(TAG, "Entering Saflok KDF");
|
||||||
|
|
||||||
|
furi_assert(nfc);
|
||||||
|
furi_assert(device);
|
||||||
|
|
||||||
|
bool is_read = false;
|
||||||
|
|
||||||
|
MfClassicData* data = mf_classic_alloc();
|
||||||
|
nfc_device_copy_data(device, NfcProtocolMfClassic, data);
|
||||||
|
|
||||||
|
do {
|
||||||
|
MfClassicType type = MfClassicType1k;
|
||||||
|
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
|
||||||
|
if(error != MfClassicErrorNone) break;
|
||||||
|
data->type = type;
|
||||||
|
|
||||||
|
size_t uid_len;
|
||||||
|
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG, "Saflok: UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]);
|
||||||
|
if(uid_len != UID_LENGTH) break;
|
||||||
|
|
||||||
|
uint8_t key[KEY_LENGTH];
|
||||||
|
generate_saflok_key(uid, key);
|
||||||
|
uint64_t num_key = nfc_util_bytes2num(key, KEY_LENGTH);
|
||||||
|
FURI_LOG_D(TAG, "Saflok: Key generated for UID: %012llX", num_key);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||||
|
if(saflok_1k_keys[i].a == 0x000000000000) {
|
||||||
|
saflok_1k_keys[i].a = num_key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MfClassicDeviceKeys keys = {};
|
||||||
|
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
|
||||||
|
nfc_util_num2bytes(saflok_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
|
||||||
|
FURI_BIT_SET(keys.key_a_mask, i);
|
||||||
|
nfc_util_num2bytes(saflok_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
|
||||||
|
FURI_BIT_SET(keys.key_b_mask, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
error = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||||
|
if(error != MfClassicErrorNone) {
|
||||||
|
FURI_LOG_W(TAG, "Failed to read data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nfc_device_set_data(device, NfcProtocolMfClassic, data);
|
||||||
|
|
||||||
|
is_read = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
mf_classic_free(data);
|
||||||
|
|
||||||
|
return is_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual implementation of app<>plugin interface */
|
||||||
|
static const NfcSupportedCardsPlugin saflok_plugin = {
|
||||||
|
.protocol = NfcProtocolMfClassic,
|
||||||
|
.verify = saflok_verify,
|
||||||
|
.read = saflok_read,
|
||||||
|
// KDF mode
|
||||||
|
.parse = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin descriptor to comply with basic plugin specification */
|
||||||
|
static const FlipperAppPluginDescriptor saflok_plugin_descriptor = {
|
||||||
|
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||||
|
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||||
|
.entry_point = &saflok_plugin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Plugin entry point - must return a pointer to const descriptor */
|
||||||
|
const FlipperAppPluginDescriptor* saflok_plugin_ep() {
|
||||||
|
return &saflok_plugin_descriptor;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user