1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Merge remote-tracking branch 'OFW/dev' into dev

This commit is contained in:
MX
2025-09-24 00:45:00 +03:00
3 changed files with 293 additions and 0 deletions

View File

@@ -574,3 +574,12 @@ App(
requires=["cli"],
sources=["nfc_cli.c"],
)
App(
appid="banapass_parser",
apptype=FlipperAppType.PLUGIN,
entry_point="banapass_plugin_ep",
targets=["f7"],
requires=["nfc"],
sources=["plugins/supported_cards/banapass.c"],
)

View File

@@ -0,0 +1,284 @@
#include "nfc_supported_card_plugin.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>
#define TAG "Banapass"
static const uint64_t banapass_key_b_value_block = 0x019761AA8082;
static const uint64_t banapass_key_b_access_code = 0x574343467632;
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
static MfClassicKeyPair banapass_keys_if_value_block[] = {
{.a = 0x6090D00632F5, .b = 0x019761AA8082},
{.a = 0xA99164400748, .b = 0x62742819AD7C},
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
{.a = 0x30424C029001, .b = 0x024E4E44001F},
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
{.a = 0x730050555253, .b = 0x4146D4A956C4},
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
};
static MfClassicKeyPair banapass_keys_if_access_code[] = {
{.a = 0x6090D00632F5, .b = 0x574343467632},
{.a = 0xA99164400748, .b = 0x62742819AD7C},
{.a = 0xCC5075E42BA1, .b = 0xB9DF35A0814C},
{.a = 0x8AF9C718F23D, .b = 0x58CD5C3673CB},
{.a = 0xFC80E88EB88C, .b = 0x7A3CDAD7C023},
{.a = 0x30424C029001, .b = 0x024E4E44001F},
{.a = 0xECBBFA57C6AD, .b = 0x4757698143BD},
{.a = 0x1D30972E6485, .b = 0xF8526D1A8D6D},
{.a = 0x1300EC8C7E80, .b = 0xF80A65A87FFA},
{.a = 0xDEB06ED4AF8E, .b = 0x4AD96BF28190},
{.a = 0x000390014D41, .b = 0x0800F9917CB0},
{.a = 0x730050555253, .b = 0x4146D4A956C4},
{.a = 0x131157FBB126, .b = 0xE69DD9015A43},
{.a = 0x337237F254D5, .b = 0x9A8389F32FBF},
{.a = 0x7B8FB4A7100B, .b = 0xC8382A233993},
{.a = 0x7B304F2A12A6, .b = 0xFC9418BF788B},
};
static bool banapass_verify(Nfc* nfc) {
bool verified = true;
const uint8_t verify_sector = 0;
uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
MfClassicKey key_a_0 = {};
bit_lib_num_to_bytes_be(
banapass_keys_if_value_block[0].a, COUNT_OF(key_a_0.data), key_a_0.data);
MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
verified = false;
}
return verified;
}
static bool banapass_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 = MfClassicType1k;
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;
if(type != MfClassicType1k) {
FURI_LOG_E(TAG, "Card not MIFARE Classic 1k");
break;
}
data->type = type;
MfClassicDeviceKeys keys = {};
// Access Code Read Attempt
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(
banapass_keys_if_access_code[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
bit_lib_num_to_bytes_be(
banapass_keys_if_access_code[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
MfClassicError error_access_code = mf_classic_poller_sync_read(nfc, &keys, data);
if(error_access_code == MfClassicErrorNone) {
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
break;
}
// Value Block Read Attempt
for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
bit_lib_num_to_bytes_be(
banapass_keys_if_value_block[i].a, sizeof(MfClassicKey), keys.key_a[i].data);
FURI_BIT_SET(keys.key_a_mask, i);
bit_lib_num_to_bytes_be(
banapass_keys_if_value_block[i].b, sizeof(MfClassicKey), keys.key_b[i].data);
FURI_BIT_SET(keys.key_b_mask, i);
}
MfClassicError error_value_block = mf_classic_poller_sync_read(nfc, &keys, data);
if(error_value_block == MfClassicErrorNone) {
nfc_device_set_data(device, NfcProtocolMfClassic, data);
is_read = true;
break;
}
FURI_LOG_E(TAG, "Failed to read data. Bad keys?");
} while(false);
mf_classic_free(data);
return is_read;
}
static bool banapass_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
bool parsed = false;
do {
// verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
uint64_t key_a = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
uint64_t key_b = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
if(key_a != banapass_keys_if_value_block[0].a) break;
furi_string_set_str(parsed_data, "\e#Banapass\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
furi_string_cat_str(parsed_data, "\nBandai Namco Passport\n");
// banapass Magic is stored at block 1, byte 2-7
uint8_t magic_bytes[6];
for(int i = 0; i < 6; i++) {
magic_bytes[i] = data->block[1].data[2 + i];
}
// verify banapass magic
if(magic_bytes[0] != 'N' || magic_bytes[1] != 'B' || magic_bytes[2] != 'G' ||
magic_bytes[3] != 'I' || magic_bytes[4] != 'C')
break;
// banapass checksum is stored at block 1, starts from byte 8-15
uint8_t check_sum[8];
for(int i = 0; i < 8; i++) {
check_sum[i] = data->block[1].data[8 + i];
}
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
bool is_block_2_null = true;
for(int i = 0; i < 16; i++) {
if(data->block[2].data[i] != 0) {
is_block_2_null = false;
break;
}
}
if(is_block_2_null) {
furi_string_cat_str(
parsed_data,
"\nPlease scan the clone at the\nnearest CHUNITHM or\nmaimai Cabinet for the\nAccess Code.\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
} else {
switch(key_b) {
case banapass_key_b_value_block:
int32_t value = 0;
uint8_t addr = 0;
bool value_found = mf_classic_block_to_value(
&data->block[2], &value, &addr); // block 2 is value block
if(value_found) {
furi_string_cat_printf(parsed_data, "\nValue: %08lX", value);
} else {
furi_string_cat_str(parsed_data, "\nPotential clone:\nInvalid value block.");
}
furi_string_cat_str(
parsed_data,
"\nPlease check the back of\nyour Bandai Namco Passport\nfor the Access Code.\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
break;
case banapass_key_b_access_code:
// banapass access code is stored as decimal hex representation in block 2, starts from byte 6, len 10 bytes
uint8_t access_code[10];
furi_string_cat_printf(parsed_data, "\nAccess Code:\n");
bool access_code_is_bcd = true;
for(int i = 0; i < 10; i++) {
access_code[i] = data->block[2].data[6 + i];
furi_string_cat_printf(parsed_data, "%02X", access_code[i]);
if(i % 2 == 1) {
furi_string_cat_str(parsed_data, " ");
}
if((access_code[i] >> 4) > 9) access_code_is_bcd = false;
if((access_code[i] & 0x0F) > 9) access_code_is_bcd = false;
}
furi_string_cat_printf(
parsed_data, "\nBCD valid: %s\n", access_code_is_bcd ? "Yes" : "No");
if((access_code[0] >> 4) != 3) {
furi_string_cat_printf(
parsed_data,
"Potential clone:\nAccess Code preamble\nexpected 3, got %d\n",
(access_code[0] >> 4));
}
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
break;
default:
break;
}
}
furi_string_cat_str(parsed_data, "\nMagic:\n");
for(int i = 0; i < 6; i++) {
furi_string_cat_printf(parsed_data, "%c", magic_bytes[i]);
}
furi_string_cat_str(parsed_data, "\nChecksum:\n");
for(int i = 0; i < 8; i++) {
furi_string_cat_printf(parsed_data, "%02X ", check_sum[i]);
}
furi_string_cat_str(parsed_data, "\n");
furi_string_cat_str(
parsed_data, "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
parsed = true;
} while(false);
return parsed;
}
/* Actual implementation of app<>plugin interface */
static const NfcSupportedCardsPlugin banapass_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = banapass_verify,
.read = banapass_read,
.parse = banapass_parse,
};
/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor banapass_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &banapass_plugin,
};
/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* banapass_plugin_ep(void) {
return &banapass_plugin_descriptor;
}