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.
This commit is contained in:
gornekich
2023-10-24 07:08:09 +04:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41488 additions and 68125 deletions

View File

@@ -1,128 +0,0 @@
#include "crypto1.h"
#include "nfc_util.h"
#include <furi.h>
// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git
#define SWAPENDIAN(x) \
((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
#define LF_POLY_ODD (0x29CE5C)
#define LF_POLY_EVEN (0x870804)
#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24)
void crypto1_reset(Crypto1* crypto1) {
furi_assert(crypto1);
crypto1->even = 0;
crypto1->odd = 0;
}
void crypto1_init(Crypto1* crypto1, uint64_t key) {
furi_assert(crypto1);
crypto1->even = 0;
crypto1->odd = 0;
for(int8_t i = 47; i > 0; i -= 2) {
crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7);
crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7);
}
}
uint32_t crypto1_filter(uint32_t in) {
uint32_t out = 0;
out = 0xf22c0 >> (in & 0xf) & 16;
out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8;
out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4;
out |= 0x1e458 >> (in >> 12 & 0xf) & 2;
out |= 0x0d938 >> (in >> 16 & 0xf) & 1;
return FURI_BIT(0xEC57E80A, out);
}
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
furi_assert(crypto1);
uint8_t out = crypto1_filter(crypto1->odd);
uint32_t feed = out & (!!is_encrypted);
feed ^= !!in;
feed ^= LF_POLY_ODD & crypto1->odd;
feed ^= LF_POLY_EVEN & crypto1->even;
crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed));
FURI_SWAP(crypto1->odd, crypto1->even);
return out;
}
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {
furi_assert(crypto1);
uint8_t out = 0;
for(uint8_t i = 0; i < 8; i++) {
out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;
}
return out;
}
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
furi_assert(crypto1);
uint32_t out = 0;
for(uint8_t i = 0; i < 32; i++) {
out |= crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i);
}
return out;
}
uint32_t prng_successor(uint32_t x, uint32_t n) {
SWAPENDIAN(x);
while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
return SWAPENDIAN(x);
}
void crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
uint16_t encrypted_data_bits,
uint8_t* decrypted_data) {
furi_assert(crypto);
furi_assert(encrypted_data);
furi_assert(decrypted_data);
if(encrypted_data_bits < 8) {
uint8_t decrypted_byte = 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
decrypted_data[0] = decrypted_byte;
} else {
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
}
}
}
void crypto1_encrypt(
Crypto1* crypto,
uint8_t* keystream,
uint8_t* plain_data,
uint16_t plain_data_bits,
uint8_t* encrypted_data,
uint8_t* encrypted_parity) {
furi_assert(crypto);
furi_assert(plain_data);
furi_assert(encrypted_data);
furi_assert(encrypted_parity);
if(plain_data_bits < 8) {
encrypted_data[0] = 0;
for(size_t i = 0; i < plain_data_bits; i++) {
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
}
} else {
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
plain_data[i];
encrypted_parity[i / 8] |=
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
<< (7 - (i & 0x0007)));
}
}
}

View File

@@ -1,444 +0,0 @@
#include "emv.h"
#include <core/common_defines.h>
#define TAG "Emv"
const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information
const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type
const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicator
const PDOLValue pdol_term_trans_qualifies = {
0x9F66,
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
const PDOLValue pdol_addtnl_term_qualifies = {
0x9F40,
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
const PDOLValue pdol_amount_authorise = {
0x9F02,
{0x00, 0x00, 0x00, 0x10, 0x00, 0x00}}; // Amount, authorised
const PDOLValue pdol_amount = {0x9F03, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Amount
const PDOLValue pdol_country_code = {0x9F1A, {0x01, 0x24}}; // Terminal country code
const PDOLValue pdol_currency_code = {0x5F2A, {0x01, 0x24}}; // Transaction currency code
const PDOLValue pdol_term_verification = {
0x95,
{0x00, 0x00, 0x00, 0x00, 0x00}}; // Terminal verification results
const PDOLValue pdol_transaction_date = {0x9A, {0x19, 0x01, 0x01}}; // Transaction date
const PDOLValue pdol_transaction_type = {0x9C, {0x00}}; // Transaction type
const PDOLValue pdol_transaction_cert = {0x98, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; // Transaction cert
const PDOLValue pdol_unpredict_number = {0x9F37, {0x82, 0x3D, 0xDE, 0x7A}}; // Unpredictable number
const PDOLValue* const pdol_values[] = {
&pdol_term_info,
&pdol_term_type,
&pdol_merchant_type,
&pdol_term_trans_qualifies,
&pdol_addtnl_term_qualifies,
&pdol_amount_authorise,
&pdol_amount,
&pdol_country_code,
&pdol_currency_code,
&pdol_term_verification,
&pdol_transaction_date,
&pdol_transaction_type,
&pdol_transaction_cert,
&pdol_unpredict_number,
};
static const uint8_t select_ppse_ans[] = {0x6F, 0x29, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E,
0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31,
0xA5, 0x17, 0xBF, 0x0C, 0x14, 0x61, 0x12, 0x4F, 0x07,
0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x04,
0x56, 0x49, 0x53, 0x41, 0x87, 0x01, 0x01, 0x90, 0x00};
static const uint8_t select_app_ans[] = {0x6F, 0x20, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03,
0x10, 0x10, 0xA5, 0x15, 0x50, 0x04, 0x56, 0x49, 0x53,
0x41, 0x9F, 0x38, 0x0C, 0x9F, 0x66, 0x04, 0x9F, 0x02,
0x06, 0x9F, 0x37, 0x04, 0x5F, 0x2A, 0x02, 0x90, 0x00};
static const uint8_t pdol_ans[] = {0x77, 0x40, 0x82, 0x02, 0x20, 0x00, 0x57, 0x13, 0x55, 0x70,
0x73, 0x83, 0x85, 0x87, 0x73, 0x31, 0xD1, 0x80, 0x22, 0x01,
0x38, 0x84, 0x77, 0x94, 0x00, 0x00, 0x1F, 0x5F, 0x34, 0x01,
0x00, 0x9F, 0x10, 0x07, 0x06, 0x01, 0x11, 0x03, 0x80, 0x00,
0x00, 0x9F, 0x26, 0x08, 0x7A, 0x65, 0x7F, 0xD3, 0x52, 0x96,
0xC9, 0x85, 0x9F, 0x27, 0x01, 0x00, 0x9F, 0x36, 0x02, 0x06,
0x0C, 0x9F, 0x6C, 0x02, 0x10, 0x00, 0x90, 0x00};
static void emv_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) {
if(furi_log_get_level() == FuriLogLevelTrace) {
FURI_LOG_T(TAG, "%s", message);
printf("TX: ");
for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) {
printf("%02X ", tx_rx->tx_data[i]);
}
printf("\r\nRX: ");
for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) {
printf("%02X ", tx_rx->rx_data[i]);
}
printf("\r\n");
}
}
static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
uint16_t i = 0;
uint16_t tag = 0, first_byte = 0;
uint16_t tlen = 0;
bool success = false;
while(i < len) {
first_byte = buff[i];
if((first_byte & 31) == 31) { // 2-byte tag
tag = buff[i] << 8 | buff[i + 1];
i++;
FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag);
} else {
tag = buff[i];
FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag);
}
i++;
tlen = buff[i];
if((tlen & 128) == 128) { // long length value
i++;
tlen = buff[i];
FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen);
} else {
FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen);
}
i++;
if((first_byte & 32) == 32) { // "Constructed" -- contains more TLV data to parse
FURI_LOG_T(TAG, "Constructed TLV %x", tag);
if(!emv_decode_response(&buff[i], tlen, app)) {
FURI_LOG_T(TAG, "Failed to decode response for %x", tag);
// return false;
} else {
success = true;
}
} else {
switch(tag) {
case EMV_TAG_AID:
app->aid_len = tlen;
memcpy(app->aid, &buff[i], tlen);
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag);
break;
case EMV_TAG_PRIORITY:
memcpy(&app->priority, &buff[i], tlen);
success = true;
break;
case EMV_TAG_CARD_NAME:
memcpy(app->name, &buff[i], tlen);
app->name[tlen] = '\0';
app->name_found = true;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name);
break;
case EMV_TAG_PDOL:
memcpy(app->pdol.data, &buff[i], tlen);
app->pdol.size = tlen;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen);
break;
case EMV_TAG_AFL:
memcpy(app->afl.data, &buff[i], tlen);
app->afl.size = tlen;
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen);
break;
case EMV_TAG_TRACK_1_EQUIV: {
char track_1_equiv[80];
memcpy(track_1_equiv, &buff[i], tlen);
track_1_equiv[tlen] = '\0';
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv);
break;
}
case EMV_TAG_TRACK_2_EQUIV: {
// 0xD0 delimits PAN from expiry (YYMM)
for(int x = 1; x < tlen; x++) {
if(buff[i + x + 1] > 0xD0) {
memcpy(app->card_number, &buff[i], x + 1);
app->card_number_len = x + 1;
app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4);
app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4);
break;
}
}
// Convert 4-bit to ASCII representation
char track_2_equiv[41];
uint8_t track_2_equiv_len = 0;
for(int x = 0; x < tlen; x++) {
char top = (buff[i + x] >> 4) + '0';
char bottom = (buff[i + x] & 0x0F) + '0';
track_2_equiv[x * 2] = top;
track_2_equiv_len++;
if(top == '?') break;
track_2_equiv[x * 2 + 1] = bottom;
track_2_equiv_len++;
if(bottom == '?') break;
}
track_2_equiv[track_2_equiv_len] = '\0';
success = true;
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv);
break;
}
case EMV_TAG_PAN:
memcpy(app->card_number, &buff[i], tlen);
app->card_number_len = tlen;
success = true;
break;
case EMV_TAG_EXP_DATE:
app->exp_year = buff[i];
app->exp_month = buff[i + 1];
success = true;
break;
case EMV_TAG_CURRENCY_CODE:
app->currency_code = (buff[i] << 8 | buff[i + 1]);
success = true;
break;
case EMV_TAG_COUNTRY_CODE:
app->country_code = (buff[i] << 8 | buff[i + 1]);
success = true;
break;
}
}
i += tlen;
}
return success;
}
static bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool app_aid_found = false;
const uint8_t emv_select_ppse_cmd[] = {
0x00, 0xA4, // SELECT ppse
0x04, 0x00, // P1:By name, P2: empty
0x0e, // Lc: Data length
0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string:
0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE)
0x00 // Le
};
memcpy(tx_rx->tx_data, emv_select_ppse_cmd, sizeof(emv_select_ppse_cmd));
tx_rx->tx_bits = sizeof(emv_select_ppse_cmd) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select PPSE");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "Select PPSE answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
app_aid_found = true;
} else {
FURI_LOG_E(TAG, "Failed to parse application");
}
} else {
FURI_LOG_E(TAG, "Failed select PPSE");
}
return app_aid_found;
}
static bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
app->app_started = false;
const uint8_t emv_select_header[] = {
0x00,
0xA4, // SELECT application
0x04,
0x00 // P1:By name, P2:First or only occurence
};
uint16_t size = sizeof(emv_select_header);
// Copy header
memcpy(tx_rx->tx_data, emv_select_header, size);
// Copy AID
tx_rx->tx_data[size++] = app->aid_len;
memcpy(&tx_rx->tx_data[size], app->aid, app->aid_len);
size += app->aid_len;
tx_rx->tx_data[size++] = 0x00;
tx_rx->tx_bits = size * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Start application");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "Start application answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
app->app_started = true;
} else {
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
}
} else {
FURI_LOG_E(TAG, "Failed to start application");
}
return app->app_started;
}
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
bool tag_found;
for(uint16_t i = 0; i < src->size; i++) {
tag_found = false;
for(uint8_t j = 0; j < sizeof(pdol_values) / sizeof(PDOLValue*); j++) {
if(src->data[i] == pdol_values[j]->tag) {
// Found tag with 1 byte length
uint8_t len = src->data[++i];
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
dest->size += len;
tag_found = true;
break;
} else if(((src->data[i] << 8) | src->data[i + 1]) == pdol_values[j]->tag) {
// Found tag with 2 byte length
i += 2;
uint8_t len = src->data[i];
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
dest->size += len;
tag_found = true;
break;
}
}
if(!tag_found) {
// Unknown tag, fill zeros
i += 2;
uint8_t len = src->data[i];
memset(dest->data + dest->size, 0, len);
dest->size += len;
}
}
return dest->size;
}
static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool card_num_read = false;
const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00};
uint16_t size = sizeof(emv_gpo_header);
// Copy header
memcpy(tx_rx->tx_data, emv_gpo_header, size);
APDU pdol_data = {0, {0}};
// Prepare and copy pdol parameters
emv_prepare_pdol(&pdol_data, &app->pdol);
tx_rx->tx_data[size++] = 0x02 + pdol_data.size;
tx_rx->tx_data[size++] = 0x83;
tx_rx->tx_data[size++] = pdol_data.size;
memcpy(tx_rx->tx_data + size, pdol_data.data, pdol_data.size);
size += pdol_data.size;
tx_rx->tx_data[size++] = 0;
tx_rx->tx_bits = size * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Get proccessing options");
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "Get processing options answer:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
if(app->card_number_len > 0) {
card_num_read = true;
}
}
} else {
FURI_LOG_E(TAG, "Failed to get processing options");
}
return card_num_read;
}
static bool emv_read_sfi_record(
FuriHalNfcTxRxContext* tx_rx,
EmvApplication* app,
uint8_t sfi,
uint8_t record_num) {
bool card_num_read = false;
uint8_t sfi_param = (sfi << 3) | (1 << 2);
uint8_t emv_sfi_header[] = {
0x00,
0xB2, // READ RECORD
record_num, // P1:record_number
sfi_param, // P2:SFI
0x00 // Le
};
memcpy(tx_rx->tx_data, emv_sfi_header, sizeof(emv_sfi_header));
tx_rx->tx_bits = sizeof(emv_sfi_header) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
emv_trace(tx_rx, "SFI record:");
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
card_num_read = true;
}
} else {
FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num);
}
return card_num_read;
}
static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
bool card_num_read = false;
if(app->afl.size == 0) {
return false;
}
FURI_LOG_D(TAG, "Search PAN in SFI");
// Iterate through all files
for(size_t i = 0; i < app->afl.size; i += 4) {
uint8_t sfi = app->afl.data[i] >> 3;
uint8_t record_start = app->afl.data[i + 1];
uint8_t record_end = app->afl.data[i + 2];
// Iterate through all records in file
for(uint8_t record = record_start; record <= record_end; ++record) {
card_num_read |= emv_read_sfi_record(tx_rx, app, sfi, record);
}
}
return card_num_read;
}
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) {
furi_assert(tx_rx);
furi_assert(emv_app);
bool card_num_read = false;
memset(emv_app, 0, sizeof(EmvApplication));
do {
if(!emv_select_ppse(tx_rx, emv_app)) break;
if(!emv_select_app(tx_rx, emv_app)) break;
if(emv_get_processing_options(tx_rx, emv_app)) {
card_num_read = true;
} else {
card_num_read = emv_read_files(tx_rx, emv_app);
}
} while(false);
return card_num_read;
}
bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) {
furi_assert(tx_rx);
bool emulation_complete = false;
tx_rx->tx_bits = 0;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
do {
FURI_LOG_D(TAG, "Read select PPSE command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, select_ppse_ans, sizeof(select_ppse_ans));
tx_rx->tx_bits = sizeof(select_ppse_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select PPSE answer and read select App command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, select_app_ans, sizeof(select_app_ans));
tx_rx->tx_bits = sizeof(select_app_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send select App answer and read get PDOL command");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
memcpy(tx_rx->tx_data, pdol_ans, sizeof(pdol_ans));
tx_rx->tx_bits = sizeof(pdol_ans) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
FURI_LOG_D(TAG, "Send get PDOL answer");
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
emulation_complete = true;
} while(false);
return emulation_complete;
}

View File

@@ -1,80 +0,0 @@
#pragma once
#include <furi_hal_nfc.h>
#define MAX_APDU_LEN 255
#define EMV_TAG_APP_TEMPLATE 0x61
#define EMV_TAG_AID 0x4F
#define EMV_TAG_PRIORITY 0x87
#define EMV_TAG_PDOL 0x9F38
#define EMV_TAG_CARD_NAME 0x50
#define EMV_TAG_FCI 0xBF0C
#define EMV_TAG_LOG_CTRL 0x9F4D
#define EMV_TAG_TRACK_1_EQUIV 0x56
#define EMV_TAG_TRACK_2_EQUIV 0x57
#define EMV_TAG_PAN 0x5A
#define EMV_TAG_AFL 0x94
#define EMV_TAG_EXP_DATE 0x5F24
#define EMV_TAG_COUNTRY_CODE 0x5F28
#define EMV_TAG_CURRENCY_CODE 0x9F42
#define EMV_TAG_CARDHOLDER_NAME 0x5F20
typedef struct {
char name[32];
uint8_t aid[16];
uint16_t aid_len;
uint8_t number[10];
uint8_t number_len;
uint8_t exp_mon;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
} EmvData;
typedef struct {
uint16_t tag;
uint8_t data[];
} PDOLValue;
typedef struct {
uint8_t size;
uint8_t data[MAX_APDU_LEN];
} APDU;
typedef struct {
uint8_t priority;
uint8_t aid[16];
uint8_t aid_len;
bool app_started;
char name[32];
bool name_found;
uint8_t card_number[10];
uint8_t card_number_len;
uint8_t exp_month;
uint8_t exp_year;
uint16_t country_code;
uint16_t currency_code;
APDU pdol;
APDU afl;
} EmvApplication;
/** Read bank card data
* @note Search EMV Application, start it, try to read AID, PAN, card name,
* expiration date, currency and country codes
*
* @param tx_rx FuriHalNfcTxRxContext instance
* @param emv_app EmvApplication instance
*
* @return true on success
*/
bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app);
/** Emulate bank card
* @note Answer to application selection and PDOL
*
* @param tx_rx FuriHalNfcTxRxContext instance
*
* @return true on success
*/
bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx);

View File

@@ -0,0 +1,147 @@
#include "felica.h"
#include <furi.h>
#include <nfc/nfc_common.h>
#define FELICA_PROTOCOL_NAME "FeliCa"
#define FELICA_DEVICE_NAME "FeliCa"
#define FELICA_DATA_FORMAT_VERSION "Data format version"
#define FELICA_MANUFACTURE_ID "Manufacture id"
#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter"
static const uint32_t felica_data_format_version = 1;
const NfcDeviceBase nfc_device_felica = {
.protocol_name = FELICA_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)felica_alloc,
.free = (NfcDeviceFree)felica_free,
.reset = (NfcDeviceReset)felica_reset,
.copy = (NfcDeviceCopy)felica_copy,
.verify = (NfcDeviceVerify)felica_verify,
.load = (NfcDeviceLoad)felica_load,
.save = (NfcDeviceSave)felica_save,
.is_equal = (NfcDeviceEqual)felica_is_equal,
.get_name = (NfcDeviceGetName)felica_get_device_name,
.get_uid = (NfcDeviceGetUid)felica_get_uid,
.set_uid = (NfcDeviceSetUid)felica_set_uid,
.get_base_data = (NfcDeviceGetBaseData)felica_get_base_data,
};
FelicaData* felica_alloc() {
FelicaData* data = malloc(sizeof(FelicaData));
return data;
}
void felica_free(FelicaData* data) {
furi_assert(data);
free(data);
}
void felica_reset(FelicaData* data) {
memset(data, 0, sizeof(FelicaData));
}
void felica_copy(FelicaData* data, const FelicaData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool felica_verify(FelicaData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
return false;
}
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
uint32_t data_format_version = 0;
if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1))
break;
if(data_format_version != felica_data_format_version) break;
if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
break;
if(!flipper_format_read_hex(
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break;
parsed = true;
} while(false);
return parsed;
}
bool felica_save(const FelicaData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break;
if(!flipper_format_write_uint32(
ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1))
break;
if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE))
break;
if(!flipper_format_write_hex(
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
break;
saved = true;
} while(false);
return saved;
}
bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(FelicaData)) == 0;
}
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return FELICA_DEVICE_NAME;
}
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
furi_assert(data);
// Consider Manufacturer ID as UID
if(uid_len) {
*uid_len = FELICA_IDM_SIZE;
}
return data->idm.data;
}
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
// Consider Manufacturer ID as UID
const bool uid_valid = uid_len == FELICA_IDM_SIZE;
if(uid_valid) {
memcpy(data->idm.data, uid, uid_len);
}
return uid_valid;
}
FelicaData* felica_get_base_data(const FelicaData* data) {
UNUSED(data);
furi_crash("No base data");
}

View File

@@ -0,0 +1,77 @@
#pragma once
#include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FELICA_IDM_SIZE (8U)
#define FELICA_PMM_SIZE (8U)
#define FELICA_GUARD_TIME_US (20000U)
#define FELICA_FDT_POLL_FC (10000U)
#define FELICA_POLL_POLL_MIN_US (1280U)
#define FELICA_SYSTEM_CODE_CODE (0xFFFFU)
#define FELICA_TIME_SLOT_1 (0x00U)
#define FELICA_TIME_SLOT_2 (0x01U)
#define FELICA_TIME_SLOT_4 (0x03U)
#define FELICA_TIME_SLOT_8 (0x07U)
#define FELICA_TIME_SLOT_16 (0x0FU)
typedef enum {
FelicaErrorNone,
FelicaErrorNotPresent,
FelicaErrorColResFailed,
FelicaErrorBufferOverflow,
FelicaErrorCommunication,
FelicaErrorFieldOff,
FelicaErrorWrongCrc,
FelicaErrorProtocol,
FelicaErrorTimeout,
} FelicaError;
typedef struct {
uint8_t data[FELICA_IDM_SIZE];
} FelicaIDm;
typedef struct {
uint8_t data[FELICA_PMM_SIZE];
} FelicaPMm;
typedef struct {
FelicaIDm idm;
FelicaPMm pmm;
} FelicaData;
extern const NfcDeviceBase nfc_device_felica;
FelicaData* felica_alloc();
void felica_free(FelicaData* data);
void felica_reset(FelicaData* data);
void felica_copy(FelicaData* data, const FelicaData* other);
bool felica_verify(FelicaData* data, const FuriString* device_type);
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version);
bool felica_save(const FelicaData* data, FlipperFormat* ff);
bool felica_is_equal(const FelicaData* data, const FelicaData* other);
const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type);
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len);
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);
FelicaData* felica_get_base_data(const FelicaData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,117 @@
#include "felica_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
FelicaPoller* instance = malloc(sizeof(FelicaPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechFelica);
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
instance->data = felica_alloc();
instance->felica_event.data = &instance->felica_event_data;
instance->general_event.protocol = NfcProtocolFelica;
instance->general_event.event_data = &instance->felica_event;
instance->general_event.instance = instance;
return instance;
}
static void felica_poller_free(FelicaPoller* instance) {
furi_assert(instance);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
felica_free(instance->data);
free(instance);
}
static void
felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
FelicaPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != FelicaPollerStateActivated) {
FelicaError error = felica_poller_async_activate(instance, instance->data);
if(error == FelicaErrorNone) {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->felica_event.type = FelicaPollerEventTypeError;
instance->felica_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->felica_event.type = FelicaPollerEventTypeReady;
instance->felica_event_data.error = FelicaErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
static bool felica_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
FelicaPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == FelicaPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
FelicaError error = felica_poller_async_activate(instance, instance->data);
protocol_detected = (error == FelicaErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_felica = {
.alloc = (NfcPollerAlloc)felica_poller_alloc,
.free = (NfcPollerFree)felica_poller_free,
.set_callback = (NfcPollerSetCallback)felica_poller_set_callback,
.run = (NfcPollerRun)felica_poller_run,
.detect = (NfcPollerDetect)felica_poller_detect,
.get_data = (NfcPollerGetData)felica_poller_get_data,
};

View File

@@ -0,0 +1,30 @@
#pragma once
#include "felica.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct FelicaPoller FelicaPoller;
typedef enum {
FelicaPollerEventTypeError,
FelicaPollerEventTypeReady,
} FelicaPollerEventType;
typedef struct {
FelicaError error;
} FelicaPollerEventData;
typedef struct {
FelicaPollerEventType type;
FelicaPollerEventData* data;
} FelicaPollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,13 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const NfcPollerBase nfc_poller_felica;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,128 @@
#include "felica_poller_i.h"
#include <nfc/helpers/felica_crc.h>
#define TAG "FelicaPoller"
static FelicaError felica_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return FelicaErrorNone;
case NfcErrorTimeout:
return FelicaErrorTimeout;
default:
return FelicaErrorNotPresent;
}
}
static FelicaError felica_poller_frame_exchange(
FelicaPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE);
felica_crc_append(instance->tx_buffer);
FelicaError ret = FelicaErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = felica_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!felica_crc_check(instance->rx_buffer)) {
ret = FelicaErrorWrongCrc;
break;
}
felica_crc_trim(rx_buffer);
} while(false);
return ret;
}
FelicaError felica_poller_async_polling(
FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp) {
furi_assert(instance);
furi_assert(cmd);
furi_assert(resp);
FelicaError error = FelicaErrorNone;
do {
bit_buffer_set_size_bytes(instance->tx_buffer, 2);
// Set frame len
bit_buffer_set_byte(
instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + FELICA_CRC_SIZE);
// Set command code
bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_REQ_CODE);
// Set other data
bit_buffer_append_bytes(
instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand));
error = felica_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
if(error != FelicaErrorNone) break;
if(bit_buffer_get_byte(instance->rx_buffer, 1) != FELICA_POLLER_CMD_POLLING_RESP_CODE) {
error = FelicaErrorProtocol;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) <
sizeof(FelicaIDm) + sizeof(FelicaPMm) + 1) {
error = FelicaErrorProtocol;
break;
}
bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm));
bit_buffer_write_bytes_mid(
instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm));
} while(false);
return error;
}
FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data) {
furi_assert(instance);
felica_reset(data);
FelicaError ret;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send Polling command
const FelicaPollerPollingCommand polling_cmd = {
.system_code = FELICA_SYSTEM_CODE_CODE,
.request_code = 0,
.time_slot = FELICA_TIME_SLOT_1,
};
FelicaPollerPollingResponse polling_resp = {};
ret = felica_poller_async_polling(instance, &polling_cmd, &polling_resp);
if(ret != FelicaErrorNone) {
FURI_LOG_T(TAG, "Activation failed error: %d", ret);
break;
}
data->idm = polling_resp.idm;
data->pmm = polling_resp.pmm;
instance->state = FelicaPollerStateActivated;
} while(false);
return ret;
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include "felica_poller.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define FELICA_POLLER_MAX_BUFFER_SIZE (256U)
#define FELICA_POLLER_POLLING_FWT (200000U)
#define FELICA_POLLER_CMD_POLLING_REQ_CODE (0x00U)
#define FELICA_POLLER_CMD_POLLING_RESP_CODE (0x01U)
typedef enum {
FelicaPollerStateIdle,
FelicaPollerStateActivated,
} FelicaPollerState;
struct FelicaPoller {
Nfc* nfc;
FelicaPollerState state;
FelicaData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
FelicaPollerEvent felica_event;
FelicaPollerEventData felica_event_data;
NfcGenericCallback callback;
void* context;
};
typedef struct {
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
} FelicaPollerPollingCommand;
typedef struct {
FelicaIDm idm;
FelicaPMm pmm;
uint8_t request_data[2];
} FelicaPollerPollingResponse;
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
FelicaError felica_poller_async_polling(
FelicaPoller* instance,
const FelicaPollerPollingCommand* cmd,
FelicaPollerPollingResponse* resp);
FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,186 @@
#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));
}

View File

@@ -0,0 +1,107 @@
#pragma once
#include <toolbox/bit_buffer.h>
#include <nfc/protocols/nfc_device_base_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3A_UID_4_BYTES (4U)
#define ISO14443_3A_UID_7_BYTES (7U)
#define ISO14443_3A_UID_10_BYTES (10U)
#define ISO14443_3A_MAX_UID_SIZE ISO14443_3A_UID_10_BYTES
#define ISO14443_3A_GUARD_TIME_US (5000)
#define ISO14443_3A_FDT_POLL_FC (1620)
#define ISO14443_3A_FDT_LISTEN_FC (1172)
#define ISO14443_3A_POLLER_MASK_RX_FS ((ISO14443_3A_FDT_LISTEN_FC) / 2)
#define ISO14443_3A_POLL_POLL_MIN_US (1100)
typedef enum {
Iso14443_3aErrorNone,
Iso14443_3aErrorNotPresent,
Iso14443_3aErrorColResFailed,
Iso14443_3aErrorBufferOverflow,
Iso14443_3aErrorCommunication,
Iso14443_3aErrorFieldOff,
Iso14443_3aErrorWrongCrc,
Iso14443_3aErrorTimeout,
} Iso14443_3aError;
typedef struct {
uint8_t sens_resp[2];
} Iso14443_3aSensResp;
typedef struct {
uint8_t sel_cmd;
uint8_t sel_par;
uint8_t data[4]; // max data bit is 32
} Iso14443_3aSddReq;
typedef struct {
uint8_t nfcid[4];
uint8_t bss;
} Iso14443_3aSddResp;
typedef struct {
uint8_t sel_cmd;
uint8_t sel_par;
uint8_t nfcid[4];
uint8_t bcc;
} Iso14443_3aSelReq;
typedef struct {
uint8_t sak;
} Iso14443_3aSelResp;
typedef struct {
uint8_t uid[ISO14443_3A_MAX_UID_SIZE];
uint8_t uid_len;
uint8_t atqa[2];
uint8_t sak;
} Iso14443_3aData;
Iso14443_3aData* iso14443_3a_alloc();
void iso14443_3a_free(Iso14443_3aData* data);
void iso14443_3a_reset(Iso14443_3aData* data);
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other);
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type);
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff);
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other);
const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len);
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data);
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data);
// Getters and tests
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data);
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data);
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]);
// Setters
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak);
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_3a;

View File

@@ -0,0 +1,127 @@
#include "iso14443_3a_listener_i.h"
#include "nfc/protocols/nfc_listener_base.h"
#include "nfc/helpers/iso14443_crc.h"
#include <furi.h>
#include <lib/nfc/nfc.h>
#define TAG "Iso14443_3aListener"
#define ISO14443_3A_LISTENER_MAX_BUFFER_SIZE (256)
static bool iso14443_3a_listener_halt_received(BitBuffer* buf) {
bool halt_cmd_received = false;
do {
if(bit_buffer_get_size_bytes(buf) != 4) break;
if(!iso14443_crc_check(Iso14443CrcTypeA, buf)) break;
if(bit_buffer_get_byte(buf, 0) != 0x50) break;
if(bit_buffer_get_byte(buf, 1) != 0x00) break;
halt_cmd_received = true;
} while(false);
return halt_cmd_received;
}
Iso14443_3aListener* iso14443_3a_listener_alloc(Nfc* nfc, Iso14443_3aData* data) {
furi_assert(nfc);
Iso14443_3aListener* instance = malloc(sizeof(Iso14443_3aListener));
instance->nfc = nfc;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_LISTENER_MAX_BUFFER_SIZE);
instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;
instance->generic_event.protocol = NfcProtocolIso14443_3a;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->iso14443_3a_event;
nfc_set_fdt_listen_fc(instance->nfc, ISO14443_3A_FDT_LISTEN_FC);
nfc_config(instance->nfc, NfcModeListener, NfcTechIso14443a);
nfc_iso14443a_listener_set_col_res_data(
instance->nfc,
instance->data->uid,
instance->data->uid_len,
instance->data->atqa,
instance->data->sak);
return instance;
}
void iso14443_3a_listener_free(Iso14443_3aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
void iso14443_3a_listener_set_callback(
Iso14443_3aListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
const Iso14443_3aData* iso14443_3a_listener_get_data(Iso14443_3aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
NfcCommand iso14443_3a_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3aListener* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypeListenerActivated) {
instance->state = Iso14443_3aListenerStateActive;
} else if(nfc_event->type == NfcEventTypeFieldOff) {
instance->state = Iso14443_3aListenerStateIdle;
if(instance->callback) {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeFieldOff;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
} else if(nfc_event->type == NfcEventTypeRxEnd) {
if(iso14443_3a_listener_halt_received(nfc_event->data.buffer)) {
if(instance->callback) {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeHalted;
instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandSleep;
} else {
if(iso14443_crc_check(Iso14443CrcTypeA, nfc_event->data.buffer)) {
instance->iso14443_3a_event.type =
Iso14443_3aListenerEventTypeReceivedStandardFrame;
iso14443_crc_trim(nfc_event->data.buffer);
} else {
instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeReceivedData;
}
instance->iso14443_3a_event_data.buffer = nfc_event->data.buffer;
if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context);
}
}
}
return command;
}
const NfcListenerBase nfc_listener_iso14443_3a = {
.alloc = (NfcListenerAlloc)iso14443_3a_listener_alloc,
.free = (NfcListenerFree)iso14443_3a_listener_free,
.set_callback = (NfcListenerSetCallback)iso14443_3a_listener_set_callback,
.get_data = (NfcListenerGetData)iso14443_3a_listener_get_data,
.run = (NfcListenerRun)iso14443_3a_listener_run,
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include "iso14443_3a.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aListener Iso14443_3aListener;
typedef enum {
Iso14443_3aListenerEventTypeFieldOff,
Iso14443_3aListenerEventTypeHalted,
Iso14443_3aListenerEventTypeReceivedStandardFrame,
Iso14443_3aListenerEventTypeReceivedData,
} Iso14443_3aListenerEventType;
typedef struct {
BitBuffer* buffer;
} Iso14443_3aListenerEventData;
typedef struct {
Iso14443_3aListenerEventType type;
Iso14443_3aListenerEventData* data;
} Iso14443_3aListenerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_iso14443_3a;

View File

@@ -0,0 +1,73 @@
#include "iso14443_3a_listener_i.h"
#include "nfc/helpers/iso14443_crc.h"
#define TAG "Iso14443_3aListener"
static Iso14443_3aError iso14443_3a_listener_process_nfc_error(NfcError error) {
Iso14443_3aError ret = Iso14443_3aErrorNone;
if(error == NfcErrorNone) {
ret = Iso14443_3aErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso14443_3aErrorTimeout;
} else {
ret = Iso14443_3aErrorFieldOff;
}
return ret;
}
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_listener_tx(instance->nfc, tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_iso14443a_listener_tx_custom_parity(instance->nfc, tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
}
return ret;
};
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(instance->tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_W(TAG, "Tx error: %d", error);
ret = iso14443_3a_listener_process_nfc_error(error);
break;
}
} while(false);
return ret;
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_3a_listener.h"
#include <nfc/protocols/nfc_generic_event.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_3aListenerStateIdle,
Iso14443_3aListenerStateActive,
} Iso14443_3aListenerState;
struct Iso14443_3aListener {
Nfc* nfc;
Iso14443_3aData* data;
Iso14443_3aListenerState state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Iso14443_3aListenerEvent iso14443_3a_event;
Iso14443_3aListenerEventData iso14443_3a_event_data;
NfcGenericCallback callback;
void* context;
};
Iso14443_3aError
iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
Iso14443_3aError iso14443_3a_listener_send_standard_frame(
Iso14443_3aListener* instance,
const BitBuffer* tx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,128 @@
#include "iso14443_3a_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "ISO14443_3A"
const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static Iso14443_3aPoller* iso14443_3a_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
Iso14443_3aPoller* instance = malloc(sizeof(Iso14443_3aPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443a);
nfc_set_guard_time_us(instance->nfc, ISO14443_3A_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3A_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3A_POLL_POLL_MIN_US);
instance->data = iso14443_3a_alloc();
instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data;
instance->general_event.protocol = NfcProtocolIso14443_3a;
instance->general_event.event_data = &instance->iso14443_3a_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_3a_poller_free_new(Iso14443_3aPoller* iso14443_3a_poller) {
furi_assert(iso14443_3a_poller);
Iso14443_3aPoller* instance = iso14443_3a_poller;
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
iso14443_3a_free(instance->data);
free(instance);
}
static void iso14443_3a_poller_set_callback(
Iso14443_3aPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_3a_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3aPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != Iso14443_3aPollerStateActivated) {
Iso14443_3aData data = {};
Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, &data);
if(error == Iso14443_3aErrorNone) {
instance->state = Iso14443_3aPollerStateActivated;
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;
instance->iso14443_3a_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeError;
instance->iso14443_3a_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady;
instance->iso14443_3a_event_data.error = Iso14443_3aErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
if(command == NfcCommandReset) {
instance->state = Iso14443_3aPollerStateIdle;
}
return command;
}
static bool iso14443_3a_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
Iso14443_3aPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == Iso14443_3aPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, NULL);
protocol_detected = (error == Iso14443_3aErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_3a = {
.alloc = (NfcPollerAlloc)iso14443_3a_poller_alloc,
.free = (NfcPollerFree)iso14443_3a_poller_free_new,
.set_callback = (NfcPollerSetCallback)iso14443_3a_poller_set_callback,
.run = (NfcPollerRun)iso14443_3a_poller_run,
.detect = (NfcPollerDetect)iso14443_3a_poller_detect,
.get_data = (NfcPollerGetData)iso14443_3a_poller_get_data,
};

View File

@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_3a.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3aPoller Iso14443_3aPoller;
typedef enum {
Iso14443_3aPollerEventTypeError,
Iso14443_3aPollerEventTypeReady,
} Iso14443_3aPollerEventType;
typedef struct {
Iso14443_3aError error;
} Iso14443_3aPollerEventData;
typedef struct {
Iso14443_3aPollerEventType type;
Iso14443_3aPollerEventData* data;
} Iso14443_3aPollerEvent;
Iso14443_3aError iso14443_3a_poller_txrx(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
Iso14443_3aError iso14443_3a_poller_send_standard_frame(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_3a;

View File

@@ -0,0 +1,293 @@
#include "iso14443_3a_poller_i.h"
#include <furi.h>
#include "nfc/helpers/iso14443_crc.h"
#define TAG "ISO14443_3A"
static Iso14443_3aError iso14443_3a_poller_process_error(NfcError error) {
Iso14443_3aError ret = Iso14443_3aErrorNone;
if(error == NfcErrorNone) {
ret = Iso14443_3aErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso14443_3aErrorTimeout;
} else {
ret = Iso14443_3aErrorNotPresent;
}
return ret;
}
static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
uint16_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - 2);
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) {
ret = Iso14443_3aErrorWrongCrc;
break;
}
iso14443_crc_trim(rx_buffer);
} while(false);
return ret;
}
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
NfcError error = NfcErrorNone;
Iso14443_3aError ret = Iso14443_3aErrorNone;
do {
error = nfc_iso14443a_poller_trx_short_frame(
instance->nfc,
NfcIso14443aShortFrameSensReq,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {
ret = Iso14443_3aErrorCommunication;
break;
}
} while(false);
return ret;
}
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(instance->tx_buffer);
uint8_t halt_cmd[2] = {0x50, 0x00};
bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd));
iso14443_3a_poller_standard_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);
instance->state = Iso14443_3aPollerStateIdle;
return Iso14443_3aErrorNone;
}
Iso14443_3aError iso14443_3a_poller_async_activate(
Iso14443_3aPoller* instance,
Iso14443_3aData* iso14443_3a_data) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
// Reset Iso14443_3a poller state
memset(&instance->col_res, 0, sizeof(instance->col_res));
memset(instance->data, 0, sizeof(Iso14443_3aData));
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Halt if necessary
if(instance->state != Iso14443_3aPollerStateIdle) {
iso14443_3a_poller_halt(instance);
instance->state = Iso14443_3aPollerStateIdle;
}
NfcError error = NfcErrorNone;
Iso14443_3aError ret = Iso14443_3aErrorNone;
bool activated = false;
do {
error = nfc_iso14443a_poller_trx_short_frame(
instance->nfc,
NfcIso14443aShortFrameSensReq,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
ret = Iso14443_3aErrorNotPresent;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) {
FURI_LOG_W(TAG, "Wrong sens response size");
ret = Iso14443_3aErrorCommunication;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer,
&instance->col_res.sens_resp,
sizeof(instance->col_res.sens_resp));
memcpy(
instance->data->atqa,
&instance->col_res.sens_resp,
sizeof(instance->col_res.sens_resp));
instance->state = Iso14443_3aPollerStateColResInProgress;
instance->col_res.cascade_level = 0;
instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;
while(instance->state == Iso14443_3aPollerStateColResInProgress) {
if(instance->col_res.state == Iso14443_3aPollerColResStateStateNewCascade) {
bit_buffer_set_size_bytes(instance->tx_buffer, 2);
bit_buffer_set_byte(
instance->tx_buffer,
0,
ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level));
bit_buffer_set_byte(instance->tx_buffer, 1, ISO14443_3A_POLLER_SEL_PAR(2, 0));
error = nfc_iso14443a_poller_trx_sdd_frame(
instance->nfc,
instance->tx_buffer,
instance->rx_buffer,
ISO14443_3A_FDT_LISTEN_FC);
if(error != NfcErrorNone) {
FURI_LOG_E(TAG, "Sdd request failed: %d", error);
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != 5) {
FURI_LOG_E(TAG, "Sdd response wrong length");
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer, &instance->col_res.sdd_resp, sizeof(Iso14443_3aSddResp));
instance->col_res.state = Iso14443_3aPollerColResStateStateSelectCascade;
} else if(instance->col_res.state == Iso14443_3aPollerColResStateStateSelectCascade) {
instance->col_res.sel_req.sel_cmd =
ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level);
instance->col_res.sel_req.sel_par = ISO14443_3A_POLLER_SEL_PAR(7, 0);
memcpy(
instance->col_res.sel_req.nfcid,
instance->col_res.sdd_resp.nfcid,
sizeof(instance->col_res.sdd_resp.nfcid));
instance->col_res.sel_req.bcc = instance->col_res.sdd_resp.bss;
bit_buffer_copy_bytes(
instance->tx_buffer,
(uint8_t*)&instance->col_res.sel_req,
sizeof(instance->col_res.sel_req));
ret = iso14443_3a_poller_send_standard_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC);
if(ret != Iso14443_3aErrorNone) {
FURI_LOG_E(TAG, "Sel request failed: %d", ret);
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) !=
sizeof(instance->col_res.sel_resp)) {
FURI_LOG_E(TAG, "Sel response wrong length");
instance->state = Iso14443_3aPollerStateColResFailed;
ret = Iso14443_3aErrorColResFailed;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer,
&instance->col_res.sel_resp,
sizeof(instance->col_res.sel_resp));
FURI_LOG_T(TAG, "Sel resp: %02X", instance->col_res.sel_resp.sak);
if(instance->col_res.sel_req.nfcid[0] == ISO14443_3A_POLLER_SDD_CL) {
// Copy part of UID
memcpy(
&instance->data->uid[instance->data->uid_len],
&instance->col_res.sel_req.nfcid[1],
3);
instance->data->uid_len += 3;
instance->col_res.cascade_level++;
instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade;
} else {
FURI_LOG_T(TAG, "Col resolution complete");
instance->data->sak = instance->col_res.sel_resp.sak;
memcpy(
&instance->data->uid[instance->data->uid_len],
&instance->col_res.sel_req.nfcid[0],
4);
instance->data->uid_len += 4;
instance->col_res.state = Iso14443_3aPollerColResStateStateSuccess;
instance->state = Iso14443_3aPollerStateActivated;
}
}
}
activated = (instance->state == Iso14443_3aPollerStateActivated);
} while(false);
if(activated && iso14443_3a_data) {
*iso14443_3a_data = *instance->data;
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error =
nfc_iso14443a_poller_trx_custom_parity(instance->nfc, tx_buffer, rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_txrx(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret = Iso14443_3aErrorNone;
NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3a_poller_process_error(error);
}
return ret;
}
Iso14443_3aError iso14443_3a_poller_send_standard_frame(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
Iso14443_3aError ret =
iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
return ret;
}

View File

@@ -0,0 +1,81 @@
#pragma once
#include "iso14443_3a_poller.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U)
#define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl))
#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits)&0x0fU))
#define ISO14443_3A_POLLER_SDD_CL (0x88U)
typedef enum {
Iso14443_3aPollerColResStateStateIdle,
Iso14443_3aPollerColResStateStateNewCascade,
Iso14443_3aPollerColResStateStateSelectCascade,
Iso14443_3aPollerColResStateStateSuccess,
Iso14443_3aPollerColResStateStateFail,
} Iso14443_3aPollerColResState;
typedef struct {
Iso14443_3aPollerColResState state;
Iso14443_3aSensResp sens_resp;
Iso14443_3aSddReq sdd_req;
Iso14443_3aSddResp sdd_resp;
Iso14443_3aSelReq sel_req;
Iso14443_3aSelResp sel_resp;
uint8_t cascade_level;
} Iso14443_3aPollerColRes;
typedef enum {
Iso14443_3aPollerStateIdle,
Iso14443_3aPollerStateColResInProgress,
Iso14443_3aPollerStateColResFailed,
Iso14443_3aPollerStateActivated,
} Iso14443_3aPollerState;
typedef enum {
Iso14443_3aPollerConfigStateIdle,
Iso14443_3aPollerConfigStateDone,
} Iso14443_3aPollerConfigState;
struct Iso14443_3aPoller {
Nfc* nfc;
Iso14443_3aPollerState state;
Iso14443_3aPollerConfigState config_state;
Iso14443_3aPollerColRes col_res;
Iso14443_3aData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
Iso14443_3aPollerEvent iso14443_3a_event;
Iso14443_3aPollerEventData iso14443_3a_event_data;
NfcGenericCallback callback;
void* context;
};
const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_async_activate(
Iso14443_3aPoller* instance,
Iso14443_3aData* iso14443_3a_data);
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance);
Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
Iso14443_3aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,58 @@
#include "iso14443_3a_poller_sync_api.h"
#include "iso14443_3a_poller_i.h"
#include <nfc/nfc_poller.h>
#include <furi/furi.h>
#define ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)
typedef struct {
Iso14443_3aPoller* instance;
FuriThreadId thread_id;
Iso14443_3aError error;
Iso14443_3aData data;
} Iso14443_3aPollerContext;
NfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
Iso14443_3aPollerContext* poller_context = context;
Iso14443_3aPoller* iso14443_3a_poller = event.instance;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
iso14443_3a_copy(&poller_context->data, iso14443_3a_poller->data);
}
poller_context->error = iso14443_3a_event->data->error;
furi_thread_flags_set(poller_context->thread_id, ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);
return NfcCommandStop;
}
Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) {
furi_assert(nfc);
furi_assert(iso14443_3a_data);
Iso14443_3aPollerContext poller_context = {};
poller_context.thread_id = furi_thread_get_current_id();
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a);
nfc_poller_start(poller, iso14443_3a_poller_read_callback, &poller_context);
furi_thread_flags_wait(
ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
furi_thread_flags_clear(ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE);
nfc_poller_stop(poller);
nfc_poller_free(poller);
if(poller_context.error == Iso14443_3aErrorNone) {
*iso14443_3a_data = poller_context.data;
}
return poller_context.error;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "iso14443_3a.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,223 @@
#include "iso14443_3b_i.h"
#include <furi.h>
#include <nfc/protocols/nfc_device_base_i.h>
#include <nfc/nfc_common.h>
#include <nfc/helpers/iso14443_crc.h>
#define ISO14443_3B_PROTOCOL_NAME "ISO14443-3B"
#define ISO14443_3B_DEVICE_NAME "ISO14443-3B (Unknown)"
#define ISO14443_3B_APP_DATA_KEY "Application data"
#define ISO14443_3B_PROTOCOL_INFO_KEY "Protocol info"
#define ISO14443_3B_FDT_POLL_DEFAULT_FC (ISO14443_3B_FDT_POLL_FC)
const NfcDeviceBase nfc_device_iso14443_3b = {
.protocol_name = ISO14443_3B_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_3b_alloc,
.free = (NfcDeviceFree)iso14443_3b_free,
.reset = (NfcDeviceReset)iso14443_3b_reset,
.copy = (NfcDeviceCopy)iso14443_3b_copy,
.verify = (NfcDeviceVerify)iso14443_3b_verify,
.load = (NfcDeviceLoad)iso14443_3b_load,
.save = (NfcDeviceSave)iso14443_3b_save,
.is_equal = (NfcDeviceEqual)iso14443_3b_is_equal,
.get_name = (NfcDeviceGetName)iso14443_3b_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_3b_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_3b_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_3b_get_base_data,
};
Iso14443_3bData* iso14443_3b_alloc() {
Iso14443_3bData* data = malloc(sizeof(Iso14443_3bData));
return data;
}
void iso14443_3b_free(Iso14443_3bData* data) {
furi_assert(data);
free(data);
}
void iso14443_3b_reset(Iso14443_3bData* data) {
memset(data, 0, sizeof(Iso14443_3bData));
}
void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other) {
furi_assert(data);
furi_assert(other);
*data = *other;
}
bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// No support for old ISO14443-3B
return false;
}
bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
if(!flipper_format_read_hex(
ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE))
break;
if(!flipper_format_read_hex(
ff,
ISO14443_3B_PROTOCOL_INFO_KEY,
(uint8_t*)&data->protocol_info,
sizeof(Iso14443_3bProtocolInfo)))
break;
parsed = true;
} while(false);
return parsed;
}
bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, ISO14443_3B_PROTOCOL_NAME " specific data"))
break;
if(!flipper_format_write_hex(
ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE))
break;
if(!flipper_format_write_hex(
ff,
ISO14443_3B_PROTOCOL_INFO_KEY,
(uint8_t*)&data->protocol_info,
sizeof(Iso14443_3bProtocolInfo)))
break;
saved = true;
} while(false);
return saved;
}
bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data, other, sizeof(Iso14443_3bData)) == 0;
}
const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_3B_DEVICE_NAME;
}
const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len) {
furi_assert(data);
furi_assert(uid_len);
*uid_len = ISO14443_3B_UID_SIZE;
return data->uid;
}
bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
const bool uid_valid = uid_len == ISO14443_3B_UID_SIZE;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
}
return uid_valid;
}
Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data) {
UNUSED(data);
furi_crash("No base data");
}
bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data) {
furi_assert(data);
return data->protocol_info.protocol_type == 0x01;
}
bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate) {
furi_assert(data);
const uint8_t capability = data->protocol_info.bit_rate_capability;
switch(bit_rate) {
case Iso14443_3bBitRateBoth106Kbit:
return capability == ISO14443_3B_BIT_RATE_BOTH_106KBIT;
case Iso14443_3bBitRatePiccToPcd212Kbit:
return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT;
case Iso14443_3bBitRatePiccToPcd424Kbit:
return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT;
case Iso14443_3bBitRatePiccToPcd848Kbit:
return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT;
case Iso14443_3bBitRatePcdToPicc212Kbit:
return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT;
case Iso14443_3bBitRatePcdToPicc424Kbit:
return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT;
case Iso14443_3bBitRatePcdToPicc848Kbit:
return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT;
default:
return false;
}
}
bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option) {
furi_assert(data);
switch(option) {
case Iso14443_3bFrameOptionNad:
return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_NAD;
case Iso14443_3bFrameOptionCid:
return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_CID;
default:
return false;
}
}
const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size) {
furi_assert(data);
furi_assert(data_size);
*data_size = ISO14443_3B_APP_DATA_SIZE;
return data->app_data;
}
uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data) {
furi_assert(data);
const uint8_t fs_bits = data->protocol_info.max_frame_size;
if(fs_bits < 5) {
return fs_bits * 8 + 16;
} else if(fs_bits == 5) {
return 64;
} else if(fs_bits == 6) {
return 96;
} else if(fs_bits < 13) {
return 128U << (fs_bits - 7);
} else {
return 0;
}
}
uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data) {
furi_assert(data);
const uint8_t fwi = data->protocol_info.fwi;
return fwi < 0x0F ? 4096UL << fwi : ISO14443_3B_FDT_POLL_DEFAULT_FC;
}

View File

@@ -0,0 +1,83 @@
#pragma once
#include <nfc/protocols/nfc_device_base.h>
#include <core/string.h>
#include <toolbox/bit_buffer.h>
#include <flipper_format/flipper_format.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_3bErrorNone,
Iso14443_3bErrorNotPresent,
Iso14443_3bErrorColResFailed,
Iso14443_3bErrorBufferOverflow,
Iso14443_3bErrorCommunication,
Iso14443_3bErrorFieldOff,
Iso14443_3bErrorWrongCrc,
Iso14443_3bErrorTimeout,
} Iso14443_3bError;
typedef enum {
Iso14443_3bBitRateBoth106Kbit,
Iso14443_3bBitRatePiccToPcd212Kbit,
Iso14443_3bBitRatePiccToPcd424Kbit,
Iso14443_3bBitRatePiccToPcd848Kbit,
Iso14443_3bBitRatePcdToPicc212Kbit,
Iso14443_3bBitRatePcdToPicc424Kbit,
Iso14443_3bBitRatePcdToPicc848Kbit,
} Iso14443_3bBitRate;
typedef enum {
Iso14443_3bFrameOptionNad,
Iso14443_3bFrameOptionCid,
} Iso14443_3bFrameOption;
typedef struct Iso14443_3bData Iso14443_3bData;
// Virtual methods
Iso14443_3bData* iso14443_3b_alloc();
void iso14443_3b_free(Iso14443_3bData* data);
void iso14443_3b_reset(Iso14443_3bData* data);
void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other);
bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type);
bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff);
bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other);
const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len);
bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data);
// Getters and tests
bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data);
bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate);
bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option);
const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size);
uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data);
uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_3b;

View File

@@ -0,0 +1 @@
#include "iso14443_3b_i.h"

View File

@@ -0,0 +1,37 @@
#pragma once
#include "iso14443_3b.h"
#define ISO14443_3B_UID_SIZE (4U)
#define ISO14443_3B_APP_DATA_SIZE (4U)
#define ISO14443_3B_GUARD_TIME_US (5000U)
#define ISO14443_3B_FDT_POLL_FC (9000U)
#define ISO14443_3B_POLL_POLL_MIN_US (1280U)
#define ISO14443_3B_BIT_RATE_BOTH_106KBIT (0U << 0)
#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT (1U << 0)
#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT (1U << 1)
#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT (1U << 2)
#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT (1U << 4)
#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT (1U << 5)
#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT (1U << 6)
#define ISO14443_3B_BIT_RATE_BOTH_SAME_COMPULSORY (1U << 7)
#define ISO14443_3B_FRAME_OPTION_NAD (1U << 1)
#define ISO14443_3B_FRAME_OPTION_CID (1U << 0)
typedef struct {
uint8_t bit_rate_capability;
uint8_t protocol_type : 4;
uint8_t max_frame_size : 4;
uint8_t fo : 2;
uint8_t adc : 2;
uint8_t fwi : 4;
} Iso14443_3bProtocolInfo;
struct Iso14443_3bData {
uint8_t uid[ISO14443_3B_UID_SIZE];
uint8_t app_data[ISO14443_3B_APP_DATA_SIZE];
Iso14443_3bProtocolInfo protocol_info;
};

View File

@@ -0,0 +1,121 @@
#include "iso14443_3b_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "ISO14443_3bPoller"
const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static Iso14443_3bPoller* iso14443_3b_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
Iso14443_3bPoller* instance = malloc(sizeof(Iso14443_3bPoller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b);
nfc_set_guard_time_us(instance->nfc, ISO14443_3B_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3B_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3B_POLL_POLL_MIN_US);
instance->data = iso14443_3b_alloc();
instance->iso14443_3b_event.data = &instance->iso14443_3b_event_data;
instance->general_event.protocol = NfcProtocolIso14443_3b;
instance->general_event.event_data = &instance->iso14443_3b_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_3b_poller_free(Iso14443_3bPoller* instance) {
furi_assert(instance);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
iso14443_3b_free(instance->data);
free(instance);
}
static void iso14443_3b_poller_set_callback(
Iso14443_3bPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_3b_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso14443_3bPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != Iso14443_3bPollerStateActivated) {
Iso14443_3bError error = iso14443_3b_poller_async_activate(instance, instance->data);
if(error == Iso14443_3bErrorNone) {
instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady;
instance->iso14443_3b_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeError;
instance->iso14443_3b_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady;
instance->iso14443_3b_event_data.error = Iso14443_3bErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
static bool iso14443_3b_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
Iso14443_3bPoller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == Iso14443_3bPollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
Iso14443_3bError error = iso14443_3b_poller_async_activate(instance, instance->data);
protocol_detected = (error == Iso14443_3bErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_3b = {
.alloc = (NfcPollerAlloc)iso14443_3b_poller_alloc,
.free = (NfcPollerFree)iso14443_3b_poller_free,
.set_callback = (NfcPollerSetCallback)iso14443_3b_poller_set_callback,
.run = (NfcPollerRun)iso14443_3b_poller_run,
.detect = (NfcPollerDetect)iso14443_3b_poller_detect,
.get_data = (NfcPollerGetData)iso14443_3b_poller_get_data,
};

View File

@@ -0,0 +1,30 @@
#pragma once
#include "iso14443_3b.h"
#include <lib/nfc/nfc.h>
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_3bPoller Iso14443_3bPoller;
typedef enum {
Iso14443_3bPollerEventTypeError,
Iso14443_3bPollerEventTypeReady,
} Iso14443_3bPollerEventType;
typedef struct {
Iso14443_3bError error;
} Iso14443_3bPollerEventData;
typedef struct {
Iso14443_3bPollerEventType type;
Iso14443_3bPollerEventData* data;
} Iso14443_3bPollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_3b;

View File

@@ -0,0 +1,194 @@
#include "iso14443_3b_poller_i.h"
#include <nfc/helpers/iso14443_crc.h>
#define TAG "Iso14443_3bPoller"
#define ISO14443_3B_ATTRIB_FRAME_SIZE_256 (0x08)
static Iso14443_3bError iso14443_3b_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso14443_3bErrorNone;
case NfcErrorTimeout:
return Iso14443_3bErrorTimeout;
default:
return Iso14443_3bErrorNotPresent;
}
}
static Iso14443_3bError iso14443_3b_poller_prepare_trx(Iso14443_3bPoller* instance) {
furi_assert(instance);
if(instance->state == Iso14443_3bPollerStateIdle) {
return iso14443_3b_poller_async_activate(instance, NULL);
}
return Iso14443_3bErrorNone;
}
static Iso14443_3bError iso14443_3b_poller_frame_exchange(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
furi_assert(
tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE);
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer);
Iso14443_3bError ret = Iso14443_3bErrorNone;
do {
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso14443_3b_poller_process_error(error);
break;
}
bit_buffer_copy(rx_buffer, instance->rx_buffer);
if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) {
ret = Iso14443_3bErrorWrongCrc;
break;
}
iso14443_crc_trim(rx_buffer);
} while(false);
return ret;
}
Iso14443_3bError
iso14443_3b_poller_async_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data) {
furi_assert(instance);
furi_assert(instance->nfc);
iso14443_3b_reset(data);
Iso14443_3bError ret;
do {
instance->state = Iso14443_3bPollerStateColResInProgress;
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send REQB
bit_buffer_append_byte(instance->tx_buffer, 0x05);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
bit_buffer_append_byte(instance->tx_buffer, 0x08);
ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC);
if(ret != Iso14443_3bErrorNone) {
instance->state = Iso14443_3bPollerStateColResFailed;
break;
}
typedef struct {
uint8_t flag;
uint8_t uid[ISO14443_3B_UID_SIZE];
uint8_t app_data[ISO14443_3B_APP_DATA_SIZE];
Iso14443_3bProtocolInfo protocol_info;
} Iso14443_3bAtqBLayout;
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(Iso14443_3bAtqBLayout)) {
FURI_LOG_D(TAG, "Unexpected REQB response");
instance->state = Iso14443_3bPollerStateColResFailed;
ret = Iso14443_3bErrorCommunication;
break;
}
instance->state = Iso14443_3bPollerStateActivationInProgress;
const Iso14443_3bAtqBLayout* atqb =
(const Iso14443_3bAtqBLayout*)bit_buffer_get_data(instance->rx_buffer);
memcpy(data->uid, atqb->uid, ISO14443_3B_UID_SIZE);
memcpy(data->app_data, atqb->app_data, ISO14443_3B_APP_DATA_SIZE);
data->protocol_info = atqb->protocol_info;
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send ATTRIB
bit_buffer_append_byte(instance->tx_buffer, 0x1d);
bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256);
bit_buffer_append_byte(instance->tx_buffer, 0x01);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data));
if(ret != Iso14443_3bErrorNone) {
instance->state = Iso14443_3bPollerStateActivationFailed;
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1 ||
bit_buffer_get_byte(instance->rx_buffer, 0) != 0) {
FURI_LOG_D(TAG, "Unexpected ATTRIB response");
instance->state = Iso14443_3bPollerStateActivationFailed;
ret = Iso14443_3bErrorCommunication;
break;
}
instance->state = Iso14443_3bPollerStateActivated;
} while(false);
return ret;
}
Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
bit_buffer_append_byte(instance->tx_buffer, 0x50);
bit_buffer_append_bytes(instance->tx_buffer, instance->data->uid, ISO14443_3B_UID_SIZE);
Iso14443_3bError ret;
do {
ret = iso14443_3b_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC);
if(ret != Iso14443_3bErrorNone) {
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(uint8_t) ||
bit_buffer_get_byte(instance->rx_buffer, 0) != 0) {
ret = Iso14443_3bErrorCommunication;
break;
}
instance->state = Iso14443_3bPollerStateIdle;
} while(false);
return ret;
}
Iso14443_3bError iso14443_3b_poller_send_frame(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
Iso14443_3bError ret;
do {
ret = iso14443_3b_poller_prepare_trx(instance);
if(ret != Iso14443_3bErrorNone) break;
ret = iso14443_3b_poller_frame_exchange(
instance, tx_buffer, rx_buffer, iso14443_3b_get_fwt_fc_max(instance->data));
} while(false);
return ret;
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include "iso14443_3b_poller.h"
#include "iso14443_3b_i.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_3B_POLLER_MAX_BUFFER_SIZE (256U)
typedef enum {
Iso14443_3bPollerStateIdle,
Iso14443_3bPollerStateColResInProgress,
Iso14443_3bPollerStateColResFailed,
Iso14443_3bPollerStateActivationInProgress,
Iso14443_3bPollerStateActivationFailed,
Iso14443_3bPollerStateActivated,
} Iso14443_3bPollerState;
struct Iso14443_3bPoller {
Nfc* nfc;
Iso14443_3bPollerState state;
Iso14443_3bData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
Iso14443_3bPollerEvent iso14443_3b_event;
Iso14443_3bPollerEventData iso14443_3b_event_data;
NfcGenericCallback callback;
void* context;
};
const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance);
Iso14443_3bError
iso14443_3b_poller_async_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data);
Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance);
Iso14443_3bError iso14443_3b_poller_send_frame(
Iso14443_3bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,300 @@
#include "iso14443_4a_i.h"
#include <furi.h>
#define ISO14443_4A_PROTOCOL_NAME "ISO14443-4A"
#define ISO14443_4A_DEVICE_NAME "ISO14443-4A (Unknown)"
#define ISO14443_4A_T0_KEY "T0"
#define ISO14443_4A_TA1_KEY "TA(1)"
#define ISO14443_4A_TB1_KEY "TB(1)"
#define ISO14443_4A_TC1_KEY "TC(1)"
#define ISO14443_4A_T1_TK_KEY "T1...Tk"
#define ISO14443_4A_FDT_DEFAULT_FC ISO14443_3A_FDT_POLL_FC
typedef enum {
Iso14443_4aInterfaceByteTA1,
Iso14443_4aInterfaceByteTB1,
Iso14443_4aInterfaceByteTC1,
} Iso14443_4aInterfaceByte;
const NfcDeviceBase nfc_device_iso14443_4a = {
.protocol_name = ISO14443_4A_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_4a_alloc,
.free = (NfcDeviceFree)iso14443_4a_free,
.reset = (NfcDeviceReset)iso14443_4a_reset,
.copy = (NfcDeviceCopy)iso14443_4a_copy,
.verify = (NfcDeviceVerify)iso14443_4a_verify,
.load = (NfcDeviceLoad)iso14443_4a_load,
.save = (NfcDeviceSave)iso14443_4a_save,
.is_equal = (NfcDeviceEqual)iso14443_4a_is_equal,
.get_name = (NfcDeviceGetName)iso14443_4a_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_4a_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_4a_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data,
};
Iso14443_4aData* iso14443_4a_alloc() {
Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData));
data->iso14443_3a_data = iso14443_3a_alloc();
data->ats_data.t1_tk = simple_array_alloc(&simple_array_config_uint8_t);
return data;
}
void iso14443_4a_free(Iso14443_4aData* data) {
furi_assert(data);
simple_array_free(data->ats_data.t1_tk);
iso14443_3a_free(data->iso14443_3a_data);
free(data);
}
void iso14443_4a_reset(Iso14443_4aData* data) {
furi_assert(data);
iso14443_3a_reset(data->iso14443_3a_data);
data->ats_data.tl = 1;
data->ats_data.t0 = 0;
data->ats_data.ta_1 = 0;
data->ats_data.tb_1 = 0;
data->ats_data.tc_1 = 0;
simple_array_reset(data->ats_data.t1_tk);
}
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) {
furi_assert(data);
furi_assert(other);
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
data->ats_data.tl = other->ats_data.tl;
data->ats_data.t0 = other->ats_data.t0;
data->ats_data.ta_1 = other->ats_data.ta_1;
data->ats_data.tb_1 = other->ats_data.tb_1;
data->ats_data.tc_1 = other->ats_data.tc_1;
simple_array_copy(data->ats_data.t1_tk, other->ats_data.t1_tk);
}
bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// Empty, unified file format only
return false;
}
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;
Iso14443_4aAtsData* ats_data = &data->ats_data;
ats_data->tl = 1;
if(flipper_format_key_exist(ff, ISO14443_4A_T0_KEY)) {
if(!flipper_format_read_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TA1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TB1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TC1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;
++ats_data->tl;
}
if(flipper_format_key_exist(ff, ISO14443_4A_T1_TK_KEY)) {
uint32_t t1_tk_size;
if(!flipper_format_get_value_count(ff, ISO14443_4A_T1_TK_KEY, &t1_tk_size)) break;
if(t1_tk_size > 0) {
simple_array_init(ats_data->t1_tk, t1_tk_size);
if(!flipper_format_read_hex(
ff,
ISO14443_4A_T1_TK_KEY,
simple_array_get_data(ats_data->t1_tk),
t1_tk_size))
break;
ats_data->tl += t1_tk_size;
}
}
parsed = true;
} while(false);
return parsed;
}
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, ISO14443_4A_PROTOCOL_NAME " specific data"))
break;
const Iso14443_4aAtsData* ats_data = &data->ats_data;
if(ats_data->tl > 1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;
if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;
}
const uint32_t t1_tk_size = simple_array_get_count(ats_data->t1_tk);
if(t1_tk_size > 0) {
if(!flipper_format_write_hex(
ff,
ISO14443_4A_T1_TK_KEY,
simple_array_cget_data(ats_data->t1_tk),
t1_tk_size))
break;
}
}
saved = true;
} while(false);
return saved;
}
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) {
return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data);
}
const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_4A_DEVICE_NAME;
}
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) {
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
}
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
}
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) {
furi_assert(data);
return data->iso14443_3a_data;
}
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {
furi_assert(data);
const uint8_t fsci = data->ats_data.t0 & 0x0F;
if(fsci < 5) {
return fsci * 8 + 16;
} else if(fsci == 5) {
return 64;
} else if(fsci == 6) {
return 96;
} else if(fsci < 13) {
return 128U << (fsci - 7);
} else {
return 0;
}
}
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {
furi_assert(data);
uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC;
do {
if(!(data->ats_data.tl > 1)) break;
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) break;
const uint8_t fwi = data->ats_data.tb_1 >> 4;
if(fwi == 0x0F) break;
fwt_fc_max = 4096UL << fwi;
} while(false);
return fwt_fc_max;
}
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) {
furi_assert(data);
furi_assert(count);
*count = simple_array_get_count(data->ats_data.t1_tk);
return simple_array_cget_data(data->ats_data.t1_tk);
}
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) {
furi_assert(data);
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1))
return bit_rate == Iso14443_4aBitRateBoth106Kbit;
const uint8_t ta_1 = data->ats_data.ta_1;
switch(bit_rate) {
case Iso14443_4aBitRateBoth106Kbit:
return ta_1 == ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY;
case Iso14443_4aBitRatePiccToPcd212Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT;
case Iso14443_4aBitRatePiccToPcd424Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT;
case Iso14443_4aBitRatePiccToPcd848Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT;
case Iso14443_4aBitRatePcdToPicc212Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT;
case Iso14443_4aBitRatePcdToPicc424Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT;
case Iso14443_4aBitRatePcdToPicc848Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT;
default:
return false;
}
}
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option) {
furi_assert(data);
const Iso14443_4aAtsData* ats_data = &data->ats_data;
if(!(ats_data->t0 & ISO14443_4A_ATS_T0_TC1)) return false;
switch(option) {
case Iso14443_4aFrameOptionNad:
return ats_data->tc_1 & ISO14443_4A_ATS_TC1_NAD;
case Iso14443_4aFrameOptionCid:
return ats_data->tc_1 & ISO14443_4A_ATS_TC1_CID;
default:
return false;
}
}

View File

@@ -0,0 +1,73 @@
#pragma once
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4aErrorNone,
Iso14443_4aErrorNotPresent,
Iso14443_4aErrorProtocol,
Iso14443_4aErrorTimeout,
} Iso14443_4aError;
typedef enum {
Iso14443_4aBitRateBoth106Kbit,
Iso14443_4aBitRatePiccToPcd212Kbit,
Iso14443_4aBitRatePiccToPcd424Kbit,
Iso14443_4aBitRatePiccToPcd848Kbit,
Iso14443_4aBitRatePcdToPicc212Kbit,
Iso14443_4aBitRatePcdToPicc424Kbit,
Iso14443_4aBitRatePcdToPicc848Kbit,
} Iso14443_4aBitRate;
typedef enum {
Iso14443_4aFrameOptionNad,
Iso14443_4aFrameOptionCid,
} Iso14443_4aFrameOption;
typedef struct Iso14443_4aData Iso14443_4aData;
// Virtual methods
Iso14443_4aData* iso14443_4a_alloc();
void iso14443_4a_free(Iso14443_4aData* data);
void iso14443_4a_reset(Iso14443_4aData* data);
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other);
bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type);
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff);
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other);
const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len);
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data);
// Getters & Tests
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data);
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data);
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count);
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate);
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_4a;

View File

@@ -0,0 +1,71 @@
#include "iso14443_4a_i.h"
bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf) {
bool can_parse = false;
do {
const size_t buf_size = bit_buffer_get_size_bytes(buf);
if(buf_size == 0) break;
size_t current_index = 0;
const uint8_t tl = bit_buffer_get_byte(buf, current_index++);
if(tl != buf_size) break;
data->tl = tl;
if(tl > 1) {
const uint8_t t0 = bit_buffer_get_byte(buf, current_index++);
const bool has_ta_1 = t0 & ISO14443_4A_ATS_T0_TA1;
const bool has_tb_1 = t0 & ISO14443_4A_ATS_T0_TB1;
const bool has_tc_1 = t0 & ISO14443_4A_ATS_T0_TC1;
const uint8_t buf_size_min =
2 + (has_ta_1 ? 1 : 0) + (has_tb_1 ? 1 : 0) + (has_tc_1 ? 1 : 0);
if(buf_size < buf_size_min) break;
data->t0 = t0;
if(has_ta_1) {
data->ta_1 = bit_buffer_get_byte(buf, current_index++);
}
if(has_tb_1) {
data->tb_1 = bit_buffer_get_byte(buf, current_index++);
}
if(has_tc_1) {
data->tc_1 = bit_buffer_get_byte(buf, current_index++);
}
const uint8_t t1_tk_size = buf_size - buf_size_min;
if(t1_tk_size > 0) {
simple_array_init(data->t1_tk, t1_tk_size);
bit_buffer_write_bytes_mid(
buf, simple_array_get_data(data->t1_tk), current_index, t1_tk_size);
}
}
can_parse = true;
} while(false);
return can_parse;
}
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error) {
switch(error) {
case Iso14443_3aErrorNone:
return Iso14443_4aErrorNone;
case Iso14443_3aErrorNotPresent:
return Iso14443_4aErrorNotPresent;
case Iso14443_3aErrorColResFailed:
case Iso14443_3aErrorCommunication:
case Iso14443_3aErrorWrongCrc:
return Iso14443_4aErrorProtocol;
case Iso14443_3aErrorTimeout:
return Iso14443_4aErrorTimeout;
default:
return Iso14443_4aErrorProtocol;
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_4a.h"
#include <lib/toolbox/simple_array.h>
#define ISO14443_4A_CMD_READ_ATS (0xE0)
// ATS bit definitions
#define ISO14443_4A_ATS_T0_TA1 (1U << 4)
#define ISO14443_4A_ATS_T0_TB1 (1U << 5)
#define ISO14443_4A_ATS_T0_TC1 (1U << 6)
#define ISO14443_4A_ATS_TA1_BOTH_106KBIT (0U << 0)
#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT (1U << 0)
#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT (1U << 1)
#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT (1U << 2)
#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT (1U << 4)
#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT (1U << 5)
#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT (1U << 6)
#define ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY (1U << 7)
#define ISO14443_4A_ATS_TC1_NAD (1U << 0)
#define ISO14443_4A_ATS_TC1_CID (1U << 1)
typedef struct {
uint8_t tl;
uint8_t t0;
uint8_t ta_1;
uint8_t tb_1;
uint8_t tc_1;
SimpleArray* t1_tk;
} Iso14443_4aAtsData;
struct Iso14443_4aData {
Iso14443_3aData* iso14443_3a_data;
Iso14443_4aAtsData ats_data;
};
bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf);
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error);

View File

@@ -0,0 +1,99 @@
#include "iso14443_4a_listener_i.h"
#include <furi.h>
#include <nfc/protocols/nfc_listener_base.h>
#define TAG "Iso14443_4aListener"
#define ISO14443_4A_LISTENER_BUF_SIZE (256U)
static Iso14443_4aListener*
iso14443_4a_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, Iso14443_4aData* data) {
furi_assert(iso14443_3a_listener);
Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener));
instance->iso14443_3a_listener = iso14443_3a_listener;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE);
instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;
instance->generic_event.protocol = NfcProtocolIso14443_4a;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->iso14443_4a_event;
return instance;
}
static void iso14443_4a_listener_free(Iso14443_4aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
static void iso14443_4a_listener_set_callback(
Iso14443_4aListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
static const Iso14443_4aData* iso14443_4a_listener_get_data(Iso14443_4aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(event.event_data);
Iso14443_4aListener* instance = context;
Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;
BitBuffer* rx_buffer = iso14443_3a_event->data->buffer;
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {
if(instance->state == Iso14443_4aListenerStateIdle) {
if(bit_buffer_get_size_bytes(rx_buffer) == 2 &&
bit_buffer_get_byte(rx_buffer, 0) == ISO14443_4A_CMD_READ_ATS) {
if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) !=
Iso14443_4aErrorNone) {
command = NfcCommandContinue;
} else {
instance->state = Iso14443_4aListenerStateActive;
}
}
} else {
instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData;
instance->iso14443_4a_event.data->buffer = rx_buffer;
if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context);
}
}
} else if(
iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted ||
iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) {
instance->state = Iso14443_4aListenerStateIdle;
command = NfcCommandContinue;
}
return command;
}
const NfcListenerBase nfc_listener_iso14443_4a = {
.alloc = (NfcListenerAlloc)iso14443_4a_listener_alloc,
.free = (NfcListenerFree)iso14443_4a_listener_free,
.set_callback = (NfcListenerSetCallback)iso14443_4a_listener_set_callback,
.get_data = (NfcListenerGetData)iso14443_4a_listener_get_data,
.run = (NfcListenerRun)iso14443_4a_listener_run,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>
#include "iso14443_4a.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_4aListener Iso14443_4aListener;
typedef enum {
Iso14443_4aListenerEventTypeHalted,
Iso14443_4aListenerEventTypeReceivedData,
} Iso14443_4aListenerEventType;
typedef struct {
BitBuffer* buffer;
} Iso14443_4aListenerEventData;
typedef struct {
Iso14443_4aListenerEventType type;
Iso14443_4aListenerEventData* data;
} Iso14443_4aListenerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_iso14443_4a;

View File

@@ -0,0 +1,32 @@
#include "iso14443_4a_listener_i.h"
#include <nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>
Iso14443_4aError
iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data) {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, data->tl);
if(data->tl > 1) {
bit_buffer_append_byte(instance->tx_buffer, data->t0);
if(data->t0 & ISO14443_4A_ATS_T0_TA1) {
bit_buffer_append_byte(instance->tx_buffer, data->ta_1);
}
if(data->t0 & ISO14443_4A_ATS_T0_TB1) {
bit_buffer_append_byte(instance->tx_buffer, data->tb_1);
}
if(data->t0 & ISO14443_4A_ATS_T0_TC1) {
bit_buffer_append_byte(instance->tx_buffer, data->tc_1);
}
const uint32_t t1_tk_size = simple_array_get_count(data->t1_tk);
if(t1_tk_size != 0) {
bit_buffer_append_bytes(
instance->tx_buffer, simple_array_cget_data(data->t1_tk), t1_tk_size);
}
}
const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame(
instance->iso14443_3a_listener, instance->tx_buffer);
return iso14443_4a_process_error(error);
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <nfc/protocols/nfc_generic_event.h>
#include "iso14443_4a_listener.h"
#include "iso14443_4a_i.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4aListenerStateIdle,
Iso14443_4aListenerStateActive,
} Iso14443_4aListenerState;
struct Iso14443_4aListener {
Iso14443_3aListener* iso14443_3a_listener;
Iso14443_4aData* data;
Iso14443_4aListenerState state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Iso14443_4aListenerEvent iso14443_4a_event;
Iso14443_4aListenerEventData iso14443_4a_event_data;
NfcGenericCallback callback;
void* context;
};
Iso14443_4aError
iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,154 @@
#include "iso14443_4a_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "Iso14443_4aPoller"
#define ISO14443_4A_POLLER_BUF_SIZE (256U)
typedef NfcCommand (*Iso14443_4aPollerStateHandler)(Iso14443_4aPoller* instance);
const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance) {
furi_assert(instance);
return instance->data;
}
static Iso14443_4aPoller* iso14443_4a_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {
Iso14443_4aPoller* instance = malloc(sizeof(Iso14443_4aPoller));
instance->iso14443_3a_poller = iso14443_3a_poller;
instance->data = iso14443_4a_alloc();
instance->iso14443_4_layer = iso14443_4_layer_alloc();
instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);
instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;
instance->general_event.protocol = NfcProtocolIso14443_4a;
instance->general_event.event_data = &instance->iso14443_4a_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_4a_poller_free(Iso14443_4aPoller* instance) {
furi_assert(instance);
iso14443_4a_free(instance->data);
iso14443_4_layer_free(instance->iso14443_4_layer);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
free(instance);
}
static NfcCommand iso14443_4a_poller_handler_idle(Iso14443_4aPoller* instance) {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
iso14443_4_layer_reset(instance->iso14443_4_layer);
instance->poller_state = Iso14443_4aPollerStateReadAts;
return NfcCommandContinue;
}
static NfcCommand iso14443_4a_poller_handler_read_ats(Iso14443_4aPoller* instance) {
Iso14443_4aError error =
iso14443_4a_poller_async_read_ats(instance, &instance->data->ats_data);
if(error == Iso14443_4aErrorNone) {
FURI_LOG_D(TAG, "Read ATS success");
instance->poller_state = Iso14443_4aPollerStateReady;
} else {
FURI_LOG_D(TAG, "Failed to read ATS");
instance->poller_state = Iso14443_4aPollerStateError;
}
return NfcCommandContinue;
}
static NfcCommand iso14443_4a_poller_handler_error(Iso14443_4aPoller* instance) {
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->iso14443_4a_event_data.error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->poller_state = Iso14443_4aPollerStateIdle;
return command;
}
static NfcCommand iso14443_4a_poller_handler_ready(Iso14443_4aPoller* instance) {
instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeReady;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const Iso14443_4aPollerStateHandler
iso14443_4a_poller_state_handler[Iso14443_4aPollerStateNum] = {
[Iso14443_4aPollerStateIdle] = iso14443_4a_poller_handler_idle,
[Iso14443_4aPollerStateReadAts] = iso14443_4a_poller_handler_read_ats,
[Iso14443_4aPollerStateError] = iso14443_4a_poller_handler_error,
[Iso14443_4aPollerStateReady] = iso14443_4a_poller_handler_ready,
};
static void iso14443_4a_poller_set_callback(
Iso14443_4aPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_4a_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3a);
Iso14443_4aPoller* instance = context;
furi_assert(instance);
furi_assert(instance->callback);
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
furi_assert(iso14443_3a_event);
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
command = iso14443_4a_poller_state_handler[instance->poller_state](instance);
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeError;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
static bool iso14443_4a_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3a);
const Iso14443_4aPoller* instance = context;
furi_assert(instance);
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
furi_assert(iso14443_3a_event);
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
bool protocol_detected = false;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
protocol_detected = iso14443_3a_supports_iso14443_4(instance->data->iso14443_3a_data);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_4a = {
.alloc = (NfcPollerAlloc)iso14443_4a_poller_alloc,
.free = (NfcPollerFree)iso14443_4a_poller_free,
.set_callback = (NfcPollerSetCallback)iso14443_4a_poller_set_callback,
.run = (NfcPollerRun)iso14443_4a_poller_run,
.detect = (NfcPollerDetect)iso14443_4a_poller_detect,
.get_data = (NfcPollerGetData)iso14443_4a_poller_get_data,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
#include "iso14443_4a.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_4aPoller Iso14443_4aPoller;
typedef enum {
Iso14443_4aPollerEventTypeError,
Iso14443_4aPollerEventTypeReady,
} Iso14443_4aPollerEventType;
typedef struct {
Iso14443_4aError error;
} Iso14443_4aPollerEventData;
typedef struct {
Iso14443_4aPollerEventType type;
Iso14443_4aPollerEventData* data;
} Iso14443_4aPollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_4a;

View File

@@ -0,0 +1,83 @@
#include "iso14443_4a_poller_i.h"
#include <furi.h>
#include "iso14443_4a_i.h"
#define TAG "Iso14443_4aPoller"
#define ISO14443_4A_FSDI_256 (0x8U)
Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) {
furi_assert(instance);
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->poller_state = Iso14443_4aPollerStateIdle;
return Iso14443_4aErrorNone;
}
Iso14443_4aError
iso14443_4a_poller_async_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_CMD_READ_ATS);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_FSDI_256 << 4);
Iso14443_4aError error = Iso14443_4aErrorNone;
do {
const Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_buffer,
instance->rx_buffer,
ISO14443_4A_POLLER_ATS_FWT_FC);
if(iso14443_3a_error != Iso14443_3aErrorNone) {
FURI_LOG_E(TAG, "ATS request failed");
error = iso14443_4a_process_error(iso14443_3a_error);
break;
} else if(!iso14443_4a_ats_parse(data, instance->rx_buffer)) {
FURI_LOG_E(TAG, "Failed to parse ATS response");
error = Iso14443_4aErrorProtocol;
break;
}
} while(false);
return error;
}
Iso14443_4aError iso14443_4a_poller_send_block(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
Iso14443_4aError error = Iso14443_4aErrorNone;
do {
Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_buffer,
instance->rx_buffer,
iso14443_4a_get_fwt_fc_max(instance->data));
if(iso14443_3a_error != Iso14443_3aErrorNone) {
error = iso14443_4a_process_error(iso14443_3a_error);
break;
} else if(!iso14443_4_layer_decode_block(
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
error = Iso14443_4aErrorProtocol;
break;
}
} while(false);
return error;
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
#include <nfc/helpers/iso14443_4_layer.h>
#include "iso14443_4a_poller.h"
#include "iso14443_4a_i.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_4A_POLLER_ATS_FWT_FC (40000)
typedef enum {
Iso14443_4aPollerStateIdle,
Iso14443_4aPollerStateReadAts,
Iso14443_4aPollerStateError,
Iso14443_4aPollerStateReady,
Iso14443_4aPollerStateNum,
} Iso14443_4aPollerState;
typedef enum {
Iso14443_4aPollerSessionStateIdle,
Iso14443_4aPollerSessionStateActive,
Iso14443_4aPollerSessionStateStopRequest,
} Iso14443_4aPollerSessionState;
struct Iso14443_4aPoller {
Iso14443_3aPoller* iso14443_3a_poller;
Iso14443_4aPollerState poller_state;
Iso14443_4aPollerSessionState session_state;
Iso14443_4aError error;
Iso14443_4aData* data;
Iso14443_4Layer* iso14443_4_layer;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
Iso14443_4aPollerEventData iso14443_4a_event_data;
Iso14443_4aPollerEvent iso14443_4a_event;
NfcGenericEvent general_event;
NfcGenericCallback callback;
void* context;
};
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error);
const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance);
Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance);
Iso14443_4aError
iso14443_4a_poller_async_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data);
Iso14443_4aError iso14443_4a_poller_send_block(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,94 @@
#include "iso14443_4b_i.h"
#include <furi.h>
#include <nfc/protocols/nfc_device_base_i.h>
#define ISO14443_4B_PROTOCOL_NAME "ISO14443-4B"
#define ISO14443_4B_DEVICE_NAME "ISO14443-4B (Unknown)"
const NfcDeviceBase nfc_device_iso14443_4b = {
.protocol_name = ISO14443_4B_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_4b_alloc,
.free = (NfcDeviceFree)iso14443_4b_free,
.reset = (NfcDeviceReset)iso14443_4b_reset,
.copy = (NfcDeviceCopy)iso14443_4b_copy,
.verify = (NfcDeviceVerify)iso14443_4b_verify,
.load = (NfcDeviceLoad)iso14443_4b_load,
.save = (NfcDeviceSave)iso14443_4b_save,
.is_equal = (NfcDeviceEqual)iso14443_4b_is_equal,
.get_name = (NfcDeviceGetName)iso14443_4b_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_4b_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_4b_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_4b_get_base_data,
};
Iso14443_4bData* iso14443_4b_alloc() {
Iso14443_4bData* data = malloc(sizeof(Iso14443_4bData));
data->iso14443_3b_data = iso14443_3b_alloc();
return data;
}
void iso14443_4b_free(Iso14443_4bData* data) {
furi_assert(data);
iso14443_3b_free(data->iso14443_3b_data);
free(data);
}
void iso14443_4b_reset(Iso14443_4bData* data) {
furi_assert(data);
iso14443_3b_reset(data->iso14443_3b_data);
}
void iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other) {
furi_assert(data);
furi_assert(other);
iso14443_3b_copy(data->iso14443_3b_data, other->iso14443_3b_data);
}
bool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// Empty, unified file format only
return false;
}
bool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
return iso14443_3b_load(data->iso14443_3b_data, ff, version);
}
bool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff) {
furi_assert(data);
return iso14443_3b_save(data->iso14443_3b_data, ff);
}
bool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other) {
return iso14443_3b_is_equal(data->iso14443_3b_data, other->iso14443_3b_data);
}
const char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_4B_DEVICE_NAME;
}
const uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len) {
return iso14443_3b_get_uid(data->iso14443_3b_data, uid_len);
}
bool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_3b_set_uid(data->iso14443_3b_data, uid, uid_len);
}
Iso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data) {
furi_assert(data);
return data->iso14443_3b_data;
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include <nfc/protocols/iso14443_3b/iso14443_3b.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4bErrorNone,
Iso14443_4bErrorNotPresent,
Iso14443_4bErrorProtocol,
Iso14443_4bErrorTimeout,
} Iso14443_4bError;
typedef struct Iso14443_4bData Iso14443_4bData;
// Virtual methods
Iso14443_4bData* iso14443_4b_alloc();
void iso14443_4b_free(Iso14443_4bData* data);
void iso14443_4b_reset(Iso14443_4bData* data);
void iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other);
bool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type);
bool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff);
bool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other);
const char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len);
bool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_4b;

View File

@@ -0,0 +1,18 @@
#include "iso14443_4b_i.h"
Iso14443_4bError iso14443_4b_process_error(Iso14443_3bError error) {
switch(error) {
case Iso14443_3bErrorNone:
return Iso14443_4bErrorNone;
case Iso14443_3bErrorNotPresent:
return Iso14443_4bErrorNotPresent;
case Iso14443_3bErrorColResFailed:
case Iso14443_3bErrorCommunication:
case Iso14443_3bErrorWrongCrc:
return Iso14443_4bErrorProtocol;
case Iso14443_3bErrorTimeout:
return Iso14443_4bErrorTimeout;
default:
return Iso14443_4bErrorProtocol;
}
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "iso14443_4b.h"
struct Iso14443_4bData {
Iso14443_3bData* iso14443_3b_data;
};
Iso14443_4bError iso14443_4b_process_error(Iso14443_3bError error);

View File

@@ -0,0 +1,138 @@
#include "iso14443_4b_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "Iso14443_4bPoller"
#define ISO14443_4A_POLLER_BUF_SIZE (256U)
typedef NfcCommand (*Iso14443_4bPollerStateHandler)(Iso14443_4bPoller* instance);
const Iso14443_4bData* iso14443_4b_poller_get_data(Iso14443_4bPoller* instance) {
furi_assert(instance);
return instance->data;
}
static Iso14443_4bPoller* iso14443_4b_poller_alloc(Iso14443_3bPoller* iso14443_3b_poller) {
Iso14443_4bPoller* instance = malloc(sizeof(Iso14443_4bPoller));
instance->iso14443_3b_poller = iso14443_3b_poller;
instance->data = iso14443_4b_alloc();
instance->iso14443_4_layer = iso14443_4_layer_alloc();
instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);
instance->iso14443_4b_event.data = &instance->iso14443_4b_event_data;
instance->general_event.protocol = NfcProtocolIso14443_4b;
instance->general_event.event_data = &instance->iso14443_4b_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_4b_poller_free(Iso14443_4bPoller* instance) {
furi_assert(instance);
iso14443_4b_free(instance->data);
iso14443_4_layer_free(instance->iso14443_4_layer);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
free(instance);
}
static NfcCommand iso14443_4b_poller_handler_idle(Iso14443_4bPoller* instance) {
iso14443_3b_copy(
instance->data->iso14443_3b_data,
iso14443_3b_poller_get_data(instance->iso14443_3b_poller));
iso14443_4_layer_reset(instance->iso14443_4_layer);
instance->poller_state = Iso14443_4bPollerStateReady;
return NfcCommandContinue;
}
static NfcCommand iso14443_4b_poller_handler_error(Iso14443_4bPoller* instance) {
iso14443_3b_poller_halt(instance->iso14443_3b_poller);
instance->iso14443_4b_event_data.error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->poller_state = Iso14443_4bPollerStateIdle;
return command;
}
static NfcCommand iso14443_4b_poller_handler_ready(Iso14443_4bPoller* instance) {
instance->iso14443_4b_event.type = Iso14443_4bPollerEventTypeReady;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const Iso14443_4bPollerStateHandler
iso14443_4b_poller_state_handler[Iso14443_4bPollerStateNum] = {
[Iso14443_4bPollerStateIdle] = iso14443_4b_poller_handler_idle,
[Iso14443_4bPollerStateError] = iso14443_4b_poller_handler_error,
[Iso14443_4bPollerStateReady] = iso14443_4b_poller_handler_ready,
};
static void iso14443_4b_poller_set_callback(
Iso14443_4bPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_4b_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3b);
Iso14443_4bPoller* instance = context;
furi_assert(instance);
furi_assert(instance->callback);
Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;
furi_assert(iso14443_3b_event);
NfcCommand command = NfcCommandContinue;
if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {
command = iso14443_4b_poller_state_handler[instance->poller_state](instance);
} else if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeError) {
instance->iso14443_4b_event.type = Iso14443_4bPollerEventTypeError;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
static bool iso14443_4b_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3b);
const Iso14443_4bPoller* instance = context;
furi_assert(instance);
const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data;
furi_assert(iso14443_3b_event);
iso14443_3b_copy(
instance->data->iso14443_3b_data,
iso14443_3b_poller_get_data(instance->iso14443_3b_poller));
bool protocol_detected = false;
if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) {
protocol_detected = iso14443_3b_supports_iso14443_4(instance->data->iso14443_3b_data);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_4b = {
.alloc = (NfcPollerAlloc)iso14443_4b_poller_alloc,
.free = (NfcPollerFree)iso14443_4b_poller_free,
.set_callback = (NfcPollerSetCallback)iso14443_4b_poller_set_callback,
.run = (NfcPollerRun)iso14443_4b_poller_run,
.detect = (NfcPollerDetect)iso14443_4b_poller_detect,
.get_data = (NfcPollerGetData)iso14443_4b_poller_get_data,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h>
#include "iso14443_4b.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_4bPoller Iso14443_4bPoller;
typedef enum {
Iso14443_4bPollerEventTypeError,
Iso14443_4bPollerEventTypeReady,
} Iso14443_4bPollerEventType;
typedef struct {
Iso14443_4bError error;
} Iso14443_4bPollerEventData;
typedef struct {
Iso14443_4bPollerEventType type;
Iso14443_4bPollerEventData* data;
} Iso14443_4bPollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_4b;

View File

@@ -0,0 +1,45 @@
#include "iso14443_4b_poller_i.h"
#include <furi.h>
#include "iso14443_4b_i.h"
#define TAG "Iso14443_4bPoller"
Iso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance) {
furi_assert(instance);
iso14443_3b_poller_halt(instance->iso14443_3b_poller);
instance->poller_state = Iso14443_4bPollerStateIdle;
return Iso14443_4bErrorNone;
}
Iso14443_4bError iso14443_4b_poller_send_block(
Iso14443_4bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
Iso14443_4bError error = Iso14443_4bErrorNone;
do {
Iso14443_3bError iso14443_3b_error = iso14443_3b_poller_send_frame(
instance->iso14443_3b_poller, instance->tx_buffer, instance->rx_buffer);
if(iso14443_3b_error != Iso14443_3bErrorNone) {
error = iso14443_4b_process_error(iso14443_3b_error);
break;
} else if(!iso14443_4_layer_decode_block(
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
error = Iso14443_4bErrorProtocol;
break;
}
} while(false);
return error;
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h>
#include <nfc/helpers/iso14443_4_layer.h>
#include "iso14443_4b_poller.h"
#include "iso14443_4b_i.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4bPollerStateIdle,
Iso14443_4bPollerStateError,
Iso14443_4bPollerStateReady,
Iso14443_4bPollerStateNum,
} Iso14443_4bPollerState;
typedef enum {
Iso14443_4bPollerSessionStateIdle,
Iso14443_4bPollerSessionStateActive,
Iso14443_4bPollerSessionStateStopRequest,
} Iso14443_4bPollerSessionState;
struct Iso14443_4bPoller {
Iso14443_3bPoller* iso14443_3b_poller;
Iso14443_4bPollerState poller_state;
Iso14443_4bPollerSessionState session_state;
Iso14443_4bError error;
Iso14443_4bData* data;
Iso14443_4Layer* iso14443_4_layer;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
Iso14443_4bPollerEventData iso14443_4b_event_data;
Iso14443_4bPollerEvent iso14443_4b_event;
NfcGenericEvent general_event;
NfcGenericCallback callback;
void* context;
};
Iso14443_4bError iso14443_4b_process_error(Iso14443_3bError error);
const Iso14443_4bData* iso14443_4b_poller_get_data(Iso14443_4bPoller* instance);
Iso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance);
Iso14443_4bError iso14443_4b_poller_send_block(
Iso14443_4bPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,358 @@
#include "iso15693_3.h"
#include "iso15693_3_device_defs.h"
#include <nfc/nfc_common.h>
#define ISO15693_3_PROTOCOL_NAME "ISO15693-3"
#define ISO15693_3_PROTOCOL_NAME_LEGACY "ISO15693"
#define ISO15693_3_DEVICE_NAME "ISO15693-3 (Unknown)"
#define ISO15693_3_LOCK_DSFID_LEGACY (1U << 0)
#define ISO15693_3_LOCK_AFI_LEGACY (1U << 1)
#define ISO15693_3_DSFID_KEY "DSFID"
#define ISO15693_3_AFI_KEY "AFI"
#define ISO15693_3_IC_REF_KEY "IC Reference"
#define ISO15693_3_BLOCK_COUNT_KEY "Block Count"
#define ISO15693_3_BLOCK_SIZE_KEY "Block Size"
#define ISO15693_3_DATA_CONTENT_KEY "Data Content"
#define ISO15693_3_LOCK_DSFID_KEY "Lock DSFID"
#define ISO15693_3_LOCK_AFI_KEY "Lock AFI"
#define ISO15693_3_SECURITY_STATUS_KEY "Security Status"
const NfcDeviceBase nfc_device_iso15693_3 = {
.protocol_name = ISO15693_3_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso15693_3_alloc,
.free = (NfcDeviceFree)iso15693_3_free,
.reset = (NfcDeviceReset)iso15693_3_reset,
.copy = (NfcDeviceCopy)iso15693_3_copy,
.verify = (NfcDeviceVerify)iso15693_3_verify,
.load = (NfcDeviceLoad)iso15693_3_load,
.save = (NfcDeviceSave)iso15693_3_save,
.is_equal = (NfcDeviceEqual)iso15693_3_is_equal,
.get_name = (NfcDeviceGetName)iso15693_3_get_device_name,
.get_uid = (NfcDeviceGetUid)iso15693_3_get_uid,
.set_uid = (NfcDeviceSetUid)iso15693_3_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data,
};
Iso15693_3Data* iso15693_3_alloc() {
Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data));
data->block_data = simple_array_alloc(&simple_array_config_uint8_t);
data->block_security = simple_array_alloc(&simple_array_config_uint8_t);
return data;
}
void iso15693_3_free(Iso15693_3Data* data) {
furi_assert(data);
simple_array_free(data->block_data);
simple_array_free(data->block_security);
free(data);
}
void iso15693_3_reset(Iso15693_3Data* data) {
furi_assert(data);
memset(data->uid, 0, ISO15693_3_UID_SIZE);
memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo));
memset(&data->settings, 0, sizeof(Iso15693_3Settings));
simple_array_reset(data->block_data);
simple_array_reset(data->block_security);
}
void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) {
furi_assert(data);
furi_assert(other);
memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE);
data->system_info = other->system_info;
data->settings = other->settings;
simple_array_copy(data->block_data, other->block_data);
simple_array_copy(data->block_security, other->block_security);
}
bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY);
}
static inline bool iso15693_3_load_security_legacy(Iso15693_3Data* data, FlipperFormat* ff) {
bool loaded = false;
uint8_t* legacy_data = NULL;
do {
uint32_t value_count;
if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))
break;
if(simple_array_get_count(data->block_security) + 1 != value_count) break;
legacy_data = malloc(value_count);
if(!flipper_format_read_hex(ff, ISO15693_3_SECURITY_STATUS_KEY, legacy_data, value_count))
break;
// First legacy data byte is lock bits
data->settings.lock_bits.dsfid = legacy_data[0] & ISO15693_3_LOCK_DSFID_LEGACY;
data->settings.lock_bits.afi = legacy_data[0] & ISO15693_3_LOCK_AFI_LEGACY;
// The rest are block security
memcpy(
&legacy_data[1],
simple_array_get_data(data->block_security),
simple_array_get_count(data->block_security));
loaded = true;
} while(false);
if(legacy_data) free(legacy_data);
return loaded;
}
static inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat* ff) {
bool loaded = false;
do {
uint32_t value_count;
if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))
break;
if(simple_array_get_count(data->block_security) != value_count) break;
if(!flipper_format_read_hex(
ff,
ISO15693_3_SECURITY_STATUS_KEY,
simple_array_get_data(data->block_security),
simple_array_get_count(data->block_security)))
break;
loaded = true;
} while(false);
return loaded;
}
bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
UNUSED(version);
bool loaded = false;
do {
if(flipper_format_key_exist(ff, ISO15693_3_DSFID_KEY)) {
if(!flipper_format_read_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))
break;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_DSFID;
}
if(flipper_format_key_exist(ff, ISO15693_3_AFI_KEY)) {
if(!flipper_format_read_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_AFI;
}
if(flipper_format_key_exist(ff, ISO15693_3_IC_REF_KEY)) {
if(!flipper_format_read_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))
break;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_IC_REF;
}
const bool has_lock_bits = flipper_format_key_exist(ff, ISO15693_3_LOCK_DSFID_KEY) &&
flipper_format_key_exist(ff, ISO15693_3_LOCK_AFI_KEY);
if(has_lock_bits) {
Iso15693_3LockBits* lock_bits = &data->settings.lock_bits;
if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_DSFID_KEY, &lock_bits->dsfid, 1))
break;
if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_AFI_KEY, &lock_bits->afi, 1)) break;
}
if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) &&
flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) {
uint32_t block_count;
if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break;
data->system_info.block_count = block_count;
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY;
if(!flipper_format_read_hex(
ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1))
break;
simple_array_init(
data->block_data, data->system_info.block_size * data->system_info.block_count);
if(!flipper_format_read_hex(
ff,
ISO15693_3_DATA_CONTENT_KEY,
simple_array_get_data(data->block_data),
simple_array_get_count(data->block_data)))
break;
if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) {
simple_array_init(data->block_security, data->system_info.block_count);
const bool security_loaded = has_lock_bits ?
iso15693_3_load_security(data, ff) :
iso15693_3_load_security_legacy(data, ff);
if(!security_loaded) break;
}
}
loaded = true;
} while(false);
return loaded;
}
bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!flipper_format_write_comment_cstr(ff, ISO15693_3_PROTOCOL_NAME " specific data"))
break;
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
if(!flipper_format_write_comment_cstr(ff, "Data Storage Format Identifier")) break;
if(!flipper_format_write_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))
break;
}
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
if(!flipper_format_write_comment_cstr(ff, "Application Family Identifier")) break;
if(!flipper_format_write_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;
}
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
if(!flipper_format_write_comment_cstr(ff, "IC Reference - Vendor specific meaning"))
break;
if(!flipper_format_write_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))
break;
}
if(!flipper_format_write_comment_cstr(ff, "Lock Bits")) break;
if(!flipper_format_write_bool(
ff, ISO15693_3_LOCK_DSFID_KEY, &data->settings.lock_bits.dsfid, 1))
break;
if(!flipper_format_write_bool(
ff, ISO15693_3_LOCK_AFI_KEY, &data->settings.lock_bits.afi, 1))
break;
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
const uint32_t block_count = data->system_info.block_count;
if(!flipper_format_write_comment_cstr(
ff, "Number of memory blocks, valid range = 1..256"))
break;
if(!flipper_format_write_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1))
break;
if(!flipper_format_write_comment_cstr(
ff, "Size of a single memory block, valid range = 01...20 (hex)"))
break;
if(!flipper_format_write_hex(
ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1))
break;
if(!flipper_format_write_hex(
ff,
ISO15693_3_DATA_CONTENT_KEY,
simple_array_cget_data(data->block_data),
simple_array_get_count(data->block_data)))
break;
if(!flipper_format_write_comment_cstr(
ff, "Block Security Status: 01 = locked, 00 = not locked"))
break;
if(!flipper_format_write_hex(
ff,
ISO15693_3_SECURITY_STATUS_KEY,
simple_array_cget_data(data->block_security),
simple_array_get_count(data->block_security)))
break;
}
saved = true;
} while(false);
return saved;
}
bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) {
furi_assert(data);
furi_assert(other);
return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 &&
memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 &&
memcmp(&data->system_info, &other->system_info, sizeof(Iso15693_3SystemInfo)) == 0 &&
simple_array_is_equal(data->block_data, other->block_data) &&
simple_array_is_equal(data->block_security, other->block_security);
}
const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO15693_3_DEVICE_NAME;
}
const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) {
furi_assert(data);
if(uid_len) *uid_len = ISO15693_3_UID_SIZE;
return data->uid;
}
bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
furi_assert(uid);
bool uid_valid = uid_len == ISO15693_3_UID_SIZE;
if(uid_valid) {
memcpy(data->uid, uid, uid_len);
// All ISO15693-3 cards must have this as first UID byte
data->uid[0] = 0xe0;
}
return uid_valid;
}
Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) {
UNUSED(data);
furi_crash("No base data");
}
bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) {
furi_assert(data);
furi_assert(block_index < data->system_info.block_count);
return *(const uint8_t*)simple_array_cget(data->block_security, block_index);
}
uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) {
furi_assert(data);
return data->uid[1];
}
uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) {
furi_assert(data);
return data->system_info.block_count;
}
uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) {
furi_assert(data);
return data->system_info.block_size;
}
const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) {
furi_assert(data);
furi_assert(data->system_info.block_count > block_index);
return (const uint8_t*)simple_array_cget(
data->block_data, block_index * data->system_info.block_size);
}

View File

@@ -0,0 +1,163 @@
#pragma once
#include <nfc/protocols/nfc_device_base.h>
#include <flipper_format/flipper_format.h>
#include <toolbox/simple_array.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO15693_3_UID_SIZE (8U)
#define ISO15693_3_GUARD_TIME_US (5000U)
#define ISO15693_3_FDT_POLL_FC (4202U)
#define ISO15693_3_FDT_LISTEN_FC (4320U)
#define ISO15693_3_POLL_POLL_MIN_US (1500U)
#define ISO15693_3_REQ_FLAG_SUBCARRIER_1 (0U << 0)
#define ISO15693_3_REQ_FLAG_SUBCARRIER_2 (1U << 0)
#define ISO15693_3_REQ_FLAG_DATA_RATE_LO (0U << 1)
#define ISO15693_3_REQ_FLAG_DATA_RATE_HI (1U << 1)
#define ISO15693_3_REQ_FLAG_INVENTORY_T4 (0U << 2)
#define ISO15693_3_REQ_FLAG_INVENTORY_T5 (1U << 2)
#define ISO15693_3_REQ_FLAG_EXTENSION (1U << 3)
#define ISO15693_3_REQ_FLAG_T4_SELECTED (1U << 4)
#define ISO15693_3_REQ_FLAG_T4_ADDRESSED (1U << 5)
#define ISO15693_3_REQ_FLAG_T4_OPTION (1U << 6)
#define ISO15693_3_REQ_FLAG_T5_AFI_PRESENT (1U << 4)
#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_16 (0U << 5)
#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_1 (1U << 5)
#define ISO15693_3_REQ_FLAG_T5_OPTION (1U << 6)
#define ISO15693_3_RESP_FLAG_NONE (0U)
#define ISO15693_3_RESP_FLAG_ERROR (1U << 0)
#define ISO15693_3_RESP_FLAG_EXTENSION (1U << 3)
#define ISO15693_3_RESP_ERROR_NOT_SUPPORTED (0x01U)
#define ISO15693_3_RESP_ERROR_FORMAT (0x02U)
#define ISO15693_3_RESP_ERROR_OPTION (0x03U)
#define ISO15693_3_RESP_ERROR_UNKNOWN (0x0FU)
#define ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE (0x10U)
#define ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED (0x11U)
#define ISO15693_3_RESP_ERROR_BLOCK_LOCKED (0x12U)
#define ISO15693_3_RESP_ERROR_BLOCK_WRITE (0x13U)
#define ISO15693_3_RESP_ERROR_BLOCK_LOCK (0x14U)
#define ISO15693_3_RESP_ERROR_CUSTOM_START (0xA0U)
#define ISO15693_3_RESP_ERROR_CUSTOM_END (0xDFU)
#define ISO15693_3_CMD_MANDATORY_START (0x01U)
#define ISO15693_3_CMD_INVENTORY (0x01U)
#define ISO15693_3_CMD_STAY_QUIET (0x02U)
#define ISO15693_3_CMD_MANDATORY_RFU (0x03U)
#define ISO15693_3_CMD_OPTIONAL_START (0x20U)
#define ISO15693_3_CMD_READ_BLOCK (0x20U)
#define ISO15693_3_CMD_WRITE_BLOCK (0x21U)
#define ISO15693_3_CMD_LOCK_BLOCK (0x22U)
#define ISO15693_3_CMD_READ_MULTI_BLOCKS (0x23U)
#define ISO15693_3_CMD_WRITE_MULTI_BLOCKS (0x24U)
#define ISO15693_3_CMD_SELECT (0x25U)
#define ISO15693_3_CMD_RESET_TO_READY (0x26U)
#define ISO15693_3_CMD_WRITE_AFI (0x27U)
#define ISO15693_3_CMD_LOCK_AFI (0x28U)
#define ISO15693_3_CMD_WRITE_DSFID (0x29U)
#define ISO15693_3_CMD_LOCK_DSFID (0x2AU)
#define ISO15693_3_CMD_GET_SYS_INFO (0x2BU)
#define ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU)
#define ISO15693_3_CMD_OPTIONAL_RFU (0x2DU)
#define ISO15693_3_CMD_CUSTOM_START (0xA0U)
#define ISO15693_3_MANDATORY_COUNT (ISO15693_3_CMD_MANDATORY_RFU - ISO15693_3_CMD_MANDATORY_START)
#define ISO15693_3_OPTIONAL_COUNT (ISO15693_3_CMD_OPTIONAL_RFU - ISO15693_3_CMD_OPTIONAL_START)
#define ISO15693_3_SYSINFO_FLAG_DSFID (1U << 0)
#define ISO15693_3_SYSINFO_FLAG_AFI (1U << 1)
#define ISO15693_3_SYSINFO_FLAG_MEMORY (1U << 2)
#define ISO15693_3_SYSINFO_FLAG_IC_REF (1U << 3)
typedef enum {
Iso15693_3ErrorNone,
Iso15693_3ErrorNotPresent,
Iso15693_3ErrorBufferEmpty,
Iso15693_3ErrorBufferOverflow,
Iso15693_3ErrorFieldOff,
Iso15693_3ErrorWrongCrc,
Iso15693_3ErrorTimeout,
Iso15693_3ErrorFormat,
Iso15693_3ErrorIgnore,
Iso15693_3ErrorNotSupported,
Iso15693_3ErrorUidMismatch,
Iso15693_3ErrorFullyHandled,
Iso15693_3ErrorUnexpectedResponse,
Iso15693_3ErrorInternal,
Iso15693_3ErrorCustom,
Iso15693_3ErrorUnknown,
} Iso15693_3Error;
typedef struct {
uint8_t flags;
uint8_t dsfid;
uint8_t afi;
uint8_t ic_ref;
uint16_t block_count;
uint8_t block_size;
} Iso15693_3SystemInfo;
typedef struct {
bool dsfid;
bool afi;
} Iso15693_3LockBits;
typedef struct {
Iso15693_3LockBits lock_bits;
} Iso15693_3Settings;
typedef struct {
uint8_t uid[ISO15693_3_UID_SIZE];
Iso15693_3SystemInfo system_info;
Iso15693_3Settings settings;
SimpleArray* block_data;
SimpleArray* block_security;
} Iso15693_3Data;
Iso15693_3Data* iso15693_3_alloc();
void iso15693_3_free(Iso15693_3Data* data);
void iso15693_3_reset(Iso15693_3Data* data);
void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other);
bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type);
bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version);
bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff);
bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other);
const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type);
const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len);
bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len);
Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data);
// Getters and tests
bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index);
uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data);
uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data);
uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data);
const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso15693_3;

View File

@@ -0,0 +1,263 @@
#include "iso15693_3_i.h"
bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf) {
furi_assert(error);
if(bit_buffer_get_size_bytes(buf) == 0) {
// YEET!
*error = Iso15693_3ErrorBufferEmpty;
return true;
}
typedef struct {
uint8_t flags;
uint8_t error;
} ErrorResponseLayout;
const ErrorResponseLayout* resp = (const ErrorResponseLayout*)bit_buffer_get_data(buf);
if((resp->flags & ISO15693_3_RESP_FLAG_ERROR) == 0) {
// No error flag is set, the data does not contain an error frame
return false;
} else if(bit_buffer_get_size_bytes(buf) < sizeof(ErrorResponseLayout)) {
// Error bit is set, but not enough data to determine the error
*error = Iso15693_3ErrorUnexpectedResponse;
return true;
} else if(
resp->error >= ISO15693_3_RESP_ERROR_CUSTOM_START &&
resp->error <= ISO15693_3_RESP_ERROR_CUSTOM_END) {
// Custom vendor-specific error, must be checked in the respective protocol implementation
*error = Iso15693_3ErrorCustom;
return true;
}
switch(resp->error) {
case ISO15693_3_RESP_ERROR_NOT_SUPPORTED:
case ISO15693_3_RESP_ERROR_OPTION:
*error = Iso15693_3ErrorNotSupported;
break;
case ISO15693_3_RESP_ERROR_FORMAT:
*error = Iso15693_3ErrorFormat;
break;
case ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE:
case ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED:
case ISO15693_3_RESP_ERROR_BLOCK_LOCKED:
case ISO15693_3_RESP_ERROR_BLOCK_WRITE:
case ISO15693_3_RESP_ERROR_BLOCK_LOCK:
*error = Iso15693_3ErrorInternal;
break;
case ISO15693_3_RESP_ERROR_UNKNOWN:
default:
*error = Iso15693_3ErrorUnknown;
}
return true;
}
Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf) {
furi_assert(data);
Iso15693_3Error ret = Iso15693_3ErrorNone;
do {
if(iso15693_3_error_response_parse(&ret, buf)) break;
typedef struct {
uint8_t flags;
uint8_t dsfid;
uint8_t uid[ISO15693_3_UID_SIZE];
} InventoryResponseLayout;
if(bit_buffer_get_size_bytes(buf) != sizeof(InventoryResponseLayout)) {
ret = Iso15693_3ErrorUnexpectedResponse;
break;
}
const InventoryResponseLayout* resp =
(const InventoryResponseLayout*)bit_buffer_get_data(buf);
// Reverse UID for backward compatibility
for(uint32_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {
data[i] = resp->uid[ISO15693_3_UID_SIZE - i - 1];
}
} while(false);
return ret;
}
Iso15693_3Error
iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf) {
furi_assert(data);
Iso15693_3Error ret = Iso15693_3ErrorNone;
do {
if(iso15693_3_error_response_parse(&ret, buf)) break;
typedef struct {
uint8_t flags;
uint8_t info_flags;
uint8_t uid[ISO15693_3_UID_SIZE];
uint8_t extra[];
} SystemInfoResponseLayout;
if(bit_buffer_get_size_bytes(buf) < sizeof(SystemInfoResponseLayout)) {
ret = Iso15693_3ErrorUnexpectedResponse;
break;
}
const SystemInfoResponseLayout* resp =
(const SystemInfoResponseLayout*)bit_buffer_get_data(buf);
const uint8_t* extra = resp->extra;
const size_t extra_size = (resp->info_flags & ISO15693_3_SYSINFO_FLAG_DSFID ? 1 : 0) +
(resp->info_flags & ISO15693_3_SYSINFO_FLAG_AFI ? 1 : 0) +
(resp->info_flags & ISO15693_3_SYSINFO_FLAG_MEMORY ? 2 : 0) +
(resp->info_flags & ISO15693_3_SYSINFO_FLAG_IC_REF ? 1 : 0);
if(extra_size != bit_buffer_get_size_bytes(buf) - sizeof(SystemInfoResponseLayout)) {
ret = Iso15693_3ErrorUnexpectedResponse;
break;
}
data->flags = resp->info_flags;
if(data->flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
data->dsfid = *extra++;
}
if(data->flags & ISO15693_3_SYSINFO_FLAG_AFI) {
data->afi = *extra++;
}
if(data->flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
// Add 1 to get actual values
data->block_count = *extra++ + 1;
data->block_size = (*extra++ & 0x1F) + 1;
}
if(data->flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
data->ic_ref = *extra;
}
} while(false);
return ret;
}
Iso15693_3Error
iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf) {
furi_assert(data);
Iso15693_3Error ret = Iso15693_3ErrorNone;
do {
if(iso15693_3_error_response_parse(&ret, buf)) break;
typedef struct {
uint8_t flags;
uint8_t block_data[];
} ReadBlockResponseLayout;
const size_t buf_size = bit_buffer_get_size_bytes(buf);
const size_t received_block_size = buf_size - sizeof(ReadBlockResponseLayout);
if(buf_size <= sizeof(ReadBlockResponseLayout) || received_block_size != block_size) {
ret = Iso15693_3ErrorUnexpectedResponse;
break;
}
const ReadBlockResponseLayout* resp =
(const ReadBlockResponseLayout*)bit_buffer_get_data(buf);
memcpy(data, resp->block_data, received_block_size);
} while(false);
return ret;
}
Iso15693_3Error iso15693_3_get_block_security_response_parse(
uint8_t* data,
uint16_t block_count,
const BitBuffer* buf) {
furi_assert(data);
furi_assert(block_count);
Iso15693_3Error ret = Iso15693_3ErrorNone;
do {
if(iso15693_3_error_response_parse(&ret, buf)) break;
typedef struct {
uint8_t flags;
uint8_t block_security[];
} GetBlockSecurityResponseLayout;
const size_t buf_size = bit_buffer_get_size_bytes(buf);
const size_t received_block_count = buf_size - sizeof(GetBlockSecurityResponseLayout);
if(buf_size <= sizeof(GetBlockSecurityResponseLayout) ||
received_block_count != block_count) {
ret = Iso15693_3ErrorUnexpectedResponse;
break;
}
const GetBlockSecurityResponseLayout* resp =
(const GetBlockSecurityResponseLayout*)bit_buffer_get_data(buf);
memcpy(data, resp->block_security, received_block_count);
} while(false);
return ret;
}
void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf) {
for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {
// Reverse the UID
bit_buffer_append_byte(buf, data->uid[ISO15693_3_UID_SIZE - i - 1]);
}
}
void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf) {
furi_assert(block_num < data->system_info.block_count);
const uint32_t block_offset = block_num * data->system_info.block_size;
const uint8_t* block_data = simple_array_cget(data->block_data, block_offset);
bit_buffer_append_bytes(buf, block_data, data->system_info.block_size);
}
void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked) {
furi_assert(data);
furi_assert(block_index < data->system_info.block_count);
*(uint8_t*)simple_array_get(data->block_security, block_index) = locked ? 1 : 0;
}
void iso15693_3_set_block_data(
Iso15693_3Data* data,
uint8_t block_num,
const uint8_t* block_data,
size_t block_data_size) {
furi_assert(block_num < data->system_info.block_count);
furi_assert(block_data_size == data->system_info.block_size);
const uint32_t block_offset = block_num * data->system_info.block_size;
uint8_t* block = simple_array_get(data->block_data, block_offset);
memcpy(block, block_data, block_data_size);
}
void iso15693_3_append_block_security(
const Iso15693_3Data* data,
uint8_t block_num,
BitBuffer* buf) {
bit_buffer_append_byte(buf, *(uint8_t*)simple_array_cget(data->block_security, block_num));
}
bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid) {
for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {
if(data->uid[i] != uid[ISO15693_3_UID_SIZE - i - 1]) return false;
}
return true;
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include "iso15693_3.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Check if the buffer contains an error frame and if it does, determine
* the error type.
* NOTE: No changes are done to the result if no error is present.
*
* @param [out] data Pointer to the resulting error value.
* @param [in] buf Data buffer to be checked
*
* @return True if data contains an error frame or is empty, false otherwise
*/
bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf);
Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf);
Iso15693_3Error
iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf);
Iso15693_3Error
iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf);
Iso15693_3Error iso15693_3_get_block_security_response_parse(
uint8_t* data,
uint16_t block_count,
const BitBuffer* buf);
void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf);
void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf);
void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked);
void iso15693_3_set_block_data(
Iso15693_3Data* data,
uint8_t block_num,
const uint8_t* block_data,
size_t block_data_size);
void iso15693_3_append_block_security(
const Iso15693_3Data* data,
uint8_t block_num,
BitBuffer* buf);
// NOTE: the uid parameter has reversed byte order with respect to data
bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,110 @@
#include "iso15693_3_listener_i.h"
#include <furi.h>
#include <nfc/nfc.h>
#include <nfc/protocols/nfc_listener_base.h>
#include <nfc/helpers/iso13239_crc.h>
#define TAG "Iso15693_3Listener"
#define ISO15693_3_LISTENER_BUFFER_SIZE (64U)
Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) {
furi_assert(nfc);
Iso15693_3Listener* instance = malloc(sizeof(Iso15693_3Listener));
instance->nfc = nfc;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(ISO15693_3_LISTENER_BUFFER_SIZE);
instance->iso15693_3_event.data = &instance->iso15693_3_event_data;
instance->generic_event.protocol = NfcProtocolIso15693_3;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->iso15693_3_event;
nfc_set_fdt_listen_fc(instance->nfc, ISO15693_3_FDT_LISTEN_FC);
nfc_config(instance->nfc, NfcModeListener, NfcTechIso15693);
return instance;
}
void iso15693_3_listener_free(Iso15693_3Listener* instance) {
furi_assert(instance);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
void iso15693_3_listener_set_callback(
Iso15693_3Listener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
const Iso15693_3Data* iso15693_3_listener_get_data(Iso15693_3Listener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso15693_3Listener* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypeRxEnd) {
BitBuffer* rx_buffer = nfc_event->data.buffer;
if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
iso13239_crc_trim(rx_buffer);
const Iso15693_3Error error = iso15693_3_listener_process_request(instance, rx_buffer);
if(error == Iso15693_3ErrorNotSupported) {
if(instance->callback) {
instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeCustomCommand;
instance->iso15693_3_event.data->buffer = rx_buffer;
command = instance->callback(instance->generic_event, instance->context);
}
} else if(error == Iso15693_3ErrorUidMismatch) {
iso15693_3_listener_process_uid_mismatch(instance, rx_buffer);
}
} else if(bit_buffer_get_size(rx_buffer) == 0) {
// Special case: Single EOF
const Iso15693_3Error error = iso15693_3_listener_process_single_eof(instance);
if(error == Iso15693_3ErrorUnexpectedResponse) {
if(instance->callback) {
instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeSingleEof;
command = instance->callback(instance->generic_event, instance->context);
}
}
} else {
FURI_LOG_D(
TAG, "Wrong CRC, buffer size: %zu", bit_buffer_get_size(nfc_event->data.buffer));
}
}
return command;
}
const NfcListenerBase nfc_listener_iso15693_3 = {
.alloc = (NfcListenerAlloc)iso15693_3_listener_alloc,
.free = (NfcListenerFree)iso15693_3_listener_free,
.set_callback = (NfcListenerSetCallback)iso15693_3_listener_set_callback,
.get_data = (NfcListenerGetData)iso15693_3_listener_get_data,
.run = (NfcListenerRun)iso15693_3_listener_run,
};

View File

@@ -0,0 +1,30 @@
#pragma once
#include <nfc/nfc_listener.h>
#include "iso15693_3.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso15693_3Listener Iso15693_3Listener;
typedef enum {
Iso15693_3ListenerEventTypeFieldOff,
Iso15693_3ListenerEventTypeCustomCommand,
Iso15693_3ListenerEventTypeSingleEof,
} Iso15693_3ListenerEventType;
typedef struct {
BitBuffer* buffer;
} Iso15693_3ListenerEventData;
typedef struct {
Iso15693_3ListenerEventType type;
Iso15693_3ListenerEventData* data;
} Iso15693_3ListenerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_iso15693_3;

View File

@@ -0,0 +1,883 @@
#include "iso15693_3_listener_i.h"
#include <nfc/helpers/iso13239_crc.h>
#define TAG "Iso15693_3Listener"
typedef Iso15693_3Error (*Iso15693_3RequestHandler)(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags);
typedef struct {
Iso15693_3RequestHandler mandatory[ISO15693_3_MANDATORY_COUNT];
Iso15693_3RequestHandler optional[ISO15693_3_OPTIONAL_COUNT];
} Iso15693_3ListenerHandlerTable;
static Iso15693_3Error
iso15693_3_listener_extension_handler(Iso15693_3Listener* instance, uint32_t command, ...) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
if(instance->extension_table == NULL) break;
Iso15693_3ExtensionHandler handler = NULL;
if(command < ISO15693_3_CMD_MANDATORY_RFU) {
const Iso15693_3ExtensionHandler* mandatory = instance->extension_table->mandatory;
handler = mandatory[command - ISO15693_3_CMD_MANDATORY_START];
} else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) {
const Iso15693_3ExtensionHandler* optional = instance->extension_table->optional;
handler = optional[command - ISO15693_3_CMD_OPTIONAL_START];
}
if(handler == NULL) break;
va_list args;
va_start(args, command);
error = handler(instance->extension_context, args);
va_end(args);
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_inventory_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
const bool afi_flag = flags & ISO15693_3_REQ_FLAG_T5_AFI_PRESENT;
const size_t data_size_min = sizeof(uint8_t) * (afi_flag ? 2 : 1);
if(data_size < data_size_min) {
error = Iso15693_3ErrorFormat;
break;
}
if(afi_flag) {
const uint8_t afi = *data++;
// When AFI flag is set, ignore non-matching requests
if(afi != instance->data->system_info.afi) break;
}
const uint8_t mask_len = *data++;
const size_t data_size_required = data_size_min + mask_len;
if(data_size != data_size_required) {
error = Iso15693_3ErrorFormat;
break;
}
if(mask_len != 0) {
// TODO FL-3633: Take mask_len and mask_value into account (if present)
}
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_INVENTORY);
if(error != Iso15693_3ErrorNone) break;
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); // DSFID
iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_stay_quiet_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
instance->state = Iso15693_3ListenerStateQuiet;
return Iso15693_3ErrorIgnore;
}
static Iso15693_3Error iso15693_3_listener_read_block_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t block_num;
} Iso15693_3ReadBlockRequestLayout;
const Iso15693_3ReadBlockRequestLayout* request =
(const Iso15693_3ReadBlockRequestLayout*)data;
if(data_size != sizeof(Iso15693_3ReadBlockRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const uint32_t block_index = request->block_num;
const uint32_t block_count_max = instance->data->system_info.block_count;
if(block_index >= block_count_max) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(
instance, ISO15693_3_CMD_READ_BLOCK, block_index);
if(error != Iso15693_3ErrorNone) break;
if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) {
iso15693_3_append_block_security(
instance->data, block_index, instance->tx_buffer); // Block security (optional)
}
iso15693_3_append_block(instance->data, block_index, instance->tx_buffer); // Block data
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_write_block_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t block_num;
uint8_t block_data[];
} Iso15693_3WriteBlockRequestLayout;
const Iso15693_3WriteBlockRequestLayout* request =
(const Iso15693_3WriteBlockRequestLayout*)data;
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
if(data_size <= sizeof(Iso15693_3WriteBlockRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const uint32_t block_index = request->block_num;
const uint32_t block_count_max = instance->data->system_info.block_count;
const uint32_t block_size_max = instance->data->system_info.block_size;
const size_t block_size_received = data_size - sizeof(Iso15693_3WriteBlockRequestLayout);
if(block_index >= block_count_max) {
error = Iso15693_3ErrorInternal;
break;
} else if(block_size_received != block_size_max) {
error = Iso15693_3ErrorInternal;
break;
} else if(iso15693_3_is_block_locked(instance->data, block_index)) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(
instance, ISO15693_3_CMD_WRITE_BLOCK, block_index, request->block_data);
if(error != Iso15693_3ErrorNone) break;
iso15693_3_set_block_data(
instance->data, block_index, request->block_data, block_size_received);
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_lock_block_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t block_num;
} Iso15693_3LockBlockRequestLayout;
const Iso15693_3LockBlockRequestLayout* request =
(const Iso15693_3LockBlockRequestLayout*)data;
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
if(data_size != sizeof(Iso15693_3LockBlockRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const uint32_t block_index = request->block_num;
const uint32_t block_count_max = instance->data->system_info.block_count;
if(block_index >= block_count_max) {
error = Iso15693_3ErrorInternal;
break;
} else if(iso15693_3_is_block_locked(instance->data, block_index)) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(
instance, ISO15693_3_CMD_LOCK_BLOCK, block_index);
if(error != Iso15693_3ErrorNone) break;
iso15693_3_set_block_locked(instance->data, block_index, true);
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t first_block_num;
uint8_t block_count;
} Iso15693_3ReadMultiBlocksRequestLayout;
const Iso15693_3ReadMultiBlocksRequestLayout* request =
(const Iso15693_3ReadMultiBlocksRequestLayout*)data;
if(data_size != sizeof(Iso15693_3ReadMultiBlocksRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const uint32_t block_index_start = request->first_block_num;
const uint32_t block_index_end = block_index_start + request->block_count;
const uint32_t block_count = request->block_count + 1;
const uint32_t block_count_max = instance->data->system_info.block_count;
const uint32_t block_count_available = block_count_max - block_index_start;
if(block_count > block_count_available) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(
instance,
ISO15693_3_CMD_READ_MULTI_BLOCKS,
(uint32_t)block_index_start,
(uint32_t)block_index_end);
if(error != Iso15693_3ErrorNone) break;
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) {
iso15693_3_append_block_security(
instance->data, i, instance->tx_buffer); // Block security (optional)
}
iso15693_3_append_block(instance->data, i, instance->tx_buffer); // Block data
}
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_write_multi_blocks_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t first_block_num;
uint8_t block_count;
uint8_t block_data[];
} Iso15693_3WriteMultiBlocksRequestLayout;
const Iso15693_3WriteMultiBlocksRequestLayout* request =
(const Iso15693_3WriteMultiBlocksRequestLayout*)data;
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
if(data_size <= sizeof(Iso15693_3WriteMultiBlocksRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const uint32_t block_index_start = request->first_block_num;
const uint32_t block_index_end = block_index_start + request->block_count;
const uint32_t block_count = request->block_count + 1;
const uint32_t block_count_max = instance->data->system_info.block_count;
const uint32_t block_count_available = block_count_max - block_index_start;
const size_t block_data_size = data_size - sizeof(Iso15693_3WriteMultiBlocksRequestLayout);
const size_t block_size = block_data_size / block_count;
const size_t block_size_max = instance->data->system_info.block_size;
if(block_count > block_count_available) {
error = Iso15693_3ErrorInternal;
break;
} else if(block_size != block_size_max) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(
instance, ISO15693_3_CMD_WRITE_MULTI_BLOCKS, block_index_start, block_index_end);
if(error != Iso15693_3ErrorNone) break;
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
if(iso15693_3_is_block_locked(instance->data, i)) {
error = Iso15693_3ErrorInternal;
break;
}
}
if(error != Iso15693_3ErrorNone) break;
for(uint32_t i = block_index_start; i < block_count + request->first_block_num; ++i) {
const uint8_t* block_data = &request->block_data[block_size * i];
iso15693_3_set_block_data(instance->data, i, block_data, block_size);
}
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_select_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
if(!(flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED)) {
error = Iso15693_3ErrorFormat;
break;
}
instance->state = Iso15693_3ListenerStateSelected;
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_reset_to_ready_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
instance->state = Iso15693_3ListenerStateReady;
return Iso15693_3ErrorNone;
}
static Iso15693_3Error iso15693_3_listener_write_afi_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t afi;
} Iso15693_3WriteAfiRequestLayout;
const Iso15693_3WriteAfiRequestLayout* request =
(const Iso15693_3WriteAfiRequestLayout*)data;
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
if(data_size <= sizeof(Iso15693_3WriteAfiRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
} else if(instance->data->settings.lock_bits.afi) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_AFI);
if(error != Iso15693_3ErrorNone) break;
instance->data->system_info.afi = request->afi;
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_lock_afi_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits;
if(lock_bits->afi) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_AFI);
if(error != Iso15693_3ErrorNone) break;
lock_bits->afi = true;
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_write_dsfid_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t dsfid;
} Iso15693_3WriteDsfidRequestLayout;
const Iso15693_3WriteDsfidRequestLayout* request =
(const Iso15693_3WriteDsfidRequestLayout*)data;
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
if(data_size <= sizeof(Iso15693_3WriteDsfidRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
} else if(instance->data->settings.lock_bits.dsfid) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_DSFID);
if(error != Iso15693_3ErrorNone) break;
instance->data->system_info.dsfid = request->dsfid;
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_lock_dsfid_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits;
if(lock_bits->dsfid) {
error = Iso15693_3ErrorInternal;
break;
}
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_DSFID);
if(error != Iso15693_3ErrorNone) break;
lock_bits->dsfid = true;
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_get_system_info_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(data);
UNUSED(data_size);
UNUSED(flags);
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
const uint8_t system_flags = instance->data->system_info.flags;
bit_buffer_append_byte(instance->tx_buffer, system_flags); // System info flags
iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID
if(system_flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid);
}
if(system_flags & ISO15693_3_SYSINFO_FLAG_AFI) {
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.afi);
}
if(system_flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
const uint8_t memory_info[2] = {
instance->data->system_info.block_count - 1,
instance->data->system_info.block_size - 1,
};
bit_buffer_append_bytes(instance->tx_buffer, memory_info, COUNT_OF(memory_info));
}
if(system_flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.ic_ref);
}
} while(false);
return error;
}
static Iso15693_3Error iso15693_3_listener_get_multi_blocks_security_handler(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t flags) {
UNUSED(flags);
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t first_block_num;
uint8_t block_count;
} Iso15693_3GetMultiBlocksSecurityRequestLayout;
const Iso15693_3GetMultiBlocksSecurityRequestLayout* request =
(const Iso15693_3GetMultiBlocksSecurityRequestLayout*)data;
if(data_size < sizeof(Iso15693_3GetMultiBlocksSecurityRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const uint32_t block_index_start = request->first_block_num;
const uint32_t block_index_end = block_index_start + request->block_count;
const uint32_t block_count_max = instance->data->system_info.block_count;
if(block_index_end >= block_count_max) {
error = Iso15693_3ErrorInternal;
break;
}
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
bit_buffer_append_byte(
instance->tx_buffer, iso15693_3_is_block_locked(instance->data, i) ? 1 : 0);
}
} while(false);
return error;
}
const Iso15693_3ListenerHandlerTable iso15693_3_handler_table = {
.mandatory =
{
iso15693_3_listener_inventory_handler,
iso15693_3_listener_stay_quiet_handler,
},
.optional =
{
iso15693_3_listener_read_block_handler,
iso15693_3_listener_write_block_handler,
iso15693_3_listener_lock_block_handler,
iso15693_3_listener_read_multi_blocks_handler,
iso15693_3_listener_write_multi_blocks_handler,
iso15693_3_listener_select_handler,
iso15693_3_listener_reset_to_ready_handler,
iso15693_3_listener_write_afi_handler,
iso15693_3_listener_lock_afi_handler,
iso15693_3_listener_write_dsfid_handler,
iso15693_3_listener_lock_dsfid_handler,
iso15693_3_listener_get_system_info_handler,
iso15693_3_listener_get_multi_blocks_security_handler,
},
};
static Iso15693_3Error iso15693_3_listener_handle_standard_request(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size,
uint8_t command,
uint8_t flags) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
Iso15693_3RequestHandler handler = NULL;
if(command < ISO15693_3_CMD_MANDATORY_RFU) {
handler = iso15693_3_handler_table.mandatory[command - ISO15693_3_CMD_MANDATORY_START];
} else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) {
handler = iso15693_3_handler_table.optional[command - ISO15693_3_CMD_OPTIONAL_START];
}
if(handler == NULL) {
error = Iso15693_3ErrorNotSupported;
break;
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE);
error = handler(instance, data, data_size, flags);
// The request was fully handled in the protocol extension, no further action necessary
if(error == Iso15693_3ErrorFullyHandled) {
error = Iso15693_3ErrorNone;
}
// Several commands may not require an answer
if(error == Iso15693_3ErrorFormat || error == Iso15693_3ErrorIgnore) break;
if(error != Iso15693_3ErrorNone) {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN);
}
Iso15693_3ListenerSessionState* session_state = &instance->session_state;
if(!session_state->wait_for_eof) {
error = iso15693_3_listener_send_frame(instance, instance->tx_buffer);
}
} while(false);
return error;
}
static inline Iso15693_3Error iso15693_3_listener_handle_custom_request(
Iso15693_3Listener* instance,
const uint8_t* data,
size_t data_size) {
Iso15693_3Error error;
do {
typedef struct {
uint8_t manufacturer;
uint8_t extra[];
} Iso15693_3CustomRequestLayout;
if(data_size < sizeof(Iso15693_3CustomRequestLayout)) {
error = Iso15693_3ErrorFormat;
break;
}
const Iso15693_3CustomRequestLayout* request = (const Iso15693_3CustomRequestLayout*)data;
if(request->manufacturer != iso15693_3_get_manufacturer_id(instance->data)) {
error = Iso15693_3ErrorIgnore;
break;
}
// This error code will trigger the CustomCommand listener event
error = Iso15693_3ErrorNotSupported;
} while(false);
return error;
}
Iso15693_3Error iso15693_3_listener_set_extension_handler_table(
Iso15693_3Listener* instance,
const Iso15693_3ExtensionHandlerTable* table,
void* context) {
furi_assert(instance);
furi_assert(context);
instance->extension_table = table;
instance->extension_context = context;
return Iso15693_3ErrorNone;
}
Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance) {
furi_assert(instance);
instance->state = Iso15693_3ListenerStateReady;
return Iso15693_3ErrorNone;
}
static Iso15693_3Error iso15693_3_listener_process_nfc_error(NfcError error) {
Iso15693_3Error ret = Iso15693_3ErrorNone;
if(error == NfcErrorNone) {
ret = Iso15693_3ErrorNone;
} else if(error == NfcErrorTimeout) {
ret = Iso15693_3ErrorTimeout;
} else {
ret = Iso15693_3ErrorFieldOff;
}
return ret;
}
Iso15693_3Error
iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer);
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
return iso15693_3_listener_process_nfc_error(error);
}
Iso15693_3Error
iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
typedef struct {
uint8_t flags;
uint8_t command;
uint8_t data[];
} Iso15693_3RequestLayout;
const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer);
const size_t buf_size_min = sizeof(Iso15693_3RequestLayout);
if(buf_size < buf_size_min) {
error = Iso15693_3ErrorFormat;
break;
}
const Iso15693_3RequestLayout* request =
(const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer);
Iso15693_3ListenerSessionState* session_state = &instance->session_state;
if((request->flags & ISO15693_3_REQ_FLAG_INVENTORY_T5) == 0) {
session_state->selected = request->flags & ISO15693_3_REQ_FLAG_T4_SELECTED;
session_state->addressed = request->flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED;
if(session_state->selected && session_state->addressed) {
// A request mode can be either addressed or selected, but not both
error = Iso15693_3ErrorUnknown;
break;
} else if(instance->state == Iso15693_3ListenerStateQuiet) {
// If the card is quiet, ignore non-addressed commands
if(session_state->addressed) {
error = Iso15693_3ErrorIgnore;
break;
}
} else if(instance->state != Iso15693_3ListenerStateSelected) {
// If the card is not selected, ignore selected commands
if(session_state->selected) {
error = Iso15693_3ErrorIgnore;
break;
}
}
} else {
// If the card is quiet, ignore inventory commands
if(instance->state == Iso15693_3ListenerStateQuiet) {
error = Iso15693_3ErrorIgnore;
break;
}
session_state->selected = false;
session_state->addressed = false;
}
if(request->command >= ISO15693_3_CMD_CUSTOM_START) {
// Custom commands are properly handled in the protocol-specific top-level poller
error = iso15693_3_listener_handle_custom_request(
instance, request->data, buf_size - buf_size_min);
break;
}
const uint8_t* data;
size_t data_size;
if(session_state->addressed) {
// In addressed mode, UID must be included in each command
const size_t buf_size_min_addr = buf_size_min + ISO15693_3_UID_SIZE;
if(buf_size < buf_size_min_addr) {
error = Iso15693_3ErrorFormat;
break;
} else if(!iso15693_3_is_equal_uid(instance->data, request->data)) {
error = Iso15693_3ErrorUidMismatch;
break;
}
data = &request->data[ISO15693_3_UID_SIZE];
data_size = buf_size - buf_size_min_addr;
} else {
data = request->data;
data_size = buf_size - buf_size_min;
}
error = iso15693_3_listener_handle_standard_request(
instance, data, data_size, request->command, request->flags);
} while(false);
return error;
}
Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance) {
Iso15693_3Error error = Iso15693_3ErrorNone;
do {
if(!instance->session_state.wait_for_eof) {
error = Iso15693_3ErrorUnexpectedResponse;
break;
}
instance->session_state.wait_for_eof = false;
error = iso15693_3_listener_send_frame(instance, instance->tx_buffer);
} while(false);
return error;
}
Iso15693_3Error iso15693_3_listener_process_uid_mismatch(
Iso15693_3Listener* instance,
const BitBuffer* rx_buffer) {
Iso15693_3Error error = Iso15693_3ErrorNone;
// No checks, assuming they have been made beforehand
typedef struct {
uint8_t flags;
uint8_t command;
} Iso15693_3RequestLayout;
const Iso15693_3RequestLayout* request =
(const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer);
if(request->command == ISO15693_3_CMD_SELECT) {
if(instance->state == Iso15693_3ListenerStateSelected) {
error = iso15693_3_listener_ready(instance);
}
}
return error;
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include <nfc/protocols/nfc_generic_event.h>
#include "iso15693_3_listener.h"
#include "iso15693_3_i.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso15693_3ListenerStateReady,
Iso15693_3ListenerStateSelected,
Iso15693_3ListenerStateQuiet,
} Iso15693_3ListenerState;
typedef struct {
bool selected;
bool addressed;
bool wait_for_eof;
} Iso15693_3ListenerSessionState;
typedef Iso15693_3Error (*Iso15693_3ExtensionHandler)(void* context, va_list args);
typedef struct {
Iso15693_3ExtensionHandler mandatory[ISO15693_3_MANDATORY_COUNT];
Iso15693_3ExtensionHandler optional[ISO15693_3_OPTIONAL_COUNT];
} Iso15693_3ExtensionHandlerTable;
struct Iso15693_3Listener {
Nfc* nfc;
Iso15693_3Data* data;
Iso15693_3ListenerState state;
Iso15693_3ListenerSessionState session_state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Iso15693_3ListenerEvent iso15693_3_event;
Iso15693_3ListenerEventData iso15693_3_event_data;
NfcGenericCallback callback;
void* context;
const Iso15693_3ExtensionHandlerTable* extension_table;
void* extension_context;
};
Iso15693_3Error iso15693_3_listener_set_extension_handler_table(
Iso15693_3Listener* instance,
const Iso15693_3ExtensionHandlerTable* table,
void* context);
Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance);
Iso15693_3Error
iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer);
Iso15693_3Error
iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer);
Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance);
Iso15693_3Error iso15693_3_listener_process_uid_mismatch(
Iso15693_3Listener* instance,
const BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,122 @@
#include "iso15693_3_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "ISO15693_3Poller"
const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static Iso15693_3Poller* iso15693_3_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
Iso15693_3Poller* instance = malloc(sizeof(Iso15693_3Poller));
instance->nfc = nfc;
instance->tx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE);
nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
nfc_set_guard_time_us(instance->nfc, ISO15693_3_GUARD_TIME_US);
nfc_set_fdt_poll_fc(instance->nfc, ISO15693_3_FDT_POLL_FC);
nfc_set_fdt_poll_poll_us(instance->nfc, ISO15693_3_POLL_POLL_MIN_US);
instance->data = iso15693_3_alloc();
instance->iso15693_3_event.data = &instance->iso15693_3_event_data;
instance->general_event.protocol = NfcProtocolIso15693_3;
instance->general_event.event_data = &instance->iso15693_3_event;
instance->general_event.instance = instance;
return instance;
}
static void iso15693_3_poller_free(Iso15693_3Poller* instance) {
furi_assert(instance);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
iso15693_3_free(instance->data);
free(instance);
}
static void iso15693_3_poller_set_callback(
Iso15693_3Poller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso15693_3_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
furi_assert(event.event_data);
Iso15693_3Poller* instance = context;
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
if(instance->state != Iso15693_3PollerStateActivated) {
Iso15693_3Error error = iso15693_3_poller_async_activate(instance, instance->data);
if(error == Iso15693_3ErrorNone) {
instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady;
instance->iso15693_3_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
} else {
instance->iso15693_3_event.type = Iso15693_3PollerEventTypeError;
instance->iso15693_3_event_data.error = error;
command = instance->callback(instance->general_event, instance->context);
// Add delay to switch context
furi_delay_ms(100);
}
} else {
instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady;
instance->iso15693_3_event_data.error = Iso15693_3ErrorNone;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
static bool iso15693_3_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolInvalid);
bool protocol_detected = false;
Iso15693_3Poller* instance = context;
NfcEvent* nfc_event = event.event_data;
furi_assert(instance->state == Iso15693_3PollerStateIdle);
if(nfc_event->type == NfcEventTypePollerReady) {
uint8_t uid[ISO15693_3_UID_SIZE];
Iso15693_3Error error = iso15693_3_poller_async_inventory(instance, uid);
protocol_detected = (error == Iso15693_3ErrorNone);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso15693_3 = {
.alloc = (NfcPollerAlloc)iso15693_3_poller_alloc,
.free = (NfcPollerFree)iso15693_3_poller_free,
.set_callback = (NfcPollerSetCallback)iso15693_3_poller_set_callback,
.run = (NfcPollerRun)iso15693_3_poller_run,
.detect = (NfcPollerDetect)iso15693_3_poller_detect,
.get_data = (NfcPollerGetData)iso15693_3_poller_get_data,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include "iso15693_3.h"
#include <nfc/nfc_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso15693_3Poller Iso15693_3Poller;
typedef enum {
Iso15693_3PollerEventTypeError,
Iso15693_3PollerEventTypeReady,
} Iso15693_3PollerEventType;
typedef struct {
Iso15693_3Error error;
} Iso15693_3PollerEventData;
typedef struct {
Iso15693_3PollerEventType type;
Iso15693_3PollerEventData* data;
} Iso15693_3PollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso15693_3;

View File

@@ -0,0 +1,303 @@
#include "iso15693_3_poller_i.h"
#include <nfc/helpers/iso13239_crc.h>
#define TAG "Iso15693_3Poller"
#define BITS_IN_BYTE (8)
#define ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY (32U)
static Iso15693_3Error iso15693_3_poller_process_nfc_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso15693_3ErrorNone;
case NfcErrorTimeout:
return Iso15693_3ErrorTimeout;
default:
return Iso15693_3ErrorNotPresent;
}
}
static Iso15693_3Error iso15693_3_poller_filter_error(Iso15693_3Error error) {
switch(error) {
/* If a particular optional command is not supported, the card might
* respond with a "Not supported" error or not respond at all.
* Therefore, treat these errors as non-critical ones. */
case Iso15693_3ErrorNotSupported:
case Iso15693_3ErrorTimeout:
return Iso15693_3ErrorNone;
default:
return error;
}
}
static Iso15693_3Error iso15693_3_poller_prepare_trx(Iso15693_3Poller* instance) {
furi_assert(instance);
if(instance->state == Iso15693_3PollerStateIdle) {
return iso15693_3_poller_async_activate(instance, NULL);
}
return Iso15693_3ErrorNone;
}
static Iso15693_3Error iso15693_3_poller_frame_exchange(
Iso15693_3Poller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
furi_assert(instance);
Iso15693_3Error ret = Iso15693_3ErrorNone;
do {
if(bit_buffer_get_size_bytes(tx_buffer) >
bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO13239_CRC_SIZE) {
ret = Iso15693_3ErrorBufferOverflow;
break;
}
bit_buffer_copy(instance->tx_buffer, tx_buffer);
iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer);
NfcError error =
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
if(error != NfcErrorNone) {
ret = iso15693_3_poller_process_nfc_error(error);
break;
}
if(!iso13239_crc_check(Iso13239CrcTypeDefault, instance->rx_buffer)) {
ret = Iso15693_3ErrorWrongCrc;
break;
}
iso13239_crc_trim(instance->rx_buffer);
bit_buffer_copy(rx_buffer, instance->rx_buffer);
} while(false);
return ret;
}
Iso15693_3Error
iso15693_3_poller_async_activate(Iso15693_3Poller* instance, Iso15693_3Data* data) {
furi_assert(instance);
furi_assert(instance->nfc);
iso15693_3_reset(data);
Iso15693_3Error ret;
do {
instance->state = Iso15693_3PollerStateColResInProgress;
// Inventory: Mandatory command
ret = iso15693_3_poller_async_inventory(instance, data->uid);
if(ret != Iso15693_3ErrorNone) {
instance->state = Iso15693_3PollerStateColResFailed;
break;
}
instance->state = Iso15693_3PollerStateActivated;
// Get system info: Optional command
Iso15693_3SystemInfo* system_info = &data->system_info;
ret = iso15693_3_poller_async_get_system_info(instance, system_info);
if(ret != Iso15693_3ErrorNone) {
ret = iso15693_3_poller_filter_error(ret);
break;
}
// Read blocks: Optional command
simple_array_init(data->block_data, system_info->block_count * system_info->block_size);
ret = iso15693_3_poller_async_read_blocks(
instance,
simple_array_get_data(data->block_data),
system_info->block_count,
system_info->block_size);
if(ret != Iso15693_3ErrorNone) {
ret = iso15693_3_poller_filter_error(ret);
break;
}
// Get block security status: Optional command
simple_array_init(data->block_security, system_info->block_count);
ret = iso15693_3_poller_async_get_blocks_security(
instance, simple_array_get_data(data->block_security), system_info->block_count);
if(ret != Iso15693_3ErrorNone) {
ret = iso15693_3_poller_filter_error(ret);
break;
}
} while(false);
return ret;
}
Iso15693_3Error iso15693_3_poller_async_inventory(Iso15693_3Poller* instance, uint8_t* uid) {
furi_assert(instance);
furi_assert(instance->nfc);
furi_assert(uid);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send INVENTORY
bit_buffer_append_byte(
instance->tx_buffer,
ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI |
ISO15693_3_REQ_FLAG_INVENTORY_T5 | ISO15693_3_REQ_FLAG_T5_N_SLOTS_1);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_INVENTORY);
bit_buffer_append_byte(instance->tx_buffer, 0x00);
Iso15693_3Error ret;
do {
ret = iso15693_3_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
if(ret != Iso15693_3ErrorNone) break;
ret = iso15693_3_inventory_response_parse(uid, instance->rx_buffer);
} while(false);
return ret;
}
Iso15693_3Error iso15693_3_poller_async_get_system_info(
Iso15693_3Poller* instance,
Iso15693_3SystemInfo* data) {
furi_assert(instance);
furi_assert(data);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
// Send GET SYSTEM INFO
bit_buffer_append_byte(
instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_SYS_INFO);
Iso15693_3Error ret;
do {
ret = iso15693_3_poller_frame_exchange(
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
if(ret != Iso15693_3ErrorNone) break;
ret = iso15693_3_system_info_response_parse(data, instance->rx_buffer);
} while(false);
return ret;
}
Iso15693_3Error iso15693_3_poller_async_read_block(
Iso15693_3Poller* instance,
uint8_t* data,
uint8_t block_number,
uint8_t block_size) {
furi_assert(instance);
furi_assert(data);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
bit_buffer_append_byte(
instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_READ_BLOCK);
bit_buffer_append_byte(instance->tx_buffer, block_number);
Iso15693_3Error ret;
do {
ret = iso15693_3_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
if(ret != Iso15693_3ErrorNone) break;
ret = iso15693_3_read_block_response_parse(data, block_size, instance->rx_buffer);
} while(false);
return ret;
}
Iso15693_3Error iso15693_3_poller_async_read_blocks(
Iso15693_3Poller* instance,
uint8_t* data,
uint16_t block_count,
uint8_t block_size) {
furi_assert(instance);
furi_assert(data);
furi_assert(block_count);
furi_assert(block_size);
Iso15693_3Error ret = Iso15693_3ErrorNone;
for(uint32_t i = 0; i < block_count; ++i) {
ret = iso15693_3_poller_async_read_block(instance, &data[block_size * i], i, block_size);
if(ret != Iso15693_3ErrorNone) break;
}
return ret;
}
Iso15693_3Error iso15693_3_poller_async_get_blocks_security(
Iso15693_3Poller* instance,
uint8_t* data,
uint16_t block_count) {
furi_assert(instance);
furi_assert(data);
// Limit the number of blocks to 32 in a single query
const uint32_t num_queries = block_count / ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY +
(block_count % ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY ? 1 : 0);
Iso15693_3Error ret = Iso15693_3ErrorNone;
for(uint32_t i = 0; i < num_queries; ++i) {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
bit_buffer_append_byte(
instance->tx_buffer,
ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_BLOCKS_SECURITY);
const uint8_t start_block_num = i * ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY;
bit_buffer_append_byte(instance->tx_buffer, start_block_num);
const uint8_t block_count_per_query =
MIN(block_count - start_block_num, (uint16_t)ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY);
// Block count byte must be 1 less than the desired count
bit_buffer_append_byte(instance->tx_buffer, block_count_per_query - 1);
ret = iso15693_3_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
if(ret != Iso15693_3ErrorNone) break;
ret = iso15693_3_get_block_security_response_parse(
&data[start_block_num], block_count_per_query, instance->rx_buffer);
if(ret != Iso15693_3ErrorNone) break;
}
return ret;
}
Iso15693_3Error iso15693_3_poller_send_frame(
Iso15693_3Poller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt) {
Iso15693_3Error ret;
do {
ret = iso15693_3_poller_prepare_trx(instance);
if(ret != Iso15693_3ErrorNone) break;
ret = iso15693_3_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
} while(false);
return ret;
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include "iso15693_3_poller.h"
#include "iso15693_3_i.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ISO15693_3_POLLER_MAX_BUFFER_SIZE (64U)
typedef enum {
Iso15693_3PollerStateIdle,
Iso15693_3PollerStateColResInProgress,
Iso15693_3PollerStateColResFailed,
Iso15693_3PollerStateActivated,
} Iso15693_3PollerState;
struct Iso15693_3Poller {
Nfc* nfc;
Iso15693_3PollerState state;
Iso15693_3Data* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
NfcGenericEvent general_event;
Iso15693_3PollerEvent iso15693_3_event;
Iso15693_3PollerEventData iso15693_3_event_data;
NfcGenericCallback callback;
void* context;
};
const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance);
Iso15693_3Error iso15693_3_poller_async_activate(Iso15693_3Poller* instance, Iso15693_3Data* data);
Iso15693_3Error iso15693_3_poller_async_inventory(Iso15693_3Poller* instance, uint8_t* uid);
Iso15693_3Error
iso15693_3_poller_async_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data);
Iso15693_3Error iso15693_3_poller_async_read_block(
Iso15693_3Poller* instance,
uint8_t* data,
uint8_t block_number,
uint8_t block_size);
Iso15693_3Error iso15693_3_poller_async_read_blocks(
Iso15693_3Poller* instance,
uint8_t* data,
uint16_t block_count,
uint8_t block_size);
Iso15693_3Error iso15693_3_poller_async_get_blocks_security(
Iso15693_3Poller* instance,
uint8_t* data,
uint16_t block_count);
Iso15693_3Error iso15693_3_poller_send_frame(
Iso15693_3Poller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,172 @@
#include "crypto1.h"
#include <lib/nfc/helpers/nfc_util.h>
#include <furi.h>
// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git
#define SWAPENDIAN(x) \
((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
#define LF_POLY_ODD (0x29CE5C)
#define LF_POLY_EVEN (0x870804)
#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24)
Crypto1* crypto1_alloc() {
Crypto1* instance = malloc(sizeof(Crypto1));
return instance;
}
void crypto1_free(Crypto1* instance) {
furi_assert(instance);
free(instance);
}
void crypto1_reset(Crypto1* crypto1) {
furi_assert(crypto1);
crypto1->even = 0;
crypto1->odd = 0;
}
void crypto1_init(Crypto1* crypto1, uint64_t key) {
furi_assert(crypto1);
crypto1->even = 0;
crypto1->odd = 0;
for(int8_t i = 47; i > 0; i -= 2) {
crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7);
crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7);
}
}
static uint32_t crypto1_filter(uint32_t in) {
uint32_t out = 0;
out = 0xf22c0 >> (in & 0xf) & 16;
out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8;
out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4;
out |= 0x1e458 >> (in >> 12 & 0xf) & 2;
out |= 0x0d938 >> (in >> 16 & 0xf) & 1;
return FURI_BIT(0xEC57E80A, out);
}
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
furi_assert(crypto1);
uint8_t out = crypto1_filter(crypto1->odd);
uint32_t feed = out & (!!is_encrypted);
feed ^= !!in;
feed ^= LF_POLY_ODD & crypto1->odd;
feed ^= LF_POLY_EVEN & crypto1->even;
crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed));
FURI_SWAP(crypto1->odd, crypto1->even);
return out;
}
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {
furi_assert(crypto1);
uint8_t out = 0;
for(uint8_t i = 0; i < 8; i++) {
out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;
}
return out;
}
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
furi_assert(crypto1);
uint32_t out = 0;
for(uint8_t i = 0; i < 32; i++) {
out |= (uint32_t)crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i);
}
return out;
}
uint32_t prng_successor(uint32_t x, uint32_t n) {
SWAPENDIAN(x);
while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
return SWAPENDIAN(x);
}
void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out) {
furi_assert(crypto);
furi_assert(buff);
furi_assert(out);
size_t bits = bit_buffer_get_size(buff);
bit_buffer_set_size(out, bits);
const uint8_t* encrypted_data = bit_buffer_get_data(buff);
if(bits < 8) {
uint8_t decrypted_byte = 0;
uint8_t encrypted_byte = encrypted_data[0];
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 0)) << 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 1)) << 1;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 2)) << 2;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 3)) << 3;
bit_buffer_set_byte(out, 0, decrypted_byte);
} else {
for(size_t i = 0; i < bits / 8; i++) {
uint8_t decrypted_byte = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
bit_buffer_set_byte(out, i, decrypted_byte);
}
}
}
void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out) {
furi_assert(crypto);
furi_assert(buff);
furi_assert(out);
size_t bits = bit_buffer_get_size(buff);
bit_buffer_set_size(out, bits);
const uint8_t* plain_data = bit_buffer_get_data(buff);
if(bits < 8) {
uint8_t encrypted_byte = 0;
for(size_t i = 0; i < bits; i++) {
encrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
}
bit_buffer_set_byte(out, 0, encrypted_byte);
} else {
for(size_t i = 0; i < bits / 8; i++) {
uint8_t encrypted_byte = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
plain_data[i];
bool parity_bit =
((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01);
bit_buffer_set_byte_with_parity(out, i, encrypted_byte, parity_bit);
}
}
}
void crypto1_encrypt_reader_nonce(
Crypto1* crypto,
uint64_t key,
uint32_t cuid,
uint8_t* nt,
uint8_t* nr,
BitBuffer* out) {
furi_assert(crypto);
furi_assert(nt);
furi_assert(nr);
furi_assert(out);
bit_buffer_set_size_bytes(out, 8);
uint32_t nt_num = nfc_util_bytes2num(nt, sizeof(uint32_t));
crypto1_init(crypto, key);
crypto1_word(crypto, nt_num ^ cuid, 0);
for(size_t i = 0; i < 4; i++) {
uint8_t byte = crypto1_byte(crypto, nr[i], 0) ^ nr[i];
bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01);
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
nr[i] = byte;
}
nt_num = prng_successor(nt_num, 32);
for(size_t i = 4; i < 8; i++) {
nt_num = prng_successor(nt_num, 8);
uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num);
bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01);
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
}
}

View File

@@ -1,7 +1,6 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
extern "C" {
@@ -12,6 +11,10 @@ typedef struct {
uint32_t even;
} Crypto1;
Crypto1* crypto1_alloc();
void crypto1_free(Crypto1* instance);
void crypto1_reset(Crypto1* crypto1);
void crypto1_init(Crypto1* crypto1, uint64_t key);
@@ -22,24 +25,20 @@ uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted);
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);
uint32_t crypto1_filter(uint32_t in);
void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out);
void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out);
void crypto1_encrypt_reader_nonce(
Crypto1* crypto,
uint64_t key,
uint32_t cuid,
uint8_t* nt,
uint8_t* nr,
BitBuffer* out);
uint32_t prng_successor(uint32_t x, uint32_t n);
void crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
uint16_t encrypted_data_bits,
uint8_t* decrypted_data);
void crypto1_encrypt(
Crypto1* crypto,
uint8_t* keystream,
uint8_t* plain_data,
uint16_t plain_data_bits,
uint8_t* encrypted_data,
uint8_t* encrypted_parity);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,740 @@
#include "mf_classic.h"
#include <furi/furi.h>
#include <toolbox/hex.h>
#include <lib/nfc/helpers/nfc_util.h>
#define MF_CLASSIC_PROTOCOL_NAME "Mifare Classic"
typedef struct {
uint8_t sectors_total;
uint16_t blocks_total;
const char* full_name;
const char* type_name;
} MfClassicFeatures;
static const uint32_t mf_classic_data_format_version = 2;
static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = {
[MfClassicTypeMini] =
{
.sectors_total = 5,
.blocks_total = 20,
.full_name = "Mifare Classic Mini 0.3K",
.type_name = "MINI",
},
[MfClassicType1k] =
{
.sectors_total = 16,
.blocks_total = 64,
.full_name = "Mifare Classic 1K",
.type_name = "1K",
},
[MfClassicType4k] =
{
.sectors_total = 40,
.blocks_total = 256,
.full_name = "Mifare Classic 4K",
.type_name = "4K",
},
};
const NfcDeviceBase nfc_device_mf_classic = {
.protocol_name = MF_CLASSIC_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)mf_classic_alloc,
.free = (NfcDeviceFree)mf_classic_free,
.reset = (NfcDeviceReset)mf_classic_reset,
.copy = (NfcDeviceCopy)mf_classic_copy,
.verify = (NfcDeviceVerify)mf_classic_verify,
.load = (NfcDeviceLoad)mf_classic_load,
.save = (NfcDeviceSave)mf_classic_save,
.is_equal = (NfcDeviceEqual)mf_classic_is_equal,
.get_name = (NfcDeviceGetName)mf_classic_get_device_name,
.get_uid = (NfcDeviceGetUid)mf_classic_get_uid,
.set_uid = (NfcDeviceSetUid)mf_classic_set_uid,
.get_base_data = (NfcDeviceGetBaseData)mf_classic_get_base_data,
};
MfClassicData* mf_classic_alloc() {
MfClassicData* data = malloc(sizeof(MfClassicData));
data->iso14443_3a_data = iso14443_3a_alloc();
return data;
}
void mf_classic_free(MfClassicData* data) {
furi_assert(data);
iso14443_3a_free(data->iso14443_3a_data);
free(data);
}
void mf_classic_reset(MfClassicData* data) {
furi_assert(data);
iso14443_3a_reset(data->iso14443_3a_data);
}
void mf_classic_copy(MfClassicData* data, const MfClassicData* other) {
furi_assert(data);
furi_assert(other);
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
for(size_t i = 0; i < COUNT_OF(data->block); i++) {
data->block[i] = other->block[i];
}
for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) {
data->block_read_mask[i] = other->block_read_mask[i];
}
data->type = other->type;
data->key_a_mask = other->key_a_mask;
data->key_b_mask = other->key_b_mask;
}
bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal_str(device_type, "Mifare Classic");
}
static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) {
furi_string_trim(block_str);
MfClassicBlock block_tmp = {};
bool is_sector_trailer = mf_classic_is_sector_trailer(block_num);
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
uint16_t block_unknown_bytes_mask = 0;
furi_string_trim(block_str);
for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {
char hi = furi_string_get_char(block_str, 3 * i);
char low = furi_string_get_char(block_str, 3 * i + 1);
uint8_t byte = 0;
if(hex_char_to_uint8(hi, low, &byte)) {
block_tmp.data[i] = byte;
} else {
FURI_BIT_SET(block_unknown_bytes_mask, i);
}
}
if(block_unknown_bytes_mask != 0xffff) {
if(is_sector_trailer) {
MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp;
// Load Key A
// Key A mask 0b0000000000111111 = 0x003f
if((block_unknown_bytes_mask & 0x003f) == 0) {
uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a.data, sizeof(MfClassicKey));
mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeA, key);
}
// Load Access Bits
// Access bits mask 0b0000001111000000 = 0x03c0
if((block_unknown_bytes_mask & 0x03c0) == 0) {
mf_classic_set_block_read(data, block_num, &block_tmp);
}
// Load Key B
// Key B mask 0b1111110000000000 = 0xfc00
if((block_unknown_bytes_mask & 0xfc00) == 0) {
uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b.data, sizeof(MfClassicKey));
mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeB, key);
}
} else {
if(block_unknown_bytes_mask == 0) {
mf_classic_set_block_read(data, block_num, &block_tmp);
}
}
}
}
bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
FuriString* temp_str = furi_string_alloc();
bool parsed = false;
do {
// Read ISO14443_3A data
if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;
// Read Mifare Classic type
if(!flipper_format_read_string(ff, "Mifare Classic type", temp_str)) break;
bool type_parsed = false;
for(size_t i = 0; i < MfClassicTypeNum; i++) {
if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) {
data->type = i;
type_parsed = true;
}
}
if(!type_parsed) break;
// Read format version
uint32_t data_format_version = 0;
bool old_format = false;
// Read Mifare Classic format version
if(!flipper_format_read_uint32(ff, "Data format version", &data_format_version, 1)) {
// Load unread sectors with zero keys access for backward compatibility
if(!flipper_format_rewind(ff)) break;
old_format = true;
} else {
if(data_format_version < mf_classic_data_format_version) {
old_format = true;
}
}
// Read Mifare Classic blocks
bool block_read = true;
FuriString* block_str = furi_string_alloc();
uint16_t blocks_total = mf_classic_get_total_block_num(data->type);
for(size_t i = 0; i < blocks_total; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_string(ff, furi_string_get_cstr(temp_str), block_str)) {
block_read = false;
break;
}
mf_classic_parse_block(block_str, data, i);
}
furi_string_free(block_str);
if(!block_read) break;
// Set keys and blocks as unknown for backward compatibility
if(old_format) {
data->key_a_mask = 0ULL;
data->key_b_mask = 0ULL;
memset(data->block_read_mask, 0, sizeof(data->block_read_mask));
}
parsed = true;
} while(false);
furi_string_free(temp_str);
return parsed;
}
static void
mf_classic_set_block_str(FuriString* block_str, const MfClassicData* data, uint8_t block_num) {
furi_string_reset(block_str);
bool is_sec_trailer = mf_classic_is_sector_trailer(block_num);
if(is_sec_trailer) {
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);
// Write key A
for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) {
if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) {
furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a.data[i]);
} else {
furi_string_cat_printf(block_str, "?? ");
}
}
// Write Access bytes
for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) {
if(mf_classic_is_block_read(data, block_num)) {
furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits.data[i]);
} else {
furi_string_cat_printf(block_str, "?? ");
}
}
// Write key B
for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) {
if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) {
furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b.data[i]);
} else {
furi_string_cat_printf(block_str, "?? ");
}
}
} else {
// Write data block
for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) {
if(mf_classic_is_block_read(data, block_num)) {
furi_string_cat_printf(block_str, "%02X ", data->block[block_num].data[i]);
} else {
furi_string_cat_printf(block_str, "?? ");
}
}
}
furi_string_trim(block_str);
}
bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) {
furi_assert(data);
FuriString* temp_str = furi_string_alloc();
bool saved = false;
do {
if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, "Mifare Classic specific data")) break;
if(!flipper_format_write_string_cstr(
ff, "Mifare Classic type", mf_classic_features[data->type].type_name))
break;
if(!flipper_format_write_uint32(
ff, "Data format version", &mf_classic_data_format_version, 1))
break;
if(!flipper_format_write_comment_cstr(
ff, "Mifare Classic blocks, \'??\' means unknown data"))
break;
uint16_t blocks_total = mf_classic_get_total_block_num(data->type);
FuriString* block_str = furi_string_alloc();
bool block_saved = true;
for(size_t i = 0; i < blocks_total; i++) {
furi_string_printf(temp_str, "Block %d", i);
mf_classic_set_block_str(block_str, data, i);
if(!flipper_format_write_string(ff, furi_string_get_cstr(temp_str), block_str)) {
block_saved = false;
break;
}
}
furi_string_free(block_str);
if(!block_saved) break;
saved = true;
} while(false);
furi_string_free(temp_str);
return saved;
}
bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other) {
bool is_equal = false;
bool data_array_is_equal = true;
do {
if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break;
if(data->type != other->type) break;
if(data->key_a_mask != other->key_a_mask) break;
if(data->key_b_mask != other->key_b_mask) break;
for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) {
if(data->block_read_mask[i] != other->block_read_mask[i]) {
data_array_is_equal = false;
break;
}
}
if(!data_array_is_equal) break;
for(size_t i = 0; i < COUNT_OF(data->block); i++) {
if(memcmp(&data->block[i], &other->block[i], sizeof(data->block[i])) != 0) {
data_array_is_equal = false;
break;
}
}
if(!data_array_is_equal) break;
is_equal = true;
} while(false);
return is_equal;
}
const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type) {
furi_assert(data);
furi_assert(data->type < MfClassicTypeNum);
if(name_type == NfcDeviceNameTypeFull) {
return mf_classic_features[data->type].full_name;
} else {
return mf_classic_features[data->type].type_name;
}
}
const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) {
furi_assert(data);
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
}
bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
}
Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) {
furi_assert(data);
return data->iso14443_3a_data;
}
uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
return mf_classic_features[type].sectors_total;
}
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
return mf_classic_features[type].blocks_total;
}
uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector) {
uint8_t block_num = 0;
if(sector < 32) {
block_num = sector * 4 + 3;
} else if(sector < 40) {
block_num = 32 * 4 + (sector - 32) * 16 + 15;
} else {
furi_crash("Wrong sector num");
}
return block_num;
}
uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) {
uint8_t sec_tr_block_num = 0;
if(block < 128) {
sec_tr_block_num = block | 0x03;
} else {
sec_tr_block_num = block | 0x0f;
}
return sec_tr_block_num;
}
MfClassicSectorTrailer*
mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num) {
furi_assert(data);
uint8_t sec_tr_block = mf_classic_get_sector_trailer_num_by_sector(sector_num);
MfClassicSectorTrailer* sec_trailer = (MfClassicSectorTrailer*)&data->block[sec_tr_block];
return sec_trailer;
}
bool mf_classic_is_sector_trailer(uint8_t block) {
return block == mf_classic_get_sector_trailer_num_by_block(block);
}
uint8_t mf_classic_get_sector_by_block(uint8_t block) {
uint8_t sector = 0;
if(block < 128) {
sector = (block | 0x03) / 4;
} else {
sector = 32 + ((block | 0x0f) - 32 * 4) / 16;
}
return sector;
}
bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr) {
furi_assert(block);
uint32_t v = *(uint32_t*)&block->data[0];
uint32_t v_inv = *(uint32_t*)&block->data[sizeof(uint32_t)];
uint32_t v1 = *(uint32_t*)&block->data[sizeof(uint32_t) * 2];
bool val_checks =
((v == v1) && (v == ~v_inv) && (block->data[12] == (~block->data[13] & 0xFF)) &&
(block->data[14] == (~block->data[15] & 0xFF)) && (block->data[12] == block->data[14]));
if(value) {
*value = (int32_t)v;
}
if(addr) {
*addr = block->data[12];
}
return val_checks;
}
void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block) {
furi_assert(block);
uint32_t v_inv = ~((uint32_t)value);
memcpy(&block->data[0], &value, 4); //-V1086
memcpy(&block->data[4], &v_inv, 4); //-V1086
memcpy(&block->data[8], &value, 4); //-V1086
block->data[12] = addr;
block->data[13] = ~addr & 0xFF;
block->data[14] = addr;
block->data[15] = ~addr & 0xFF;
}
bool mf_classic_is_key_found(
const MfClassicData* data,
uint8_t sector_num,
MfClassicKeyType key_type) {
furi_assert(data);
bool key_found = false;
if(key_type == MfClassicKeyTypeA) {
key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1);
} else if(key_type == MfClassicKeyTypeB) {
key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1);
}
return key_found;
}
void mf_classic_set_key_found(
MfClassicData* data,
uint8_t sector_num,
MfClassicKeyType key_type,
uint64_t key) {
furi_assert(data);
uint8_t key_arr[6] = {};
MfClassicSectorTrailer* sec_trailer =
mf_classic_get_sector_trailer_by_sector(data, sector_num);
nfc_util_num2bytes(key, 6, key_arr);
if(key_type == MfClassicKeyTypeA) {
memcpy(sec_trailer->key_a.data, key_arr, sizeof(MfClassicKey));
FURI_BIT_SET(data->key_a_mask, sector_num);
} else if(key_type == MfClassicKeyTypeB) {
memcpy(sec_trailer->key_b.data, key_arr, sizeof(MfClassicKey));
FURI_BIT_SET(data->key_b_mask, sector_num);
}
}
void mf_classic_set_key_not_found(
MfClassicData* data,
uint8_t sector_num,
MfClassicKeyType key_type) {
furi_assert(data);
if(key_type == MfClassicKeyTypeA) {
FURI_BIT_CLEAR(data->key_a_mask, sector_num);
} else if(key_type == MfClassicKeyTypeB) {
FURI_BIT_CLEAR(data->key_b_mask, sector_num);
}
}
bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) {
furi_assert(data);
return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1);
}
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) {
furi_assert(data);
if(mf_classic_is_sector_trailer(block_num)) {
memcpy(&data->block[block_num].data[6], &block_data->data[6], 4);
} else {
memcpy(data->block[block_num].data, block_data->data, MF_CLASSIC_BLOCK_SIZE);
}
FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
}
uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {
furi_assert(sector < 40);
uint8_t block = 0;
if(sector < 32) {
block = sector * 4;
} else {
block = 32 * 4 + (sector - 32) * 16;
}
return block;
}
uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) {
furi_assert(sector < 40);
return sector < 32 ? 4 : 16;
}
void mf_classic_get_read_sectors_and_keys(
const MfClassicData* data,
uint8_t* sectors_read,
uint8_t* keys_found) {
furi_assert(data);
furi_assert(sectors_read);
furi_assert(keys_found);
*sectors_read = 0;
*keys_found = 0;
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
for(size_t i = 0; i < sectors_total; i++) {
if(mf_classic_is_key_found(data, i, MfClassicKeyTypeA)) {
*keys_found += 1;
}
if(mf_classic_is_key_found(data, i, MfClassicKeyTypeB)) {
*keys_found += 1;
}
uint8_t first_block = mf_classic_get_first_block_num_of_sector(i);
uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i);
bool blocks_read = true;
for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) {
blocks_read = mf_classic_is_block_read(data, j);
if(!blocks_read) break;
}
if(blocks_read) {
*sectors_read += 1;
}
}
}
bool mf_classic_is_card_read(const MfClassicData* data) {
furi_assert(data);
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
uint8_t sectors_read = 0;
uint8_t keys_found = 0;
mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);
bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2);
return card_read;
}
bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num) {
furi_assert(data);
bool sector_read = false;
do {
if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) break;
if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) break;
uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num);
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);
uint8_t block_read = true;
for(size_t i = start_block; i < start_block + total_blocks; i++) {
block_read = mf_classic_is_block_read(data, i);
if(!block_read) break;
}
sector_read = block_read;
} while(false);
return sector_read;
}
static bool mf_classic_is_allowed_access_sector_trailer(
MfClassicData* data,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicAction action) {
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);
uint8_t* access_bits_arr = sec_tr->access_bits.data;
uint8_t AC = ((access_bits_arr[1] >> 5) & 0x04) | ((access_bits_arr[2] >> 2) & 0x02) |
((access_bits_arr[2] >> 7) & 0x01);
switch(action) {
case MfClassicActionKeyARead: {
return false;
}
case MfClassicActionKeyAWrite:
case MfClassicActionKeyBWrite: {
return (
(key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) ||
(key_type == MfClassicKeyTypeB && (AC == 0x04 || AC == 0x03)));
}
case MfClassicActionKeyBRead: {
return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01));
}
case MfClassicActionACRead: {
return (
(key_type == MfClassicKeyTypeA) ||
(key_type == MfClassicKeyTypeB && !(AC == 0x00 || AC == 0x02 || AC == 0x01)));
}
case MfClassicActionACWrite: {
return (
(key_type == MfClassicKeyTypeA && (AC == 0x01)) ||
(key_type == MfClassicKeyTypeB && (AC == 0x03 || AC == 0x05)));
}
default:
return false;
}
return true;
}
bool mf_classic_is_allowed_access_data_block(
MfClassicSectorTrailer* sec_tr,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicAction action) {
furi_assert(sec_tr);
uint8_t* access_bits_arr = sec_tr->access_bits.data;
if(block_num == 0 && action == MfClassicActionDataWrite) {
return false;
}
uint8_t sector_block = 0;
if(block_num <= 128) {
sector_block = block_num & 0x03;
} else {
sector_block = (block_num & 0x0f) / 5;
}
uint8_t AC;
switch(sector_block) {
case 0x00: {
AC = ((access_bits_arr[1] >> 2) & 0x04) | ((access_bits_arr[2] << 1) & 0x02) |
((access_bits_arr[2] >> 4) & 0x01);
break;
}
case 0x01: {
AC = ((access_bits_arr[1] >> 3) & 0x04) | ((access_bits_arr[2] >> 0) & 0x02) |
((access_bits_arr[2] >> 5) & 0x01);
break;
}
case 0x02: {
AC = ((access_bits_arr[1] >> 4) & 0x04) | ((access_bits_arr[2] >> 1) & 0x02) |
((access_bits_arr[2] >> 6) & 0x01);
break;
}
default:
return false;
}
switch(action) {
case MfClassicActionDataRead: {
return (
(key_type == MfClassicKeyTypeA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) ||
(key_type == MfClassicKeyTypeB && !(AC == 0x07)));
}
case MfClassicActionDataWrite: {
return (
(key_type == MfClassicKeyTypeA && (AC == 0x00)) ||
(key_type == MfClassicKeyTypeB &&
(AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03)));
}
case MfClassicActionDataInc: {
return (
(key_type == MfClassicKeyTypeA && (AC == 0x00)) ||
(key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06)));
}
case MfClassicActionDataDec: {
return (
(key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) ||
(key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06 || AC == 0x01)));
}
default:
return false;
}
return false;
}
bool mf_classic_is_allowed_access(
MfClassicData* data,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicAction action) {
furi_assert(data);
bool access_allowed = false;
if(mf_classic_is_sector_trailer(block_num)) {
access_allowed =
mf_classic_is_allowed_access_sector_trailer(data, block_num, key_type, action);
} else {
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num);
access_allowed =
mf_classic_is_allowed_access_data_block(sec_tr, block_num, key_type, action);
}
return access_allowed;
}
bool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num) {
furi_assert(sec_tr);
// Check if key A can write, if it can, it's transport configuration, not data block
return !mf_classic_is_allowed_access_data_block(
sec_tr, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) &&
(mf_classic_is_allowed_access_data_block(
sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) ||
mf_classic_is_allowed_access_data_block(
sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataDec));
}

View File

@@ -0,0 +1,235 @@
#pragma once
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U)
#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U)
#define MF_CLASSIC_CMD_READ_BLOCK (0x30U)
#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U)
#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U)
#define MF_CLASSIC_CMD_VALUE_INC (0xC1U)
#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U)
#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U)
#define MF_CLASSIC_CMD_HALT_MSB (0x50)
#define MF_CLASSIC_CMD_HALT_LSB (0x00)
#define MF_CLASSIC_CMD_ACK (0x0A)
#define MF_CLASSIC_CMD_NACK (0x00)
#define MF_CLASSIC_TOTAL_SECTORS_MAX (40)
#define MF_CLASSIC_TOTAL_BLOCKS_MAX (256)
#define MF_CLASSIC_READ_MASK_SIZE (MF_CLASSIC_TOTAL_BLOCKS_MAX / 32)
#define MF_CLASSIC_BLOCK_SIZE (16)
#define MF_CLASSIC_KEY_SIZE (6)
#define MF_CLASSIC_ACCESS_BYTES_SIZE (4)
#define MF_CLASSIC_NT_SIZE (4)
#define MF_CLASSIC_NR_SIZE (4)
#define MF_CLASSIC_AR_SIZE (4)
#define MF_CLASSIC_AT_SIZE (4)
typedef enum {
MfClassicErrorNone,
MfClassicErrorNotPresent,
MfClassicErrorProtocol,
MfClassicErrorAuth,
MfClassicErrorTimeout,
} MfClassicError;
typedef enum {
MfClassicTypeMini,
MfClassicType1k,
MfClassicType4k,
MfClassicTypeNum,
} MfClassicType;
typedef enum {
MfClassicActionDataRead,
MfClassicActionDataWrite,
MfClassicActionDataInc,
MfClassicActionDataDec,
MfClassicActionKeyARead,
MfClassicActionKeyAWrite,
MfClassicActionKeyBRead,
MfClassicActionKeyBWrite,
MfClassicActionACRead,
MfClassicActionACWrite,
} MfClassicAction;
typedef enum {
MfClassicValueCommandIncrement,
MfClassicValueCommandDecrement,
MfClassicValueCommandRestore,
MfClassicValueCommandInvalid,
} MfClassicValueCommand;
typedef struct {
uint8_t data[MF_CLASSIC_BLOCK_SIZE];
} MfClassicBlock;
typedef enum {
MfClassicKeyTypeA,
MfClassicKeyTypeB,
} MfClassicKeyType;
typedef struct {
uint8_t data[MF_CLASSIC_KEY_SIZE];
} MfClassicKey;
typedef struct {
uint8_t data[MF_CLASSIC_ACCESS_BYTES_SIZE];
} MfClassicAccessBits;
typedef struct {
uint8_t data[MF_CLASSIC_NT_SIZE];
} MfClassicNt;
typedef struct {
uint8_t data[MF_CLASSIC_AT_SIZE];
} MfClassicAt;
typedef struct {
uint8_t data[MF_CLASSIC_NR_SIZE];
} MfClassicNr;
typedef struct {
uint8_t data[MF_CLASSIC_AR_SIZE];
} MfClassicAr;
typedef struct {
uint8_t block_num;
MfClassicKey key;
MfClassicKeyType key_type;
MfClassicNt nt;
MfClassicNr nr;
MfClassicAr ar;
MfClassicAt at;
} MfClassicAuthContext;
typedef union {
MfClassicBlock block;
struct {
MfClassicKey key_a;
MfClassicAccessBits access_bits;
MfClassicKey key_b;
};
} MfClassicSectorTrailer;
typedef struct {
uint64_t key_a_mask;
MfClassicKey key_a[MF_CLASSIC_TOTAL_SECTORS_MAX];
uint64_t key_b_mask;
MfClassicKey key_b[MF_CLASSIC_TOTAL_SECTORS_MAX];
} MfClassicDeviceKeys;
typedef struct {
Iso14443_3aData* iso14443_3a_data;
MfClassicType type;
uint32_t block_read_mask[MF_CLASSIC_READ_MASK_SIZE];
uint64_t key_a_mask;
uint64_t key_b_mask;
MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX];
} MfClassicData;
extern const NfcDeviceBase nfc_device_mf_classic;
MfClassicData* mf_classic_alloc();
void mf_classic_free(MfClassicData* data);
void mf_classic_reset(MfClassicData* data);
void mf_classic_copy(MfClassicData* data, const MfClassicData* other);
bool mf_classic_verify(MfClassicData* data, const FuriString* device_type);
bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version);
bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff);
bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other);
const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type);
const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len);
bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data);
uint8_t mf_classic_get_total_sectors_num(MfClassicType type);
uint16_t mf_classic_get_total_block_num(MfClassicType type);
uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector);
uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector);
uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector);
uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block);
MfClassicSectorTrailer*
mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num);
bool mf_classic_is_sector_trailer(uint8_t block);
uint8_t mf_classic_get_sector_by_block(uint8_t block);
bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr);
void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block);
bool mf_classic_is_key_found(
const MfClassicData* data,
uint8_t sector_num,
MfClassicKeyType key_type);
void mf_classic_set_key_found(
MfClassicData* data,
uint8_t sector_num,
MfClassicKeyType key_type,
uint64_t key);
void mf_classic_set_key_not_found(
MfClassicData* data,
uint8_t sector_num,
MfClassicKeyType key_type);
bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num);
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num);
void mf_classic_get_read_sectors_and_keys(
const MfClassicData* data,
uint8_t* sectors_read,
uint8_t* keys_found);
bool mf_classic_is_card_read(const MfClassicData* data);
bool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num);
bool mf_classic_is_allowed_access_data_block(
MfClassicSectorTrailer* sec_tr,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicAction action);
bool mf_classic_is_allowed_access(
MfClassicData* data,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicAction action);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,656 @@
#include "mf_classic_listener_i.h"
#include <nfc/protocols/nfc_listener_base.h>
#include <nfc/helpers/iso14443_crc.h>
#include <nfc/helpers/nfc_util.h>
#include <furi.h>
#include <furi_hal_random.h>
#define TAG "MfClassicListener"
#define MF_CLASSIC_MAX_BUFF_SIZE (64)
typedef MfClassicListenerCommand (
*MfClassicListenerCommandHandler)(MfClassicListener* instance, BitBuffer* buf);
typedef struct {
uint8_t cmd_start_byte;
size_t cmd_len_bits;
size_t command_num;
MfClassicListenerCommandHandler* handler;
} MfClassicListenerCmd;
static void mf_classic_listener_prepare_emulation(MfClassicListener* instance) {
instance->total_block_num = mf_classic_get_total_block_num(instance->data->type);
}
static void mf_classic_listener_reset_state(MfClassicListener* instance) {
crypto1_reset(instance->crypto);
memset(&instance->auth_context, 0, sizeof(MfClassicAuthContext));
instance->comm_state = MfClassicListenerCommStatePlain;
instance->state = MfClassicListenerStateIdle;
instance->cmd_in_progress = false;
instance->current_cmd_handler_idx = 0;
instance->transfer_value = 0;
instance->value_cmd = MfClassicValueCommandInvalid;
}
static MfClassicListenerCommand
mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) {
mf_classic_listener_reset_state(instance);
command = MfClassicListenerCommandSleep;
}
return command;
}
static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler(
MfClassicListener* instance,
MfClassicKeyType key_type,
uint8_t block_num) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
do {
instance->state = MfClassicListenerStateIdle;
if(block_num >= instance->total_block_num) {
mf_classic_listener_reset_state(instance);
break;
}
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
MfClassicSectorTrailer* sec_tr =
mf_classic_get_sector_trailer_by_sector(instance->data, sector_num);
MfClassicKey* key = (key_type == MfClassicKeyTypeA) ? &sec_tr->key_a : &sec_tr->key_b;
uint64_t key_num = nfc_util_bytes2num(key->data, sizeof(MfClassicKey));
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
instance->auth_context.key_type = key_type;
instance->auth_context.block_num = block_num;
furi_hal_random_fill_buf(instance->auth_context.nt.data, sizeof(MfClassicNt));
uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt));
crypto1_init(instance->crypto, key_num);
if(instance->comm_state == MfClassicListenerCommStatePlain) {
crypto1_word(instance->crypto, nt_num ^ cuid, 0);
bit_buffer_copy_bytes(
instance->tx_encrypted_buffer,
instance->auth_context.nt.data,
sizeof(MfClassicNt));
iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_encrypted_buffer);
command = MfClassicListenerCommandProcessed;
} else {
uint8_t key_stream[4] = {};
nfc_util_num2bytes(nt_num ^ cuid, sizeof(uint32_t), key_stream);
bit_buffer_copy_bytes(
instance->tx_plain_buffer, instance->auth_context.nt.data, sizeof(MfClassicNt));
crypto1_encrypt(
instance->crypto,
key_stream,
instance->tx_plain_buffer,
instance->tx_encrypted_buffer);
iso14443_3a_listener_tx_with_custom_parity(
instance->iso14443_3a_listener, instance->tx_encrypted_buffer);
command = MfClassicListenerCommandProcessed;
}
instance->cmd_in_progress = true;
instance->current_cmd_handler_idx++;
} while(false);
return command;
}
static MfClassicListenerCommand
mf_classic_listener_auth_key_a_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = mf_classic_listener_auth_first_part_handler(
instance, MfClassicKeyTypeA, bit_buffer_get_byte(buff, 1));
return command;
}
static MfClassicListenerCommand
mf_classic_listener_auth_key_b_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = mf_classic_listener_auth_first_part_handler(
instance, MfClassicKeyTypeB, bit_buffer_get_byte(buff, 1));
return command;
}
static MfClassicListenerCommand
mf_classic_listener_auth_second_part_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandSilent;
do {
instance->cmd_in_progress = false;
if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) {
mf_classic_listener_reset_state(instance);
break;
}
bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr));
bit_buffer_write_bytes_mid(
buff, instance->auth_context.ar.data, sizeof(MfClassicNr), sizeof(MfClassicAr));
if(instance->callback) {
instance->mfc_event.type = MfClassicListenerEventTypeAuthContextPartCollected,
instance->mfc_event_data.auth_context = instance->auth_context;
instance->callback(instance->generic_event, instance->context);
}
uint32_t nr_num = nfc_util_bytes2num(instance->auth_context.nr.data, sizeof(MfClassicNr));
uint32_t ar_num = nfc_util_bytes2num(instance->auth_context.ar.data, sizeof(MfClassicAr));
crypto1_word(instance->crypto, nr_num, 1);
uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt));
uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0);
if(secret_poller != prng_successor(nt_num, 64)) {
FURI_LOG_D(
TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64));
mf_classic_listener_reset_state(instance);
break;
}
uint32_t at_num = prng_successor(nt_num, 96);
nfc_util_num2bytes(at_num, sizeof(uint32_t), instance->auth_context.at.data);
bit_buffer_copy_bytes(
instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr));
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
iso14443_3a_listener_tx_with_custom_parity(
instance->iso14443_3a_listener, instance->tx_encrypted_buffer);
instance->state = MfClassicListenerStateAuthComplete;
instance->comm_state = MfClassicListenerCommStateEncrypted;
command = MfClassicListenerCommandProcessed;
if(instance->callback) {
instance->mfc_event.type = MfClassicListenerEventTypeAuthContextFullCollected,
instance->mfc_event_data.auth_context = instance->auth_context;
instance->callback(instance->generic_event, instance->context);
}
} while(false);
return command;
}
static MfClassicListenerCommand
mf_classic_listener_read_block_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
MfClassicAuthContext* auth_ctx = &instance->auth_context;
do {
if(instance->state != MfClassicListenerStateAuthComplete) break;
uint8_t block_num = bit_buffer_get_byte(buff, 1);
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num);
if(sector_num != auth_sector_num) break;
MfClassicBlock access_block = instance->data->block[block_num];
if(mf_classic_is_sector_trailer(block_num)) {
MfClassicSectorTrailer* access_sec_tr = (MfClassicSectorTrailer*)&access_block;
if(!mf_classic_is_allowed_access(
instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyARead)) {
memset(access_sec_tr->key_a.data, 0, sizeof(MfClassicKey));
}
if(!mf_classic_is_allowed_access(
instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyBRead)) {
memset(access_sec_tr->key_b.data, 0, sizeof(MfClassicKey));
}
if(!mf_classic_is_allowed_access(
instance->data, block_num, auth_ctx->key_type, MfClassicActionACRead)) {
memset(access_sec_tr->access_bits.data, 0, sizeof(MfClassicAccessBits));
}
} else if(!mf_classic_is_allowed_access(
instance->data, block_num, auth_ctx->key_type, MfClassicActionDataRead)) {
break;
}
bit_buffer_copy_bytes(
instance->tx_plain_buffer, access_block.data, sizeof(MfClassicBlock));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
iso14443_3a_listener_tx_with_custom_parity(
instance->iso14443_3a_listener, instance->tx_encrypted_buffer);
command = MfClassicListenerCommandProcessed;
} while(false);
return command;
}
static MfClassicListenerCommand mf_classic_listener_write_block_first_part_handler(
MfClassicListener* instance,
BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
MfClassicAuthContext* auth_ctx = &instance->auth_context;
do {
if(instance->state != MfClassicListenerStateAuthComplete) break;
uint8_t block_num = bit_buffer_get_byte(buff, 1);
if(block_num >= instance->total_block_num) break;
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num);
if(sector_num != auth_sector_num) break;
instance->cmd_in_progress = true;
instance->current_cmd_handler_idx++;
command = MfClassicListenerCommandAck;
} while(false);
return command;
}
static MfClassicListenerCommand mf_classic_listener_write_block_second_part_handler(
MfClassicListener* instance,
BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
MfClassicAuthContext* auth_ctx = &instance->auth_context;
do {
instance->cmd_in_progress = false;
size_t buff_size = bit_buffer_get_size_bytes(buff);
if(buff_size != sizeof(MfClassicBlock)) break;
uint8_t block_num = auth_ctx->block_num;
MfClassicKeyType key_type = auth_ctx->key_type;
MfClassicBlock block = instance->data->block[block_num];
if(mf_classic_is_sector_trailer(block_num)) {
MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)&block;
if(mf_classic_is_allowed_access(
instance->data, block_num, key_type, MfClassicActionKeyAWrite)) {
bit_buffer_write_bytes_mid(buff, sec_tr->key_a.data, 0, sizeof(MfClassicKey));
}
if(mf_classic_is_allowed_access(
instance->data, block_num, key_type, MfClassicActionKeyBWrite)) {
bit_buffer_write_bytes_mid(buff, sec_tr->key_b.data, 10, sizeof(MfClassicKey));
}
if(mf_classic_is_allowed_access(
instance->data, block_num, key_type, MfClassicActionACWrite)) {
bit_buffer_write_bytes_mid(
buff, sec_tr->access_bits.data, 6, sizeof(MfClassicAccessBits));
}
} else {
if(mf_classic_is_allowed_access(
instance->data, block_num, key_type, MfClassicActionDataWrite)) {
bit_buffer_write_bytes_mid(buff, block.data, 0, sizeof(MfClassicBlock));
} else {
break;
}
}
instance->data->block[block_num] = block;
command = MfClassicListenerCommandAck;
} while(false);
return command;
}
static MfClassicListenerCommand
mf_classic_listener_value_cmd_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
MfClassicAuthContext* auth_ctx = &instance->auth_context;
do {
if(instance->state != MfClassicListenerStateAuthComplete) break;
uint8_t block_num = bit_buffer_get_byte(buff, 1);
if(block_num >= instance->total_block_num) break;
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num);
if(sector_num != auth_sector_num) break;
uint8_t cmd = bit_buffer_get_byte(buff, 0);
MfClassicAction action = MfClassicActionDataDec;
if(cmd == MF_CLASSIC_CMD_VALUE_DEC) {
instance->value_cmd = MfClassicValueCommandDecrement;
} else if(cmd == MF_CLASSIC_CMD_VALUE_INC) {
instance->value_cmd = MfClassicValueCommandIncrement;
action = MfClassicActionDataInc;
} else if(cmd == MF_CLASSIC_CMD_VALUE_RESTORE) {
instance->value_cmd = MfClassicValueCommandRestore;
} else {
break;
}
if(!mf_classic_is_allowed_access(instance->data, block_num, auth_ctx->key_type, action)) {
break;
}
if(!mf_classic_block_to_value(
&instance->data->block[block_num], &instance->transfer_value, NULL)) {
break;
}
instance->cmd_in_progress = true;
instance->current_cmd_handler_idx++;
command = MfClassicListenerCommandAck;
} while(false);
return command;
}
static MfClassicListenerCommand
mf_classic_listener_value_dec_handler(MfClassicListener* instance, BitBuffer* buff) {
return mf_classic_listener_value_cmd_handler(instance, buff);
}
static MfClassicListenerCommand
mf_classic_listener_value_inc_handler(MfClassicListener* instance, BitBuffer* buff) {
return mf_classic_listener_value_cmd_handler(instance, buff);
}
static MfClassicListenerCommand
mf_classic_listener_value_restore_handler(MfClassicListener* instance, BitBuffer* buff) {
return mf_classic_listener_value_cmd_handler(instance, buff);
}
static MfClassicListenerCommand
mf_classic_listener_value_data_receive_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
do {
if(bit_buffer_get_size_bytes(buff) != 4) break;
int32_t data;
bit_buffer_write_bytes_mid(buff, &data, 0, sizeof(data));
if(data < 0) {
data = -data;
}
if(instance->value_cmd == MfClassicValueCommandDecrement) {
data = -data;
} else if(instance->value_cmd == MfClassicValueCommandRestore) {
data = 0;
}
instance->transfer_value += data;
instance->cmd_in_progress = true;
instance->current_cmd_handler_idx++;
command = MfClassicListenerCommandSilent;
} while(false);
return command;
}
static MfClassicListenerCommand
mf_classic_listener_value_transfer_handler(MfClassicListener* instance, BitBuffer* buff) {
MfClassicListenerCommand command = MfClassicListenerCommandNack;
MfClassicAuthContext* auth_ctx = &instance->auth_context;
do {
instance->cmd_in_progress = false;
if(bit_buffer_get_size_bytes(buff) != 2) break;
if(bit_buffer_get_byte(buff, 0) != MF_CLASSIC_CMD_VALUE_TRANSFER) break;
uint8_t block_num = bit_buffer_get_byte(buff, 1);
if(!mf_classic_is_allowed_access(
instance->data, block_num, auth_ctx->key_type, MfClassicActionDataDec)) {
break;
}
mf_classic_value_to_block(
instance->transfer_value, block_num, &instance->data->block[block_num]);
instance->transfer_value = 0;
command = MfClassicListenerCommandAck;
} while(false);
return command;
}
static MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = {
mf_classic_listener_halt_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = {
mf_classic_listener_auth_key_a_handler,
mf_classic_listener_auth_second_part_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = {
mf_classic_listener_auth_key_b_handler,
mf_classic_listener_auth_second_part_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = {
mf_classic_listener_read_block_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = {
mf_classic_listener_write_block_first_part_handler,
mf_classic_listener_write_block_second_part_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = {
mf_classic_listener_value_dec_handler,
mf_classic_listener_value_data_receive_handler,
mf_classic_listener_value_transfer_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = {
mf_classic_listener_value_inc_handler,
mf_classic_listener_value_data_receive_handler,
mf_classic_listener_value_transfer_handler,
};
static MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = {
mf_classic_listener_value_restore_handler,
mf_classic_listener_value_data_receive_handler,
mf_classic_listener_value_transfer_handler,
};
static const MfClassicListenerCmd mf_classic_listener_cmd_handlers[] = {
{
.cmd_start_byte = MF_CLASSIC_CMD_HALT_MSB,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_halt_handlers),
.handler = mf_classic_listener_halt_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_A,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_auth_key_a_handlers),
.handler = mf_classic_listener_auth_key_a_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_B,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_auth_key_b_handlers),
.handler = mf_classic_listener_auth_key_b_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_READ_BLOCK,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_read_block_handlers),
.handler = mf_classic_listener_read_block_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_WRITE_BLOCK,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_write_block_handlers),
.handler = mf_classic_listener_write_block_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_VALUE_DEC,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_value_dec_handlers),
.handler = mf_classic_listener_value_dec_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_VALUE_INC,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_value_inc_handlers),
.handler = mf_classic_listener_value_inc_handlers,
},
{
.cmd_start_byte = MF_CLASSIC_CMD_VALUE_RESTORE,
.cmd_len_bits = 2 * 8,
.command_num = COUNT_OF(mf_classic_listener_value_restore_handlers),
.handler = mf_classic_listener_value_restore_handlers,
},
};
static void mf_classic_listener_send_short_frame(MfClassicListener* instance, uint8_t data) {
BitBuffer* tx_buffer = instance->tx_plain_buffer;
bit_buffer_set_size(instance->tx_plain_buffer, 4);
bit_buffer_set_byte(instance->tx_plain_buffer, 0, data);
if(instance->comm_state == MfClassicListenerCommStateEncrypted) {
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
tx_buffer = instance->tx_encrypted_buffer;
}
iso14443_3a_listener_tx_with_custom_parity(instance->iso14443_3a_listener, tx_buffer);
}
NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
NfcCommand command = NfcCommandContinue;
MfClassicListener* instance = context;
Iso14443_3aListenerEvent* iso3_event = event.event_data;
BitBuffer* rx_buffer_plain;
if(iso3_event->type == Iso14443_3aListenerEventTypeFieldOff) {
mf_classic_listener_reset_state(instance);
command = NfcCommandSleep;
} else if(
(iso3_event->type == Iso14443_3aListenerEventTypeReceivedData) ||
(iso3_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame)) {
if(instance->comm_state == MfClassicListenerCommStateEncrypted) {
if(instance->state == MfClassicListenerStateAuthComplete) {
crypto1_decrypt(
instance->crypto, iso3_event->data->buffer, instance->rx_plain_buffer);
rx_buffer_plain = instance->rx_plain_buffer;
if(iso14443_crc_check(Iso14443CrcTypeA, rx_buffer_plain)) {
iso14443_crc_trim(rx_buffer_plain);
}
} else {
rx_buffer_plain = iso3_event->data->buffer;
}
} else {
rx_buffer_plain = iso3_event->data->buffer;
}
MfClassicListenerCommand mfc_command = MfClassicListenerCommandNack;
if(instance->cmd_in_progress) {
mfc_command =
mf_classic_listener_cmd_handlers[instance->current_cmd_idx]
.handler[instance->current_cmd_handler_idx](instance, rx_buffer_plain);
} else {
for(size_t i = 0; i < COUNT_OF(mf_classic_listener_cmd_handlers); i++) {
if(bit_buffer_get_size(rx_buffer_plain) !=
mf_classic_listener_cmd_handlers[i].cmd_len_bits) {
continue;
}
if(bit_buffer_get_byte(rx_buffer_plain, 0) !=
mf_classic_listener_cmd_handlers[i].cmd_start_byte) {
continue;
}
instance->current_cmd_idx = i;
instance->current_cmd_handler_idx = 0;
mfc_command =
mf_classic_listener_cmd_handlers[i].handler[0](instance, rx_buffer_plain);
break;
}
}
if(mfc_command == MfClassicListenerCommandAck) {
mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_ACK);
} else if(mfc_command == MfClassicListenerCommandNack) {
mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_NACK);
} else if(mfc_command == MfClassicListenerCommandSilent) {
command = NfcCommandReset;
} else if(mfc_command == MfClassicListenerCommandSleep) {
command = NfcCommandSleep;
}
} else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) {
mf_classic_listener_reset_state(instance);
}
return command;
}
MfClassicListener*
mf_classic_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, MfClassicData* data) {
MfClassicListener* instance = malloc(sizeof(MfClassicListener));
instance->iso14443_3a_listener = iso14443_3a_listener;
instance->data = data;
mf_classic_listener_prepare_emulation(instance);
instance->crypto = crypto1_alloc();
instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->mfc_event.data = &instance->mfc_event_data;
instance->generic_event.protocol = NfcProtocolMfClassic;
instance->generic_event.event_data = &instance->mfc_event;
instance->generic_event.instance = instance;
return instance;
}
void mf_classic_listener_free(MfClassicListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->crypto);
furi_assert(instance->rx_plain_buffer);
furi_assert(instance->tx_encrypted_buffer);
furi_assert(instance->tx_plain_buffer);
crypto1_free(instance->crypto);
bit_buffer_free(instance->rx_plain_buffer);
bit_buffer_free(instance->tx_encrypted_buffer);
bit_buffer_free(instance->tx_plain_buffer);
free(instance);
}
void mf_classic_listener_set_callback(
MfClassicListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
const MfClassicData* mf_classic_listener_get_data(const MfClassicListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
const NfcListenerBase mf_classic_listener = {
.alloc = (NfcListenerAlloc)mf_classic_listener_alloc,
.free = (NfcListenerFree)mf_classic_listener_free,
.set_callback = (NfcListenerSetCallback)mf_classic_listener_set_callback,
.get_data = (NfcListenerGetData)mf_classic_listener_get_data,
.run = (NfcListenerRun)mf_classic_listener_run,
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include "mf_classic.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct MfClassicListener MfClassicListener;
typedef enum {
MfClassicListenerEventTypeAuthContextPartCollected,
MfClassicListenerEventTypeAuthContextFullCollected,
} MfClassicListenerEventType;
typedef union {
MfClassicAuthContext auth_context;
} MfClassicListenerEventData;
typedef struct {
MfClassicListenerEventType type;
MfClassicListenerEventData* data;
} MfClassicListenerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,13 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const NfcListenerBase mf_classic_listener;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,62 @@
#pragma once
#include "mf_classic_listener.h"
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>
#include <nfc/protocols/nfc_generic_event.h>
#include "crypto1.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MfClassicListenerCommandProcessed,
MfClassicListenerCommandAck,
MfClassicListenerCommandNack,
MfClassicListenerCommandSilent,
MfClassicListenerCommandSleep,
} MfClassicListenerCommand;
typedef enum {
MfClassicListenerStateIdle,
MfClassicListenerStateAuthComplete,
} MfClassicListenerState;
typedef enum {
MfClassicListenerCommStatePlain,
MfClassicListenerCommStateEncrypted,
} MfClassicListenerCommState;
struct MfClassicListener {
Iso14443_3aListener* iso14443_3a_listener;
MfClassicListenerState state;
MfClassicListenerCommState comm_state;
MfClassicData* data;
BitBuffer* tx_plain_buffer;
BitBuffer* tx_encrypted_buffer;
BitBuffer* rx_plain_buffer;
Crypto1* crypto;
MfClassicAuthContext auth_context;
// Value operation data
int32_t transfer_value;
MfClassicValueCommand value_cmd;
NfcGenericEvent generic_event;
MfClassicListenerEvent mfc_event;
MfClassicListenerEventData mfc_event_data;
NfcGenericCallback callback;
void* context;
bool cmd_in_progress;
size_t current_cmd_idx;
size_t current_cmd_handler_idx;
size_t total_block_num;
};
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,951 @@
#include "mf_classic_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "MfClassicPoller"
#define MF_CLASSIC_MAX_BUFF_SIZE (64)
typedef NfcCommand (*MfClassicPollerReadHandler)(MfClassicPoller* instance);
MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {
furi_assert(iso14443_3a_poller);
MfClassicPoller* instance = malloc(sizeof(MfClassicPoller));
instance->iso14443_3a_poller = iso14443_3a_poller;
instance->data = mf_classic_alloc();
instance->crypto = crypto1_alloc();
instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE);
instance->current_type_check = MfClassicType4k;
instance->mfc_event.data = &instance->mfc_event_data;
instance->general_event.protocol = NfcProtocolMfClassic;
instance->general_event.event_data = &instance->mfc_event;
instance->general_event.instance = instance;
return instance;
}
void mf_classic_poller_free(MfClassicPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->crypto);
furi_assert(instance->tx_plain_buffer);
furi_assert(instance->rx_plain_buffer);
furi_assert(instance->tx_encrypted_buffer);
furi_assert(instance->rx_encrypted_buffer);
mf_classic_free(instance->data);
crypto1_free(instance->crypto);
bit_buffer_free(instance->tx_plain_buffer);
bit_buffer_free(instance->rx_plain_buffer);
bit_buffer_free(instance->tx_encrypted_buffer);
bit_buffer_free(instance->rx_encrypted_buffer);
free(instance);
}
static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance) {
MfClassicPollerEventDataUpdate* data_update = &instance->mfc_event_data.data_update;
mf_classic_get_read_sectors_and_keys(
instance->data, &data_update->sectors_read, &data_update->keys_found);
data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector;
instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate;
return instance->callback(instance->general_event, instance->context);
}
static void mf_classic_poller_check_key_b_is_readable(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicBlock* data) {
do {
if(!mf_classic_is_sector_trailer(block_num)) break;
if(!mf_classic_is_allowed_access(
instance->data, block_num, MfClassicKeyTypeA, MfClassicActionKeyBRead))
break;
MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data;
uint64_t key_b = nfc_util_bytes2num(sec_tr->key_b.data, sizeof(MfClassicKey));
uint8_t sector_num = mf_classic_get_sector_by_block(block_num);
mf_classic_set_key_found(instance->data, sector_num, MfClassicKeyTypeB, key_b);
} while(false);
}
NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {
NfcCommand command = NfcCommandReset;
if(instance->current_type_check == MfClassicType4k) {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
MfClassicError error = mf_classic_async_get_nt(instance, 254, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType4k;
instance->state = MfClassicPollerStateStart;
instance->current_type_check = MfClassicType4k;
FURI_LOG_D(TAG, "4K detected");
} else {
instance->current_type_check = MfClassicType1k;
}
} else if(instance->current_type_check == MfClassicType1k) {
MfClassicError error = mf_classic_async_get_nt(instance, 62, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType1k;
FURI_LOG_D(TAG, "1K detected");
} else {
instance->data->type = MfClassicTypeMini;
FURI_LOG_D(TAG, "Mini detected");
}
instance->current_type_check = MfClassicType4k;
instance->state = MfClassicPollerStateStart;
}
return command;
}
NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type);
memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext));
instance->mfc_event.type = MfClassicPollerEventTypeRequestMode;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttack) {
mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);
instance->state = MfClassicPollerStateRequestKey;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) {
instance->state = MfClassicPollerStateRequestReadSector;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) {
instance->state = MfClassicPollerStateRequestSectorTrailer;
} else {
furi_crash("Invalid mode selected");
}
return command;
}
NfcCommand mf_classic_poller_handler_request_sector_trailer(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
if(write_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateSuccess;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeRequestSectorTrailer;
instance->mfc_event_data.sec_tr_data.sector_num = write_ctx->current_sector;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.sec_tr_data.sector_trailer_provided) {
instance->state = MfClassicPollerStateCheckWriteConditions;
memcpy(
&write_ctx->sec_tr,
&instance->mfc_event_data.sec_tr_data.sector_trailer,
sizeof(MfClassicSectorTrailer));
write_ctx->current_block =
MAX(1, mf_classic_get_first_block_num_of_sector(write_ctx->current_sector));
} else {
write_ctx->current_sector++;
}
}
return command;
}
NfcCommand mf_classic_handler_check_write_conditions(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
MfClassicSectorTrailer* sec_tr = &write_ctx->sec_tr;
do {
// Check last block in sector to write
uint8_t sec_tr_block_num =
mf_classic_get_sector_trailer_num_by_sector(write_ctx->current_sector);
if(write_ctx->current_block == sec_tr_block_num) {
write_ctx->current_sector++;
instance->state = MfClassicPollerStateRequestSectorTrailer;
break;
}
// Check write and read access
if(mf_classic_is_allowed_access_data_block(
sec_tr, write_ctx->current_block, MfClassicKeyTypeA, MfClassicActionDataWrite)) {
write_ctx->key_type_write = MfClassicKeyTypeA;
} else if(mf_classic_is_allowed_access_data_block(
sec_tr,
write_ctx->current_block,
MfClassicKeyTypeB,
MfClassicActionDataWrite)) {
write_ctx->key_type_write = MfClassicKeyTypeB;
} else if(mf_classic_is_value_block(sec_tr, write_ctx->current_block)) {
write_ctx->is_value_block = true;
} else {
FURI_LOG_D(TAG, "Not allowed to write block %d", write_ctx->current_block);
write_ctx->current_block++;
break;
}
if(mf_classic_is_allowed_access_data_block(
sec_tr,
write_ctx->current_block,
write_ctx->key_type_write,
MfClassicActionDataRead)) {
write_ctx->key_type_read = write_ctx->key_type_write;
} else {
write_ctx->key_type_read = write_ctx->key_type_write == MfClassicKeyTypeA ?
MfClassicKeyTypeB :
MfClassicKeyTypeA;
if(!mf_classic_is_allowed_access_data_block(
sec_tr,
write_ctx->current_block,
write_ctx->key_type_read,
MfClassicActionDataRead)) {
FURI_LOG_D(TAG, "Not allowed to read block %d", write_ctx->current_block);
write_ctx->current_block++;
break;
}
}
write_ctx->need_halt_before_write =
(write_ctx->key_type_read != write_ctx->key_type_write);
instance->state = MfClassicPollerStateReadBlock;
} while(false);
return command;
}
NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
MfClassicKey* auth_key = write_ctx->key_type_read == MfClassicKeyTypeA ?
&write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error = MfClassicErrorNone;
do {
// Authenticate to sector
error = mf_classic_async_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
// Read block from tag
error =
mf_classic_async_read_block(instance, write_ctx->current_block, &write_ctx->tag_block);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
if(write_ctx->is_value_block) {
mf_classic_async_halt(instance);
instance->state = MfClassicPollerStateWriteValueBlock;
} else {
if(write_ctx->need_halt_before_write) {
mf_classic_async_halt(instance);
}
instance->state = MfClassicPollerStateWriteBlock;
}
} while(false);
return command;
}
NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
MfClassicKey* auth_key = write_ctx->key_type_write == MfClassicKeyTypeA ?
&write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error = MfClassicErrorNone;
do {
// Request block to write
instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock;
instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfc_event_data.write_block_data.write_block_provided) break;
// Compare tag and saved block
if(memcmp(
write_ctx->tag_block.data,
instance->mfc_event_data.write_block_data.write_block.data,
sizeof(MfClassicBlock)) == 0) {
FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block);
break;
}
// Reauth if necessary
if(write_ctx->need_halt_before_write) {
error = mf_classic_async_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL);
if(error != MfClassicErrorNone) {
FURI_LOG_D(
TAG, "Failed to auth to block %d for writing", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
}
// Write block
error = mf_classic_async_write_block(
instance,
write_ctx->current_block,
&instance->mfc_event_data.write_block_data.write_block);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to write block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
break;
}
} while(false);
mf_classic_async_halt(instance);
write_ctx->current_block++;
instance->state = MfClassicPollerStateCheckWriteConditions;
return command;
}
NfcCommand mf_classic_poller_handler_write_value_block(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx;
do {
// Request block to write
instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock;
instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block;
command = instance->callback(instance->general_event, instance->context);
if(!instance->mfc_event_data.write_block_data.write_block_provided) break;
// Compare tag and saved block
if(memcmp(
write_ctx->tag_block.data,
instance->mfc_event_data.write_block_data.write_block.data,
sizeof(MfClassicBlock)) == 0) {
FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block);
break;
}
bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeA,
MfClassicActionDataInc);
bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeB,
MfClassicActionDataInc);
bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeA,
MfClassicActionDataDec);
bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block(
&write_ctx->sec_tr,
write_ctx->current_block,
MfClassicKeyTypeB,
MfClassicActionDataDec);
int32_t source_value = 0;
int32_t target_value = 0;
if(!mf_classic_block_to_value(
&instance->mfc_event_data.write_block_data.write_block, &source_value, NULL))
break;
if(!mf_classic_block_to_value(&write_ctx->tag_block, &target_value, NULL)) break;
MfClassicKeyType auth_key_type = MfClassicKeyTypeA;
MfClassicValueCommand value_cmd = MfClassicValueCommandIncrement;
int32_t diff = source_value - target_value;
if(diff > 0) {
if(key_a_inc_allowed) {
auth_key_type = MfClassicKeyTypeA;
value_cmd = MfClassicValueCommandIncrement;
} else if(key_b_inc_allowed) {
auth_key_type = MfClassicKeyTypeB;
value_cmd = MfClassicValueCommandIncrement;
} else {
FURI_LOG_D(TAG, "Unable to increment value block");
break;
}
} else {
if(key_a_dec_allowed) {
auth_key_type = MfClassicKeyTypeA;
value_cmd = MfClassicValueCommandDecrement;
diff *= -1;
} else if(key_b_dec_allowed) {
auth_key_type = MfClassicKeyTypeB;
value_cmd = MfClassicValueCommandDecrement;
diff *= -1;
} else {
FURI_LOG_D(TAG, "Unable to decrement value block");
break;
}
}
MfClassicKey* key = (auth_key_type == MfClassicKeyTypeA) ? &write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error =
mf_classic_async_auth(instance, write_ctx->current_block, key, auth_key_type, NULL);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_value_cmd(instance, write_ctx->current_block, value_cmd, diff);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_value_transfer(instance, write_ctx->current_block);
if(error != MfClassicErrorNone) break;
} while(false);
mf_classic_async_halt(instance);
write_ctx->is_value_block = false;
write_ctx->current_block++;
instance->state = MfClassicPollerStateCheckWriteConditions;
return command;
}
NfcCommand mf_classic_poller_handler_request_read_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx;
MfClassicPollerEventDataReadSectorRequest* sec_read =
&instance->mfc_event_data.read_sector_request_data;
instance->mfc_event.type = MfClassicPollerEventTypeRequestReadSector;
command = instance->callback(instance->general_event, instance->context);
if(!sec_read->key_provided) {
instance->state = MfClassicPollerStateSuccess;
} else {
sec_read_ctx->current_sector = sec_read->sector_num;
sec_read_ctx->key = sec_read->key;
sec_read_ctx->key_type = sec_read->key_type;
sec_read_ctx->current_block =
mf_classic_get_first_block_num_of_sector(sec_read->sector_num);
sec_read_ctx->auth_passed = false;
instance->state = MfClassicPollerStateReadSectorBlocks;
}
return command;
}
NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx;
do {
MfClassicError error = MfClassicErrorNone;
if(!sec_read_ctx->auth_passed) {
uint64_t key = nfc_util_bytes2num(sec_read_ctx->key.data, sizeof(MfClassicKey));
FURI_LOG_D(
TAG,
"Auth to block %d with key %c: %06llx",
sec_read_ctx->current_block,
sec_read_ctx->key_type == MfClassicKeyTypeA ? 'A' : 'B',
key);
error = mf_classic_async_auth(
instance,
sec_read_ctx->current_block,
&sec_read_ctx->key,
sec_read_ctx->key_type,
NULL);
if(error != MfClassicErrorNone) break;
sec_read_ctx->auth_passed = true;
if(!mf_classic_is_key_found(
instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type)) {
mf_classic_set_key_found(
instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type, key);
}
}
if(mf_classic_is_block_read(instance->data, sec_read_ctx->current_block)) break;
FURI_LOG_D(TAG, "Reading block %d", sec_read_ctx->current_block);
MfClassicBlock read_block = {};
error = mf_classic_async_read_block(instance, sec_read_ctx->current_block, &read_block);
if(error == MfClassicErrorNone) {
mf_classic_set_block_read(instance->data, sec_read_ctx->current_block, &read_block);
if(sec_read_ctx->key_type == MfClassicKeyTypeA) {
mf_classic_poller_check_key_b_is_readable(
instance, sec_read_ctx->current_block, &read_block);
}
} else {
mf_classic_async_halt(instance);
sec_read_ctx->auth_passed = false;
}
} while(false);
uint8_t sec_tr_num = mf_classic_get_sector_trailer_num_by_sector(sec_read_ctx->current_sector);
sec_read_ctx->current_block++;
if(sec_read_ctx->current_block > sec_tr_num) {
mf_classic_async_halt(instance);
instance->state = MfClassicPollerStateRequestReadSector;
}
return command;
}
NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
instance->mfc_event.type = MfClassicPollerEventTypeRequestKey;
command = instance->callback(instance->general_event, instance->context);
if(instance->mfc_event_data.key_request_data.key_provided) {
dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key;
instance->state = MfClassicPollerStateAuthKeyA;
} else {
instance->state = MfClassicPollerStateNextSector;
}
return command;
}
NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateAuthKeyB;
} else {
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);
uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key);
MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateReadSector;
} else {
mf_classic_async_halt(instance);
instance->state = MfClassicPollerStateAuthKeyB;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) {
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateNextSector;
} else {
instance->state = MfClassicPollerStateRequestKey;
}
} else {
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector);
uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key);
MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeB;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateReadSector;
} else {
mf_classic_async_halt(instance);
instance->state = MfClassicPollerStateRequestKey;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
dict_attack_ctx->current_sector++;
if(dict_attack_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateSuccess;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeNextSector;
instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateRequestKey;
}
return command;
}
NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicError error = MfClassicErrorNone;
uint8_t block_num = dict_attack_ctx->current_block;
MfClassicBlock block = {};
do {
if(mf_classic_is_block_read(instance->data, block_num)) break;
if(!dict_attack_ctx->auth_passed) {
error = mf_classic_async_auth(
instance,
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateNextSector;
FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector");
break;
}
}
FURI_LOG_D(TAG, "Reading block %d", block_num);
error = mf_classic_async_read_block(instance, block_num, &block);
if(error != MfClassicErrorNone) {
mf_classic_async_halt(instance);
dict_attack_ctx->auth_passed = false;
FURI_LOG_D(TAG, "Failed to read block %d", block_num);
} else {
mf_classic_set_block_read(instance->data, block_num, &block);
if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {
mf_classic_poller_check_key_b_is_readable(instance, block_num, &block);
}
}
} while(false);
uint8_t sec_tr_block_num =
mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->current_sector);
dict_attack_ctx->current_block++;
if(dict_attack_ctx->current_block > sec_tr_block_num) {
mf_classic_poller_handle_data_update(instance);
mf_classic_async_halt(instance);
dict_attack_ctx->auth_passed = false;
if(dict_attack_ctx->current_sector == instance->sectors_total) {
instance->state = MfClassicPollerStateNextSector;
} else {
dict_attack_ctx->reuse_key_sector = dict_attack_ctx->current_sector;
instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;
instance->mfc_event_data.key_attack_data.current_sector =
dict_attack_ctx->reuse_key_sector;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateKeyReuseStart;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_start(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {
dict_attack_ctx->current_key_type = MfClassicKeyTypeB;
instance->state = MfClassicPollerStateKeyReuseAuthKeyB;
} else {
dict_attack_ctx->reuse_key_sector++;
if(dict_attack_ctx->reuse_key_sector == instance->sectors_total) {
instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateRequestKey;
} else {
instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart;
instance->mfc_event_data.key_attack_data.current_sector =
dict_attack_ctx->reuse_key_sector;
command = instance->callback(instance->general_event, instance->context);
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
instance->state = MfClassicPollerStateKeyReuseAuthKeyA;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA)) {
instance->state = MfClassicPollerStateKeyReuseStart;
} else {
uint8_t block =
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key);
MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeA;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateKeyReuseReadSector;
} else {
mf_classic_async_halt(instance);
dict_attack_ctx->auth_passed = false;
instance->state = MfClassicPollerStateKeyReuseStart;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(mf_classic_is_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB)) {
instance->state = MfClassicPollerStateKeyReuseStart;
} else {
uint8_t block =
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey));
FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key);
MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB, key);
command = mf_classic_poller_handle_data_update(instance);
dict_attack_ctx->current_key_type = MfClassicKeyTypeB;
dict_attack_ctx->current_block = block;
dict_attack_ctx->auth_passed = true;
instance->state = MfClassicPollerStateKeyReuseReadSector;
} else {
mf_classic_async_halt(instance);
dict_attack_ctx->auth_passed = false;
instance->state = MfClassicPollerStateKeyReuseStart;
}
}
return command;
}
NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicError error = MfClassicErrorNone;
uint8_t block_num = dict_attack_ctx->current_block;
MfClassicBlock block = {};
do {
if(mf_classic_is_block_read(instance->data, block_num)) break;
if(!dict_attack_ctx->auth_passed) {
error = mf_classic_async_auth(
instance,
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateKeyReuseStart;
break;
}
}
FURI_LOG_D(TAG, "Reading block %d", block_num);
error = mf_classic_async_read_block(instance, block_num, &block);
if(error != MfClassicErrorNone) {
mf_classic_async_halt(instance);
dict_attack_ctx->auth_passed = false;
FURI_LOG_D(TAG, "Failed to read block %d", block_num);
} else {
mf_classic_set_block_read(instance->data, block_num, &block);
if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) {
mf_classic_poller_check_key_b_is_readable(instance, block_num, &block);
}
}
} while(false);
uint16_t sec_tr_block_num =
mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->reuse_key_sector);
dict_attack_ctx->current_block++;
if(dict_attack_ctx->current_block > sec_tr_block_num) {
mf_classic_async_halt(instance);
dict_attack_ctx->auth_passed = false;
mf_classic_poller_handle_data_update(instance);
instance->state = MfClassicPollerStateKeyReuseStart;
}
return command;
}
NfcCommand mf_classic_poller_handler_success(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->mfc_event.type = MfClassicPollerEventTypeSuccess;
command = instance->callback(instance->general_event, instance->context);
return command;
}
NfcCommand mf_classic_poller_handler_fail(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->mfc_event.type = MfClassicPollerEventTypeFail;
command = instance->callback(instance->general_event, instance->context);
instance->state = MfClassicPollerStateDetectType;
return command;
}
static const MfClassicPollerReadHandler
mf_classic_poller_dict_attack_handler[MfClassicPollerStateNum] = {
[MfClassicPollerStateDetectType] = mf_classic_poller_handler_detect_type,
[MfClassicPollerStateStart] = mf_classic_poller_handler_start,
[MfClassicPollerStateRequestSectorTrailer] =
mf_classic_poller_handler_request_sector_trailer,
[MfClassicPollerStateCheckWriteConditions] = mf_classic_handler_check_write_conditions,
[MfClassicPollerStateReadBlock] = mf_classic_poller_handler_read_block,
[MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block,
[MfClassicPollerStateWriteValueBlock] = mf_classic_poller_handler_write_value_block,
[MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector,
[MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key,
[MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector,
[MfClassicPollerStateReadSectorBlocks] =
mf_classic_poller_handler_request_read_sector_blocks,
[MfClassicPollerStateAuthKeyA] = mf_classic_poller_handler_auth_a,
[MfClassicPollerStateAuthKeyB] = mf_classic_poller_handler_auth_b,
[MfClassicPollerStateReadSector] = mf_classic_poller_handler_read_sector,
[MfClassicPollerStateKeyReuseStart] = mf_classic_poller_handler_key_reuse_start,
[MfClassicPollerStateKeyReuseAuthKeyA] = mf_classic_poller_handler_key_reuse_auth_key_a,
[MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b,
[MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector,
[MfClassicPollerStateSuccess] = mf_classic_poller_handler_success,
[MfClassicPollerStateFail] = mf_classic_poller_handler_fail,
};
NfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(context);
MfClassicPoller* instance = context;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
if(instance->card_state == MfClassicCardStateLost) {
instance->card_state = MfClassicCardStateDetected;
instance->mfc_event.type = MfClassicPollerEventTypeCardDetected;
instance->callback(instance->general_event, instance->context);
}
command = mf_classic_poller_dict_attack_handler[instance->state](instance);
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
if(instance->card_state == MfClassicCardStateDetected) {
instance->card_state = MfClassicCardStateLost;
instance->mfc_event.type = MfClassicPollerEventTypeCardLost;
command = instance->callback(instance->general_event, instance->context);
}
}
return command;
}
bool mf_classic_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(context);
Iso14443_3aPoller* iso3_poller = event.instance;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
bool detected = false;
const uint8_t auth_cmd[] = {MF_CLASSIC_CMD_AUTH_KEY_A, 0};
BitBuffer* tx_buffer = bit_buffer_alloc(COUNT_OF(auth_cmd));
bit_buffer_copy_bytes(tx_buffer, auth_cmd, COUNT_OF(auth_cmd));
BitBuffer* rx_buffer = bit_buffer_alloc(sizeof(MfClassicNt));
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
iso3_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);
if(error == Iso14443_3aErrorWrongCrc) {
if(bit_buffer_get_size_bytes(rx_buffer) == sizeof(MfClassicNt)) {
detected = true;
}
}
}
bit_buffer_free(tx_buffer);
bit_buffer_free(rx_buffer);
return detected;
}
void mf_classic_poller_set_callback(
MfClassicPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
const MfClassicData* mf_classic_poller_get_data(const MfClassicPoller* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
const NfcPollerBase mf_classic_poller = {
.alloc = (NfcPollerAlloc)mf_classic_poller_alloc,
.free = (NfcPollerFree)mf_classic_poller_free,
.set_callback = (NfcPollerSetCallback)mf_classic_poller_set_callback,
.run = (NfcPollerRun)mf_classic_poller_run,
.detect = (NfcPollerDetect)mf_classic_poller_detect,
.get_data = (NfcPollerGetData)mf_classic_poller_get_data,
};

View File

@@ -0,0 +1,109 @@
#pragma once
#include "mf_classic.h"
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct MfClassicPoller MfClassicPoller;
typedef enum {
// Start event
MfClassicPollerEventTypeRequestMode,
// Read with key cache events
MfClassicPollerEventTypeRequestReadSector,
// Write events
MfClassicPollerEventTypeRequestSectorTrailer,
MfClassicPollerEventTypeRequestWriteBlock,
// Dictionary attack events
MfClassicPollerEventTypeRequestKey,
MfClassicPollerEventTypeNextSector,
MfClassicPollerEventTypeDataUpdate,
MfClassicPollerEventTypeFoundKeyA,
MfClassicPollerEventTypeFoundKeyB,
MfClassicPollerEventTypeCardNotDetected,
MfClassicPollerEventTypeKeyAttackStart,
MfClassicPollerEventTypeKeyAttackStop,
MfClassicPollerEventTypeKeyAttackNextSector,
// Common events
MfClassicPollerEventTypeCardDetected,
MfClassicPollerEventTypeCardLost,
MfClassicPollerEventTypeSuccess,
MfClassicPollerEventTypeFail,
} MfClassicPollerEventType;
typedef enum {
MfClassicPollerModeRead,
MfClassicPollerModeWrite,
MfClassicPollerModeDictAttack,
} MfClassicPollerMode;
typedef struct {
MfClassicPollerMode mode;
const MfClassicData* data;
} MfClassicPollerEventDataRequestMode;
typedef struct {
uint8_t current_sector;
} MfClassicPollerEventDataDictAttackNextSector;
typedef struct {
uint8_t sectors_read;
uint8_t keys_found;
uint8_t current_sector;
} MfClassicPollerEventDataUpdate;
typedef struct {
MfClassicKey key;
bool key_provided;
} MfClassicPollerEventDataKeyRequest;
typedef struct {
uint8_t sector_num;
MfClassicKey key;
MfClassicKeyType key_type;
bool key_provided;
} MfClassicPollerEventDataReadSectorRequest;
typedef struct {
uint8_t sector_num;
MfClassicBlock sector_trailer;
bool sector_trailer_provided;
} MfClassicPollerEventDataSectorTrailerRequest;
typedef struct {
uint8_t block_num;
MfClassicBlock write_block;
bool write_block_provided;
} MfClassicPollerEventDataWriteBlockRequest;
typedef struct {
uint8_t current_sector;
} MfClassicPollerEventKeyAttackData;
typedef union {
MfClassicError error;
MfClassicPollerEventDataRequestMode poller_mode;
MfClassicPollerEventDataDictAttackNextSector next_sector_data;
MfClassicPollerEventDataKeyRequest key_request_data;
MfClassicPollerEventDataUpdate data_update;
MfClassicPollerEventDataReadSectorRequest read_sector_request_data;
MfClassicPollerEventKeyAttackData key_attack_data;
MfClassicPollerEventDataSectorTrailerRequest sec_tr_data;
MfClassicPollerEventDataWriteBlockRequest write_block_data;
} MfClassicPollerEventData;
typedef struct {
MfClassicPollerEventType type;
MfClassicPollerEventData* data;
} MfClassicPollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,13 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const NfcPollerBase mf_classic_poller;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,386 @@
#include "mf_classic_poller_i.h"
#include <furi.h>
#include <furi_hal_random.h>
#include <nfc/helpers/iso14443_crc.h>
#define TAG "MfCLassicPoller"
MfClassicError mf_classic_process_error(Iso14443_3aError error) {
MfClassicError ret = MfClassicErrorNone;
switch(error) {
case Iso14443_3aErrorNone:
ret = MfClassicErrorNone;
break;
case Iso14443_3aErrorNotPresent:
ret = MfClassicErrorNotPresent;
break;
case Iso14443_3aErrorColResFailed:
case Iso14443_3aErrorCommunication:
case Iso14443_3aErrorWrongCrc:
ret = MfClassicErrorProtocol;
break;
case Iso14443_3aErrorTimeout:
ret = MfClassicErrorTimeout;
break;
default:
ret = MfClassicErrorProtocol;
break;
}
return ret;
}
MfClassicError mf_classic_async_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
uint8_t auth_cmd[2] = {auth_type, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_plain_buffer,
instance->rx_plain_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorWrongCrc) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) {
ret = MfClassicErrorProtocol;
break;
}
if(nt) {
bit_buffer_write_bytes(instance->rx_plain_buffer, nt->data, sizeof(MfClassicNt));
}
} while(false);
return ret;
}
MfClassicError mf_classic_async_auth(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
MfClassicNt nt = {};
ret = mf_classic_async_get_nt(instance, block_num, key_type, &nt);
if(ret != MfClassicErrorNone) break;
if(data) {
data->nt = nt;
}
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
uint64_t key_num = nfc_util_bytes2num(key->data, sizeof(MfClassicKey));
MfClassicNr nr = {};
furi_hal_random_fill_buf(nr.data, sizeof(MfClassicNr));
crypto1_encrypt_reader_nonce(
instance->crypto, key_num, cuid, nt.data, nr.data, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != 4) {
ret = MfClassicErrorAuth;
}
crypto1_word(instance->crypto, 0, 0);
instance->auth_state = MfClassicAuthStatePassed;
if(data) {
data->nr = nr;
const uint8_t* nr_ar = bit_buffer_get_data(instance->tx_encrypted_buffer);
memcpy(data->ar.data, &nr_ar[4], sizeof(MfClassicAr));
bit_buffer_write_bytes(
instance->rx_encrypted_buffer, data->at.data, sizeof(MfClassicAt));
}
} while(false);
if(ret != MfClassicErrorNone) {
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
}
return ret;
}
MfClassicError mf_classic_async_halt(MfClassicPoller* instance) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t halt_cmd[2] = {MF_CLASSIC_CMD_HALT_MSB, MF_CLASSIC_CMD_HALT_LSB};
bit_buffer_copy_bytes(instance->tx_plain_buffer, halt_cmd, sizeof(halt_cmd));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorTimeout) {
ret = mf_classic_process_error(error);
break;
}
instance->auth_state = MfClassicAuthStateIdle;
instance->iso14443_3a_poller->state = Iso14443_3aPollerStateIdle;
} while(false);
return ret;
}
MfClassicError mf_classic_async_read_block(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicBlock* data) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t read_block_cmd[2] = {MF_CLASSIC_CMD_READ_BLOCK, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, read_block_cmd, sizeof(read_block_cmd));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) !=
(sizeof(MfClassicBlock) + 2)) {
ret = MfClassicErrorProtocol;
break;
}
crypto1_decrypt(
instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_plain_buffer)) {
FURI_LOG_D(TAG, "CRC error");
ret = MfClassicErrorProtocol;
break;
}
iso14443_crc_trim(instance->rx_plain_buffer);
bit_buffer_write_bytes(instance->rx_plain_buffer, data->data, sizeof(MfClassicBlock));
} while(false);
return ret;
}
MfClassicError mf_classic_async_write_block(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicBlock* data) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t write_block_cmd[2] = {MF_CLASSIC_CMD_WRITE_BLOCK, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, write_block_cmd, sizeof(write_block_cmd));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
ret = MfClassicErrorProtocol;
break;
}
crypto1_decrypt(
instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {
FURI_LOG_D(TAG, "Not ACK received");
ret = MfClassicErrorProtocol;
break;
}
bit_buffer_copy_bytes(instance->tx_plain_buffer, data->data, sizeof(MfClassicBlock));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
ret = MfClassicErrorProtocol;
break;
}
crypto1_decrypt(
instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {
FURI_LOG_D(TAG, "Not ACK received");
ret = MfClassicErrorProtocol;
break;
}
} while(false);
return ret;
}
MfClassicError mf_classic_async_value_cmd(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicValueCommand cmd,
int32_t data) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t cmd_value = 0;
if(cmd == MfClassicValueCommandDecrement) {
cmd_value = MF_CLASSIC_CMD_VALUE_DEC;
} else if(cmd == MfClassicValueCommandIncrement) {
cmd_value = MF_CLASSIC_CMD_VALUE_INC;
} else {
cmd_value = MF_CLASSIC_CMD_VALUE_RESTORE;
}
uint8_t value_cmd[2] = {cmd_value, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, value_cmd, sizeof(value_cmd));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
ret = MfClassicErrorProtocol;
break;
}
crypto1_decrypt(
instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {
FURI_LOG_D(TAG, "Not ACK received");
ret = MfClassicErrorProtocol;
break;
}
bit_buffer_copy_bytes(instance->tx_plain_buffer, (uint8_t*)&data, sizeof(data));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
// Command processed if tag doesn't respond
if(error != Iso14443_3aErrorTimeout) {
ret = MfClassicErrorProtocol;
break;
}
ret = MfClassicErrorNone;
} while(false);
return ret;
}
MfClassicError mf_classic_async_value_transfer(MfClassicPoller* instance, uint8_t block_num) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t transfer_cmd[2] = {MF_CLASSIC_CMD_VALUE_TRANSFER, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, transfer_cmd, sizeof(transfer_cmd));
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_encrypted_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) {
ret = MfClassicErrorProtocol;
break;
}
crypto1_decrypt(
instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer);
if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) {
FURI_LOG_D(TAG, "Not ACK received");
ret = MfClassicErrorProtocol;
break;
}
} while(false);
return ret;
}

View File

@@ -0,0 +1,205 @@
#pragma once
#include "mf_classic_poller.h"
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
#include <lib/nfc/helpers/nfc_util.h>
#include "crypto1.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MF_CLASSIC_FWT_FC (60000)
typedef enum {
MfClassicAuthStateIdle,
MfClassicAuthStatePassed,
} MfClassicAuthState;
typedef enum {
MfClassicCardStateDetected,
MfClassicCardStateLost,
} MfClassicCardState;
typedef enum {
MfClassicPollerStateDetectType,
MfClassicPollerStateStart,
// Write states
MfClassicPollerStateRequestSectorTrailer,
MfClassicPollerStateCheckWriteConditions,
MfClassicPollerStateReadBlock,
MfClassicPollerStateWriteBlock,
MfClassicPollerStateWriteValueBlock,
// Read states
MfClassicPollerStateRequestReadSector,
MfClassicPollerStateReadSectorBlocks,
// Dict attack states
MfClassicPollerStateNextSector,
MfClassicPollerStateRequestKey,
MfClassicPollerStateReadSector,
MfClassicPollerStateAuthKeyA,
MfClassicPollerStateAuthKeyB,
MfClassicPollerStateKeyReuseStart,
MfClassicPollerStateKeyReuseAuthKeyA,
MfClassicPollerStateKeyReuseAuthKeyB,
MfClassicPollerStateKeyReuseReadSector,
MfClassicPollerStateSuccess,
MfClassicPollerStateFail,
MfClassicPollerStateNum,
} MfClassicPollerState;
typedef struct {
uint8_t current_sector;
MfClassicSectorTrailer sec_tr;
uint16_t current_block;
bool is_value_block;
MfClassicKeyType key_type_read;
MfClassicKeyType key_type_write;
bool need_halt_before_write;
MfClassicBlock tag_block;
} MfClassicPollerWriteContext;
typedef struct {
uint8_t current_sector;
MfClassicKey current_key;
MfClassicKeyType current_key_type;
bool auth_passed;
uint16_t current_block;
uint8_t reuse_key_sector;
} MfClassicPollerDictAttackContext;
typedef struct {
uint8_t current_sector;
uint16_t current_block;
MfClassicKeyType key_type;
MfClassicKey key;
bool auth_passed;
} MfClassicPollerReadContext;
typedef union {
MfClassicPollerWriteContext write_ctx;
MfClassicPollerDictAttackContext dict_attack_ctx;
MfClassicPollerReadContext read_ctx;
} MfClassicPollerModeContext;
struct MfClassicPoller {
Iso14443_3aPoller* iso14443_3a_poller;
MfClassicPollerState state;
MfClassicAuthState auth_state;
MfClassicCardState card_state;
MfClassicType current_type_check;
uint8_t sectors_total;
MfClassicPollerModeContext mode_ctx;
Crypto1* crypto;
BitBuffer* tx_plain_buffer;
BitBuffer* tx_encrypted_buffer;
BitBuffer* rx_plain_buffer;
BitBuffer* rx_encrypted_buffer;
MfClassicData* data;
NfcGenericEvent general_event;
MfClassicPollerEvent mfc_event;
MfClassicPollerEventData mfc_event_data;
NfcGenericCallback callback;
void* context;
};
typedef struct {
uint8_t block;
MfClassicKeyType key_type;
MfClassicNt nt;
} MfClassicCollectNtContext;
typedef struct {
uint8_t block_num;
MfClassicKey key;
MfClassicKeyType key_type;
MfClassicBlock block;
} MfClassicReadBlockContext;
typedef struct {
uint8_t block_num;
MfClassicKey key;
MfClassicKeyType key_type;
MfClassicBlock block;
} MfClassicWriteBlockContext;
typedef struct {
uint8_t block_num;
MfClassicKey key;
MfClassicKeyType key_type;
int32_t value;
} MfClassicReadValueContext;
typedef struct {
uint8_t block_num;
MfClassicKey key;
MfClassicKeyType key_type;
MfClassicValueCommand value_cmd;
int32_t data;
int32_t new_value;
} MfClassicChangeValueContext;
typedef struct {
MfClassicDeviceKeys keys;
uint8_t current_sector;
} MfClassicReadContext;
typedef union {
MfClassicCollectNtContext collect_nt_context;
MfClassicAuthContext auth_context;
MfClassicReadBlockContext read_block_context;
MfClassicWriteBlockContext write_block_context;
MfClassicReadValueContext read_value_context;
MfClassicChangeValueContext change_value_context;
MfClassicReadContext read_context;
} MfClassicPollerContextData;
MfClassicError mf_classic_process_error(Iso14443_3aError error);
MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller);
void mf_classic_poller_free(MfClassicPoller* instance);
MfClassicError mf_classic_async_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicError mf_classic_async_auth(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicError mf_classic_async_halt(MfClassicPoller* instance);
MfClassicError
mf_classic_async_read_block(MfClassicPoller* instance, uint8_t block_num, MfClassicBlock* data);
MfClassicError mf_classic_async_write_block(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicBlock* data);
MfClassicError mf_classic_async_value_cmd(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicValueCommand cmd,
int32_t data);
MfClassicError mf_classic_async_value_transfer(MfClassicPoller* instance, uint8_t block_num);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,524 @@
#include "mf_classic_poller_i.h"
#include <nfc/nfc_poller.h>
#include <furi.h>
#define TAG "MfClassicPoller"
#define MF_CLASSIC_POLLER_COMPLETE_EVENT (1UL << 0)
typedef enum {
MfClassicPollerCmdTypeCollectNt,
MfClassicPollerCmdTypeAuth,
MfClassicPollerCmdTypeReadBlock,
MfClassicPollerCmdTypeWriteBlock,
MfClassicPollerCmdTypeReadValue,
MfClassicPollerCmdTypeChangeValue,
MfClassicPollerCmdTypeNum,
} MfClassicPollerCmdType;
typedef struct {
MfClassicPollerCmdType cmd_type;
FuriThreadId thread_id;
MfClassicError error;
MfClassicPollerContextData data;
} MfClassicPollerContext;
typedef MfClassicError (
*MfClassicPollerCmdHandler)(MfClassicPoller* poller, MfClassicPollerContextData* data);
static MfClassicError mf_classic_poller_collect_nt_handler(
MfClassicPoller* poller,
MfClassicPollerContextData* data) {
return mf_classic_async_get_nt(
poller,
data->collect_nt_context.block,
data->collect_nt_context.key_type,
&data->collect_nt_context.nt);
}
static MfClassicError
mf_classic_poller_auth_handler(MfClassicPoller* poller, MfClassicPollerContextData* data) {
return mf_classic_async_auth(
poller,
data->auth_context.block_num,
&data->auth_context.key,
data->auth_context.key_type,
&data->auth_context);
}
static MfClassicError mf_classic_poller_read_block_handler(
MfClassicPoller* poller,
MfClassicPollerContextData* data) {
MfClassicError error = MfClassicErrorNone;
do {
error = mf_classic_async_auth(
poller,
data->read_block_context.block_num,
&data->read_block_context.key,
data->read_block_context.key_type,
NULL);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_read_block(
poller, data->read_block_context.block_num, &data->read_block_context.block);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_halt(poller);
if(error != MfClassicErrorNone) break;
} while(false);
return error;
}
static MfClassicError mf_classic_poller_write_block_handler(
MfClassicPoller* poller,
MfClassicPollerContextData* data) {
MfClassicError error = MfClassicErrorNone;
do {
error = mf_classic_async_auth(
poller,
data->read_block_context.block_num,
&data->read_block_context.key,
data->read_block_context.key_type,
NULL);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_write_block(
poller, data->write_block_context.block_num, &data->write_block_context.block);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_halt(poller);
if(error != MfClassicErrorNone) break;
} while(false);
return error;
}
static MfClassicError mf_classic_poller_read_value_handler(
MfClassicPoller* poller,
MfClassicPollerContextData* data) {
MfClassicError error = MfClassicErrorNone;
do {
error = mf_classic_async_auth(
poller,
data->read_value_context.block_num,
&data->read_value_context.key,
data->read_value_context.key_type,
NULL);
if(error != MfClassicErrorNone) break;
MfClassicBlock block = {};
error = mf_classic_async_read_block(poller, data->read_value_context.block_num, &block);
if(error != MfClassicErrorNone) break;
if(!mf_classic_block_to_value(&block, &data->read_value_context.value, NULL)) {
error = MfClassicErrorProtocol;
break;
}
error = mf_classic_async_halt(poller);
if(error != MfClassicErrorNone) break;
} while(false);
return error;
}
static MfClassicError mf_classic_poller_change_value_handler(
MfClassicPoller* poller,
MfClassicPollerContextData* data) {
MfClassicError error = MfClassicErrorNone;
do {
error = mf_classic_async_auth(
poller,
data->change_value_context.block_num,
&data->change_value_context.key,
data->change_value_context.key_type,
NULL);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_value_cmd(
poller,
data->change_value_context.block_num,
data->change_value_context.value_cmd,
data->change_value_context.data);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_value_transfer(poller, data->change_value_context.block_num);
if(error != MfClassicErrorNone) break;
MfClassicBlock block = {};
error = mf_classic_async_read_block(poller, data->change_value_context.block_num, &block);
if(error != MfClassicErrorNone) break;
error = mf_classic_async_halt(poller);
if(error != MfClassicErrorNone) break;
if(!mf_classic_block_to_value(&block, &data->change_value_context.new_value, NULL)) {
error = MfClassicErrorProtocol;
break;
}
} while(false);
return error;
}
static const MfClassicPollerCmdHandler mf_classic_poller_cmd_handlers[MfClassicPollerCmdTypeNum] = {
[MfClassicPollerCmdTypeCollectNt] = mf_classic_poller_collect_nt_handler,
[MfClassicPollerCmdTypeAuth] = mf_classic_poller_auth_handler,
[MfClassicPollerCmdTypeReadBlock] = mf_classic_poller_read_block_handler,
[MfClassicPollerCmdTypeWriteBlock] = mf_classic_poller_write_block_handler,
[MfClassicPollerCmdTypeReadValue] = mf_classic_poller_read_value_handler,
[MfClassicPollerCmdTypeChangeValue] = mf_classic_poller_change_value_handler,
};
static NfcCommand mf_classic_poller_cmd_callback(NfcGenericEvent event, void* context) {
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(event.event_data);
furi_assert(context);
MfClassicPollerContext* poller_context = context;
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
Iso14443_3aPoller* iso14443_3a_poller = event.instance;
MfClassicPoller* mfc_poller = mf_classic_poller_alloc(iso14443_3a_poller);
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
poller_context->error = mf_classic_poller_cmd_handlers[poller_context->cmd_type](
mfc_poller, &poller_context->data);
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
poller_context->error = mf_classic_process_error(iso14443_3a_event->data->error);
}
furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT);
mf_classic_poller_free(mfc_poller);
return NfcCommandStop;
}
static MfClassicError mf_classic_poller_cmd_execute(Nfc* nfc, MfClassicPollerContext* poller_ctx) {
furi_assert(poller_ctx->cmd_type < MfClassicPollerCmdTypeNum);
poller_ctx->thread_id = furi_thread_get_current_id();
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a);
nfc_poller_start(poller, mf_classic_poller_cmd_callback, poller_ctx);
furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever);
furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT);
nfc_poller_stop(poller);
nfc_poller_free(poller);
return poller_ctx->error;
}
MfClassicError mf_classic_poller_collect_nt(
Nfc* nfc,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
furi_assert(nfc);
MfClassicPollerContext poller_context = {
.cmd_type = MfClassicPollerCmdTypeCollectNt,
.data.collect_nt_context.block = block_num,
.data.collect_nt_context.key_type = key_type,
};
MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);
if(error == MfClassicErrorNone) {
if(nt) {
*nt = poller_context.data.collect_nt_context.nt;
}
}
return error;
}
MfClassicError mf_classic_poller_auth(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
furi_assert(nfc);
furi_assert(key);
MfClassicPollerContext poller_context = {
.cmd_type = MfClassicPollerCmdTypeAuth,
.data.auth_context.block_num = block_num,
.data.auth_context.key = *key,
.data.auth_context.key_type = key_type,
};
MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);
if(error == MfClassicErrorNone) {
if(data) {
*data = poller_context.data.auth_context;
}
}
return error;
}
MfClassicError mf_classic_poller_read_block(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicBlock* data) {
furi_assert(nfc);
furi_assert(key);
furi_assert(data);
MfClassicPollerContext poller_context = {
.cmd_type = MfClassicPollerCmdTypeReadBlock,
.data.read_block_context.block_num = block_num,
.data.read_block_context.key = *key,
.data.read_block_context.key_type = key_type,
};
MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);
if(error == MfClassicErrorNone) {
*data = poller_context.data.read_block_context.block;
}
return error;
}
MfClassicError mf_classic_poller_write_block(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicBlock* data) {
furi_assert(nfc);
furi_assert(key);
furi_assert(data);
MfClassicPollerContext poller_context = {
.cmd_type = MfClassicPollerCmdTypeWriteBlock,
.data.write_block_context.block_num = block_num,
.data.write_block_context.key = *key,
.data.write_block_context.key_type = key_type,
.data.write_block_context.block = *data,
};
MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);
return error;
}
MfClassicError mf_classic_poller_read_value(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
int32_t* value) {
furi_assert(nfc);
furi_assert(key);
furi_assert(value);
MfClassicPollerContext poller_context = {
.cmd_type = MfClassicPollerCmdTypeReadValue,
.data.write_block_context.block_num = block_num,
.data.write_block_context.key = *key,
.data.write_block_context.key_type = key_type,
};
MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);
if(error == MfClassicErrorNone) {
*value = poller_context.data.read_value_context.value;
}
return error;
}
MfClassicError mf_classic_poller_change_value(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
int32_t data,
int32_t* new_value) {
furi_assert(nfc);
furi_assert(key);
furi_assert(new_value);
MfClassicValueCommand command = MfClassicValueCommandRestore;
int32_t command_data = 0;
if(data > 0) {
command = MfClassicValueCommandIncrement;
command_data = data;
} else if(data < 0) {
command = MfClassicValueCommandDecrement;
command_data = -data;
}
MfClassicPollerContext poller_context = {
.cmd_type = MfClassicPollerCmdTypeChangeValue,
.data.change_value_context.block_num = block_num,
.data.change_value_context.key = *key,
.data.change_value_context.key_type = key_type,
.data.change_value_context.value_cmd = command,
.data.change_value_context.data = command_data,
};
MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context);
if(error == MfClassicErrorNone) {
*new_value = poller_context.data.change_value_context.new_value;
}
return error;
}
static bool mf_classic_poller_read_get_next_key(
MfClassicReadContext* read_ctx,
uint8_t* sector_num,
MfClassicKey* key,
MfClassicKeyType* key_type) {
bool next_key_found = false;
for(uint8_t i = read_ctx->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) {
if(FURI_BIT(read_ctx->keys.key_a_mask, i)) {
FURI_BIT_CLEAR(read_ctx->keys.key_a_mask, i);
*key = read_ctx->keys.key_a[i];
*key_type = MfClassicKeyTypeA;
*sector_num = i;
next_key_found = true;
break;
}
if(FURI_BIT(read_ctx->keys.key_b_mask, i)) {
FURI_BIT_CLEAR(read_ctx->keys.key_b_mask, i);
*key = read_ctx->keys.key_b[i];
*key_type = MfClassicKeyTypeB;
*sector_num = i;
next_key_found = true;
read_ctx->current_sector = i;
break;
}
}
return next_key_found;
}
NfcCommand mf_classic_poller_read_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.protocol == NfcProtocolMfClassic);
NfcCommand command = NfcCommandContinue;
MfClassicPollerContext* poller_context = context;
MfClassicPollerEvent* mfc_event = event.event_data;
if(mfc_event->type == MfClassicPollerEventTypeCardLost) {
poller_context->error = MfClassicErrorNotPresent;
command = NfcCommandStop;
} else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) {
mfc_event->data->poller_mode.mode = MfClassicPollerModeRead;
} else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) {
MfClassicPollerEventDataReadSectorRequest* req_data =
&mfc_event->data->read_sector_request_data;
MfClassicKey key = {};
MfClassicKeyType key_type = MfClassicKeyTypeA;
uint8_t sector_num = 0;
if(mf_classic_poller_read_get_next_key(
&poller_context->data.read_context, &sector_num, &key, &key_type)) {
req_data->sector_num = sector_num;
req_data->key = key;
req_data->key_type = key_type;
req_data->key_provided = true;
} else {
req_data->key_provided = false;
}
} else if(mfc_event->type == MfClassicPollerEventTypeSuccess) {
command = NfcCommandStop;
}
if(command == NfcCommandStop) {
furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT);
}
return command;
}
MfClassicError
mf_classic_poller_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data) {
furi_assert(nfc);
furi_assert(keys);
furi_assert(data);
MfClassicError error = MfClassicErrorNone;
MfClassicPollerContext poller_context = {};
poller_context.thread_id = furi_thread_get_current_id();
poller_context.data.read_context.keys = *keys;
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic);
nfc_poller_start(poller, mf_classic_poller_read_callback, &poller_context);
furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever);
furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT);
nfc_poller_stop(poller);
if(poller_context.error != MfClassicErrorNone) {
error = poller_context.error;
} else {
const MfClassicData* mfc_data = nfc_poller_get_data(poller);
uint8_t sectors_read = 0;
uint8_t keys_found = 0;
mf_classic_get_read_sectors_and_keys(mfc_data, &sectors_read, &keys_found);
if((sectors_read > 0) || (keys_found > 0)) {
mf_classic_copy(data, mfc_data);
} else {
error = MfClassicErrorNotPresent;
}
}
nfc_poller_free(poller);
return error;
}
MfClassicError mf_classic_poller_detect_type(Nfc* nfc, MfClassicType* type) {
furi_assert(nfc);
furi_assert(type);
MfClassicError error = MfClassicErrorNone;
const uint8_t mf_classic_verify_block[MfClassicTypeNum] = {
[MfClassicTypeMini] = 0,
[MfClassicType1k] = 62,
[MfClassicType4k] = 254,
};
size_t i = 0;
for(i = 0; i < COUNT_OF(mf_classic_verify_block); i++) {
error = mf_classic_poller_collect_nt(
nfc, mf_classic_verify_block[MfClassicTypeNum - i - 1], MfClassicKeyTypeA, NULL);
if(error == MfClassicErrorNone) {
*type = MfClassicTypeNum - i - 1;
break;
}
}
return error;
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include "mf_classic.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
MfClassicError mf_classic_poller_collect_nt(
Nfc* nfc,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicError mf_classic_poller_auth(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicError mf_classic_poller_read_block(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicBlock* data);
MfClassicError mf_classic_poller_write_block(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicBlock* data);
MfClassicError mf_classic_poller_read_value(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
int32_t* value);
MfClassicError mf_classic_poller_change_value(
Nfc* nfc,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
int32_t data,
int32_t* new_value);
MfClassicError mf_classic_poller_detect_type(Nfc* nfc, MfClassicType* type);
MfClassicError
mf_classic_poller_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,290 @@
#include "mf_desfire_i.h"
#include <furi.h>
#define MF_DESFIRE_PROTOCOL_NAME "Mifare DESFire"
const NfcDeviceBase nfc_device_mf_desfire = {
.protocol_name = MF_DESFIRE_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)mf_desfire_alloc,
.free = (NfcDeviceFree)mf_desfire_free,
.reset = (NfcDeviceReset)mf_desfire_reset,
.copy = (NfcDeviceCopy)mf_desfire_copy,
.verify = (NfcDeviceVerify)mf_desfire_verify,
.load = (NfcDeviceLoad)mf_desfire_load,
.save = (NfcDeviceSave)mf_desfire_save,
.is_equal = (NfcDeviceEqual)mf_desfire_is_equal,
.get_name = (NfcDeviceGetName)mf_desfire_get_device_name,
.get_uid = (NfcDeviceGetUid)mf_desfire_get_uid,
.set_uid = (NfcDeviceSetUid)mf_desfire_set_uid,
.get_base_data = (NfcDeviceGetBaseData)mf_desfire_get_base_data,
};
MfDesfireData* mf_desfire_alloc() {
MfDesfireData* data = malloc(sizeof(MfDesfireData));
data->iso14443_4a_data = iso14443_4a_alloc();
data->master_key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);
data->application_ids = simple_array_alloc(&mf_desfire_app_id_array_config);
data->applications = simple_array_alloc(&mf_desfire_application_array_config);
return data;
}
void mf_desfire_free(MfDesfireData* data) {
furi_assert(data);
mf_desfire_reset(data);
simple_array_free(data->applications);
simple_array_free(data->application_ids);
simple_array_free(data->master_key_versions);
iso14443_4a_free(data->iso14443_4a_data);
free(data);
}
void mf_desfire_reset(MfDesfireData* data) {
furi_assert(data);
iso14443_4a_reset(data->iso14443_4a_data);
memset(&data->version, 0, sizeof(MfDesfireVersion));
memset(&data->free_memory, 0, sizeof(MfDesfireFreeMemory));
simple_array_reset(data->master_key_versions);
simple_array_reset(data->application_ids);
simple_array_reset(data->applications);
}
void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other) {
furi_assert(data);
furi_assert(other);
mf_desfire_reset(data);
iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);
data->version = other->version;
data->free_memory = other->free_memory;
data->master_key_settings = other->master_key_settings;
simple_array_copy(data->master_key_versions, other->master_key_versions);
simple_array_copy(data->application_ids, other->application_ids);
simple_array_copy(data->applications, other->applications);
}
bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal_str(device_type, MF_DESFIRE_PROTOCOL_NAME);
}
bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
FuriString* prefix = furi_string_alloc();
bool success = false;
do {
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
if(!mf_desfire_version_load(&data->version, ff)) break;
if(!mf_desfire_free_memory_load(&data->free_memory, ff)) break;
if(!mf_desfire_key_settings_load(
&data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff))
break;
const uint32_t master_key_version_count = data->master_key_settings.max_keys;
simple_array_init(data->master_key_versions, master_key_version_count);
uint32_t i;
for(i = 0; i < master_key_version_count; ++i) {
if(!mf_desfire_key_version_load(
simple_array_get(data->master_key_versions, i),
MF_DESFIRE_FFF_PICC_PREFIX,
i,
ff))
break;
}
if(i != master_key_version_count) break;
uint32_t application_count;
if(!mf_desfire_application_count_load(&application_count, ff)) break;
if(application_count > 0) {
simple_array_init(data->application_ids, application_count);
if(!mf_desfire_application_ids_load(
simple_array_get_data(data->application_ids), application_count, ff))
break;
simple_array_init(data->applications, application_count);
for(i = 0; i < application_count; ++i) {
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
furi_string_printf(
prefix,
"%s %02x%02x%02x",
MF_DESFIRE_FFF_APP_PREFIX,
app_id->data[0],
app_id->data[1],
app_id->data[2]);
if(!mf_desfire_application_load(
simple_array_get(data->applications, i), furi_string_get_cstr(prefix), ff))
break;
}
if(i != application_count) break;
}
success = true;
} while(false);
furi_string_free(prefix);
return success;
}
bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) {
furi_assert(data);
FuriString* prefix = furi_string_alloc();
bool success = false;
do {
if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, MF_DESFIRE_PROTOCOL_NAME " specific data"))
break;
if(!mf_desfire_version_save(&data->version, ff)) break;
if(!mf_desfire_free_memory_save(&data->free_memory, ff)) break;
if(!mf_desfire_key_settings_save(
&data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff))
break;
const uint32_t master_key_version_count =
simple_array_get_count(data->master_key_versions);
uint32_t i;
for(i = 0; i < master_key_version_count; ++i) {
if(!mf_desfire_key_version_save(
simple_array_cget(data->master_key_versions, i),
MF_DESFIRE_FFF_PICC_PREFIX,
i,
ff))
break;
}
if(i != master_key_version_count) break;
const uint32_t application_count = simple_array_get_count(data->application_ids);
if(!mf_desfire_application_count_save(&application_count, ff)) break;
if(application_count > 0) {
if(!mf_desfire_application_ids_save(
simple_array_cget_data(data->application_ids), application_count, ff))
break;
for(i = 0; i < application_count; ++i) {
const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i);
furi_string_printf(
prefix,
"%s %02x%02x%02x",
MF_DESFIRE_FFF_APP_PREFIX,
app_id->data[0],
app_id->data[1],
app_id->data[2]);
const MfDesfireApplication* app = simple_array_cget(data->applications, i);
if(!mf_desfire_application_save(app, furi_string_get_cstr(prefix), ff)) break;
}
if(i != application_count) break;
}
success = true;
} while(false);
furi_string_free(prefix);
return success;
}
bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other) {
furi_assert(data);
furi_assert(other);
return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&
memcmp(&data->version, &other->version, sizeof(MfDesfireVersion)) == 0 &&
memcmp(&data->free_memory, &other->free_memory, sizeof(MfDesfireFreeMemory)) == 0 &&
memcmp(
&data->master_key_settings,
&other->master_key_settings,
sizeof(MfDesfireKeySettings)) == 0 &&
simple_array_is_equal(data->master_key_versions, other->master_key_versions) &&
simple_array_is_equal(data->application_ids, other->application_ids) &&
simple_array_is_equal(data->applications, other->applications);
}
const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return MF_DESFIRE_PROTOCOL_NAME;
}
const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len) {
furi_assert(data);
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
}
bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
}
Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data) {
furi_assert(data);
return data->iso14443_4a_data;
}
const MfDesfireApplication*
mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id) {
MfDesfireApplication* app = NULL;
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
const MfDesfireApplicationId* current_app_id = simple_array_cget(data->application_ids, i);
if(memcmp(app_id, current_app_id, sizeof(MfDesfireApplicationId)) == 0) {
app = simple_array_get(data->applications, i);
}
}
return app;
}
const MfDesfireFileSettings*
mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {
MfDesfireFileSettings* file_settings = NULL;
for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {
const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i);
if(*file_id == *current_file_id) {
file_settings = simple_array_get(data->file_settings, i);
}
}
return file_settings;
}
const MfDesfireFileData*
mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {
MfDesfireFileData* file_data = NULL;
for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {
const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i);
if(*file_id == *current_file_id) {
file_data = simple_array_get(data->file_data, i);
}
}
return file_data;
}

View File

@@ -0,0 +1,191 @@
#pragma once
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#include <lib/toolbox/simple_array.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MF_DESFIRE_CMD_GET_VERSION (0x60)
#define MF_DESFIRE_CMD_GET_FREE_MEMORY (0x6E)
#define MF_DESFIRE_CMD_GET_KEY_SETTINGS (0x45)
#define MF_DESFIRE_CMD_GET_KEY_VERSION (0x64)
#define MF_DESFIRE_CMD_GET_APPLICATION_IDS (0x6A)
#define MF_DESFIRE_CMD_SELECT_APPLICATION (0x5A)
#define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F)
#define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5)
#define MF_DESFIRE_CMD_READ_DATA (0xBD)
#define MF_DESFIRE_CMD_GET_VALUE (0x6C)
#define MF_DESFIRE_CMD_READ_RECORDS (0xBB)
#define MF_DESFIRE_FLAG_HAS_NEXT (0xAF)
#define MF_DESFIRE_MAX_KEYS (14)
#define MF_DESFIRE_MAX_FILES (32)
#define MF_DESFIRE_UID_SIZE (7)
#define MF_DESFIRE_BATCH_SIZE (5)
#define MF_DESFIRE_APP_ID_SIZE (3)
typedef struct {
uint8_t hw_vendor;
uint8_t hw_type;
uint8_t hw_subtype;
uint8_t hw_major;
uint8_t hw_minor;
uint8_t hw_storage;
uint8_t hw_proto;
uint8_t sw_vendor;
uint8_t sw_type;
uint8_t sw_subtype;
uint8_t sw_major;
uint8_t sw_minor;
uint8_t sw_storage;
uint8_t sw_proto;
uint8_t uid[MF_DESFIRE_UID_SIZE];
uint8_t batch[MF_DESFIRE_BATCH_SIZE];
uint8_t prod_week;
uint8_t prod_year;
} MfDesfireVersion;
typedef struct {
uint32_t bytes_free;
bool is_present;
} MfDesfireFreeMemory; // EV1+ only
typedef struct {
bool is_master_key_changeable;
bool is_free_directory_list;
bool is_free_create_delete;
bool is_config_changeable;
uint8_t change_key_id;
uint8_t max_keys;
uint8_t flags;
} MfDesfireKeySettings;
typedef uint8_t MfDesfireKeyVersion;
typedef struct {
MfDesfireKeySettings key_settings;
SimpleArray* key_versions;
} MfDesfireKeyConfiguration;
typedef enum {
MfDesfireFileTypeStandard = 0,
MfDesfireFileTypeBackup = 1,
MfDesfireFileTypeValue = 2,
MfDesfireFileTypeLinearRecord = 3,
MfDesfireFileTypeCyclicRecord = 4,
} MfDesfireFileType;
typedef enum {
MfDesfireFileCommunicationSettingsPlaintext = 0,
MfDesfireFileCommunicationSettingsAuthenticated = 1,
MfDesfireFileCommunicationSettingsEnciphered = 3,
} MfDesfireFileCommunicationSettings;
typedef uint8_t MfDesfireFileId;
typedef uint16_t MfDesfireFileAccessRights;
typedef struct {
MfDesfireFileType type;
MfDesfireFileCommunicationSettings comm;
MfDesfireFileAccessRights access_rights;
union {
struct {
uint32_t size;
} data;
struct {
uint32_t lo_limit;
uint32_t hi_limit;
uint32_t limited_credit_value;
bool limited_credit_enabled;
} value;
struct {
uint32_t size;
uint32_t max;
uint32_t cur;
} record;
};
} MfDesfireFileSettings;
typedef struct {
SimpleArray* data;
} MfDesfireFileData;
typedef struct {
uint8_t data[MF_DESFIRE_APP_ID_SIZE];
} MfDesfireApplicationId;
typedef struct MfDesfireApplication {
MfDesfireKeySettings key_settings;
SimpleArray* key_versions;
SimpleArray* file_ids;
SimpleArray* file_settings;
SimpleArray* file_data;
} MfDesfireApplication;
typedef enum {
MfDesfireErrorNone,
MfDesfireErrorNotPresent,
MfDesfireErrorProtocol,
MfDesfireErrorTimeout,
} MfDesfireError;
typedef struct {
Iso14443_4aData* iso14443_4a_data;
MfDesfireVersion version;
MfDesfireFreeMemory free_memory;
MfDesfireKeySettings master_key_settings;
SimpleArray* master_key_versions;
SimpleArray* application_ids;
SimpleArray* applications;
} MfDesfireData;
extern const NfcDeviceBase nfc_device_mf_desfire;
// Virtual methods
MfDesfireData* mf_desfire_alloc();
void mf_desfire_free(MfDesfireData* data);
void mf_desfire_reset(MfDesfireData* data);
void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other);
bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type);
bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version);
bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff);
bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other);
const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type);
const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len);
bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len);
Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data);
// Getters and tests
const MfDesfireApplication*
mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id);
const MfDesfireFileSettings*
mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id);
const MfDesfireFileData*
mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,790 @@
#include "mf_desfire_i.h"
#define BITS_IN_BYTE (8U)
#define MF_DESFIRE_FFF_VERSION_KEY \
MF_DESFIRE_FFF_PICC_PREFIX " " \
"Version"
#define MF_DESFIRE_FFF_FREE_MEM_KEY \
MF_DESFIRE_FFF_PICC_PREFIX " " \
"Free Memory"
#define MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY "Change Key ID"
#define MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY "Config Changeable"
#define MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY "Free Create Delete"
#define MF_DESFIRE_FFF_FREE_DIR_LIST_KEY "Free Directory List"
#define MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY "Key Changeable"
#define MF_DESFIRE_FFF_FLAGS_KEY "Flags"
#define MF_DESFIRE_FFF_MAX_KEYS_KEY "Max Keys"
#define MF_DESFIRE_FFF_KEY_SUB_PREFIX "Key"
#define MF_DESFIRE_FFF_KEY_VERSION_KEY "Version"
#define MF_DESFIRE_FFF_APPLICATION_COUNT_KEY \
MF_DESFIRE_FFF_APP_PREFIX " " \
"Count"
#define MF_DESFIRE_FFF_APPLICATION_IDS_KEY \
MF_DESFIRE_FFF_APP_PREFIX " " \
"IDs"
#define MF_DESFIRE_FFF_FILE_SUB_PREFIX "File"
#define MF_DESFIRE_FFF_FILE_IDS_KEY \
MF_DESFIRE_FFF_FILE_SUB_PREFIX " " \
"IDs"
#define MF_DESFIRE_FFF_FILE_TYPE_KEY "Type"
#define MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY "Communication Settings"
#define MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY "Access Rights"
#define MF_DESFIRE_FFF_FILE_SIZE_KEY "Size"
#define MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY "Hi Limit"
#define MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY "Lo Limit"
#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY "Limited Credit Value"
#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY "Limited Credit Enabled"
#define MF_DESFIRE_FFF_FILE_MAX_KEY "Max"
#define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur"
bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion));
}
return can_parse;
}
bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) {
typedef struct __attribute__((packed)) {
uint32_t bytes_free : 3 * BITS_IN_BYTE;
} MfDesfireFreeMemoryLayout;
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireFreeMemoryLayout);
if(can_parse) {
MfDesfireFreeMemoryLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFreeMemoryLayout));
data->bytes_free = layout.bytes_free;
}
data->is_present = can_parse;
return can_parse;
}
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) {
typedef struct __attribute__((packed)) {
bool is_master_key_changeable : 1;
bool is_free_directory_list : 1;
bool is_free_create_delete : 1;
bool is_config_changeable : 1;
uint8_t change_key_id : 4;
uint8_t max_keys : 4;
uint8_t flags : 4;
} MfDesfireKeySettingsLayout;
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout);
if(can_parse) {
MfDesfireKeySettingsLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireKeySettingsLayout));
data->is_master_key_changeable = layout.is_master_key_changeable;
data->is_free_directory_list = layout.is_free_directory_list;
data->is_free_create_delete = layout.is_free_create_delete;
data->is_config_changeable = layout.is_config_changeable;
data->change_key_id = layout.change_key_id;
data->max_keys = layout.max_keys;
data->flags = layout.flags;
}
return can_parse;
}
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfDesfireKeyVersion));
}
return can_parse;
}
bool mf_desfire_application_id_parse(
MfDesfireApplicationId* data,
uint32_t index,
const BitBuffer* buf) {
const bool can_parse =
bit_buffer_get_size_bytes(buf) >=
(index * sizeof(MfDesfireApplicationId) + sizeof(MfDesfireApplicationId));
if(can_parse) {
bit_buffer_write_bytes_mid(
buf, data, index * sizeof(MfDesfireApplicationId), sizeof(MfDesfireApplicationId));
}
return can_parse;
}
bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf) {
const bool can_parse = bit_buffer_get_size_bytes(buf) >=
(index * sizeof(MfDesfireFileId) + sizeof(MfDesfireFileId));
if(can_parse) {
bit_buffer_write_bytes_mid(
buf, data, index * sizeof(MfDesfireFileId), sizeof(MfDesfireFileId));
}
return can_parse;
}
bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf) {
bool parsed = false;
typedef struct __attribute__((packed)) {
uint8_t type;
uint8_t comm;
uint16_t access_rights;
} MfDesfireFileSettingsHeader;
typedef struct __attribute__((packed)) {
uint32_t size : 3 * BITS_IN_BYTE;
} MfDesfireFileSettingsData;
typedef struct __attribute__((packed)) {
uint32_t lo_limit;
uint32_t hi_limit;
uint32_t limited_credit_value;
uint8_t limited_credit_enabled;
} MfDesfireFileSettingsValue;
typedef struct __attribute__((packed)) {
uint32_t size : 3 * BITS_IN_BYTE;
uint32_t max : 3 * BITS_IN_BYTE;
uint32_t cur : 3 * BITS_IN_BYTE;
} MfDesfireFileSettingsRecord;
typedef struct __attribute__((packed)) {
MfDesfireFileSettingsHeader header;
union {
MfDesfireFileSettingsData data;
MfDesfireFileSettingsValue value;
MfDesfireFileSettingsRecord record;
};
} MfDesfireFileSettingsLayout;
do {
const size_t data_size = bit_buffer_get_size_bytes(buf);
const size_t min_data_size =
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData);
if(data_size < min_data_size) break;
MfDesfireFileSettingsLayout layout;
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout));
data->type = layout.header.type;
data->comm = layout.header.comm;
data->access_rights = layout.header.access_rights;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
if(data_size != min_data_size) break;
data->data.size = layout.data.size;
} else if(data->type == MfDesfireFileTypeValue) {
if(data_size !=
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue))
break;
data->value.lo_limit = layout.value.lo_limit;
data->value.hi_limit = layout.value.hi_limit;
data->value.limited_credit_value = layout.value.hi_limit;
data->value.limited_credit_enabled = layout.value.limited_credit_enabled;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
if(data_size !=
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord))
break;
data->record.size = layout.record.size;
data->record.max = layout.record.max;
data->record.cur = layout.record.cur;
} else {
break;
}
parsed = true;
} while(false);
return parsed;
}
bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf) {
const size_t data_size = bit_buffer_get_size_bytes(buf);
if(data_size > 0) {
simple_array_init(data->data, data_size);
bit_buffer_write_bytes(buf, simple_array_get_data(data->data), data_size);
}
// Success no matter whether there is data or not
return true;
}
void mf_desfire_file_data_init(MfDesfireFileData* data) {
data->data = simple_array_alloc(&simple_array_config_uint8_t);
}
void mf_desfire_application_init(MfDesfireApplication* data) {
data->key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);
data->file_ids = simple_array_alloc(&mf_desfire_file_id_array_config);
data->file_settings = simple_array_alloc(&mf_desfire_file_settings_array_config);
data->file_data = simple_array_alloc(&mf_desfire_file_data_array_config);
}
void mf_desfire_file_data_reset(MfDesfireFileData* data) {
simple_array_free(data->data);
memset(data, 0, sizeof(MfDesfireFileData));
}
void mf_desfire_application_reset(MfDesfireApplication* data) {
simple_array_free(data->key_versions);
simple_array_free(data->file_ids);
simple_array_free(data->file_settings);
simple_array_free(data->file_data);
memset(data, 0, sizeof(MfDesfireApplication));
}
void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other) {
simple_array_copy(data->data, other->data);
}
void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other) {
data->key_settings = other->key_settings;
simple_array_copy(data->key_versions, other->key_versions);
simple_array_copy(data->file_ids, other->file_ids);
simple_array_copy(data->file_settings, other->file_settings);
simple_array_copy(data->file_data, other->file_data);
}
bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff) {
return flipper_format_read_hex(
ff, MF_DESFIRE_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfDesfireVersion));
}
bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff) {
data->is_present = flipper_format_key_exist(ff, MF_DESFIRE_FFF_FREE_MEM_KEY);
return data->is_present ?
flipper_format_read_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) :
true;
}
bool mf_desfire_key_settings_load(
MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY);
if(!flipper_format_read_bool(ff, furi_string_get_cstr(key), &data->is_config_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY);
if(flipper_format_key_exist(ff, furi_string_get_cstr(key))) {
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break;
}
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break;
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_key_version_load(
MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf(
"%s %s %lu %s",
prefix,
MF_DESFIRE_FFF_KEY_SUB_PREFIX,
index,
MF_DESFIRE_FFF_KEY_VERSION_KEY);
const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, 1);
furi_string_free(key);
return success;
}
bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);
const bool success = flipper_format_get_value_count(ff, furi_string_get_cstr(key), data);
furi_string_free(key);
return success;
}
bool mf_desfire_file_ids_load(
MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);
const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, count);
furi_string_free(key);
return success;
}
bool mf_desfire_file_settings_load(
MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->type, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY);
if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->comm, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);
if(!flipper_format_read_hex(
ff,
furi_string_get_cstr(key),
(uint8_t*)&data->access_rights,
sizeof(MfDesfireFileAccessRights)))
break;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))
break;
} else if(data->type == MfDesfireFileTypeValue) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.lo_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY);
if(!flipper_format_read_uint32(
ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY);
if(!flipper_format_read_bool(
ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1))
break;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))
break;
}
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff) {
bool success = false;
do {
if(!flipper_format_key_exist(ff, prefix)) {
success = true;
break;
}
uint32_t data_size;
if(!flipper_format_get_value_count(ff, prefix, &data_size)) break;
simple_array_init(data->data, data_size);
if(!flipper_format_read_hex(ff, prefix, simple_array_get_data(data->data), data_size))
break;
success = true;
} while(false);
return success;
}
bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff) {
return flipper_format_read_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1);
}
bool mf_desfire_application_ids_load(
MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff) {
return flipper_format_read_hex(
ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId));
}
bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff) {
FuriString* sub_prefix = furi_string_alloc();
bool success = false;
do {
if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break;
const uint32_t key_version_count = data->key_settings.max_keys;
simple_array_init(data->key_versions, key_version_count);
uint32_t i;
for(i = 0; i < key_version_count; ++i) {
if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff))
break;
}
if(i != key_version_count) break;
uint32_t file_count;
if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break;
simple_array_init(data->file_ids, file_count);
if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff))
break;
simple_array_init(data->file_settings, file_count);
simple_array_init(data->file_data, file_count);
for(i = 0; i < file_count; ++i) {
const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);
furi_string_printf(
sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id);
MfDesfireFileSettings* file_settings = simple_array_get(data->file_settings, i);
if(!mf_desfire_file_settings_load(file_settings, furi_string_get_cstr(sub_prefix), ff))
break;
MfDesfireFileData* file_data = simple_array_get(data->file_data, i);
if(!mf_desfire_file_data_load(file_data, furi_string_get_cstr(sub_prefix), ff)) break;
}
if(i != file_count) break;
success = true;
} while(false);
furi_string_free(sub_prefix);
return success;
}
bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff) {
return flipper_format_write_hex(
ff, MF_DESFIRE_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfDesfireVersion));
}
bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff) {
return data->is_present ?
flipper_format_write_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) :
true;
}
bool mf_desfire_key_settings_save(
const MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_config_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break;
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_key_version_save(
const MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf(
"%s %s %lu %s",
prefix,
MF_DESFIRE_FFF_KEY_SUB_PREFIX,
index,
MF_DESFIRE_FFF_KEY_VERSION_KEY);
const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, 1);
furi_string_free(key);
return success;
}
bool mf_desfire_file_ids_save(
const MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff) {
FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY);
const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, count);
furi_string_free(key);
return success;
}
bool mf_desfire_file_settings_save(
const MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff) {
bool success = false;
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->type, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY);
if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->comm, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);
if(!flipper_format_write_hex(
ff,
furi_string_get_cstr(key),
(const uint8_t*)&data->access_rights,
sizeof(MfDesfireFileAccessRights)))
break;
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))
break;
} else if(data->type == MfDesfireFileTypeValue) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->value.lo_limit, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY);
if(!flipper_format_write_bool(
ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1))
break;
} else if(
data->type == MfDesfireFileTypeLinearRecord ||
data->type == MfDesfireFileTypeCyclicRecord) {
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1))
break;
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))
break;
}
success = true;
} while(false);
furi_string_free(key);
return success;
}
bool mf_desfire_file_data_save(
const MfDesfireFileData* data,
const char* prefix,
FlipperFormat* ff) {
const uint32_t data_size = simple_array_get_count(data->data);
return data_size > 0 ? flipper_format_write_hex(
ff, prefix, simple_array_cget_data(data->data), data_size) :
true;
}
bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff) {
return flipper_format_write_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1);
}
bool mf_desfire_application_ids_save(
const MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff) {
return flipper_format_write_hex(
ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId));
}
bool mf_desfire_application_save(
const MfDesfireApplication* data,
const char* prefix,
FlipperFormat* ff) {
FuriString* sub_prefix = furi_string_alloc();
bool success = false;
do {
if(!mf_desfire_key_settings_save(&data->key_settings, prefix, ff)) break;
const uint32_t key_version_count = data->key_settings.max_keys;
uint32_t i;
for(i = 0; i < key_version_count; ++i) {
if(!mf_desfire_key_version_save(
simple_array_cget(data->key_versions, i), prefix, i, ff))
break;
}
if(i != key_version_count) break;
const uint32_t file_count = simple_array_get_count(data->file_ids);
if(!mf_desfire_file_ids_save(simple_array_get_data(data->file_ids), file_count, prefix, ff))
break;
for(i = 0; i < file_count; ++i) {
const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);
furi_string_printf(
sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id);
const MfDesfireFileSettings* file_settings = simple_array_cget(data->file_settings, i);
if(!mf_desfire_file_settings_save(file_settings, furi_string_get_cstr(sub_prefix), ff))
break;
const MfDesfireFileData* file_data = simple_array_cget(data->file_data, i);
if(!mf_desfire_file_data_save(file_data, furi_string_get_cstr(sub_prefix), ff)) break;
}
if(i != file_count) break;
success = true;
} while(false);
furi_string_free(sub_prefix);
return success;
}
const SimpleArrayConfig mf_desfire_key_version_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireKeyVersion),
};
const SimpleArrayConfig mf_desfire_app_id_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireApplicationId),
};
const SimpleArrayConfig mf_desfire_file_id_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireFileId),
};
const SimpleArrayConfig mf_desfire_file_settings_array_config = {
.init = NULL,
.copy = NULL,
.reset = NULL,
.type_size = sizeof(MfDesfireFileSettings),
};
const SimpleArrayConfig mf_desfire_file_data_array_config = {
.init = (SimpleArrayInit)mf_desfire_file_data_init,
.copy = (SimpleArrayCopy)mf_desfire_file_data_copy,
.reset = (SimpleArrayReset)mf_desfire_file_data_reset,
.type_size = sizeof(MfDesfireData),
};
const SimpleArrayConfig mf_desfire_application_array_config = {
.init = (SimpleArrayInit)mf_desfire_application_init,
.copy = (SimpleArrayCopy)mf_desfire_application_copy,
.reset = (SimpleArrayReset)mf_desfire_application_reset,
.type_size = sizeof(MfDesfireApplication),
};

View File

@@ -0,0 +1,140 @@
#pragma once
#include "mf_desfire.h"
#define MF_DESFIRE_FFF_PICC_PREFIX "PICC"
#define MF_DESFIRE_FFF_APP_PREFIX "Application"
// SimpleArray configurations
extern const SimpleArrayConfig mf_desfire_key_version_array_config;
extern const SimpleArrayConfig mf_desfire_app_id_array_config;
extern const SimpleArrayConfig mf_desfire_file_id_array_config;
extern const SimpleArrayConfig mf_desfire_file_settings_array_config;
extern const SimpleArrayConfig mf_desfire_file_data_array_config;
extern const SimpleArrayConfig mf_desfire_application_array_config;
// Parse internal MfDesfire structures
bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf);
bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf);
bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf);
bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf);
bool mf_desfire_application_id_parse(
MfDesfireApplicationId* data,
uint32_t index,
const BitBuffer* buf);
bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf);
bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf);
bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf);
// Init internal MfDesfire structures
void mf_desfire_file_data_init(MfDesfireFileData* data);
void mf_desfire_application_init(MfDesfireApplication* data);
// Reset internal MfDesfire structures
void mf_desfire_file_data_reset(MfDesfireFileData* data);
void mf_desfire_application_reset(MfDesfireApplication* data);
// Copy internal MfDesfire structures
void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other);
void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other);
// Load internal MfDesfire structures
bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff);
bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff);
bool mf_desfire_key_settings_load(
MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_key_version_load(
MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff);
bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff);
bool mf_desfire_file_ids_load(
MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_settings_load(
MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff);
bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff);
bool mf_desfire_application_ids_load(
MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff);
bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff);
// Save internal MFDesfire structures
bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff);
bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff);
bool mf_desfire_key_settings_save(
const MfDesfireKeySettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_key_version_save(
const MfDesfireKeyVersion* data,
const char* prefix,
uint32_t index,
FlipperFormat* ff);
bool mf_desfire_file_ids_save(
const MfDesfireFileId* data,
uint32_t count,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_settings_save(
const MfDesfireFileSettings* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_file_data_save(
const MfDesfireFileData* data,
const char* prefix,
FlipperFormat* ff);
bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff);
bool mf_desfire_application_ids_save(
const MfDesfireApplicationId* data,
uint32_t count,
FlipperFormat* ff);
bool mf_desfire_application_save(
const MfDesfireApplication* data,
const char* prefix,
FlipperFormat* ff);

View File

@@ -0,0 +1,243 @@
#include "mf_desfire_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "MfDesfirePoller"
#define MF_DESFIRE_BUF_SIZE_MAX (64U)
typedef NfcCommand (*MfDesfirePollerReadHandler)(MfDesfirePoller* instance);
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance) {
furi_assert(instance);
return instance->data;
}
static MfDesfirePoller* mf_desfire_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
MfDesfirePoller* instance = malloc(sizeof(MfDesfirePoller));
instance->iso14443_4a_poller = iso14443_4a_poller;
instance->data = mf_desfire_alloc();
instance->tx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->rx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->input_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->result_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX);
instance->mf_desfire_event.data = &instance->mf_desfire_event_data;
instance->general_event.protocol = NfcProtocolMfDesfire;
instance->general_event.event_data = &instance->mf_desfire_event;
instance->general_event.instance = instance;
return instance;
}
static void mf_desfire_poller_free(MfDesfirePoller* instance) {
furi_assert(instance);
mf_desfire_free(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
bit_buffer_free(instance->input_buffer);
bit_buffer_free(instance->result_buffer);
free(instance);
}
static NfcCommand mf_desfire_poller_handler_idle(MfDesfirePoller* instance) {
bit_buffer_reset(instance->input_buffer);
bit_buffer_reset(instance->result_buffer);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_reset(instance->rx_buffer);
iso14443_4a_copy(
instance->data->iso14443_4a_data,
iso14443_4a_poller_get_data(instance->iso14443_4a_poller));
instance->state = MfDesfirePollerStateReadVersion;
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instance) {
instance->error = mf_desfire_poller_async_read_version(instance, &instance->data->version);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read version success");
instance->state = MfDesfirePollerStateReadFreeMemory;
} else {
FURI_LOG_E(TAG, "Failed to read version");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) {
instance->error =
mf_desfire_poller_async_read_free_memory(instance, &instance->data->free_memory);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read free memory success");
instance->state = MfDesfirePollerStateReadMasterKeySettings;
} else {
FURI_LOG_E(TAG, "Failed to read free memory");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) {
instance->error =
mf_desfire_poller_async_read_key_settings(instance, &instance->data->master_key_settings);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read master key settings success");
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
} else {
FURI_LOG_E(TAG, "Failed to read master key settings");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePoller* instance) {
instance->error = mf_desfire_poller_async_read_key_versions(
instance,
instance->data->master_key_versions,
instance->data->master_key_settings.max_keys);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read master key version success");
instance->state = MfDesfirePollerStateReadApplicationIds;
} else {
FURI_LOG_E(TAG, "Failed to read master key version");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller* instance) {
instance->error =
mf_desfire_poller_async_read_application_ids(instance, instance->data->application_ids);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read application ids success");
instance->state = MfDesfirePollerStateReadApplications;
} else {
FURI_LOG_E(TAG, "Failed to read application ids");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_applications(MfDesfirePoller* instance) {
instance->error = mf_desfire_poller_async_read_applications(
instance, instance->data->application_ids, instance->data->applications);
if(instance->error == MfDesfireErrorNone) {
FURI_LOG_D(TAG, "Read applications success");
instance->state = MfDesfirePollerStateReadSuccess;
} else {
FURI_LOG_E(TAG, "Failed to read applications");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->state = MfDesfirePollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_desfire_poller_handler_read_fail(MfDesfirePoller* instance) {
FURI_LOG_D(TAG, "Read Failed");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->mf_desfire_event.data->error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->state = MfDesfirePollerStateIdle;
return command;
}
static NfcCommand mf_desfire_poller_handler_read_success(MfDesfirePoller* instance) {
FURI_LOG_D(TAG, "Read success.");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadSuccess;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const MfDesfirePollerReadHandler mf_desfire_poller_read_handler[MfDesfirePollerStateNum] = {
[MfDesfirePollerStateIdle] = mf_desfire_poller_handler_idle,
[MfDesfirePollerStateReadVersion] = mf_desfire_poller_handler_read_version,
[MfDesfirePollerStateReadFreeMemory] = mf_desfire_poller_handler_read_free_memory,
[MfDesfirePollerStateReadMasterKeySettings] =
mf_desfire_poller_handler_read_master_key_settings,
[MfDesfirePollerStateReadMasterKeyVersion] = mf_desfire_poller_handler_read_master_key_version,
[MfDesfirePollerStateReadApplicationIds] = mf_desfire_poller_handler_read_application_ids,
[MfDesfirePollerStateReadApplications] = mf_desfire_poller_handler_read_applications,
[MfDesfirePollerStateReadFailed] = mf_desfire_poller_handler_read_fail,
[MfDesfirePollerStateReadSuccess] = mf_desfire_poller_handler_read_success,
};
static void mf_desfire_poller_set_callback(
MfDesfirePoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand mf_desfire_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_4a);
MfDesfirePoller* instance = context;
furi_assert(instance);
furi_assert(instance->callback);
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
furi_assert(iso14443_4a_event);
NfcCommand command = NfcCommandContinue;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
command = mf_desfire_poller_read_handler[instance->state](instance);
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadFailed;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_4a);
MfDesfirePoller* instance = context;
furi_assert(instance);
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
furi_assert(iso14443_4a_event);
bool protocol_detected = false;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
MfDesfireVersion version = {};
const MfDesfireError error = mf_desfire_poller_async_read_version(instance, &version);
protocol_detected = (error == MfDesfireErrorNone);
}
return protocol_detected;
}
const NfcPollerBase mf_desfire_poller = {
.alloc = (NfcPollerAlloc)mf_desfire_poller_alloc,
.free = (NfcPollerFree)mf_desfire_poller_free,
.set_callback = (NfcPollerSetCallback)mf_desfire_poller_set_callback,
.run = (NfcPollerRun)mf_desfire_poller_run,
.detect = (NfcPollerDetect)mf_desfire_poller_detect,
.get_data = (NfcPollerGetData)mf_desfire_poller_get_data,
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include "mf_desfire.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct MfDesfirePoller MfDesfirePoller;
typedef enum {
MfDesfirePollerEventTypeReadSuccess,
MfDesfirePollerEventTypeReadFailed,
} MfDesfirePollerEventType;
typedef struct {
union {
MfDesfireError error;
};
} MfDesfirePollerEventData;
typedef struct {
MfDesfirePollerEventType type;
MfDesfirePollerEventData* data;
} MfDesfirePollerEvent;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase mf_desfire_poller;

View File

@@ -0,0 +1,474 @@
#include "mf_desfire_poller_i.h"
#include <furi.h>
#include "mf_desfire_i.h"
#define TAG "MfDesfirePoller"
MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
return MfDesfireErrorNone;
case Iso14443_4aErrorNotPresent:
return MfDesfireErrorNotPresent;
case Iso14443_4aErrorTimeout:
return MfDesfireErrorTimeout;
default:
return MfDesfireErrorProtocol;
}
}
MfDesfireError mf_desfire_send_chunks(
MfDesfirePoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
furi_assert(instance);
furi_assert(instance->iso14443_4a_poller);
furi_assert(instance->tx_buffer);
furi_assert(instance->rx_buffer);
furi_assert(tx_buffer);
furi_assert(rx_buffer);
MfDesfireError error = MfDesfireErrorNone;
do {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_desfire_process_error(iso14443_4a_error);
break;
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT);
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
} else {
bit_buffer_reset(rx_buffer);
}
while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) {
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
if(iso14443_4a_error != Iso14443_4aErrorNone) {
error = mf_desfire_process_error(iso14443_4a_error);
break;
}
bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
}
} while(false);
return error;
}
MfDesfireError
mf_desfire_poller_async_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_version_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError
mf_desfire_poller_async_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_key_settings(
MfDesfirePoller* instance,
MfDesfireKeySettings* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_key_settings_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_key_versions(
MfDesfirePoller* instance,
SimpleArray* data,
uint32_t count) {
furi_assert(instance);
furi_assert(count > 0);
simple_array_init(data, count);
bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);
bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);
MfDesfireError error = MfDesfireErrorNone;
for(uint32_t i = 0; i < count; ++i) {
bit_buffer_set_byte(instance->input_buffer, 1, i);
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_key_version_parse(simple_array_get(data, i), instance->result_buffer)) {
error = MfDesfireErrorProtocol;
break;
}
}
return error;
}
MfDesfireError
mf_desfire_poller_async_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
const uint32_t app_id_count =
bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireApplicationId);
if(app_id_count == 0) break;
simple_array_init(data, app_id_count);
for(uint32_t i = 0; i < app_id_count; ++i) {
if(!mf_desfire_application_id_parse(
simple_array_get(data, i), i, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
break;
}
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_select_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION);
bit_buffer_append_bytes(
instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId));
MfDesfireError error =
mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
return error;
}
MfDesfireError
mf_desfire_poller_async_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
const uint32_t id_count =
bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireFileId);
if(id_count == 0) break;
simple_array_init(data, id_count);
for(uint32_t i = 0; i < id_count; ++i) {
if(!mf_desfire_file_id_parse(simple_array_get(data, i), i, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
break;
}
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_settings(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileSettings* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS);
bit_buffer_append_byte(instance->input_buffer, id);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_settings_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_settings_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
SimpleArray* data) {
furi_assert(instance);
MfDesfireError error = MfDesfireErrorNone;
const uint32_t file_id_count = simple_array_get_count(file_ids);
if(file_id_count > 0) {
simple_array_init(data, file_id_count);
}
for(uint32_t i = 0; i < file_id_count; ++i) {
const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
error = mf_desfire_poller_async_read_file_settings(
instance, file_id, simple_array_get(data, i));
if(error != MfDesfireErrorNone) break;
}
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_data(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
bit_buffer_append_byte(instance->input_buffer, id);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_value(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileData* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE);
bit_buffer_append_byte(instance->input_buffer, id);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_records(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data) {
furi_assert(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
bit_buffer_append_byte(instance->input_buffer, id);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3);
bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3);
MfDesfireError error;
do {
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
if(error != MfDesfireErrorNone) break;
if(!mf_desfire_file_data_parse(data, instance->result_buffer)) {
error = MfDesfireErrorProtocol;
}
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_file_data_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
const SimpleArray* file_settings,
SimpleArray* data) {
furi_assert(instance);
furi_assert(simple_array_get_count(file_ids) == simple_array_get_count(file_settings));
MfDesfireError error = MfDesfireErrorNone;
const uint32_t file_id_count = simple_array_get_count(file_ids);
if(file_id_count > 0) {
simple_array_init(data, file_id_count);
}
for(uint32_t i = 0; i < file_id_count; ++i) {
const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i);
const MfDesfireFileType file_type = file_settings_cur->type;
MfDesfireFileData* file_data = simple_array_get(data, i);
if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {
error = mf_desfire_poller_async_read_file_data(
instance, file_id, 0, file_settings_cur->data.size, file_data);
} else if(file_type == MfDesfireFileTypeValue) {
error = mf_desfire_poller_async_read_file_value(instance, file_id, file_data);
} else if(
file_type == MfDesfireFileTypeLinearRecord ||
file_type == MfDesfireFileTypeCyclicRecord) {
error = mf_desfire_poller_async_read_file_records(
instance, file_id, 0, file_settings_cur->data.size, file_data);
}
if(error != MfDesfireErrorNone) break;
}
return error;
}
MfDesfireError mf_desfire_poller_async_read_application(
MfDesfirePoller* instance,
MfDesfireApplication* data) {
furi_assert(instance);
furi_assert(data);
MfDesfireError error;
do {
error = mf_desfire_poller_async_read_key_settings(instance, &data->key_settings);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_key_versions(
instance, data->key_versions, data->key_settings.max_keys);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_file_ids(instance, data->file_ids);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_file_settings_multi(
instance, data->file_ids, data->file_settings);
if(error != MfDesfireErrorNone) break;
error = mf_desfire_poller_async_read_file_data_multi(
instance, data->file_ids, data->file_settings, data->file_data);
if(error != MfDesfireErrorNone) break;
} while(false);
return error;
}
MfDesfireError mf_desfire_poller_async_read_applications(
MfDesfirePoller* instance,
const SimpleArray* app_ids,
SimpleArray* data) {
furi_assert(instance);
MfDesfireError error = MfDesfireErrorNone;
const uint32_t app_id_count = simple_array_get_count(app_ids);
if(app_id_count > 0) {
simple_array_init(data, app_id_count);
}
for(uint32_t i = 0; i < app_id_count; ++i) {
do {
error = mf_desfire_poller_async_select_application(
instance, simple_array_cget(app_ids, i));
if(error != MfDesfireErrorNone) break;
MfDesfireApplication* current_app = simple_array_get(data, i);
error = mf_desfire_poller_async_read_application(instance, current_app);
} while(false);
}
return error;
}

View File

@@ -0,0 +1,126 @@
#pragma once
#include "mf_desfire_poller.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MfDesfirePollerStateIdle,
MfDesfirePollerStateReadVersion,
MfDesfirePollerStateReadFreeMemory,
MfDesfirePollerStateReadMasterKeySettings,
MfDesfirePollerStateReadMasterKeyVersion,
MfDesfirePollerStateReadApplicationIds,
MfDesfirePollerStateReadApplications,
MfDesfirePollerStateReadFailed,
MfDesfirePollerStateReadSuccess,
MfDesfirePollerStateNum,
} MfDesfirePollerState;
typedef enum {
MfDesfirePollerSessionStateIdle,
MfDesfirePollerSessionStateActive,
MfDesfirePollerSessionStateStopRequest,
} MfDesfirePollerSessionState;
struct MfDesfirePoller {
Iso14443_4aPoller* iso14443_4a_poller;
MfDesfirePollerSessionState session_state;
MfDesfirePollerState state;
MfDesfireError error;
MfDesfireData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
BitBuffer* input_buffer;
BitBuffer* result_buffer;
MfDesfirePollerEventData mf_desfire_event_data;
MfDesfirePollerEvent mf_desfire_event;
NfcGenericEvent general_event;
NfcGenericCallback callback;
void* context;
};
MfDesfireError mf_desfire_process_error(Iso14443_4aError error);
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance);
MfDesfireError mf_desfire_send_chunks(
MfDesfirePoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
MfDesfireError
mf_desfire_poller_async_read_version(MfDesfirePoller* instance, MfDesfireVersion* data);
MfDesfireError
mf_desfire_poller_async_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data);
MfDesfireError mf_desfire_poller_async_read_key_settings(
MfDesfirePoller* instance,
MfDesfireKeySettings* data);
MfDesfireError mf_desfire_poller_async_read_key_versions(
MfDesfirePoller* instance,
SimpleArray* data,
uint32_t count);
MfDesfireError
mf_desfire_poller_async_read_application_ids(MfDesfirePoller* instance, SimpleArray* data);
MfDesfireError mf_desfire_poller_async_select_application(
MfDesfirePoller* instance,
const MfDesfireApplicationId* id);
MfDesfireError mf_desfire_poller_async_read_file_ids(MfDesfirePoller* instance, SimpleArray* data);
MfDesfireError mf_desfire_poller_async_read_file_settings(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileSettings* data);
MfDesfireError mf_desfire_poller_async_read_file_settings_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
SimpleArray* data);
MfDesfireError mf_desfire_poller_async_read_file_data(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data);
MfDesfireError mf_desfire_poller_async_read_file_value(
MfDesfirePoller* instance,
MfDesfireFileId id,
MfDesfireFileData* data);
MfDesfireError mf_desfire_poller_async_read_file_records(
MfDesfirePoller* instance,
MfDesfireFileId id,
uint32_t offset,
size_t size,
MfDesfireFileData* data);
MfDesfireError mf_desfire_poller_async_read_file_data_multi(
MfDesfirePoller* instance,
const SimpleArray* file_ids,
const SimpleArray* file_settings,
SimpleArray* data);
MfDesfireError
mf_desfire_poller_async_read_application(MfDesfirePoller* instance, MfDesfireApplication* data);
MfDesfireError mf_desfire_poller_async_read_applications(
MfDesfirePoller* instance,
const SimpleArray* app_ids,
SimpleArray* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,627 @@
#include "mf_ultralight.h"
#include <nfc/helpers/nfc_util.h>
#include <furi.h>
#define MF_ULTRALIGHT_PROTOCOL_NAME "NTAG/Ultralight"
#define MF_ULTRALIGHT_FORMAT_VERSION_KEY "Data format version"
#define MF_ULTRALIGHT_TYPE_KEY MF_ULTRALIGHT_PROTOCOL_NAME " type"
#define MF_ULTRALIGHT_SIGNATURE_KEY "Signature"
#define MF_ULTRALIGHT_MIFARE_VERSION_KEY "Mifare version"
#define MF_ULTRALIGHT_COUNTER_KEY "Counter"
#define MF_ULTRALIGHT_TEARING_KEY "Tearing"
#define MF_ULTRALIGHT_PAGES_TOTAL_KEY "Pages total"
#define MF_ULTRALIGHT_PAGES_READ_KEY "Pages read"
#define MF_ULTRALIGHT_PAGE_KEY "Page"
#define MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY "Failed authentication attempts"
typedef struct {
const char* device_name;
uint16_t total_pages;
uint16_t config_page;
uint32_t feature_set;
} MfUltralightFeatures;
static const uint32_t mf_ultralight_data_format_version = 2;
static const MfUltralightFeatures mf_ultralight_features[MfUltralightTypeNum] = {
[MfUltralightTypeUnknown] =
{
.device_name = "Mifare Ultralight",
.total_pages = 16,
.config_page = 0,
.feature_set = MfUltralightFeatureSupportCompatibleWrite,
},
[MfUltralightTypeMfulC] =
{
.device_name = "Mifare Ultralight C",
.total_pages = 48,
.config_page = 0,
.feature_set = MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportAuthenticate,
},
[MfUltralightTypeNTAG203] =
{
.device_name = "NTAG203",
.total_pages = 42,
.config_page = 0,
.feature_set = MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportCounterInMemory,
},
[MfUltralightTypeUL11] =
{
.device_name = "Mifare Ultralight 11",
.total_pages = 20,
.config_page = 16,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportReadCounter |
MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportVcsl,
},
[MfUltralightTypeUL21] =
{
.device_name = "Mifare Ultralight 21",
.total_pages = 41,
.config_page = 37,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportReadCounter |
MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportVcsl |
MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAG213] =
{
.device_name = "NTAG213",
.total_pages = 45,
.config_page = 41,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter |
MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAG215] =
{
.device_name = "NTAG215",
.total_pages = 135,
.config_page = 131,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter |
MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAG216] =
{
.device_name = "NTAG216",
.total_pages = 231,
.config_page = 227,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportCompatibleWrite |
MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter |
MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAGI2C1K] =
{
.device_name = "NTAG I2C 1K",
.total_pages = 231,
.config_page = 0,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAGI2C2K] =
{
.device_name = "NTAG I2C 2K",
.total_pages = 485,
.config_page = 0,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead |
MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAGI2CPlus1K] =
{
.device_name = "NTAG I2C Plus 1K",
.total_pages = 236,
.config_page = 227,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportPasswordAuth |
MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportFastWrite |
MfUltralightFeatureSupportDynamicLock,
},
[MfUltralightTypeNTAGI2CPlus2K] =
{
.device_name = "NTAG I2C Plus 2K",
.total_pages = 492,
.config_page = 227,
.feature_set =
MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature |
MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportPasswordAuth |
MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportFastWrite |
MfUltralightFeatureSupportDynamicLock,
},
};
const NfcDeviceBase nfc_device_mf_ultralight = {
.protocol_name = MF_ULTRALIGHT_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)mf_ultralight_alloc,
.free = (NfcDeviceFree)mf_ultralight_free,
.reset = (NfcDeviceReset)mf_ultralight_reset,
.copy = (NfcDeviceCopy)mf_ultralight_copy,
.verify = (NfcDeviceVerify)mf_ultralight_verify,
.load = (NfcDeviceLoad)mf_ultralight_load,
.save = (NfcDeviceSave)mf_ultralight_save,
.is_equal = (NfcDeviceEqual)mf_ultralight_is_equal,
.get_name = (NfcDeviceGetName)mf_ultralight_get_device_name,
.get_uid = (NfcDeviceGetUid)mf_ultralight_get_uid,
.set_uid = (NfcDeviceSetUid)mf_ultralight_set_uid,
.get_base_data = (NfcDeviceGetBaseData)mf_ultralight_get_base_data,
};
MfUltralightData* mf_ultralight_alloc() {
MfUltralightData* data = malloc(sizeof(MfUltralightData));
data->iso14443_3a_data = iso14443_3a_alloc();
return data;
}
void mf_ultralight_free(MfUltralightData* data) {
furi_assert(data);
iso14443_3a_free(data->iso14443_3a_data);
free(data);
}
void mf_ultralight_reset(MfUltralightData* data) {
furi_assert(data);
iso14443_3a_reset(data->iso14443_3a_data);
}
void mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other) {
furi_assert(data);
furi_assert(other);
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
for(size_t i = 0; i < COUNT_OF(data->counter); i++) {
data->counter[i] = other->counter[i];
}
for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) {
data->tearing_flag[i] = other->tearing_flag[i];
}
for(size_t i = 0; i < COUNT_OF(data->page); i++) {
data->page[i] = other->page[i];
}
data->type = other->type;
data->version = other->version;
data->signature = other->signature;
data->pages_read = other->pages_read;
data->pages_total = other->pages_total;
data->auth_attempts = other->auth_attempts;
}
static const char*
mf_ultralight_get_device_name_by_type(MfUltralightType type, NfcDeviceNameType name_type) {
if(name_type == NfcDeviceNameTypeShort &&
(type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21)) {
type = MfUltralightTypeUnknown;
}
return mf_ultralight_features[type].device_name;
}
bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type) {
furi_assert(data);
bool verified = false;
for(MfUltralightType i = 0; i < MfUltralightTypeNum; i++) {
const char* name = mf_ultralight_get_device_name_by_type(i, NfcDeviceNameTypeFull);
verified = furi_string_equal(device_type, name);
if(verified) {
data->type = i;
break;
}
}
return verified;
}
bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
FuriString* temp_str = furi_string_alloc();
bool parsed = false;
do {
// Read ISO14443_3A data
if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;
// Read Ultralight specific data
// Read Mifare Ultralight format version
uint32_t data_format_version = 0;
if(!flipper_format_read_uint32(
ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &data_format_version, 1)) {
if(!flipper_format_rewind(ff)) break;
}
// Read Mifare Ultralight type
if(data_format_version > 1) {
if(!flipper_format_read_string(ff, MF_ULTRALIGHT_TYPE_KEY, temp_str)) break;
if(!mf_ultralight_verify(data, temp_str)) break;
}
// Read signature
if(!flipper_format_read_hex(
ff,
MF_ULTRALIGHT_SIGNATURE_KEY,
data->signature.data,
sizeof(MfUltralightSignature)))
break;
// Read Mifare version
if(!flipper_format_read_hex(
ff,
MF_ULTRALIGHT_MIFARE_VERSION_KEY,
(uint8_t*)&data->version,
sizeof(MfUltralightVersion)))
break;
// Read counters and tearing flags
bool counters_parsed = true;
for(size_t i = 0; i < 3; i++) {
furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_COUNTER_KEY, i);
if(!flipper_format_read_uint32(
ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) {
counters_parsed = false;
break;
}
furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_TEARING_KEY, i);
if(!flipper_format_read_hex(
ff, furi_string_get_cstr(temp_str), &data->tearing_flag[i].data, 1)) {
counters_parsed = false;
break;
}
}
if(!counters_parsed) break;
// Read pages
uint32_t pages_total = 0;
if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break;
uint32_t pages_read = 0;
if(data_format_version < mf_ultralight_data_format_version) {
pages_read = pages_total;
} else {
if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1))
break;
}
data->pages_total = pages_total;
data->pages_read = pages_read;
if((pages_read > MF_ULTRALIGHT_MAX_PAGE_NUM) || (pages_total > MF_ULTRALIGHT_MAX_PAGE_NUM))
break;
bool pages_parsed = true;
for(size_t i = 0; i < pages_total; i++) {
furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_PAGE_KEY, i);
if(!flipper_format_read_hex(
ff,
furi_string_get_cstr(temp_str),
data->page[i].data,
sizeof(MfUltralightPage))) {
pages_parsed = false;
break;
}
}
if(!pages_parsed) break;
// Read authentication counter
if(!flipper_format_read_uint32(
ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1)) {
data->auth_attempts = 0;
}
parsed = true;
} while(false);
furi_string_free(temp_str);
return parsed;
}
bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff) {
furi_assert(data);
FuriString* temp_str = furi_string_alloc();
bool saved = false;
do {
if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, MF_ULTRALIGHT_PROTOCOL_NAME " specific data"))
break;
if(!flipper_format_write_uint32(
ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &mf_ultralight_data_format_version, 1))
break;
const char* device_type_name =
mf_ultralight_get_device_name_by_type(data->type, NfcDeviceNameTypeFull);
if(!flipper_format_write_string_cstr(ff, MF_ULTRALIGHT_TYPE_KEY, device_type_name)) break;
if(!flipper_format_write_hex(
ff,
MF_ULTRALIGHT_SIGNATURE_KEY,
data->signature.data,
sizeof(MfUltralightSignature)))
break;
if(!flipper_format_write_hex(
ff,
MF_ULTRALIGHT_MIFARE_VERSION_KEY,
(uint8_t*)&data->version,
sizeof(MfUltralightVersion)))
break;
// Write conters and tearing flags data
bool counters_saved = true;
for(size_t i = 0; i < 3; i++) {
furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_COUNTER_KEY, i);
if(!flipper_format_write_uint32(
ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) {
counters_saved = false;
break;
}
furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_TEARING_KEY, i);
if(!flipper_format_write_hex(
ff, furi_string_get_cstr(temp_str), &data->tearing_flag[i].data, 1)) {
counters_saved = false;
break;
}
}
if(!counters_saved) break;
// Write pages data
uint32_t pages_total = data->pages_total;
uint32_t pages_read = data->pages_read;
if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break;
if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1)) break;
bool pages_saved = true;
for(size_t i = 0; i < data->pages_total; i++) {
furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_PAGE_KEY, i);
if(!flipper_format_write_hex(
ff,
furi_string_get_cstr(temp_str),
data->page[i].data,
sizeof(MfUltralightPage))) {
pages_saved = false;
break;
}
}
if(!pages_saved) break;
// Write authentication counter
if(!flipper_format_write_uint32(
ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1))
break;
saved = true;
} while(false);
furi_string_free(temp_str);
return saved;
}
bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other) {
bool is_equal = false;
bool data_array_is_equal = true;
do {
if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break;
if(data->type != other->type) break;
if(data->pages_read != other->pages_read) break;
if(data->pages_total != other->pages_total) break;
if(data->auth_attempts != other->auth_attempts) break;
if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break;
if(memcmp(&data->signature, &other->signature, sizeof(data->signature)) != 0) break;
for(size_t i = 0; i < COUNT_OF(data->counter); i++) {
if(memcmp(&data->counter[i], &other->counter[i], sizeof(data->counter[i])) != 0) {
data_array_is_equal = false;
break;
}
}
if(!data_array_is_equal) break;
for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) {
if(memcmp(
&data->tearing_flag[i],
&other->tearing_flag[i],
sizeof(data->tearing_flag[i])) != 0) {
data_array_is_equal = false;
break;
}
}
if(!data_array_is_equal) break;
for(size_t i = 0; i < COUNT_OF(data->page); i++) {
if(memcmp(&data->page[i], &other->page[i], sizeof(data->page[i])) != 0) {
data_array_is_equal = false;
break;
}
}
if(!data_array_is_equal) break;
is_equal = true;
} while(false);
return is_equal;
}
const char*
mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type) {
furi_assert(data);
furi_assert(data->type < MfUltralightTypeNum);
return mf_ultralight_get_device_name_by_type(data->type, name_type);
}
const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len) {
furi_assert(data);
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
}
bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
}
Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data) {
furi_assert(data);
return data->iso14443_3a_data;
}
MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version) {
furi_assert(version);
MfUltralightType type = MfUltralightTypeUnknown;
if(version->storage_size == 0x0B || version->storage_size == 0x00) {
type = MfUltralightTypeUL11;
} else if(version->storage_size == 0x0E) {
type = MfUltralightTypeUL21;
} else if(version->storage_size == 0x0F) {
type = MfUltralightTypeNTAG213;
} else if(version->storage_size == 0x11) {
type = MfUltralightTypeNTAG215;
} else if(version->prod_subtype == 5 && version->prod_ver_major == 2) {
if(version->prod_ver_minor == 1) {
if(version->storage_size == 0x13) {
type = MfUltralightTypeNTAGI2C1K;
} else if(version->storage_size == 0x15) {
type = MfUltralightTypeNTAGI2C2K;
}
} else if(version->prod_ver_minor == 2) {
if(version->storage_size == 0x13) {
type = MfUltralightTypeNTAGI2CPlus1K;
} else if(version->storage_size == 0x15) {
type = MfUltralightTypeNTAGI2CPlus2K;
}
}
} else if(version->storage_size == 0x13) {
type = MfUltralightTypeNTAG216;
}
return type;
}
uint16_t mf_ultralight_get_pages_total(MfUltralightType type) {
return mf_ultralight_features[type].total_pages;
}
uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type) {
return mf_ultralight_features[type].feature_set;
}
bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data) {
furi_assert(iso14443_3a_data);
bool mfu_detected = (iso14443_3a_data->atqa[0] == 0x44) &&
(iso14443_3a_data->atqa[1] == 0x00) && (iso14443_3a_data->sak == 0x00);
return mfu_detected;
}
uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) {
return mf_ultralight_features[type].config_page;
}
uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type) {
uint8_t config_page = mf_ultralight_features[type].config_page;
return (config_page != 0) ? config_page + 2 : 0;
}
bool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page) {
uint8_t pwd_page = mf_ultralight_get_pwd_page_num(type);
uint8_t pack_page = pwd_page + 1;
return ((pwd_page != 0) && (page == pwd_page || page == pack_page));
}
bool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check) {
return (feature_set & features_to_check) != 0;
}
bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config) {
furi_assert(data);
furi_assert(config);
bool config_pages_found = false;
uint16_t config_page = mf_ultralight_features[data->type].config_page;
if(config_page != 0) {
*config = (MfUltralightConfigPages*)&data->page[config_page]; //-V1027
config_pages_found = true;
}
return config_pages_found;
}
bool mf_ultralight_is_all_data_read(const MfUltralightData* data) {
furi_assert(data);
bool all_read = false;
if(data->pages_read == data->pages_total ||
(data->type == MfUltralightTypeMfulC && data->pages_read == data->pages_total - 4)) {
// Having read all the pages doesn't mean that we've got everything.
// By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000,
// so a default read on an auth-supported NTAG is never complete.
uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type);
if(!mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportPasswordAuth)) {
all_read = true;
} else {
MfUltralightConfigPages* config = NULL;
if(mf_ultralight_get_config_page(data, &config)) {
uint32_t pass =
nfc_util_bytes2num(config->password.data, sizeof(MfUltralightAuthPassword));
uint16_t pack =
nfc_util_bytes2num(config->pack.data, sizeof(MfUltralightAuthPack));
all_read = ((pass != 0) || (pack != 0));
}
}
}
return all_read;
}
bool mf_ultralight_is_counter_configured(const MfUltralightData* data) {
furi_assert(data);
MfUltralightConfigPages* config = NULL;
bool configured = false;
switch(data->type) {
case MfUltralightTypeNTAG213:
case MfUltralightTypeNTAG215:
case MfUltralightTypeNTAG216:
if(mf_ultralight_get_config_page(data, &config)) {
configured = config->access.nfc_cnt_en;
}
break;
default:
configured = true;
break;
}
return configured;
}

Some files were not shown because too many files have changed in this diff Show More