mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
423 lines
14 KiB
C
423 lines
14 KiB
C
#include "mf_plus_i.h"
|
|
|
|
#define MF_PLUS_FFF_VERSION_KEY \
|
|
MF_PLUS_FFF_PICC_PREFIX " " \
|
|
"Version"
|
|
|
|
#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[][7] = {
|
|
{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
|
|
};
|
|
|
|
bool 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);
|
|
|
|
bool detected = false;
|
|
|
|
if(mf_plus_data->version.hw_major == 0x02 || mf_plus_data->version.hw_major == 0x82) {
|
|
detected = true;
|
|
if(iso14443_4a_data->iso14443_3a_data->sak == 0x10) {
|
|
// Mifare Plus 2K SL2
|
|
mf_plus_data->type = MfPlusTypePlus;
|
|
mf_plus_data->size = MfPlusSize2K;
|
|
mf_plus_data->security_level = MfPlusSecurityLevel2;
|
|
FURI_LOG_D(TAG, "Mifare Plus 2K SL2");
|
|
} else if(iso14443_4a_data->iso14443_3a_data->sak == 0x11) {
|
|
// Mifare Plus 4K SL3
|
|
mf_plus_data->type = MfPlusTypePlus;
|
|
mf_plus_data->size = MfPlusSize4K;
|
|
mf_plus_data->security_level = MfPlusSecurityLevel3;
|
|
FURI_LOG_D(TAG, "Mifare Plus 4K SL3");
|
|
} else {
|
|
// 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, "Miare Plus EV1/2 SL3");
|
|
} else {
|
|
// Mifare Plus EV1/2 SL1
|
|
mf_plus_data->security_level = MfPlusSecurityLevel1;
|
|
FURI_LOG_D(TAG, "Miare Plus EV1/2 SL1");
|
|
}
|
|
}
|
|
}
|
|
|
|
return detected;
|
|
}
|
|
|
|
bool mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data) {
|
|
furi_assert(iso4_data);
|
|
furi_assert(mf_plus_data);
|
|
|
|
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");
|
|
return true;
|
|
} 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");
|
|
return true;
|
|
} 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");
|
|
return true;
|
|
} else {
|
|
FURI_LOG_D(TAG, "Sak 08 but no known Mifare Plus type");
|
|
return false;
|
|
}
|
|
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");
|
|
return true;
|
|
} 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");
|
|
return true;
|
|
} else {
|
|
FURI_LOG_D(TAG, "Sak 18 but no known Mifare Plus type");
|
|
return false;
|
|
}
|
|
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[1] & 0x0F) == 0x04) {
|
|
// Mifare Plus S 2K SL3
|
|
mf_plus_data->size = MfPlusSize2K;
|
|
FURI_LOG_D(TAG, "Mifare Plus S 2K SL3");
|
|
} else if((iso4_data->iso14443_3a_data->atqa[1] & 0x0F) == 0x02) {
|
|
// Mifare Plus S 4K SL3
|
|
mf_plus_data->size = MfPlusSize4K;
|
|
FURI_LOG_D(TAG, "Mifare Plus S 4K SL3");
|
|
} else {
|
|
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (S)");
|
|
return false;
|
|
}
|
|
return true;
|
|
} 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) {
|
|
FURI_LOG_D(TAG, "Mifare Plus X SL3");
|
|
mf_plus_data->type = MfPlusTypeX;
|
|
mf_plus_data->security_level = MfPlusSecurityLevel3;
|
|
|
|
if((iso4_data->iso14443_3a_data->atqa[1] & 0x0F) == 0x04) {
|
|
mf_plus_data->size = MfPlusSize2K;
|
|
FURI_LOG_D(TAG, "Mifare Plus X 2K SL3");
|
|
} else if((iso4_data->iso14443_3a_data->atqa[1] & 0x0F) == 0x02) {
|
|
mf_plus_data->size = MfPlusSize4K;
|
|
FURI_LOG_D(TAG, "Mifare Plus X 4K SL3");
|
|
} else {
|
|
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type (X)");
|
|
return false;
|
|
}
|
|
return true;
|
|
} else {
|
|
FURI_LOG_D(TAG, "Sak 20 but no known Mifare Plus type");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FURI_LOG_D(TAG, "No known Mifare Plus type");
|
|
return false;
|
|
}
|
|
|
|
bool mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf) {
|
|
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusVersion);
|
|
|
|
if(can_parse) {
|
|
bit_buffer_write_bytes(buf, data, sizeof(MfPlusVersion));
|
|
}
|
|
|
|
return can_parse;
|
|
}
|
|
|
|
bool mf_plus_security_level_parse(MfPlusSecurityLevel* data, const BitBuffer* buf) {
|
|
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusSecurityLevel);
|
|
|
|
if(can_parse) {
|
|
bit_buffer_write_bytes(buf, data, sizeof(MfPlusSecurityLevel));
|
|
}
|
|
|
|
return can_parse;
|
|
}
|
|
|
|
bool mf_plus_type_parse(MfPlusType* data, const BitBuffer* buf) {
|
|
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusType);
|
|
|
|
if(can_parse) {
|
|
bit_buffer_write_bytes(buf, data, sizeof(MfPlusType));
|
|
}
|
|
|
|
return can_parse;
|
|
}
|
|
|
|
bool mf_plus_size_parse(MfPlusSize* data, const BitBuffer* buf) {
|
|
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfPlusSize);
|
|
|
|
if(can_parse) {
|
|
bit_buffer_write_bytes(buf, data, sizeof(MfPlusSize));
|
|
}
|
|
|
|
return can_parse;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
flipper_format_write_string(ff, MF_PLUS_FFF_SECURITY_LEVEL_KEY, security_level_string);
|
|
furi_string_free(security_level_string);
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
flipper_format_write_string(ff, MF_PLUS_FFF_CARD_TYPE_KEY, type_string);
|
|
furi_string_free(type_string);
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
flipper_format_write_string(ff, MF_PLUS_FFF_MEMORY_SIZE_KEY, size_string);
|
|
furi_string_free(size_string);
|
|
|
|
return true;
|
|
} |