Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions applications/main/nfc/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ App(
sources=["plugins/supported_cards/smartrider.c"],
)

#Microel
App(
appid="microel_parser",
apptype=FlipperAppType.PLUGIN,
Expand All @@ -247,6 +248,16 @@ App(
sources=["plugins/supported_cards/microel.c"],
)

#TreaA
App(
appid="trea_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="trea_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/trea.c"],
)

App(
appid="mizip_parser",
apptype=FlipperAppType.PLUGIN,
Expand Down
113 changes: 80 additions & 33 deletions applications/main/nfc/plugins/supported_cards/microel.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#include "nfc_supported_card_plugin.h"
#include <flipper_application.h>

#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>

#include <bit_lib.h>

#define TAG "Microel"

#define KEY_LENGTH 6
#define UID_LENGTH 4
// offset for year conversion (5 bit: da 0 a 31 -> range 2010–2041)
#define BASE_YEAR 2010

typedef struct {
uint64_t a;
Expand Down Expand Up @@ -62,19 +62,17 @@ void generateKeyA(const uint8_t* uid, uint8_t uidSize, uint8_t keyA[]) {

if(firstCharacter == 0x2 || firstCharacter == 0x3 || firstCharacter == 0xA ||
firstCharacter == 0xB) {
// XOR WITH 0x40
for(size_t i = 0; i < sizeof(sumHex); i++) {
keyA[i] = 0x40 ^ sumHex[i];
}
} else if(
firstCharacter == 0x6 || firstCharacter == 0x7 || firstCharacter == 0xE ||
firstCharacter == 0xF) {
// XOR WITH 0xC0
for(size_t i = 0; i < sizeof(sumHex); i++) {
keyA[i] = 0xC0 ^ sumHex[i];
}
} else {
//Key a is the same as sumHex
// key a same aa sumHex
for(size_t i = 0; i < sizeof(sumHex); i++) {
keyA[i] = sumHex[i];
}
Expand Down Expand Up @@ -103,19 +101,18 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) {
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;

//Get UID and check if it is 4 bytes
size_t uid_len;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
FURI_LOG_D(TAG, "UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]);
if(uid_len != UID_LENGTH) break;

// Generate keys
// generate keys
uint8_t keyA[KEY_LENGTH];
uint8_t keyB[KEY_LENGTH];
generateKeyA(uid, UID_LENGTH, keyA);
generateKeyB(keyA, KEY_LENGTH, keyB);

// Check key 0a to verify if it is a microel card
// check key 0a to verify if it is a microel card
MfClassicKey key = {0};
bit_lib_num_to_bytes_be(
bit_lib_bytes_to_num_be(keyA, KEY_LENGTH), COUNT_OF(key.data), key.data);
Expand All @@ -127,7 +124,6 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) {
break;
}

// Save keys generated to stucture
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
if(microel_1k_keys[i].a == 0x000000000000) {
microel_1k_keys[i].a = bit_lib_bytes_to_num_be(keyA, KEY_LENGTH);
Expand Down Expand Up @@ -171,62 +167,113 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) {
bool parsed = false;

do {
//Get UID
size_t uid_len;
const uint8_t* uid = mf_classic_get_uid(data, &uid_len);
if(uid_len != UID_LENGTH) break;

// Generate key from uid
uint8_t keyA[KEY_LENGTH];
generateKeyA(uid, UID_LENGTH, keyA);

// Verify key
MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(data, verify_sector);
uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
uint64_t key_for_check_from_array = bit_lib_bytes_to_num_be(keyA, KEY_LENGTH);
if(key != key_for_check_from_array) break;

//Get credit in block number 8
const uint8_t* temp_ptr = data->block[4].data;
uint16_t balance = (temp_ptr[6] << 8) | (temp_ptr[5]);
uint16_t previous_balance = (data->block[5].data[6] << 8) | (data->block[5].data[5]);
furi_string_cat_printf(parsed_data, "\e#Microel Card\n");
furi_string_cat_printf(parsed_data, "UID:");
for(size_t i = 0; i < UID_LENGTH; i++) {
furi_string_cat_printf(parsed_data, " %02X", uid[i]);
furi_string_cat_printf(parsed_data, "\e#Microel Card\n");
furi_string_cat_printf(parsed_data, "(Mifare Classic 1k)\n");
furi_string_cat_printf(parsed_data, "====================\n");

furi_string_cat_printf(parsed_data, "UID:");
for(size_t i = 0; i < UID_LENGTH; i++) {
furi_string_cat_printf(parsed_data, " %02X", data->block[0].data[i]);
}
furi_string_cat_printf(parsed_data, "\n");

uint8_t atqa_lsb = data->block[0].data[6];
uint8_t atqa_msb = data->block[0].data[7];
uint8_t sak = data->block[0].data[5];
furi_string_cat_printf(parsed_data, "ATQA: %02X %02X ~ SAK: %02X\n", atqa_msb, atqa_lsb, sak);

furi_string_cat_printf(parsed_data, "--------------------\n");

uint16_t credito_raw = (data->block[4].data[6] << 8) | data->block[4].data[5];
uint16_t credito_precedente = (data->block[5].data[6] << 8) | data->block[5].data[5];
uint32_t raw_data_op = (data->block[4].data[10] << 24) | (data->block[4].data[9] << 16) | (data->block[4].data[8] << 8) | data->block[4].data[7];
uint32_t useful_bits = raw_data_op & 0x07FFFFFF;

uint8_t minuti = (useful_bits >> 21) & 0x3F; // 6 bit
uint8_t ore = (useful_bits >> 16) & 0x1F; // 5 bit
uint8_t tipo_op = (useful_bits >> 14) & 0x03; // 2 bit
uint16_t anno = ((useful_bits >> 9) & 0x1F) + BASE_YEAR; // 5 bit + offset
uint8_t mese = (useful_bits >> 5) & 0x0F; // 4 bit
uint8_t giorno = useful_bits & 0x1F; // lasts 5 bit

const char* tipo_op_str;
if(tipo_op == 0) tipo_op_str = "Firts operation"; // 00
else if(tipo_op == 1) tipo_op_str = "Recharge"; // 01
else tipo_op_str = "Payment"; // 10

uint16_t num_op = (data->block[4].data[1] << 8) | data->block[4].data[0];
uint16_t saldo_punti = (data->block[4].data[11] << 8) | data->block[4].data[12];
uint16_t transazione_raw = (data->block[4].data[14] << 8) | data->block[4].data[13];
uint16_t somma_raw = data->block[4].data[2] | (data->block[4].data[3] << 8);
float somma = somma_raw / 10.0f;
bool cauzione = data->block[4].data[4] != 0;
uint8_t cauzione_raw = data->block[4].data[4];

furi_string_cat_printf(parsed_data, "-> Credit Available: %d.%02d\n", credito_raw / 100, credito_raw % 100);
furi_string_cat_printf(parsed_data, "-> Previous Credit: %d.%02d\n", credito_precedente / 100, credito_precedente % 100);
furi_string_cat_printf(parsed_data, "--------------------\n");
furi_string_cat_printf(parsed_data, "Last transaction: %d.%02d\n", transazione_raw / 100, transazione_raw % 100);
furi_string_cat_printf(parsed_data, "Date: %02d/%02d/%04d ~ %02d:%02d\n", giorno, mese, anno, ore, minuti);
furi_string_cat_printf(parsed_data, "Operation Nr: (%d)\n", num_op);
furi_string_cat_printf(parsed_data, "Op. Type: [%s]\n", tipo_op_str);
furi_string_cat_printf(parsed_data, "====================\n");

furi_string_cat_printf(parsed_data, "Vendor Univocal ID:\n");
for (size_t i = 0; i < 16; i++) {
furi_string_cat_printf(parsed_data, "%02X", data->block[1].data[i]);
if (i % 8 == 7) {
furi_string_cat_printf(parsed_data, "\n");
} else {
furi_string_cat_printf(parsed_data, " ");
}
}
furi_string_cat_printf(
parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100);
furi_string_cat_printf(
parsed_data,
"Previous Credit: %d.%02d E \n",
previous_balance / 100,
previous_balance % 100);


furi_string_cat_printf(parsed_data, "--------------------\n");

furi_string_cat_printf(parsed_data, "Points balance: (%d pt.)\n", saldo_punti);
furi_string_cat_printf(parsed_data, "Admission credit: %d.%02d\n", (int)somma, (int)(somma * 100) % 100);

if (cauzione) {
furi_string_cat_printf(parsed_data, "Deposit: [Yes] ~ (%d.%02d)\n",
cauzione_raw / 100, cauzione_raw % 100);
} else {
furi_string_cat_printf(parsed_data, "Deposit: [No]\n");
}

furi_string_cat_printf(parsed_data, "--------------------\n");

parsed = true;
} while(false);

return parsed;
}

/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin microel_plugin = {
.protocol = NfcProtocolMfClassic,
.verify =
NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 but added verify in read function
.verify = NULL,
.read = microel_read,
.parse = microel_parse,
};

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

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