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:
@@ -574,3 +574,12 @@ App(
|
|||||||
requires=["cli"],
|
requires=["cli"],
|
||||||
sources=["nfc_cli.c"],
|
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"],
|
||||||
|
)
|
||||||
|
|||||||
284
applications/main/nfc/plugins/supported_cards/banapass.c
Normal file
284
applications/main/nfc/plugins/supported_cards/banapass.c
Normal 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;
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 96 B |
Reference in New Issue
Block a user