mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 12:51:22 +04:00
NFC: Fix NDEF parser for MIFARE Classic (#4153)
* Add div() to API
* Revert "Add div() to API"
This reverts commit e03b5c4244.
* Use / and %
* NFC: More MFC NDEF fixes
* Simplify duplicated code in MFC data generator
* NFC: Print NDEF hex data with pretty format
* NFC: Consider NDEF strings with last \0 byte as text
* Pretty Format: Add padding to last line to keep table width
---------
Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
#include <nfc/protocols/slix/slix.h>
|
#include <nfc/protocols/slix/slix.h>
|
||||||
|
|
||||||
#include <bit_lib.h>
|
#include <bit_lib.h>
|
||||||
|
#include <toolbox/pretty_format.h>
|
||||||
|
|
||||||
#define TAG "NDEF"
|
#define TAG "NDEF"
|
||||||
|
|
||||||
@@ -181,30 +182,34 @@ static bool ndef_get(Ndef* ndef, size_t pos, size_t len, void* buf) {
|
|||||||
// So the first 93 (31*3) data blocks correspond to 128 real blocks.
|
// So the first 93 (31*3) data blocks correspond to 128 real blocks.
|
||||||
// Last 128 blocks are 8 sectors: 15 data blocks, 1 sector trailer.
|
// Last 128 blocks are 8 sectors: 15 data blocks, 1 sector trailer.
|
||||||
// So the last 120 (8*15) data blocks correspond to 128 real blocks.
|
// So the last 120 (8*15) data blocks correspond to 128 real blocks.
|
||||||
div_t small_sector_data_blocks = div(pos, MF_CLASSIC_BLOCK_SIZE);
|
const size_t real_block_data_offset = pos % MF_CLASSIC_BLOCK_SIZE;
|
||||||
|
size_t small_sector_data_blocks = pos / MF_CLASSIC_BLOCK_SIZE;
|
||||||
size_t large_sector_data_blocks = 0;
|
size_t large_sector_data_blocks = 0;
|
||||||
if(small_sector_data_blocks.quot > 93) {
|
if(small_sector_data_blocks > 93) {
|
||||||
large_sector_data_blocks = small_sector_data_blocks.quot - 93;
|
large_sector_data_blocks = small_sector_data_blocks - 93;
|
||||||
small_sector_data_blocks.quot = 93;
|
small_sector_data_blocks = 93;
|
||||||
}
|
}
|
||||||
|
|
||||||
div_t small_sectors = div(small_sector_data_blocks.quot, 3);
|
const size_t small_sector_block_offset = small_sector_data_blocks % 3;
|
||||||
size_t real_block = small_sectors.quot * 4 + small_sectors.rem;
|
const size_t small_sectors = small_sector_data_blocks / 3;
|
||||||
if(small_sectors.quot >= 16) {
|
size_t real_block = small_sectors * 4 + small_sector_block_offset;
|
||||||
|
if(small_sectors >= 16) {
|
||||||
real_block += 4; // Skip MAD2
|
real_block += 4; // Skip MAD2
|
||||||
}
|
}
|
||||||
if(large_sector_data_blocks) {
|
if(large_sector_data_blocks) {
|
||||||
div_t large_sectors = div(large_sector_data_blocks, 15);
|
const size_t large_sector_block_offset = large_sector_data_blocks % 15;
|
||||||
real_block += large_sectors.quot * 16 + large_sectors.rem;
|
const size_t large_sectors = large_sector_data_blocks / 15;
|
||||||
|
real_block += large_sectors * 16 + large_sector_block_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* cur = &ndef->mfc.blocks[real_block].data[small_sector_data_blocks.rem];
|
const uint8_t* cur = &ndef->mfc.blocks[real_block].data[real_block_data_offset];
|
||||||
while(len) {
|
while(len) {
|
||||||
size_t sector_trailer = mf_classic_get_sector_trailer_num_by_block(real_block);
|
size_t sector_trailer = mf_classic_get_sector_trailer_num_by_block(real_block);
|
||||||
const uint8_t* end = &ndef->mfc.blocks[sector_trailer].data[0];
|
const uint8_t* end = &ndef->mfc.blocks[sector_trailer].data[0];
|
||||||
|
|
||||||
size_t chunk_len = MIN((size_t)(end - cur), len);
|
const size_t chunk_len = MIN((size_t)(end - cur), len);
|
||||||
memcpy(buf, cur, chunk_len);
|
memcpy(buf, cur, chunk_len);
|
||||||
|
buf += chunk_len;
|
||||||
len -= chunk_len;
|
len -= chunk_len;
|
||||||
|
|
||||||
if(len) {
|
if(len) {
|
||||||
@@ -244,7 +249,9 @@ static inline bool is_printable(char c) {
|
|||||||
|
|
||||||
static bool is_text(const uint8_t* buf, size_t len) {
|
static bool is_text(const uint8_t* buf, size_t len) {
|
||||||
for(size_t i = 0; i < len; i++) {
|
for(size_t i = 0; i < len; i++) {
|
||||||
if(!is_printable(buf[i])) return false;
|
if(!is_printable(buf[i]) && !(buf[i] == '\0' && i == len - 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -260,7 +267,7 @@ static bool ndef_dump(Ndef* ndef, const char* prefix, size_t pos, size_t len, bo
|
|||||||
for(size_t i = 0; i < len; i++) {
|
for(size_t i = 0; i < len; i++) {
|
||||||
char c;
|
char c;
|
||||||
if(!ndef_get(ndef, pos + i, 1, &c)) return false;
|
if(!ndef_get(ndef, pos + i, 1, &c)) return false;
|
||||||
if(!is_printable(c)) {
|
if(!is_printable(c) && !(c == '\0' && i == len - 1)) {
|
||||||
furi_string_left(ndef->output, string_prev);
|
furi_string_left(ndef->output, string_prev);
|
||||||
force_hex = true;
|
force_hex = true;
|
||||||
break;
|
break;
|
||||||
@@ -268,14 +275,18 @@ static bool ndef_dump(Ndef* ndef, const char* prefix, size_t pos, size_t len, bo
|
|||||||
furi_string_push_back(ndef->output, c);
|
furi_string_push_back(ndef->output, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(force_hex) {
|
if(!force_hex) {
|
||||||
for(size_t i = 0; i < len; i++) {
|
furi_string_cat(ndef->output, "\n");
|
||||||
uint8_t b;
|
} else {
|
||||||
if(!ndef_get(ndef, pos + i, 1, &b)) return false;
|
uint8_t buf[4];
|
||||||
furi_string_cat_printf(ndef->output, "%02X ", b);
|
for(size_t i = 0; i < len; i += sizeof(buf)) {
|
||||||
|
uint8_t buf_len = MIN(sizeof(buf), len - i);
|
||||||
|
if(!ndef_get(ndef, pos + i, buf_len, &buf)) return false;
|
||||||
|
pretty_format_bytes_hex_canonical(
|
||||||
|
ndef->output, 4, PRETTY_FORMAT_FONT_MONOSPACE, buf, buf_len);
|
||||||
|
furi_string_cat(ndef->output, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
furi_string_cat(ndef->output, "\n");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,9 +296,7 @@ static void
|
|||||||
if(!force_hex && is_text(buf, len)) {
|
if(!force_hex && is_text(buf, len)) {
|
||||||
furi_string_cat_printf(ndef->output, "%.*s", len, (const char*)buf);
|
furi_string_cat_printf(ndef->output, "%.*s", len, (const char*)buf);
|
||||||
} else {
|
} else {
|
||||||
for(size_t i = 0; i < len; i++) {
|
pretty_format_bytes_hex_canonical(ndef->output, 4, PRETTY_FORMAT_FONT_MONOSPACE, buf, len);
|
||||||
furi_string_cat_printf(ndef->output, "%02X ", ((const uint8_t*)buf)[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
furi_string_cat(ndef->output, "\n");
|
furi_string_cat(ndef->output, "\n");
|
||||||
}
|
}
|
||||||
@@ -582,7 +591,7 @@ bool ndef_parse_record(
|
|||||||
NdefTnf tnf,
|
NdefTnf tnf,
|
||||||
const char* type,
|
const char* type,
|
||||||
uint8_t type_len) {
|
uint8_t type_len) {
|
||||||
FURI_LOG_D(TAG, "payload type: %.*s len: %hu", type_len, type, len);
|
FURI_LOG_D(TAG, "payload type: %.*s len: %hu pos: %zu", type_len, type, len, pos);
|
||||||
if(!len) {
|
if(!len) {
|
||||||
furi_string_cat(ndef->output, "Empty\n");
|
furi_string_cat(ndef->output, "Empty\n");
|
||||||
return true;
|
return true;
|
||||||
@@ -887,13 +896,13 @@ static bool ndef_mfc_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
for(uint8_t mad = 0; mad < COUNT_OF(mads); mad++) {
|
for(uint8_t mad = 0; mad < COUNT_OF(mads); mad++) {
|
||||||
const size_t block = mads[mad].block;
|
const size_t block = mads[mad].block;
|
||||||
const size_t sector = mf_classic_get_sector_by_block(block);
|
const size_t sector = mf_classic_get_sector_by_block(block);
|
||||||
if(sector_count <= sector) break; // Skip this MAD if not present
|
if(sector_count <= sector) continue; // Skip this MAD if not present
|
||||||
// Check MAD key
|
// Check MAD key
|
||||||
const MfClassicSectorTrailer* sector_trailer =
|
const MfClassicSectorTrailer* sector_trailer =
|
||||||
mf_classic_get_sector_trailer_by_sector(data, sector);
|
mf_classic_get_sector_trailer_by_sector(data, sector);
|
||||||
const uint64_t sector_key_a = bit_lib_bytes_to_num_be(
|
const uint64_t sector_key_a = bit_lib_bytes_to_num_be(
|
||||||
sector_trailer->key_a.data, COUNT_OF(sector_trailer->key_a.data));
|
sector_trailer->key_a.data, COUNT_OF(sector_trailer->key_a.data));
|
||||||
if(sector_key_a != mad_key) return false;
|
if(sector_key_a != mad_key) continue;
|
||||||
// Find NDEF AIDs
|
// Find NDEF AIDs
|
||||||
for(uint8_t aid_index = 0; aid_index < mads[mad].aid_count; aid_index++) {
|
for(uint8_t aid_index = 0; aid_index < mads[mad].aid_count; aid_index++) {
|
||||||
const uint8_t* aid = &data->block[block].data[2 + aid_index * AID_SIZE];
|
const uint8_t* aid = &data->block[block].data[2 + aid_index * AID_SIZE];
|
||||||
@@ -917,7 +926,7 @@ static bool ndef_mfc_parse(const NfcDevice* device, FuriString* parsed_data) {
|
|||||||
data_size = 93 + (sector_count - 32) * 15;
|
data_size = 93 + (sector_count - 32) * 15;
|
||||||
} else {
|
} else {
|
||||||
data_size = sector_count * 3;
|
data_size = sector_count * 3;
|
||||||
if(sector_count >= 16) {
|
if(sector_count > 16) {
|
||||||
data_size -= 3; // Skip MAD2
|
data_size -= 3; // Skip MAD2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -392,37 +392,15 @@ static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfCl
|
|||||||
|
|
||||||
mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]);
|
mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]);
|
||||||
|
|
||||||
|
// Set every block to 0x00
|
||||||
uint16_t block_num = mf_classic_get_total_block_num(type);
|
uint16_t block_num = mf_classic_get_total_block_num(type);
|
||||||
if(type == MfClassicType4k) {
|
for(uint16_t i = 1; i < block_num; i++) {
|
||||||
// Set every block to 0x00
|
if(mf_classic_is_sector_trailer(i)) {
|
||||||
for(uint16_t i = 1; i < block_num; i++) {
|
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||||
if(mf_classic_is_sector_trailer(i)) {
|
} else {
|
||||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
memset(&mfc_data->block[i].data, 0x00, MF_CLASSIC_BLOCK_SIZE);
|
||||||
} else {
|
|
||||||
memset(&mfc_data->block[i].data, 0x00, 16);
|
|
||||||
}
|
|
||||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
|
||||||
}
|
|
||||||
} else if(type == MfClassicType1k) {
|
|
||||||
// Set every block to 0x00
|
|
||||||
for(uint16_t i = 1; i < block_num; i++) {
|
|
||||||
if(mf_classic_is_sector_trailer(i)) {
|
|
||||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
|
||||||
} else {
|
|
||||||
memset(&mfc_data->block[i].data, 0x00, 16);
|
|
||||||
}
|
|
||||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
|
||||||
}
|
|
||||||
} else if(type == MfClassicTypeMini) {
|
|
||||||
// Set every block to 0x00
|
|
||||||
for(uint16_t i = 1; i < block_num; i++) {
|
|
||||||
if(mf_classic_is_sector_trailer(i)) {
|
|
||||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
|
||||||
} else {
|
|
||||||
memset(&mfc_data->block[i].data, 0x00, 16);
|
|
||||||
}
|
|
||||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
|
||||||
}
|
}
|
||||||
|
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc_generate_mf_classic_block_0(
|
nfc_generate_mf_classic_block_0(
|
||||||
|
|||||||
@@ -36,11 +36,17 @@ void pretty_format_bytes_hex_canonical(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const size_t begin_idx = i;
|
const size_t begin_idx = i;
|
||||||
const size_t end_idx = MIN(i + num_places, data_size);
|
const size_t wrap_idx = i + num_places;
|
||||||
|
const size_t end_idx = MIN(wrap_idx, data_size);
|
||||||
|
|
||||||
for(size_t j = begin_idx; j < end_idx; j++) {
|
for(size_t j = begin_idx; j < end_idx; j++) {
|
||||||
furi_string_cat_printf(result, "%02X ", data[j]);
|
furi_string_cat_printf(result, "%02X ", data[j]);
|
||||||
}
|
}
|
||||||
|
if(end_idx < wrap_idx) {
|
||||||
|
for(size_t j = end_idx; j < wrap_idx; j++) {
|
||||||
|
furi_string_cat_printf(result, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
furi_string_push_back(result, '|');
|
furi_string_push_back(result, '|');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user