Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Moscow social card parser #3464

Open
wants to merge 56 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
380bfd3
Updated troyka layout (full version)
assasinfil Jan 23, 2024
fbe2387
Changed to furi func
assasinfil Jan 23, 2024
4254766
Small refactor
assasinfil Jan 24, 2024
39d4925
Merge branch 'dev' into troyka
assasinfil Feb 15, 2024
d13c862
Merge branch 'flipperdevices:dev' into troyka
assasinfil Feb 15, 2024
de991f3
Bitlib refactor
assasinfil Feb 15, 2024
029d86d
Moved to API
assasinfil Feb 15, 2024
2be5da0
Rollback troyka parser
assasinfil Feb 15, 2024
3f16155
Fix functions
assasinfil Feb 15, 2024
751ac9d
Added MSK Social card parser
assasinfil Feb 15, 2024
2e304ab
Merge branch 'flipperdevices:dev' into troyka
assasinfil Feb 17, 2024
8c25e30
Merge branch 'flipperdevices:dev' into troyka
assasinfil Feb 18, 2024
3a95260
Merge branch 'flipperdevices:dev' into msk-social
assasinfil Feb 18, 2024
ba19f56
Parser func refactor start
assasinfil Feb 18, 2024
4689a0f
Merge branch 'troyka' of https://github.com/assasinfil/flipperzero-fi…
assasinfil Feb 18, 2024
dd9c7a6
Layout E3 refactored
assasinfil Feb 18, 2024
a31ba57
Layout E4 refactored
assasinfil Feb 18, 2024
9aa93cf
Layout 6 refactored
assasinfil Feb 19, 2024
c5b1a0f
Layout E5 refactored
assasinfil Feb 19, 2024
2f5e654
Layout 2 refactored
assasinfil Feb 19, 2024
4d52d9f
Layout E5 fix
assasinfil Feb 19, 2024
3a727ec
Layout E6 refactored, valid_date need fix
assasinfil Feb 19, 2024
9408f91
Layout E6 fix
assasinfil Feb 19, 2024
40d2aa4
Layout FCB refactored
assasinfil Feb 19, 2024
ba60283
Layout F0B refactored
assasinfil Feb 19, 2024
beb6d83
Layout 8 refactored
assasinfil Feb 19, 2024
92ad9cb
Layout A refactored
assasinfil Feb 19, 2024
218bf03
Layout C refactored
assasinfil Feb 19, 2024
b61063f
Layout D refactored
assasinfil Feb 19, 2024
e4ef65f
Layout E1 refactored
assasinfil Feb 19, 2024
6e31a89
Layout E2 refactored
assasinfil Feb 19, 2024
3c8d54a
Fixes
assasinfil Feb 19, 2024
c15825e
Old code cleanup
assasinfil Feb 19, 2024
5c0db22
Memory cleanup
assasinfil Feb 19, 2024
54aa354
Merge branch 'troyka' into msk-social
assasinfil Feb 19, 2024
bdf466e
Unused imports cleanup
assasinfil Feb 19, 2024
35b074a
Keys struct refactor
assasinfil Feb 19, 2024
aa28ce4
Merge branch 'troyka' into msk-social
assasinfil Feb 19, 2024
395884e
Keys struct refactor
assasinfil Feb 19, 2024
9a4b421
Layout E1 fix
assasinfil Feb 19, 2024
854eedb
Merge branch 'troyka' into msk-social
assasinfil Feb 19, 2024
9e7ba3e
Merge branch 'dev' into troyka
gornekich Mar 1, 2024
8534b39
Merge branch 'troyka' into msk-social
assasinfil Mar 2, 2024
e615b52
Added debug info for layout and department
assasinfil Mar 4, 2024
ed26684
Merge branch 'dev' into troyka
gornekich Mar 4, 2024
973b155
Merge branch 'dev' into troyka
skotopes Mar 5, 2024
2f6ea28
Fix PVS warnings
skotopes Mar 5, 2024
7797276
Fix more PVS warnings
skotopes Mar 5, 2024
280b50b
Merge branch 'troyka' into msk-social
assasinfil Apr 19, 2024
0c2e842
Merge branch 'dev' into msk-social
assasinfil Apr 19, 2024
984c1bd
Merge branch 'dev' into msk-social
assasinfil Sep 17, 2024
4b9a3f4
Fix social card parse validation
assasinfil Sep 18, 2024
f4c3f07
Added card number validation
assasinfil Sep 20, 2024
a68bd33
Added transport data ui improvements from Astrrra's troyka render func.
assasinfil Sep 21, 2024
20b35f7
Merge branch 'flipperdevices:dev' into msk-social
assasinfil Oct 3, 2024
d598516
fbt format
assasinfil Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,302 changes: 1,302 additions & 0 deletions applications/main/nfc/api/mosgortrans/mosgortrans_util.c

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions applications/main/nfc/api/mosgortrans/mosgortrans_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <bit_lib.h>
#include <datetime.h>
#include <furi/core/string.h>
#include <nfc/protocols/mf_classic/mf_classic.h>

#ifdef __cplusplus
extern "C" {
#endif

bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result);

#ifdef __cplusplus
}
#endif
7 changes: 6 additions & 1 deletion applications/main/nfc/api/nfc_app_api_table_i.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "gallagher/gallagher_util.h"
#include "mosgortrans/mosgortrans_util.h"

/*
* A list of app's private functions and objects to expose for plugins.
Expand All @@ -10,4 +11,8 @@ static constexpr auto nfc_app_api_table = sort(create_array_t<sym_entry>(
gallagher_deobfuscate_and_parse_credential,
void,
(GallagherCredential * credential, const uint8_t* cardholder_data_obfuscated)),
API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*)));
API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*),
API_METHOD(
mosgortrans_parse_transport_block,
bool,
(const MfClassicBlock* block, FuriString* result))));
9 changes: 9 additions & 0 deletions applications/main/nfc/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ App(
sources=["plugins/supported_cards/troika.c"],
)

App(
appid="social_moscow_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="social_moscow_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/social_moscow.c"],
)

App(
appid="washcity_parser",
apptype=FlipperAppType.PLUGIN,
Expand Down
254 changes: 254 additions & 0 deletions applications/main/nfc/plugins/supported_cards/social_moscow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#include "nfc_supported_card_plugin.h"
#include <core/check.h>

#include <flipper_application/flipper_application.h>

#include <nfc/nfc_device.h>
#include <bit_lib/bit_lib.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include "../../api/mosgortrans/mosgortrans_util.h"
#include "furi_hal_rtc.h"

#define TAG "Social_Moscow"

typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;

typedef struct {
const MfClassicKeyPair* keys;
uint32_t data_sector;
} SocialMoscowCardConfig;

static const MfClassicKeyPair social_moscow_1k_keys[] = {
{.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025},
{.a = 0x2735fc181807, .b = 0xbf23a53c1f63},
{.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368},
{.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f},
{.a = 0x73068f118c13, .b = 0x2b7f3253fac5},
{.a = 0x186d8c4b93f9, .b = 0x9f131d8c2057},
{.a = 0x3a4bba8adaf0, .b = 0x67362d90f973},
{.a = 0x8765b17968a2, .b = 0x6202a38f69e2},
{.a = 0x40ead80721ce, .b = 0x100533b89331},
{.a = 0x0db5e6523f7c, .b = 0x653a87594079},
{.a = 0x51119dae5216, .b = 0xd8a274b2e026},
{.a = 0x51119dae5216, .b = 0xd8a274b2e026},
{.a = 0x51119dae5216, .b = 0xd8a274b2e026},
{.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368},
{.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f},
{.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}};

static const MfClassicKeyPair social_moscow_4k_keys[] = {
{.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //1
{.a = 0x2735fc181807, .b = 0xbf23a53c1f63}, //2
{.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368}, //3
{.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f}, //4
{.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, //5
{.a = 0x186d8c4b93f9, .b = 0x9f131d8c2057}, //6
{.a = 0x3a4bba8adaf0, .b = 0x67362d90f973}, //7
{.a = 0x8765b17968a2, .b = 0x6202a38f69e2}, //8
{.a = 0x40ead80721ce, .b = 0x100533b89331}, //9
{.a = 0x0db5e6523f7c, .b = 0x653a87594079}, //10
{.a = 0x51119dae5216, .b = 0xd8a274b2e026}, //11
{.a = 0x51119dae5216, .b = 0xd8a274b2e026}, //12
{.a = 0x51119dae5216, .b = 0xd8a274b2e026}, //13
{.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //14
{.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //15
{.a = 0xa0a1a2a3a4a5, .b = 0x7de02a7f6025}, //16
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //17
{.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368}, //18
{.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f}, //19
{.a = 0x2aba9519f574, .b = 0xcb9a1f2d7368}, //20
{.a = 0x84fd7f7a12b6, .b = 0xc7c0adb3284f}, //21
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //22
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //23
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //24
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //25
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //26
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //27
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //28
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //29
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //30
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //31
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //32
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //33
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //34
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //35
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //36
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //37
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //38
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //39
{.a = 0xa229e68ad9e5, .b = 0x49c2b5296ef4}, //40
};

static bool social_moscow_get_card_config(SocialMoscowCardConfig* config, MfClassicType type) {
bool success = true;
if(type == MfClassicType1k) {
config->data_sector = 15;
config->keys = social_moscow_1k_keys;
} else if(type == MfClassicType4k) {
config->data_sector = 15;
assasinfil marked this conversation as resolved.
Show resolved Hide resolved
config->keys = social_moscow_4k_keys;
} else {
success = false;
}

return success;
}

static bool social_moscow_verify_type(Nfc* nfc, MfClassicType type) {
bool verified = false;

do {
SocialMoscowCardConfig cfg = {};
if(!social_moscow_get_card_config(&cfg, type)) break;

const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector);
FURI_LOG_D(TAG, "Verifying sector %lu", cfg.data_sector);

MfClassicKey key = {0};
bit_lib_num_to_bytes_be(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data);

MfClassicAuthContext auth_context;
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
}
FURI_LOG_D(TAG, "Verify success!");
verified = true;
} while(false);

return verified;
}

static bool social_moscow_verify(Nfc* nfc) {
return social_moscow_verify_type(nfc, MfClassicType1k) ||
social_moscow_verify_type(nfc, MfClassicType4k);
}

static bool social_moscow_read(Nfc* nfc, NfcDevice* device) {
furi_assert(nfc);
furi_assert(device);

bool is_read = false;

MfClassicData* data = mf_classic_alloc();
nfc_device_copy_data(device, NfcProtocolMfClassic, data);

do {
MfClassicType type = MfClassicType4k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;

data->type = type;
SocialMoscowCardConfig cfg = {};
if(!social_moscow_get_card_config(&cfg, data->type)) break;

MfClassicDeviceKeys keys = {};
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
bit_lib_num_to_bytes_be(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}

error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error == MfClassicErrorNotPresent) {
FURI_LOG_W(TAG, "Failed to read data");
break;
}

nfc_device_set_data(device, NfcProtocolMfClassic, data);

is_read = (error == MfClassicErrorNone);
} while(false);

mf_classic_free(data);

return is_read;
}

static bool social_moscow_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);

const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);

bool parsed = false;

do {
// Verify card type
SocialMoscowCardConfig cfg = {};
if(!social_moscow_get_card_config(&cfg, data->type)) break;

// Verify key
const MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector);

const uint64_t key_a =
bit_lib_bytes_to_num_be(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data));
const uint64_t key_b =
bit_lib_bytes_to_num_be(sec_tr->key_b.data, COUNT_OF(sec_tr->key_b.data));
if((key_a != cfg.keys[cfg.data_sector].a) || (key_b != cfg.keys[cfg.data_sector].b)) break;

uint32_t card_code = bit_lib_get_bits_32(data->block[60].data, 8, 24);
uint8_t card_region = bit_lib_get_bits(data->block[60].data, 32, 8);
uint64_t card_number = bit_lib_get_bits_64(data->block[60].data, 40, 40);
uint8_t card_control = bit_lib_get_bits(data->block[60].data, 80, 4);
uint64_t omc_number = bit_lib_get_bits_64(data->block[21].data, 8, 64);
uint8_t year = data->block[60].data[11];
uint8_t month = data->block[60].data[12];

FuriString* metro_result = furi_string_alloc();
FuriString* ground_result = furi_string_alloc();
bool result1 = mosgortrans_parse_transport_block(&data->block[4], metro_result);
bool result2 = mosgortrans_parse_transport_block(&data->block[16], ground_result);
furi_string_cat_printf(
parsed_data,
"\e#Social \ecard\nNumber: %lx %x %llx %x\nOMC: %llx\nValid for: %02x/%02x %02x%02x\n",
card_code,
card_region,
card_number,
card_control,
omc_number,
month,
year,
data->block[60].data[13],
data->block[60].data[14]);
if(result1) {
furi_string_cat_printf(
parsed_data, "\e#Metro\n%s\n", furi_string_get_cstr(metro_result));
}
if(result2) {
furi_string_cat_printf(
parsed_data, "\e#Ground\n%s\n", furi_string_get_cstr(ground_result));
}
furi_string_free(ground_result);
furi_string_free(metro_result);
parsed = result1 || result2;
} while(false);

return parsed;
}

/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin social_moscow_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = social_moscow_verify,
.read = social_moscow_read,
.parse = social_moscow_parse,
};

/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor social_moscow_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &social_moscow_plugin,
};

/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* social_moscow_plugin_ep() {
return &social_moscow_plugin_descriptor;
}
Loading