diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index b7e7497f74e..ba8f3429543 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -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; } diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c index e3dfb703c97..20bd685eeb8 100644 --- a/applications/main/nfc/plugins/supported_cards/troika.c +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -5,6 +5,7 @@ #include #include #include +#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); diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c index e9309f719ee..7e29cd08510 100644 --- a/applications/main/nfc/plugins/supported_cards/two_cities.c +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -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; }