diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index cd141e8a1..4a31458d8 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -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"], diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c new file mode 100644 index 000000000..d7a0ea4fd --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -0,0 +1,137 @@ +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include + +//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; +} \ No newline at end of file diff --git a/applications/main/nfc/plugins/supported_cards/saflok.c b/applications/main/nfc/plugins/supported_cards/saflok.c new file mode 100644 index 000000000..55edd2efa --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/saflok.c @@ -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 +#include +#include +#include +#include + +#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; +}