From 9f3b80e606bab67d61c365be9bf90a2498c0a4f9 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 19 Sep 2022 19:05:04 +0300 Subject: [PATCH 1/2] Add new card parsers (#1503) * Add the "Two cities" parser * Add plantain and plantain4k parsers * Add new parsers to the supported list * United card PoC * Fix nfc device not sleeping * Completely read the 4K troika variants * Correct naming * Update to reflect upstream changes * Add support for MfUl info * Fix parsers * Card type detection fixes * Remove debug info * Fixes for the verification of cards * nfc: fix verification for supported cards * nfc: remove unused vars * Improve card reading reliability and fix plantain * plantain: change log level Co-authored-by: gornekich --- .../main/nfc/scenes/nfc_scene_device_info.c | 4 +- .../nfc_scene_mf_ultralight_read_success.c | 22 ++- .../main/nfc/scenes/nfc_scene_saved_menu.c | 4 +- lib/nfc/nfc_worker.c | 25 ++- lib/nfc/parsers/all_in_one.c | 113 ++++++++++++ lib/nfc/parsers/all_in_one.h | 9 + lib/nfc/parsers/nfc_supported_card.c | 50 ++++- lib/nfc/parsers/nfc_supported_card.h | 7 +- lib/nfc/parsers/plantain_4k_parser.c | 153 ++++++++++++++++ lib/nfc/parsers/plantain_4k_parser.h | 9 + lib/nfc/parsers/plantain_parser.c | 147 +++++++++++++++ lib/nfc/parsers/plantain_parser.h | 13 ++ lib/nfc/parsers/troika_4k_parser.c | 104 +++++++++++ lib/nfc/parsers/troika_4k_parser.h | 9 + .../{troyka_parser.c => troika_parser.c} | 42 +++-- lib/nfc/parsers/troika_parser.h | 9 + lib/nfc/parsers/troyka_parser.h | 9 - lib/nfc/parsers/two_cities.c | 171 ++++++++++++++++++ lib/nfc/parsers/two_cities.h | 9 + 19 files changed, 864 insertions(+), 45 deletions(-) create mode 100644 lib/nfc/parsers/all_in_one.c create mode 100644 lib/nfc/parsers/all_in_one.h create mode 100644 lib/nfc/parsers/plantain_4k_parser.c create mode 100644 lib/nfc/parsers/plantain_4k_parser.h create mode 100644 lib/nfc/parsers/plantain_parser.c create mode 100644 lib/nfc/parsers/plantain_parser.h create mode 100644 lib/nfc/parsers/troika_4k_parser.c create mode 100644 lib/nfc/parsers/troika_4k_parser.h rename lib/nfc/parsers/{troyka_parser.c => troika_parser.c} (68%) create mode 100644 lib/nfc/parsers/troika_parser.h delete mode 100644 lib/nfc/parsers/troyka_parser.h create mode 100644 lib/nfc/parsers/two_cities.c create mode 100644 lib/nfc/parsers/two_cities.h diff --git a/applications/main/nfc/scenes/nfc_scene_device_info.c b/applications/main/nfc/scenes/nfc_scene_device_info.c index 8228c7ea3..245aea5c5 100644 --- a/applications/main/nfc/scenes/nfc_scene_device_info.c +++ b/applications/main/nfc/scenes/nfc_scene_device_info.c @@ -47,7 +47,9 @@ void nfc_scene_device_info_on_enter(void* context) { } string_clear(country_name); } - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + } else if( + dev_data->protocol == NfcDeviceProtocolMifareClassic || + dev_data->protocol == NfcDeviceProtocolMifareUl) { string_set(temp_str, nfc->dev->dev_data.parsed_data); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c index 56fa20578..f6dc5984e 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -34,15 +34,19 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { nfc); string_t temp_str; - string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); - string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", data->uid[i]); - } - string_cat_printf( - temp_str, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); - if(mf_ul_data->data_read != mf_ul_data->data_size) { - string_cat_printf(temp_str, "\nPassword-protected pages!"); + if(string_size(nfc->dev->dev_data.parsed_data)) { + string_init_set(temp_str, nfc->dev->dev_data.parsed_data); + } else { + string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); + string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + string_cat_printf(temp_str, " %02X", data->uid[i]); + } + string_cat_printf( + temp_str, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + if(mf_ul_data->data_read != mf_ul_data->data_size) { + string_cat_printf(temp_str, "\nPassword-protected pages!"); + } } widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); string_clear(temp_str); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index c7aec5d87..c1043b3a7 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -91,7 +91,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { bool application_info_present = false; if(dev_data->protocol == NfcDeviceProtocolEMV) { application_info_present = true; - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + } else if( + dev_data->protocol == NfcDeviceProtocolMifareClassic || + dev_data->protocol == NfcDeviceProtocolMifareUl) { application_info_present = nfc_supported_card_verify_and_parse(dev_data); } diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 6355f8d1e..2feae443f 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -123,7 +123,25 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC } do { - // Read card + // Try to read supported card + FURI_LOG_I(TAG, "Trying to read a supported card ..."); + for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { + if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareUl) { + if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { + if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { + read_success = true; + nfc_supported_card[i].parse(nfc_worker->dev_data); + break; + } + } else { + furi_hal_nfc_sleep(); + } + } + } + if(read_success) break; + furi_hal_nfc_sleep(); + + // Otherwise, try to read as usual if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; if(!mf_ul_read_card(tx_rx, &reader, &data)) break; // Copy data @@ -149,14 +167,17 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont do { // Try to read supported card - FURI_LOG_I(TAG, "Try read supported card ..."); + FURI_LOG_I(TAG, "Trying to read a supported card ..."); for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { read_success = true; nfc_supported_card[i].parse(nfc_worker->dev_data); + break; } + } else { + furi_hal_nfc_sleep(); } } } diff --git a/lib/nfc/parsers/all_in_one.c b/lib/nfc/parsers/all_in_one.c new file mode 100644 index 000000000..b49a32f7c --- /dev/null +++ b/lib/nfc/parsers/all_in_one.c @@ -0,0 +1,113 @@ +#include "nfc_supported_card.h" +#include "all_in_one.h" + +#include +#include + +#include "furi_hal.h" + +#define ALL_IN_ONE_LAYOUT_UNKNOWN 0 +#define ALL_IN_ONE_LAYOUT_A 1 +#define ALL_IN_ONE_LAYOUT_D 2 +#define ALL_IN_ONE_LAYOUT_E2 3 +#define ALL_IN_ONE_LAYOUT_E3 4 +#define ALL_IN_ONE_LAYOUT_E5 5 +#define ALL_IN_ONE_LAYOUT_2 6 + +uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) { + // I absolutely hate what's about to happen here. + + // Switch on the second half of the third byte of page 5 + FURI_LOG_I("all_in_one", "Layout byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 2]); + FURI_LOG_I( + "all_in_one", "Layout half-byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 3] & 0x0F); + switch(dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F) { + // If it is A, the layout type is a type A layout + case 0x0A: + return ALL_IN_ONE_LAYOUT_A; + case 0x0D: + return ALL_IN_ONE_LAYOUT_D; + case 0x02: + return ALL_IN_ONE_LAYOUT_2; + default: + FURI_LOG_I( + "all_in_one", + "Unknown layout type: %d", + dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F); + return ALL_IN_ONE_LAYOUT_UNKNOWN; + } +} + +bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + UNUSED(nfc_worker); + // If this is a all_in_one pass, first 2 bytes of page 4 are 0x45 0xD9 + MfUltralightReader reader = {}; + MfUltralightData data = {}; + + if(!mf_ul_read_card(tx_rx, &reader, &data)) { + return false; + } else { + if(data.data[4 * 4] == 0x45 && data.data[4 * 4 + 1] == 0xD9) { + FURI_LOG_I("all_in_one", "Pass verified"); + return true; + } + } + return false; +} + +bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + MfUltralightReader reader = {}; + MfUltralightData data = {}; + if(!mf_ul_read_card(tx_rx, &reader, &data)) { + return false; + } else { + memcpy(&nfc_worker->dev_data->mf_ul_data, &data, sizeof(data)); + FURI_LOG_I("all_in_one", "Card read"); + return true; + } +} + +bool all_in_one_parser_parse(NfcDeviceData* dev_data) { + if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) { + FURI_LOG_I("all_in_one", "Pass not verified"); + return false; + } + + // If the layout is a then the ride count is stored in the first byte of page 8 + uint8_t ride_count = 0; + uint32_t serial = 0; + if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) { + ride_count = dev_data->mf_ul_data.data[4 * 8]; + } else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) { + // If the layout is D, the ride count is stored in the second byte of page 9 + ride_count = dev_data->mf_ul_data.data[4 * 9 + 1]; + // I hate this with a burning passion. + + // The number starts at the second half of the third byte on page 4, and is 32 bits long + // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte + // B8 17 A2 A4 BD becomes 81 7A 2A 4B + serial = (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | + dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | + dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | + dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | + (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); + } else { + FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data)); + ride_count = 137; + } + + // I hate this with a burning passion. + + // The number starts at the second half of the third byte on page 4, and is 32 bits long + // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte + // B8 17 A2 A4 BD becomes 81 7A 2A 4B + serial = + (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | + dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | + dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); + + // Format string for rides count + string_printf( + dev_data->parsed_data, "\e#All-In-One\nNumber: %u\nRides left: %u", serial, ride_count); + return true; +} \ No newline at end of file diff --git a/lib/nfc/parsers/all_in_one.h b/lib/nfc/parsers/all_in_one.h new file mode 100644 index 000000000..9b646d4dc --- /dev/null +++ b/lib/nfc/parsers/all_in_one.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool all_in_one_parser_parse(NfcDeviceData* dev_data); \ No newline at end of file diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/parsers/nfc_supported_card.c index 480c970e7..fc2dc34e0 100644 --- a/lib/nfc/parsers/nfc_supported_card.c +++ b/lib/nfc/parsers/nfc_supported_card.c @@ -1,14 +1,54 @@ #include "nfc_supported_card.h" -#include "troyka_parser.h" +#include "plantain_parser.h" +#include "troika_parser.h" +#include "plantain_4k_parser.h" +#include "troika_4k_parser.h" +#include "two_cities.h" +#include "all_in_one.h" NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { - [NfcSupportedCardTypeTroyka] = + [NfcSupportedCardTypePlantain] = { .protocol = NfcDeviceProtocolMifareClassic, - .verify = troyka_parser_verify, - .read = troyka_parser_read, - .parse = troyka_parser_parse, + .verify = plantain_parser_verify, + .read = plantain_parser_read, + .parse = plantain_parser_parse, + }, + [NfcSupportedCardTypeTroika] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troika_parser_verify, + .read = troika_parser_read, + .parse = troika_parser_parse, + }, + [NfcSupportedCardTypePlantain4K] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = plantain_4k_parser_verify, + .read = plantain_4k_parser_read, + .parse = plantain_4k_parser_parse, + }, + [NfcSupportedCardTypeTroika4K] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troika_4k_parser_verify, + .read = troika_4k_parser_read, + .parse = troika_4k_parser_parse, + }, + [NfcSupportedCardTypeTwoCities] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = two_cities_parser_verify, + .read = two_cities_parser_read, + .parse = two_cities_parser_parse, + }, + [NfcSupportedCardTypeAllInOne] = + { + .protocol = NfcDeviceProtocolMifareUl, + .verify = all_in_one_parser_verify, + .read = all_in_one_parser_read, + .parse = all_in_one_parser_parse, }, }; diff --git a/lib/nfc/parsers/nfc_supported_card.h b/lib/nfc/parsers/nfc_supported_card.h index 9b5d1c053..d34b5794a 100644 --- a/lib/nfc/parsers/nfc_supported_card.h +++ b/lib/nfc/parsers/nfc_supported_card.h @@ -7,7 +7,12 @@ #include typedef enum { - NfcSupportedCardTypeTroyka, + NfcSupportedCardTypePlantain, + NfcSupportedCardTypeTroika, + NfcSupportedCardTypePlantain4K, + NfcSupportedCardTypeTroika4K, + NfcSupportedCardTypeTwoCities, + NfcSupportedCardTypeAllInOne, NfcSupportedCardTypeEnd, } NfcSupportedCardType; diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/parsers/plantain_4k_parser.c new file mode 100644 index 000000000..77387707b --- /dev/null +++ b/lib/nfc/parsers/plantain_4k_parser.c @@ -0,0 +1,153 @@ +#include "nfc_supported_card.h" +#include "plantain_parser.h" // For luhn and string_push_uint64 + +#include +#include + +#include "furi_hal.h" + +static const MfClassicAuthContext plantain_keys_4k[] = { + {.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF}, + {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, + {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, + {.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, + {.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, + {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, + {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, + {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, + {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf}, + {.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a}, + {.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b}, + {.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3}, + {.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22}, + {.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056}, + {.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510}, + {.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf}, + {.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439}, + {.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406}, + {.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3}, + {.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA}, + {.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f}, + {.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3}, + {.sector = 30, .key_a = 0x0eb23cc8110b, .key_b = 0x04dc35277635}, + {.sector = 31, .key_a = 0xbc4580b7f20b, .key_b = 0xd0a4131fb290}, + {.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023}, + {.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9}, + {.sector = 34, .key_a = 0xfd8705e721b0, .key_b = 0x296fc317a513}, + {.sector = 35, .key_a = 0x22052b480d11, .key_b = 0xe19504c39461}, + {.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7}, + {.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a}, + {.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7}, + {.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085}, +}; + +bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { + return false; + } + + uint8_t sector = 8; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Plant4K", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { + FURI_LOG_D("Plant4K", "Sector %d verified", sector); + return true; + } + return false; +} + +bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(plantain_keys_4k); i++) { + mf_classic_reader_add_sector( + &reader, + plantain_keys_4k[i].sector, + plantain_keys_4k[i].key_a, + plantain_keys_4k[i].key_b); + FURI_LOG_T("plant4k", "Added sector %d", plantain_keys_4k[i].sector); + } + for(int i = 0; i < 5; i++) { + if(mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40) { + return true; + } + } + return false; +} + +bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != plantain_keys_4k[8].key_a) return false; + + // Point to block 0 of sector 4, value 0 + uint8_t* temp_ptr = &data->block[4 * 4].value[0]; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = &data->block[0 * 4].value[0]; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + // Convert card number to string + string_t card_number_str; + string_init(card_number_str); + // Should look like "361301047292848684" + // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead + string_push_uint64(card_number, card_number_str); + // Add suffix with luhn checksum (1 digit) to the card number string + string_t card_number_suffix; + string_init(card_number_suffix); + + // The number to calculate the checksum on doesn't fit into uint64_t, idk + //uint8_t luhn_checksum = plantain_calculate_luhn(card_number); + + // // Convert luhn checksum to string + // string_t luhn_checksum_str; + // string_init(luhn_checksum_str); + // string_push_uint64(luhn_checksum, luhn_checksum_str); + + string_cat_printf(card_number_suffix, "-"); + // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum); + string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); + // Free all not needed strings + string_clear(card_number_suffix); + // string_clear(luhn_checksum_str); + + string_printf( + dev_data->parsed_data, + "\e#Plantain\nN:%s\nBalance:%d\n", + string_get_cstr(card_number_str), + balance); + string_clear(card_number_str); + + return true; +} diff --git a/lib/nfc/parsers/plantain_4k_parser.h b/lib/nfc/parsers/plantain_4k_parser.h new file mode 100644 index 000000000..29998af15 --- /dev/null +++ b/lib/nfc/parsers/plantain_4k_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_4k_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/parsers/plantain_parser.c new file mode 100644 index 000000000..ff81b8e9b --- /dev/null +++ b/lib/nfc/parsers/plantain_parser.c @@ -0,0 +1,147 @@ +#include "nfc_supported_card.h" + +#include +#include + +#include "furi_hal.h" + +static const MfClassicAuthContext plantain_keys[] = { + {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, + {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, + {.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, + {.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, + {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, + {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, + {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, + {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, +}; + +bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) { + return false; + } + + uint8_t sector = 8; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Plant", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { + FURI_LOG_D("Plant", "Sector %d verified", sector); + return true; + } + return false; +} + +bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(plantain_keys); i++) { + mf_classic_reader_add_sector( + &reader, plantain_keys[i].sector, plantain_keys[i].key_a, plantain_keys[i].key_b); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; +} + +void string_push_uint64(uint64_t input, string_t output) { + const uint8_t base = 10; + + do { + char c = input % base; + input /= base; + + if(c < 10) + c += '0'; + else + c += 'A' - 10; + string_push_back(output, c); + } while(input); + + // reverse string + for(uint8_t i = 0; i < string_size(output) / 2; i++) { + char c = string_get_char(output, i); + string_set_char(output, i, string_get_char(output, string_size(output) - i - 1)); + string_set_char(output, string_size(output) - i - 1, c); + } +} + +uint8_t plantain_calculate_luhn(uint64_t number) { + // No. + UNUSED(number); + return 0; +} + +bool plantain_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != plantain_keys[8].key_a) return false; + + // Point to block 0 of sector 4, value 0 + uint8_t* temp_ptr = &data->block[4 * 4].value[0]; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = &data->block[0 * 4].value[0]; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + // Convert card number to string + string_t card_number_str; + string_init(card_number_str); + // Should look like "361301047292848684" + // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead + string_push_uint64(card_number, card_number_str); + // Add suffix with luhn checksum (1 digit) to the card number string + string_t card_number_suffix; + string_init(card_number_suffix); + + // The number to calculate the checksum on doesn't fit into uint64_t, idk + //uint8_t luhn_checksum = plantain_calculate_luhn(card_number); + + // // Convert luhn checksum to string + // string_t luhn_checksum_str; + // string_init(luhn_checksum_str); + // string_push_uint64(luhn_checksum, luhn_checksum_str); + + string_cat_printf(card_number_suffix, "-"); + // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum); + string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); + // Free all not needed strings + string_clear(card_number_suffix); + // string_clear(luhn_checksum_str); + + string_printf( + dev_data->parsed_data, + "\e#Plantain\nN:%s\nBalance:%d\n", + string_get_cstr(card_number_str), + balance); + string_clear(card_number_str); + + return true; +} diff --git a/lib/nfc/parsers/plantain_parser.h b/lib/nfc/parsers/plantain_parser.h new file mode 100644 index 000000000..d37f0dd48 --- /dev/null +++ b/lib/nfc/parsers/plantain_parser.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool plantain_parser_parse(NfcDeviceData* dev_data); + +void string_push_uint64(uint64_t input, string_t output); + +uint8_t plantain_calculate_luhn(uint64_t number); diff --git a/lib/nfc/parsers/troika_4k_parser.c b/lib/nfc/parsers/troika_4k_parser.c new file mode 100644 index 000000000..8c32381f2 --- /dev/null +++ b/lib/nfc/parsers/troika_4k_parser.c @@ -0,0 +1,104 @@ +#include "nfc_supported_card.h" + +#include +#include + +static const MfClassicAuthContext troika_4k_keys[] = { + {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, + {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, + {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5}, + {.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698}, + {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb}, + {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, + {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, + {.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d}, + {.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b}, + {.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0}, + {.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, + {.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75}, + {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408}, + {.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df}, + {.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9}, + {.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4}, + {.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216}, + {.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba}, + {.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252}, + {.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01}, + {.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994}, + {.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6}, + {.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32}, + {.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3}, + {.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5}, + {.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a}, + {.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80}, + {.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d}, + {.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2}, + {.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324}, + {.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae}, + {.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda}, + {.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d}, + {.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465}, + {.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4}, + {.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70}, +}; + +bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { + return false; + } + + uint8_t sector = 11; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Troika", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { + FURI_LOG_D("Troika", "Sector %d verified", sector); + return true; + } + return false; +} + +bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) { + mf_classic_reader_add_sector( + &reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; +} + +bool troika_4k_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != troika_4k_keys[4].key_a) return false; + + // Verify card type + if(data->type != MfClassicType4k) return false; + + uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; + uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; + temp_ptr = &data->block[8 * 4].value[3]; + uint32_t number = 0; + for(size_t i = 0; i < 4; i++) { + number <<= 8; + number |= temp_ptr[i]; + } + number >>= 4; + + string_printf(dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); + + return true; +} diff --git a/lib/nfc/parsers/troika_4k_parser.h b/lib/nfc/parsers/troika_4k_parser.h new file mode 100644 index 000000000..c1d6f01d3 --- /dev/null +++ b/lib/nfc/parsers/troika_4k_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_4k_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/troyka_parser.c b/lib/nfc/parsers/troika_parser.c similarity index 68% rename from lib/nfc/parsers/troyka_parser.c rename to lib/nfc/parsers/troika_parser.c index 51ffa42e1..f396b1680 100644 --- a/lib/nfc/parsers/troyka_parser.c +++ b/lib/nfc/parsers/troika_parser.c @@ -3,7 +3,7 @@ #include #include -static const MfClassicAuthContext troyka_keys[] = { +static const MfClassicAuthContext troika_keys[] = { {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, @@ -22,42 +22,50 @@ static const MfClassicAuthContext troyka_keys[] = { {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, }; -bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { furi_assert(nfc_worker); UNUSED(nfc_worker); + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) { + return false; + } - MfClassicAuthContext auth_ctx = { - .key_a = MF_CLASSIC_NO_KEY, - .key_b = MF_CLASSIC_NO_KEY, - .sector = 8, - }; - return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333); + uint8_t sector = 11; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("Troika", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { + FURI_LOG_D("Troika", "Sector %d verified", sector); + return true; + } + return false; } -bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { furi_assert(nfc_worker); MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); - for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { + for(size_t i = 0; i < COUNT_OF(troika_keys); i++) { mf_classic_reader_add_sector( - &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); + &reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b); } return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; } -bool troyka_parser_parse(NfcDeviceData* dev_data) { +bool troika_parser_parse(NfcDeviceData* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; - bool troyka_parsed = false; + bool troika_parsed = false; do { // Verify key MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(key != troyka_keys[8].key_a) break; + if(key != troika_keys[8].key_a) break; + + // Verify card type + if(data->type != MfClassicType1k) break; // Parse data uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; @@ -71,9 +79,9 @@ bool troyka_parser_parse(NfcDeviceData* dev_data) { number >>= 4; string_printf( - dev_data->parsed_data, "\e#Troyka\nNum: %ld\nBalance: %d rur.", number, balance); - troyka_parsed = true; + dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); + troika_parsed = true; } while(false); - return troyka_parsed; + return troika_parsed; } diff --git a/lib/nfc/parsers/troika_parser.h b/lib/nfc/parsers/troika_parser.h new file mode 100644 index 000000000..2aae48d29 --- /dev/null +++ b/lib/nfc/parsers/troika_parser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool troika_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/troyka_parser.h b/lib/nfc/parsers/troyka_parser.h deleted file mode 100644 index 445fe40e5..000000000 --- a/lib/nfc/parsers/troyka_parser.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "nfc_supported_card.h" - -bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -bool troyka_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/parsers/two_cities.c new file mode 100644 index 000000000..d052dff19 --- /dev/null +++ b/lib/nfc/parsers/two_cities.c @@ -0,0 +1,171 @@ +#include "nfc_supported_card.h" +#include "plantain_parser.h" // For plantain-specific stuff + +#include +#include + +#include "furi_hal.h" + +static const MfClassicAuthContext two_cities_keys_4k[] = { + {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, + {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, + {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, + {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, + {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, + {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, + {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, + {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, + {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf}, + {.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a}, + {.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b}, + {.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3}, + {.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22}, + {.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056}, + {.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510}, + {.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf}, + {.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439}, + {.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406}, + {.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3}, + {.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA}, + {.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f}, + {.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3}, + {.sector = 30, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, + {.sector = 31, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, + {.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023}, + {.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9}, + {.sector = 34, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 35, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, + {.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7}, + {.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a}, + {.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7}, + {.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085}, +}; + +bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + UNUSED(nfc_worker); + + if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { + return false; + } + + uint8_t sector = 4; + uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + FURI_LOG_D("2cities", "Verifying sector %d", sector); + if(mf_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) { + FURI_LOG_D("2cities", "Sector %d verified", sector); + return true; + } + return false; +} + +bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + + MfClassicReader reader = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(two_cities_keys_4k); i++) { + mf_classic_reader_add_sector( + &reader, + two_cities_keys_4k[i].sector, + two_cities_keys_4k[i].key_a, + two_cities_keys_4k[i].key_b); + FURI_LOG_T("2cities", "Added sector %d", two_cities_keys_4k[i].sector); + } + + return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; +} + +bool two_cities_parser_parse(NfcDeviceData* dev_data) { + MfClassicData* data = &dev_data->mf_classic_data; + + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(key != two_cities_keys_4k[4].key_a) return false; + + // ===== + // PLANTAIN + // ===== + + // Point to block 0 of sector 4, value 0 + uint8_t* temp_ptr = &data->block[4 * 4].value[0]; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = &data->block[0 * 4].value[0]; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + // Convert card number to string + string_t card_number_str; + string_init(card_number_str); + // Should look like "361301047292848684" + // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead + string_push_uint64(card_number, card_number_str); + // Add suffix with luhn checksum (1 digit) to the card number string + string_t card_number_suffix; + string_init(card_number_suffix); + + // The number to calculate the checksum on doesn't fit into uint64_t, idk + //uint8_t luhn_checksum = two_cities_calculate_luhn(card_number); + + // // Convert luhn checksum to string + // string_t luhn_checksum_str; + // string_init(luhn_checksum_str); + // string_push_uint64(luhn_checksum, luhn_checksum_str); + + string_cat_printf(card_number_suffix, "-"); + // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum); + string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); + // Free all not needed strings + string_clear(card_number_suffix); + // string_clear(luhn_checksum_str); + + // ===== + // --PLANTAIN-- + // ===== + // TROIKA + // ===== + + uint8_t* troika_temp_ptr = &data->block[8 * 4 + 1].value[5]; + uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25; + troika_temp_ptr = &data->block[8 * 4].value[3]; + uint32_t troika_number = 0; + for(size_t i = 0; i < 4; i++) { + troika_number <<= 8; + troika_number |= troika_temp_ptr[i]; + } + troika_number >>= 4; + + string_printf( + dev_data->parsed_data, + "\e#Troika+Plantain\nPN: %s\nPB: %d rur.\nTN: %d\nTB: %d rur.\n", + string_get_cstr(card_number_str), + balance, + troika_number, + troika_balance); + string_clear(card_number_str); + + return true; +} diff --git a/lib/nfc/parsers/two_cities.h b/lib/nfc/parsers/two_cities.h new file mode 100644 index 000000000..e735bea8e --- /dev/null +++ b/lib/nfc/parsers/two_cities.h @@ -0,0 +1,9 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); + +bool two_cities_parser_parse(NfcDeviceData* dev_data); From f5ff6438d11c44e33a8e7178499b43ccc0084208 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 19 Sep 2022 11:43:53 -0500 Subject: [PATCH 2/2] NFC user dict list, delete, and de-duplication. (#1533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add MFC user keys list * Leakey submenu fix * Set next target for Save/Delete success scenes * Delete individual user keys * Update count of total keys * Fix memory leak * Check for duplicate keys * Remove a submodule that I never added? * Swap and position icons * Revamp according to design doc * Rename icons to include size and replace keychain icon with smaller variant * Fix typos * Final fixes * Fufill requested changes * Cleanup comments * Merge dev after SD app loading * Fixing icon names * Revert merge mistakes and API version * Scene switching adjustments * F7: add/change/remove some nfc icons in api_symbols.csv Co-authored-by: あく --- .vscode/example/settings.json | 2 +- .../main/lfrfid/views/lfrfid_view_read.c | 2 +- applications/main/nfc/nfc_i.h | 5 + .../main/nfc/scenes/nfc_scene_config.h | 3 + .../nfc/scenes/nfc_scene_delete_success.c | 9 +- .../nfc/scenes/nfc_scene_dict_not_found.c | 5 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 +- .../nfc/scenes/nfc_scene_mf_classic_keys.c | 24 +- .../scenes/nfc_scene_mf_classic_keys_add.c | 13 +- .../scenes/nfc_scene_mf_classic_keys_delete.c | 77 ++++++ .../scenes/nfc_scene_mf_classic_keys_list.c | 60 +++++ ...nfc_scene_mf_classic_keys_warn_duplicate.c | 47 ++++ .../nfc_scene_mf_ultralight_read_auth.c | 2 +- applications/main/nfc/scenes/nfc_scene_read.c | 2 +- .../nfc_scene_restore_original_confirm.c | 2 +- .../main/nfc/scenes/nfc_scene_save_success.c | 5 +- .../bt_hid_app/views/bt_hid_keyboard.c | 2 +- .../plugins/bt_hid_app/views/bt_hid_mouse.c | 2 +- .../desktop/views/desktop_view_pin_input.c | 2 +- assets/icons/NFC/Keychain.png | Bin 3750 -> 0 bytes assets/icons/NFC/Keychain_39x36.png | Bin 0 -> 3775 bytes .../{NFC_manual.png => NFC_manual_60x50.png} | Bin assets/icons/NFC/Reader_detect_43x40.png | Bin 0 -> 3799 bytes .../{Restoring.png => Restoring_38x32.png} | Bin ...n_arrow_up7x9.png => Pin_arrow_up_7x9.png} | Bin firmware/targets/f7/api_symbols.csv | 11 +- lib/nfc/helpers/mf_classic_dict.c | 230 +++++++++++++++--- lib/nfc/helpers/mf_classic_dict.h | 22 +- 28 files changed, 465 insertions(+), 64 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c delete mode 100644 assets/icons/NFC/Keychain.png create mode 100644 assets/icons/NFC/Keychain_39x36.png rename assets/icons/NFC/{NFC_manual.png => NFC_manual_60x50.png} (100%) create mode 100644 assets/icons/NFC/Reader_detect_43x40.png rename assets/icons/NFC/{Restoring.png => Restoring_38x32.png} (100%) rename assets/icons/PIN/{Pin_arrow_up7x9.png => Pin_arrow_up_7x9.png} (100%) diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index d2917a90e..d84707e07 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -22,4 +22,4 @@ "SConstruct": "python", "*.fam": "python", } -} \ No newline at end of file +} diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 2b63175da..66caf8df7 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -16,7 +16,7 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { LfRfidReadViewModel* model = _model; canvas_set_color(canvas, ColorBlack); - canvas_draw_icon(canvas, 0, 8, &I_NFC_manual); + canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50); canvas_set_font(canvas, FontPrimary); diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index 60e1c1997..15ea5348f 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -37,6 +37,10 @@ #include "rpc/rpc_app.h" +#include + +ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST); + #define NFC_TEXT_STORE_SIZE 128 typedef enum { @@ -60,6 +64,7 @@ struct Nfc { char text_store[NFC_TEXT_STORE_SIZE + 1]; string_t text_box_store; uint8_t byte_input_store[6]; + MfClassicUserKeys_t mfc_key_strs; // Used in MFC key listing void* rpc_ctx; NfcRpcState rpc_state; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 540fe1098..a25850c84 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -32,6 +32,9 @@ ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) +ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) +ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) +ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 713b99ebf..1664a9e5b 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -25,8 +25,13 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneFileSelect); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } } } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_dict_not_found.c b/applications/main/nfc/scenes/nfc_scene_dict_not_found.c index dc21b08b1..781c5a932 100644 --- a/applications/main/nfc/scenes/nfc_scene_dict_not_found.c +++ b/applications/main/nfc/scenes/nfc_scene_dict_not_found.c @@ -31,7 +31,10 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneExtraActions); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 43e49e5a0..e888e9d35 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -17,7 +17,7 @@ void nfc_scene_extra_actions_on_enter(void* context) { submenu_add_item( submenu, - "Mf Classic Keys", + "Mifare Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index fcb8bc189..a2e6ae745 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -26,15 +26,25 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) { } widget_add_string_element( - nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys"); + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys"); char temp_str[32]; - snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "Flipper list: %ld", flipper_dict_keys_total); widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); - snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total); + snprintf(temp_str, sizeof(temp_str), "User list: %ld", user_dict_keys_total); widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_button_element( nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain); + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc); + widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); + if(user_dict_keys_total > 0) { + widget_add_button_element( + nfc->widget, + GuiButtonTypeRight, + "List", + nfc_scene_mf_classic_keys_widget_callback, + nfc); + } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } @@ -47,6 +57,12 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) if(event.event == GuiButtonTypeCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); consumed = true; + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(nfc->scene_manager); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); + consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index 9f56b0f45..2921d21c9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -29,15 +29,16 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { // Add key to dict - bool key_added = false; MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); if(dict) { - if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { - key_added = true; + if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) { + scene_manager_next_scene( + nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); + } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } - } - if(key_added) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c new file mode 100644 index 000000000..16a189da6 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c @@ -0,0 +1,77 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_delete_on_enter(void* context) { + Nfc* nfc = context; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + uint32_t key_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + // Setup Custom Widget view + string_t key_str; + string_init(key_str); + + widget_add_string_element( + nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); + widget_add_button_element( + nfc->widget, + GuiButtonTypeLeft, + "Cancel", + nfc_scene_mf_classic_keys_delete_widget_callback, + nfc); + widget_add_button_element( + nfc->widget, + GuiButtonTypeRight, + "Delete", + nfc_scene_mf_classic_keys_delete_widget_callback, + nfc); + + mf_classic_dict_get_key_at_index_str(dict, key_str, key_index); + widget_add_string_element( + nfc->widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(key_str)); + + string_clear(key_str); + mf_classic_dict_free(dict); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + uint32_t key_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(event.event == GuiButtonTypeRight) { + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(mf_classic_dict_delete_index(dict, key_index)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } + mf_classic_dict_free(dict); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_delete_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c new file mode 100644 index 000000000..36f01897e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_classic_keys_list_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + uint32_t index = 0; + string_t temp_key; + MfClassicUserKeys_init(nfc->mfc_key_strs); + string_init(temp_key); + if(dict) { + mf_classic_dict_rewind(dict); + while(mf_classic_dict_get_next_key_str(dict, temp_key)) { + char* current_key = (char*)malloc(sizeof(char) * 13); + strncpy(current_key, string_get_cstr(temp_key), 12); + MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key); + FURI_LOG_D("ListKeys", "Key %d: %s", index, current_key); + submenu_add_item( + submenu, + current_key, + index++, + nfc_scene_mf_classic_keys_list_submenu_callback, + nfc); + } + } + submenu_set_header(submenu, "Select key to delete:"); + mf_classic_dict_free(dict); + string_clear(temp_key); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + consumed = true; + } + return consumed; +} + +void nfc_scene_mf_classic_keys_list_on_exit(void* context) { + Nfc* nfc = context; + + MfClassicUserKeys_it_t it; + for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it); + MfClassicUserKeys_next(it)) { + free(*MfClassicUserKeys_ref(it)); + } + MfClassicUserKeys_clear(nfc->mfc_key_strs); + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c new file mode 100644 index 000000000..ab41989b2 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Please enter a\n" + "different key.", + 4, + 24, + AlignLeft, + AlignTop); + popup_set_timeout(popup, 5000); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeysAdd); + } + } + return consumed; +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) { + Nfc* nfc = context; + + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c index 1a106bdb4..25008004b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -27,7 +27,7 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState popup_reset(nfc->popup); popup_set_text( nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); } else if(state == NfcSceneMfUlReadStateReading) { popup_reset(nfc->popup); popup_set_header( diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index da21b9f3d..e6df476f0 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -26,7 +26,7 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { popup_reset(nfc->popup); popup_set_text( nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); } else if(state == NfcSceneReadStateReading) { popup_reset(nfc->popup); popup_set_header( diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 2c12749df..730dd41e8 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring); + dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index a3b17451f..dcd2519f1 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -27,7 +27,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); } else { diff --git a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c b/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c index 1088e2959..3617dc0f1 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c +++ b/applications/plugins/bt_hid_app/views/bt_hid_keyboard.c @@ -109,7 +109,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, }, { - {.width = 1, .icon = &I_Pin_arrow_up7x9, .value = HID_KEYBOARD_L_SHIFT}, + {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, diff --git a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c b/applications/plugins/bt_hid_app/views/bt_hid_mouse.c index f9d84f9fb..395cb52c9 100644 --- a/applications/plugins/bt_hid_app/views/bt_hid_mouse.c +++ b/applications/plugins/bt_hid_app/views/bt_hid_mouse.c @@ -53,7 +53,7 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9); + canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9); canvas_set_color(canvas, ColorBlack); // Down diff --git a/applications/services/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c index 5502d5f6d..bf05f06b9 100644 --- a/applications/services/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -117,7 +117,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9); break; case InputKeyUp: - canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9); + canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9); break; case InputKeyLeft: canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7); diff --git a/assets/icons/NFC/Keychain.png b/assets/icons/NFC/Keychain.png deleted file mode 100644 index 7ba1b11da6fde4e2b4b148a35593937f2a2ac471..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3750 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 } -bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - uint8_t key_byte_tmp = 0; - string_t next_line; - string_init(next_line); - - bool key_read = false; - *key = 0ULL; - while(!key_read) { - if(!stream_read_line(dict->stream, next_line)) break; - if(string_get_char(next_line, 0) == '#') continue; - if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - for(uint8_t i = 0; i < 12; i += 2) { - args_char_to_hex( - string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); - *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); - } - key_read = true; - } - - string_clear(next_line); - return key_read; -} - bool mf_classic_dict_rewind(MfClassicDict* dict) { furi_assert(dict); furi_assert(dict->stream); @@ -125,24 +117,194 @@ bool mf_classic_dict_rewind(MfClassicDict* dict) { return stream_rewind(dict->stream); } -bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { +bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key) { furi_assert(dict); furi_assert(dict->stream); - string_t key_str; - string_init(key_str); - for(size_t i = 0; i < 6; i++) { - string_cat_printf(key_str, "%02X", key[i]); + bool key_read = false; + string_reset(key); + while(!key_read) { + if(!stream_read_line(dict->stream, key)) break; + if(string_get_char(key, 0) == '#') continue; + if(string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue; + string_left(key, 12); + key_read = true; } - string_cat_printf(key_str, "\n"); + + return key_read; +} + +bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + bool key_read = mf_classic_dict_get_next_key_str(dict, temp_key); + if(key_read) { + mf_classic_dict_str_to_int(temp_key, key); + } + string_clear(temp_key); + return key_read; +} + +bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + string_init(next_line); + + bool key_found = false; + stream_rewind(dict->stream); + while(!key_found) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + string_left(next_line, 12); + if(!string_equal_p(key, next_line)) continue; + key_found = true; + } + + string_clear(next_line); + return key_found; +} + +bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) { + string_t temp_key; + + string_init(temp_key); + mf_classic_dict_int_to_str(key, temp_key); + bool key_found = mf_classic_dict_is_key_present_str(dict, temp_key); + string_clear(temp_key); + return key_found; +} + +bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_cat_printf(key, "\n"); bool key_added = false; do { if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; - if(!stream_insert_string(dict->stream, key_str)) break; + if(!stream_insert_string(dict->stream, key)) break; + dict->total_keys++; key_added = true; } while(false); - string_clear(key_str); + string_left(key, 12); return key_added; } + +bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + mf_classic_dict_int_to_str(key, temp_key); + bool key_added = mf_classic_dict_add_key_str(dict, temp_key); + + string_clear(temp_key); + return key_added; +} + +bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + uint32_t index = 0; + string_init(next_line); + string_reset(key); + + bool key_found = false; + while(!key_found) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + if(index++ != target) continue; + string_set_n(key, next_line, 0, 12); + key_found = true; + } + + string_clear(next_line); + return key_found; +} + +bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + bool key_found = mf_classic_dict_get_key_at_index_str(dict, temp_key, target); + if(key_found) { + mf_classic_dict_str_to_int(temp_key, key); + } + string_clear(temp_key); + return key_found; +} + +bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + string_init(next_line); + + bool key_found = false; + uint32_t index = 0; + stream_rewind(dict->stream); + while(!key_found) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + string_left(next_line, 12); + if(!string_equal_p(key, next_line)) continue; + key_found = true; + *target = index; + } + + string_clear(next_line); + return key_found; +} + +bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t temp_key; + string_init(temp_key); + mf_classic_dict_int_to_str(key, temp_key); + bool key_found = mf_classic_dict_find_index_str(dict, temp_key, target); + + string_clear(temp_key); + return key_found; +} + +bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target) { + furi_assert(dict); + furi_assert(dict->stream); + + string_t next_line; + string_init(next_line); + uint32_t index = 0; + + bool key_removed = false; + while(!key_removed) { + if(!stream_read_line(dict->stream, next_line)) break; + if(string_get_char(next_line, 0) == '#') continue; + if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + if(index++ != target) continue; + stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent); + if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break; + dict->total_keys--; + key_removed = true; + } + + string_clear(next_line); + return key_removed; +} diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/helpers/mf_classic_dict.h index 2654e668c..aaea3c400 100644 --- a/lib/nfc/helpers/mf_classic_dict.h +++ b/lib/nfc/helpers/mf_classic_dict.h @@ -21,8 +21,26 @@ void mf_classic_dict_free(MfClassicDict* dict); uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict); -bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); - bool mf_classic_dict_rewind(MfClassicDict* dict); +bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key); + +bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key); + +bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); + +bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key); + +bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target); + +bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target); + bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key); + +bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key); + +bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target); + +bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target); + +bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target);