unleashed-firmware/applications/main/nfc/plugins/supported_cards/gallagher.c
Nick Mooney f6eb79e1e5
NFC: Add support for Gallagher access control (MIFARE Classic only) (#3306)
* Merge remote-tracking branch 'origin/dev' into dev
* Add basic API interface (inspired by advanced plugins example), modify NfcSupportedCardsLoadContext to contain a pointer to a resolver
* WIP: API resolver implemented / passed to plugin via context, still having resolution issues
* Attempt to add constants to the nfc_app_api_table list
* WIP: We're defining the constants directly in nfc_app_api.c to see if this fixes our woes, which it does not.
* WIP: Remove furi_assert(false) (lmao)
* Working implementation of Gallagher decoding via exposed plugin API
* lib: api_hashtable: change log level for symbol not found message
* nfc app: alloc composite resolver along with supported cards
* nfc app: rework nfc api structure
* nfc app: fix memory leakage in supported cards

Co-authored-by: gornekich <n.gorbadey@gmail.com>
2024-02-09 15:00:17 +07:00

87 lines
No EOL
3.2 KiB
C

/* gallagher.c - NFC supported cards plugin for Gallagher access control cards (New Zealand).
* Author: Nick Mooney (nick@mooney.nz)
*
* Reference: https://github.com/megabug/gallagher-research
*/
#include "nfc_supported_card_plugin.h"
#include "../../api/gallagher/gallagher_util.h"
#include <flipper_application/flipper_application.h>
#include <nfc/protocols/mf_classic/mf_classic.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
#include <nfc/helpers/nfc_util.h>
static bool gallagher_parse(const NfcDevice* device, FuriString* parsed_data) {
furi_assert(device);
furi_assert(parsed_data);
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
if(!(data->type == MfClassicType1k || data->type == MfClassicType4k)) {
return false;
}
// It's possible for a single tag to contain multiple credentials,
// but this is currently unimplementecd.
const uint8_t credential_sector_start_block_number =
mf_classic_get_first_block_num_of_sector(GALLAGHER_CREDENTIAL_SECTOR);
// Test 1: The first 8 bytes and the second 8 bytes should be bitwise inverses.
const uint8_t* credential_block_start_ptr =
&data->block[credential_sector_start_block_number].data[0];
uint64_t cardholder_credential = nfc_util_bytes2num(credential_block_start_ptr, 8);
uint64_t cardholder_credential_inverse = nfc_util_bytes2num(credential_block_start_ptr + 8, 8);
// Due to endianness, this is testing the bytes in the wrong order,
// but the result still should be correct.
if(cardholder_credential != ~cardholder_credential_inverse) {
return false;
}
// Test 2: The contents of the second block should be equal to the GALLAGHER_CARDAX_ASCII constant.
const uint8_t* cardax_block_start_ptr =
&data->block[credential_sector_start_block_number + 1].data[0];
if(memcmp(cardax_block_start_ptr, GALLAGHER_CARDAX_ASCII, MF_CLASSIC_BLOCK_SIZE) != 0) {
return false;
}
// Deobfuscate the credential data
GallagherCredential credential;
gallagher_deobfuscate_and_parse_credential(&credential, credential_block_start_ptr);
char display_region = 'A';
// Per https://github.com/megabug/gallagher-research/blob/master/formats/cardholder/cardholder.md,
// regions are generally A-P.
if(credential.region < 16) {
display_region = display_region + (char)credential.region;
} else {
display_region = '?';
}
furi_string_cat_printf(
parsed_data,
"\e#Gallagher NZ\nFacility %c%u\nCard %lu (IL %u)",
display_region,
credential.facility,
credential.card,
credential.issue);
return true;
}
static const NfcSupportedCardsPlugin gallagher_plugin = {
.protocol = NfcProtocolMfClassic,
.verify = NULL,
.read = NULL,
.parse = gallagher_parse,
};
static const FlipperAppPluginDescriptor gallagher_plugin_descriptor = {
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
.entry_point = &gallagher_plugin,
};
/* Plugin entry point */
const FlipperAppPluginDescriptor* gallagher_plugin_ep() {
return &gallagher_plugin_descriptor;
}