unleashed-firmware/lib/nfc/protocols/iso14443_3a/iso14443_3a.c
gornekich d92b0a82cc
NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring.

Starring:

- @gornekich - NFC refactoring project lead, architect, senior developer
- @gsurkov - architect, senior developer
- @RebornedBrain - senior developer

Supporting roles:

- @skotopes, @DrZlo13, @hedger - general architecture advisors, code review
- @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance

Special thanks:

@bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
2023-10-24 12:08:09 +09:00

187 lines
5.0 KiB
C

#include "iso14443_3a.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define ISO14443A_ATS_BIT (1U << 5)
#define ISO14443_3A_PROTOCOL_NAME_LEGACY "UID"
#define ISO14443_3A_PROTOCOL_NAME "ISO14443-3A"
#define ISO14443_3A_DEVICE_NAME "ISO14443-3A (Unknown)"
#define ISO14443_3A_ATQA_KEY "ATQA"
#define ISO14443_3A_SAK_KEY "SAK"
const NfcDeviceBase nfc_device_iso14443_3a = {
.protocol_name = ISO14443_3A_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_3a_alloc,
.free = (NfcDeviceFree)iso14443_3a_free,
.reset = (NfcDeviceReset)iso14443_3a_reset,
.copy = (NfcDeviceCopy)iso14443_3a_copy,
.verify = (NfcDeviceVerify)iso14443_3a_verify,
.load = (NfcDeviceLoad)iso14443_3a_load,
.save = (NfcDeviceSave)iso14443_3a_save,
.is_equal = (NfcDeviceEqual)iso14443_3a_is_equal,
.get_name = (NfcDeviceGetName)iso14443_3a_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_3a_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_3a_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data,
};
Iso14443_3aData* iso14443_3a_alloc() {
Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData));
return data;
}
void iso14443_3a_free(Iso14443_3aData* data) {
furi_assert(data);
free(data);
}
void iso14443_3a_reset(Iso14443_3aData* data) {
furi_assert(data);
memset(data, 0, sizeof(Iso14443_3aData));
}
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY);
}
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
// Common to all format versions
if(!flipper_format_read_hex(ff, ISO14443_3A_ATQA_KEY, data->atqa, 2)) break;
if(!flipper_format_read_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;
if(version > NFC_LSB_ATQA_FORMAT_VERSION) {
// Swap ATQA bytes for newer versions
FURI_SWAP(data->atqa[0], data->atqa[1]);
}
parsed = true;
} while(false);
return parsed;
}
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
// Save ATQA in MSB order for correct companion apps display
const uint8_t atqa[2] = {data->atqa[1], data->atqa[0]};
if(!flipper_format_write_comment_cstr(ff, ISO14443_3A_PROTOCOL_NAME " specific data"))
break;
// Write ATQA and SAK
if(!flipper_format_write_hex(ff, ISO14443_3A_ATQA_KEY, atqa, 2)) break;
if(!flipper_format_write_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break;
saved = true;
} while(false);
return saved;
}
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(Iso14443_3aData)) == 0;
}
const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_3A_DEVICE_NAME;
}
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) {
furi_assert(data);
if(uid_len) {
*uid_len = data->uid_len;
}
return data->uid;
}
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES ||
uid_len == ISO14443_3A_UID_7_BYTES ||
uid_len == ISO14443_3A_UID_10_BYTES;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
data->uid_len = uid_len;
}
return uid_valid;
}
Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) {
UNUSED(data);
furi_crash("No base data");
}
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) {
furi_assert(data);
uint32_t cuid = 0;
const uint8_t* cuid_start = data->uid;
if(data->uid_len == ISO14443_3A_UID_7_BYTES) {
cuid_start = &data->uid[3];
}
cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | (cuid_start[3]);
return cuid;
}
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data) {
furi_assert(data);
return data->sak & ISO14443A_ATS_BIT;
}
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data) {
furi_assert(data);
return data->sak;
}
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]) {
furi_assert(data);
furi_assert(atqa);
memcpy(atqa, data->atqa, sizeof(data->atqa));
}
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak) {
furi_assert(data);
data->sak = sak;
}
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]) {
furi_assert(data);
furi_assert(atqa);
memcpy(data->atqa, atqa, sizeof(data->atqa));
}