mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 15:04:19 +00:00
[FL-3661] Troika layout fixes (#3365)
* Add support for different troika layouts * Display additional data if debug is enabled in settings * Support for layout 2, where there's no balance * nfc app plugins: fix mfc read error processing * nfc app: clean up troika plugin * nfc app: troika parser more clean up --------- Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
parent
ed34dfa1c6
commit
e0782966d4
3 changed files with 165 additions and 14 deletions
|
@ -135,7 +135,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
|
|||
}
|
||||
|
||||
error = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||
if(error != MfClassicErrorNotPresent) {
|
||||
if(error == MfClassicErrorNotPresent) {
|
||||
FURI_LOG_W(TAG, "Failed to read data");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <nfc/nfc_device.h>
|
||||
#include <nfc/helpers/nfc_util.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
#include "furi_hal_rtc.h"
|
||||
|
||||
#define TAG "Troika"
|
||||
|
||||
|
@ -18,6 +19,19 @@ typedef struct {
|
|||
uint32_t data_sector;
|
||||
} TroikaCardConfig;
|
||||
|
||||
typedef enum {
|
||||
TroikaLayoutUnknown = 0x0,
|
||||
TroikaLayout2 = 0x2,
|
||||
TroikaLayoutE = 0xE,
|
||||
} TroikaLayout;
|
||||
|
||||
typedef enum {
|
||||
TroikaSublayoutUnknown = 0x0,
|
||||
TroikaSublayout3 = 0x3,
|
||||
TroikaSublayout5 = 0x5,
|
||||
TroikaSublayout6 = 0x6,
|
||||
} TroikaSubLayout;
|
||||
|
||||
static const MfClassicKeyPair troika_1k_keys[] = {
|
||||
{.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58},
|
||||
{.a = 0xa82607b01c0d, .b = 0x2910989b6880},
|
||||
|
@ -67,7 +81,7 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type)
|
|||
config->data_sector = 8;
|
||||
config->keys = troika_1k_keys;
|
||||
} else if(type == MfClassicType4k) {
|
||||
config->data_sector = 4;
|
||||
config->data_sector = 8; // Further testing needed
|
||||
config->keys = troika_4k_keys;
|
||||
} else {
|
||||
success = false;
|
||||
|
@ -76,6 +90,126 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type)
|
|||
return success;
|
||||
}
|
||||
|
||||
static TroikaLayout troika_get_layout(const MfClassicData* data, uint8_t start_block_num) {
|
||||
furi_assert(data);
|
||||
|
||||
// Layout is stored in byte 6 of block, length 4 bits (bits 52 - 55), second nibble.
|
||||
const uint8_t* layout_ptr = &data->block[start_block_num].data[6];
|
||||
const uint8_t layout = (*layout_ptr & 0x0F);
|
||||
|
||||
TroikaLayout result = TroikaLayoutUnknown;
|
||||
switch(layout) {
|
||||
case TroikaLayout2:
|
||||
case TroikaLayoutE:
|
||||
result = layout;
|
||||
break;
|
||||
default:
|
||||
// If debug is enabled - pass the actual layout value for the debug text
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
return layout;
|
||||
} else {
|
||||
return TroikaLayoutUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static TroikaSubLayout troika_get_sub_layout(const MfClassicData* data, uint8_t start_block_num) {
|
||||
furi_assert(data);
|
||||
|
||||
// Sublayout is stored in byte 7 (bits 56 - 60) of block, length 5 bits (first nibble and one bit from second nibble)
|
||||
const uint8_t* sub_layout_ptr = &data->block[start_block_num].data[7];
|
||||
const uint8_t sub_layout = (*sub_layout_ptr & 0x3F) >> 3;
|
||||
|
||||
TroikaSubLayout result = TroikaSublayoutUnknown;
|
||||
switch(sub_layout) {
|
||||
case TroikaSublayout3:
|
||||
case TroikaSublayout5:
|
||||
case TroikaSublayout6:
|
||||
result = sub_layout;
|
||||
break;
|
||||
default:
|
||||
// If debug is enabled - pass the actual sublayout value for the debug text
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
return sub_layout;
|
||||
} else {
|
||||
return TroikaSublayoutUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool troika_has_balance(TroikaLayout layout, TroikaSubLayout sub_layout) {
|
||||
UNUSED(sub_layout);
|
||||
// Layout 0x2 has no balance
|
||||
|
||||
if(layout == TroikaLayout2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t troika_get_balance(
|
||||
const MfClassicData* data,
|
||||
uint8_t start_block_num,
|
||||
TroikaLayout layout,
|
||||
TroikaSubLayout sub_layout) {
|
||||
furi_assert(data);
|
||||
|
||||
// In layout 0x3 balance in bits 188:209 ( from sector start, length 22).
|
||||
// In layout 0x5 balance in bits 165:185 ( from sector start, length 20).
|
||||
|
||||
uint32_t balance = 0;
|
||||
uint8_t balance_data_offset = 0;
|
||||
bool supported_layout = false;
|
||||
|
||||
if(layout == TroikaLayoutE && sub_layout == TroikaSublayout3) {
|
||||
balance_data_offset = 7;
|
||||
supported_layout = true;
|
||||
} else if(layout == TroikaLayoutE && sub_layout == TroikaSublayout5) {
|
||||
balance_data_offset = 4;
|
||||
supported_layout = true;
|
||||
}
|
||||
|
||||
if(supported_layout) {
|
||||
const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[balance_data_offset];
|
||||
balance |= (temp_ptr[0] & 0x3) << 18;
|
||||
balance |= temp_ptr[1] << 10;
|
||||
balance |= temp_ptr[2] << 2;
|
||||
balance |= (temp_ptr[3] & 0xC0) >> 6;
|
||||
}
|
||||
|
||||
return balance / 100;
|
||||
}
|
||||
|
||||
static uint32_t troika_get_number(
|
||||
const MfClassicData* data,
|
||||
uint8_t start_block_num,
|
||||
TroikaLayout layout,
|
||||
TroikaSubLayout sub_layout) {
|
||||
furi_assert(data);
|
||||
UNUSED(sub_layout);
|
||||
|
||||
if(layout == TroikaLayoutE || layout == TroikaLayout2) {
|
||||
const uint8_t* temp_ptr = &data->block[start_block_num].data[2];
|
||||
|
||||
uint32_t number = 0;
|
||||
for(size_t i = 1; i < 5; i++) {
|
||||
number <<= 8;
|
||||
number |= temp_ptr[i];
|
||||
}
|
||||
number >>= 4;
|
||||
number |= (temp_ptr[0] & 0xf) << 28;
|
||||
|
||||
return number;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool troika_verify_type(Nfc* nfc, MfClassicType type) {
|
||||
bool verified = false;
|
||||
|
||||
|
@ -171,22 +305,39 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||
const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
|
||||
if(key != cfg.keys[cfg.data_sector].a) break;
|
||||
|
||||
// Parse data
|
||||
// Get the block number of the block that contains the data
|
||||
const uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector);
|
||||
|
||||
const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[5];
|
||||
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
|
||||
temp_ptr = &data->block[start_block_num].data[2];
|
||||
// Get layout, sublayout, balance and number
|
||||
TroikaLayout layout = troika_get_layout(data, start_block_num);
|
||||
TroikaSubLayout sub_layout = troika_get_sub_layout(data, start_block_num);
|
||||
|
||||
uint32_t number = 0;
|
||||
for(size_t i = 1; i < 5; i++) {
|
||||
number <<= 8;
|
||||
number |= temp_ptr[i];
|
||||
if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
// If debug is enabled - proceed even if layout or sublayout is unknown, that will make collecting data easier
|
||||
if(layout == TroikaLayoutUnknown || sub_layout == TroikaSublayoutUnknown) break;
|
||||
}
|
||||
|
||||
uint32_t number = troika_get_number(data, start_block_num, layout, sub_layout);
|
||||
|
||||
furi_string_printf(parsed_data, "\e#Troika\nNum: %lu", number);
|
||||
|
||||
if(troika_has_balance(layout, sub_layout) ||
|
||||
furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
uint16_t balance = troika_get_balance(data, start_block_num, layout, sub_layout);
|
||||
furi_string_cat_printf(parsed_data, "\nBalance: %u RUR", balance);
|
||||
} else {
|
||||
furi_string_cat_printf(parsed_data, "\nBalance: Not available");
|
||||
}
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
furi_string_cat_printf(
|
||||
parsed_data,
|
||||
"\nLayout: %02x\nSublayout: %02x\nData Block: %u",
|
||||
layout,
|
||||
sub_layout,
|
||||
start_block_num);
|
||||
}
|
||||
number >>= 4;
|
||||
number |= (temp_ptr[0] & 0xf) << 28;
|
||||
|
||||
furi_string_printf(parsed_data, "\e#Troika\nNum: %lu\nBalance: %u RUR", number, balance);
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) {
|
|||
}
|
||||
|
||||
error = mf_classic_poller_sync_read(nfc, &keys, data);
|
||||
if(error != MfClassicErrorNotPresent) {
|
||||
if(error == MfClassicErrorNotPresent) {
|
||||
FURI_LOG_W(TAG, "Failed to read data");
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue