Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Saflok and MyKey KDFs #662

Merged
merged 1 commit into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions applications/main/nfc/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ App(
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(
appid="nfc_start",
targets=["f7"],
Expand Down
137 changes: 137 additions & 0 deletions applications/main/nfc/plugins/supported_cards/mykey.c
Original file line number Diff line number Diff line change
@@ -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 changes: 174 additions & 0 deletions applications/main/nfc/plugins/supported_cards/saflok.c
Original file line number Diff line number Diff line change
@@ -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;
}