1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 04:41:26 +04:00
Files
flipperzero-firmware/lib/nfc/protocols/mf_plus/mf_plus_i.c
Yukai Li a0d1d3fa0f nfc: Fix MIFARE Plus detection (#4049)
* nfc: Fix MIFARE Plus detection
  MIFARE Plus original doesn't have GetVersion support, so detection for SL2 has been moved. Also, SL2 only exists in MIFARE Plus X, so despite it not being specified in the type identification procedure chart, it's safe to call it for what it is.
* Fix spelling
* TODO: mark as non flipper one

Co-authored-by: あく <alleteam@gmail.com>
2025-01-13 03:15:52 +09:00

429 lines
14 KiB
C

#include "mf_plus_i.h"
#define MF_PLUS_FFF_VERSION_KEY \
MF_PLUS_FFF_PICC_PREFIX " " \
"Version"
#define MF_PLUS_T1_TK_VALUE_LEN 7
#define MF_PLUS_FFF_SECURITY_LEVEL_KEY "Security Level"
#define MF_PLUS_FFF_CARD_TYPE_KEY "Card Type"
#define MF_PLUS_FFF_MEMORY_SIZE_KEY "Memory Size"
#define TAG "MfPlus"
const uint8_t mf_plus_ats_t1_tk_values[][MF_PLUS_T1_TK_VALUE_LEN] = {
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0x35, 0xC7}, // Mifare Plus S
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xBC, 0xD6}, // Mifare Plus X
{0xC1, 0x05, 0x2F, 0x2F, 0x00, 0xF6, 0xD1}, // Mifare Plus SE
{0xC1, 0x05, 0x2F, 0x2F, 0x01, 0xF6, 0xD1}, // Mifare Plus SE
};
MfPlusError mf_plus_get_type_from_version(
const Iso14443_4aData* iso14443_4a_data,
MfPlusData* mf_plus_data) {
furi_assert(iso14443_4a_data);
furi_assert(mf_plus_data);
MfPlusError error = MfPlusErrorProtocol;
if(mf_plus_data->version.hw_type == 0x02 || mf_plus_data->version.hw_type == 0x82) {
error = MfPlusErrorNone;
// Mifare Plus EV1/EV2
// Revision
switch(mf_plus_data->version.hw_major) {
case 0x11:
mf_plus_data->type = MfPlusTypeEV1;
FURI_LOG_D(TAG, "Mifare Plus EV1");
break;
case 0x22:
mf_plus_data->type = MfPlusTypeEV2;
FURI_LOG_D(TAG, "Mifare Plus EV2");
break;
default:
mf_plus_data->type = MfPlusTypeUnknown;
FURI_LOG_D(TAG, "Unknown Mifare Plus EV type");
break;
}
// Storage size
switch(mf_plus_data->version.hw_storage) {
case 0x16:
mf_plus_data->size = MfPlusSize2K;
FURI_LOG_D(TAG, "2K");
break;
case 0x18:
mf_plus_data->size = MfPlusSize4K;
FURI_LOG_D(TAG, "4K");
break;
default:
mf_plus_data->size = MfPlusSizeUnknown;
FURI_LOG_D(TAG, "Unknown storage size");
break;
}
// Security level
if(iso14443_4a_data->iso14443_3a_data->sak == 0x20) {
// Mifare Plus EV1/2 SL3
mf_plus_data->security_level = MfPlusSecurityLevel3;
FURI_LOG_D(TAG, "Mifare Plus EV1/2 SL3");
} else {
// Mifare Plus EV1/2 SL1
mf_plus_data->security_level = MfPlusSecurityLevel1;
FURI_LOG_D(TAG, "Mifare Plus EV1/2 SL1");
}
}
return error;
}
MfPlusError
mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) {
furi_assert(iso4_data);
furi_assert(mf_plus_data);
MfPlusError error = MfPlusErrorProtocol;
if(simple_array_get_count(iso4_data->ats_data.t1_tk) != MF_PLUS_T1_TK_VALUE_LEN) {
return MfPlusErrorProtocol;
}
switch(iso4_data->iso14443_3a_data->sak) {
case 0x08:
if(memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[0],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus S 2K SL1
mf_plus_data->type = MfPlusTypeS;
mf_plus_data->size = MfPlusSize2K;
mf_plus_data->security_level = MfPlusSecurityLevel1;
FURI_LOG_D(TAG, "Mifare Plus S 2K SL1");
error = MfPlusErrorNone;
} else if(
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[1],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus X 2K SL1
mf_plus_data->type = MfPlusTypeX;
mf_plus_data->size = MfPlusSize2K;
mf_plus_data->security_level = MfPlusSecurityLevel1;
FURI_LOG_D(TAG, "Mifare Plus X 2K SL1");
error = MfPlusErrorNone;
} else if(
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[2],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0 ||
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[3],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus SE 1K SL1
mf_plus_data->type = MfPlusTypeSE;
mf_plus_data->size = MfPlusSize1K;
mf_plus_data->security_level = MfPlusSecurityLevel1;
FURI_LOG_D(TAG, "Mifare Plus SE 1K SL1");
error = MfPlusErrorNone;
} else {
FURI_LOG_D(TAG, "Sak 08 but no known Mifare Plus type");
}
break;
case 0x10:
// Mifare Plus X 2K SL2
mf_plus_data->type = MfPlusTypeX;
mf_plus_data->size = MfPlusSize2K;
mf_plus_data->security_level = MfPlusSecurityLevel2;
FURI_LOG_D(TAG, "Mifare Plus X 2K SL2");
error = MfPlusErrorNone;
break;
case 0x11:
// Mifare Plus X 4K SL2
mf_plus_data->type = MfPlusTypeX;
mf_plus_data->size = MfPlusSize4K;
mf_plus_data->security_level = MfPlusSecurityLevel2;
FURI_LOG_D(TAG, "Mifare Plus X 4K SL2");
error = MfPlusErrorNone;
break;
case 0x18:
if(memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[0],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus S 4K SL1
mf_plus_data->type = MfPlusTypeS;
mf_plus_data->size = MfPlusSize4K;
mf_plus_data->security_level = MfPlusSecurityLevel1;
FURI_LOG_D(TAG, "Mifare Plus S 4K SL1");
error = MfPlusErrorNone;
} else if(
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[1],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus X 4K SL1
mf_plus_data->type = MfPlusTypeX;
mf_plus_data->size = MfPlusSize4K;
mf_plus_data->security_level = MfPlusSecurityLevel1;
FURI_LOG_D(TAG, "Mifare Plus X 4K SL1");
error = MfPlusErrorNone;
} else {
FURI_LOG_D(TAG, "Sak 18 but no known Mifare Plus type");
}
break;
case 0x20:
if(memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[0],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
// Mifare Plus S 2/4K SL3
FURI_LOG_D(TAG, "Mifare Plus S SL3");
mf_plus_data->type = MfPlusTypeS;
mf_plus_data->security_level = MfPlusSecurityLevel3;
if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {
// Mifare Plus S 2K SL3
mf_plus_data->size = MfPlusSize2K;
FURI_LOG_D(TAG, "Mifare Plus S 2K SL3");
error = MfPlusErrorNone;
} else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {
// Mifare Plus S 4K SL3
mf_plus_data->size = MfPlusSize4K;
FURI_LOG_D(TAG, "Mifare Plus S 4K SL3");
error = MfPlusErrorNone;
} else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)");
}
} else if(
memcmp(
simple_array_get_data(iso4_data->ats_data.t1_tk),
mf_plus_ats_t1_tk_values[1],
simple_array_get_count(iso4_data->ats_data.t1_tk)) == 0) {
mf_plus_data->type = MfPlusTypeX;
mf_plus_data->security_level = MfPlusSecurityLevel3;
FURI_LOG_D(TAG, "Mifare Plus X SL3");
if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x04) {
mf_plus_data->size = MfPlusSize2K;
FURI_LOG_D(TAG, "Mifare Plus X 2K SL3");
error = MfPlusErrorNone;
} else if((iso4_data->iso14443_3a_data->atqa[0] & 0x0F) == 0x02) {
mf_plus_data->size = MfPlusSize4K;
FURI_LOG_D(TAG, "Mifare Plus X 4K SL3");
error = MfPlusErrorNone;
} else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)");
}
} else {
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type");
}
}
return error;
}
MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) {
bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion);
if(can_parse) {
bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion));
} else if(
bit_buffer_get_size_bytes(buf) == 8 &&
bit_buffer_get_byte(buf, 0) == MF_PLUS_STATUS_ADDITIONAL_FRAME) {
// HACK(-nofl): There are supposed to be three parts to the GetVersion command,
// with the second and third parts fetched by sending the AdditionalFrame
// command. I don't know whether the entire MIFARE Plus line uses status as
// the first byte, so let's just assume we only have the first part of
// the response if it's size 8 and starts with the AF status. The second
// part of the response is the same size and status byte, but so far
// we're only reading one response.
can_parse = true;
bit_buffer_write_bytes_mid(buf, data, 1, bit_buffer_get_size_bytes(buf) - 1);
}
return can_parse ? MfPlusErrorNone : MfPlusErrorProtocol;
}
bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff) {
return flipper_format_read_hex(
ff, MF_PLUS_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfPlusVersion));
}
bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff) {
FuriString* security_level_string = furi_string_alloc();
flipper_format_read_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
// Take the last character of the string
char security_level_char = furi_string_get_char(
security_level_string, furi_string_utf8_length(security_level_string) - 1);
switch(security_level_char) {
case '0':
*data = MfPlusSecurityLevel0;
break;
case '1':
*data = MfPlusSecurityLevel1;
break;
case '2':
*data = MfPlusSecurityLevel2;
break;
case '3':
*data = MfPlusSecurityLevel3;
break;
default:
*data = MfPlusSecurityLevelUnknown;
break;
}
furi_string_free(security_level_string);
return true;
}
bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff) {
FuriString* type_string = furi_string_alloc();
flipper_format_read_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
if(furi_string_equal_str(type_string, "Mifare Plus")) {
*data = MfPlusTypePlus;
} else if(furi_string_equal_str(type_string, "Mifare Plus X")) {
*data = MfPlusTypeX;
} else if(furi_string_equal_str(type_string, "Mifare Plus S")) {
*data = MfPlusTypeS;
} else if(furi_string_equal_str(type_string, "Mifare Plus SE")) {
*data = MfPlusTypeSE;
} else if(furi_string_equal_str(type_string, "Mifare Plus EV1")) {
*data = MfPlusTypeEV1;
} else if(furi_string_equal_str(type_string, "Mifare Plus EV2")) {
*data = MfPlusTypeEV2;
} else {
*data = MfPlusTypeUnknown;
}
furi_string_free(type_string);
return true;
}
bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff) {
FuriString* size_string = furi_string_alloc();
flipper_format_read_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
if(furi_string_equal_str(size_string, "1K")) {
*data = MfPlusSize1K;
} else if(furi_string_equal_str(size_string, "2K")) {
*data = MfPlusSize2K;
} else if(furi_string_equal_str(size_string, "4K")) {
*data = MfPlusSize4K;
} else {
*data = MfPlusSizeUnknown;
}
furi_string_free(size_string);
return true;
}
bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff) {
return flipper_format_write_hex(
ff, MF_PLUS_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfPlusVersion));
}
bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff) {
FuriString* security_level_string = furi_string_alloc();
switch(*data) {
case MfPlusSecurityLevel0:
furi_string_cat(security_level_string, "SL0");
break;
case MfPlusSecurityLevel1:
furi_string_cat(security_level_string, "SL1");
break;
case MfPlusSecurityLevel2:
furi_string_cat(security_level_string, "SL2");
break;
case MfPlusSecurityLevel3:
furi_string_cat(security_level_string, "SL3");
break;
default:
furi_string_cat(security_level_string, "Unknown");
break;
}
bool success =
flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
furi_string_free(security_level_string);
return success;
}
bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff) {
FuriString* type_string = furi_string_alloc();
switch(*data) {
case MfPlusTypePlus:
furi_string_cat(type_string, "Mifare Plus");
break;
case MfPlusTypeX:
furi_string_cat(type_string, "Mifare Plus X");
break;
case MfPlusTypeS:
furi_string_cat(type_string, "Mifare Plus S");
break;
case MfPlusTypeSE:
furi_string_cat(type_string, "Mifare Plus SE");
break;
case MfPlusTypeEV1:
furi_string_cat(type_string, "Mifare Plus EV1");
break;
case MfPlusTypeEV2:
furi_string_cat(type_string, "Mifare Plus EV2");
break;
default:
furi_string_cat(type_string, "Unknown");
break;
}
bool success = flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
furi_string_free(type_string);
return success;
}
bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff) {
FuriString* size_string = furi_string_alloc();
switch(*data) {
case MfPlusSize1K:
furi_string_cat(size_string, "1K");
break;
case MfPlusSize2K:
furi_string_cat(size_string, "2K");
break;
case MfPlusSize4K:
furi_string_cat(size_string, "4K");
break;
default:
furi_string_cat(size_string, "Unknown");
break;
}
bool success = flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
furi_string_free(size_string);
return success;
}