This commit is contained in:
Willy-JL
2023-06-15 02:32:48 +01:00
7 changed files with 468 additions and 36 deletions
+229 -27
View File
@@ -20,6 +20,7 @@ static const uint32_t nfc_keys_file_version = 1;
// Protocols format versions
static const uint32_t nfc_mifare_classic_data_format_version = 2;
static const uint32_t nfc_mifare_ultralight_data_format_version = 1;
static const uint32_t nfc_felica_data_format_version = 1;
NfcDevice* nfc_device_alloc() {
NfcDevice* nfc_dev = malloc(sizeof(NfcDevice));
@@ -60,6 +61,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_
furi_string_set(format_string, "Mifare DESFire");
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
furi_string_set(format_string, "ISO15693");
} else if(dev->format == NfcDeviceSaveFormatFelica) {
furi_string_set(format_string, "FeliCa");
} else {
furi_string_set(format_string, "Unknown");
}
@@ -1114,6 +1117,201 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice*
return saved;
}
static bool nfc_device_save_felica_lite(FlipperFormat* file, FelicaLiteInfo* info) {
bool saved = false;
FuriString* key = furi_string_alloc();
do {
flipper_format_write_comment_cstr(file, "Lite(-S) System");
flipper_format_write_hex(
file, "Data Format Code", info->data_format_code, sizeof(uint16_t));
flipper_format_write_hex(file, "ID Arbitrary Value", info->ID_value, 6);
flipper_format_write_hex(file, "Memory Config", info->memory_config, FELICA_BLOCK_SIZE);
for(uint8_t block_num = 0; block_num < 14; block_num++) {
FuriString* spad_str = furi_string_alloc();
for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) {
if(info->S_PAD[block_num] != NULL) {
furi_string_cat_printf(spad_str, "%02X ", info->S_PAD[block_num][i]);
} else {
furi_string_cat_printf(spad_str, "?? ");
}
}
furi_string_printf(key, "S_PAD%d", block_num);
flipper_format_write_string(file, furi_string_get_cstr(key), spad_str);
}
FuriString* reg_str = furi_string_alloc();
for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) {
if(info->REG != NULL) {
furi_string_cat_printf(reg_str, "%02X ", info->REG[i]);
} else {
furi_string_cat_printf(reg_str, "?? ");
}
}
flipper_format_write_string(file, "REG", reg_str);
flipper_format_write_hex(
file, "Card Key Version", &info->card_key_version, sizeof(uint16_t));
FuriString* ck1_str = furi_string_alloc();
for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) {
if(info->REG != NULL) {
furi_string_cat_printf(ck1_str, "%02X ", info->card_key_1[i]);
} else {
furi_string_cat_printf(ck1_str, "?? ");
}
}
flipper_format_write_string(file, "Card Key 1", ck1_str);
FuriString* ck2_str = furi_string_alloc();
for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) {
if(info->REG != NULL) {
furi_string_cat_printf(ck2_str, "%02X ", info->card_key_2[i]);
} else {
furi_string_cat_printf(ck2_str, "?? ");
}
}
flipper_format_write_string(file, "Card Key 2", ck2_str);
flipper_format_write_hex(file, "Fixed Challenge MAC Response", info->MAC, 8);
flipper_format_write_bool(file, "Is Lite-S", &info->is_lite_s, 1);
if(info->is_lite_s) {
flipper_format_write_hex(file, "Fixed Challenge MAC-A Response", info->MAC_A, 8);
flipper_format_write_uint32(file, "Write Count", &info->write_count, 1);
}
} while(false);
return saved;
}
static bool nfc_device_save_felica_area(FlipperFormat* file, FelicaArea* area) {
bool saved = false;
FuriString* prefix = furi_string_alloc_printf("Area %d", area->number);
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s Can Create Subareas", prefix);
flipper_format_write_bool(file, furi_string_get_cstr(key), &area->can_create_subareas, 1);
furi_string_printf(key, "%s End Service Code", prefix);
flipper_format_write_hex(
file, furi_string_get_cstr(key), (uint8_t*)&area->end_service_code, sizeof(uint16_t));
bool node_saved = true;
for
M_EACH(node, area->nodes, FelicaNodeArray_t) {
if(nfc_device_save_felica_node(file, node)) {
node_saved = false;
break;
}
}
if(!node_saved) break;
saved = true;
} while(false);
return saved;
}
static bool nfc_device_save_felica_service(FlipperFormat* file, FelicaService* service) {
bool saved = false;
FuriString* prefix = furi_string_alloc_printf("Service %d", service->number);
FuriString* key = furi_string_alloc();
do {
furi_string_printf(key, "%s Is Extended Overlap", prefix);
flipper_format_write_bool(
file, furi_string_get_cstr(key), &service->is_extended_overlap, 1);
if(service->is_extended_overlap) {
furi_string_printf(key, "%s Overlap Target", prefix);
flipper_format_write_hex(
file,
furi_string_get_cstr(key),
(uint8_t*)&service->overlap_target,
sizeof(uint16_t));
furi_string_printf(key, "%s Block Start", prefix);
const uint32_t block_start = service->block_start;
flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_start, 1);
furi_string_printf(key, "%s Block Count", prefix);
const uint32_t block_count = service->block_count;
flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1);
uint32_t i = 0;
for
M_EACH(block, service->blocks, FelicaBlockArray_t) {
furi_string_printf(key, "%s Block %d", prefix, i);
flipper_format_write_hex(
file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE);
}
} else {
furi_string_printf(key, "%s Block Count", prefix);
uint32_t block_count = FelicaBlockArray_size(service->blocks);
flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1);
uint32_t i = 0;
for
M_EACH(block, service->blocks, FelicaBlockArray_t) {
furi_string_printf(key, "%s Block %d", prefix, i);
flipper_format_write_hex(
file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE);
}
}
saved = true;
} while(false);
}
static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node) {
bool saved = false;
FuriString* key = furi_string_alloc();
do {
if(node->type == FelicaNodeTypeArea) {
if(!nfc_device_save_felica_node(file, node->area)) {
saved = false;
break;
}
} else if(node->type == FelicaNodeTypeService) {
if(!nfc_device_save_felica_service(file, node->area)) {
saved = false;
break;
}
}
saved = true;
} while(false);
return saved;
}
static bool nfc_device_save_felica_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
FelicaData* data = &dev->dev_data.felica_data;
// Save FeliCa specific data
do {
if(!flipper_format_write_comment_cstr(file, "FeliCa specific data")) break;
if(!flipper_format_write_uint32(
file, "Data format version", &nfc_felica_data_format_version, 1))
break;
for
M_EACH(system, data->systems, FelicaSystemArray_t) {
flipper_format_write_hex(file, "System", &system->number, sizeof(uint8_t));
flipper_format_write_hex(file, "Code", (uint8_t*)&system->code, sizeof(uint16_t));
if(system->code == LITE_SYSTEM_CODE) {
nfc_device_save_felica_lite(file, &system->lite_info);
} else {
nfc_device_save_felica_node(file, &system->root);
}
}
} while(false);
return saved;
}
static void nfc_device_load_mifare_classic_block(
FuriString* block_str,
MfClassicData* data,
@@ -1402,39 +1600,43 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break;
// Write nfc device type
if(!flipper_format_write_comment_cstr(
file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693"))
file,
"Nfc device type can be UID, Mifare Ultralight, Mifare Classic, FeliCa or ISO15693"))
break;
nfc_device_prepare_format_string(dev, temp_str);
if(!flipper_format_write_string(file, "Device type", temp_str)) break;
// Write UID
if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break;
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
if(data->type == FuriHalNfcTypeA) {
if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break;
if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break;
if(dev->format != NfcDeviceSaveFormatNfcV) {
// Write ATQA, SAK
if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break;
// Save ATQA in MSB order for correct companion apps display
uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]};
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break;
}
if(dev->format != NfcDeviceSaveFormatNfcV) {
// Write ATQA, SAK
if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break;
// Save ATQA in MSB order for correct companion apps display
uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]};
if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break;
if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break;
}
// Save more data if necessary
if(dev->format == NfcDeviceSaveFormatMifareUl) {
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
if(!nfc_device_save_mifare_df_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
if(!nfc_device_save_nfcv_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
if(!nfc_device_save_bank_card_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
// Save data
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
// Save keys cache
if(!nfc_device_save_mifare_classic_keys(dev)) break;
// Save more data if necessary
if(dev->format == NfcDeviceSaveFormatMifareUl) {
if(!nfc_device_save_mifare_ul_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareDesfire) {
if(!nfc_device_save_mifare_df_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatNfcV) {
if(!nfc_device_save_nfcv_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatBankCard) {
if(!nfc_device_save_bank_card_data(file, dev)) break;
} else if(dev->format == NfcDeviceSaveFormatMifareClassic) {
// Save data
if(!nfc_device_save_mifare_classic_data(file, dev)) break;
// Save keys cache
if(!nfc_device_save_mifare_classic_keys(dev)) break;
}
saved = true;
} else if(data->type == FuriHalNfcTypeF) {
if(!nfc_device_save_felica_data(file, dev)) break;
}
saved = true;
} while(0);
if(!saved) { //-V547
+216 -4
View File
@@ -1,4 +1,5 @@
#include <limits.h>
#include <mbedtls/des.h>
#include <mbedtls/sha1.h>
#include "felica.h"
#include "nfc_util.h"
@@ -151,6 +152,215 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) {
return FelicaICType2K;
}
static void felica_lite_diversify_key(uint8_t* id_block, uint8_t* master_key, uint8_t* card_key) {
uint8_t ZERO[8] = {0};
uint8_t L[8];
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set3key_enc(&ctx, master_key);
mbedtls_des3_crypt_ecb(&ctx, ZERO, L);
mbedtls_des3_free(&ctx);
uint8_t K1[8];
for(int i = 0; i < 8; i++) {
K1[i] = L[i] << 1;
if(i < 7) {
K1[i] |= (L[i + 1] >> 7);
}
}
if((L[0] ^ 0x80) == 0) {
K1[7] ^= 0x1B;
}
uint8_t M1[8];
uint8_t M2[8];
memcpy(M1, id_block, 8);
memcpy(M2, id_block + 8, 8);
for(int i = 0; i < 8; i++) {
M2[i] ^= K1[i];
}
uint8_t C1[8];
mbedtls_des3_init(&ctx);
mbedtls_des3_set3key_enc(&ctx, master_key);
mbedtls_des3_crypt_ecb(&ctx, M1, C1);
for(int i = 0; i < 8; i++) {
C1[i] ^= M2[i];
}
mbedtls_des3_crypt_ecb(&ctx, C1, card_key); // T
M1[0] ^= 0x80; // M'1
mbedtls_des3_crypt_ecb(&ctx, M1, C1); // C'1
for(int i = 0; i < 8; i++) {
C1[i] ^= M2[i];
}
mbedtls_des3_crypt_ecb(&ctx, C1, card_key + 8); // T'
mbedtls_des3_free(&ctx);
}
static void felica_lite_generate_session_key(
uint8_t* random_challenge,
uint8_t* card_key,
uint8_t* session_key) {
uint8_t RC1[8];
uint8_t RC2[8];
uint8_t CK[16];
for(int i = 0; i < 8; i++) {
RC1[i] = random_challenge[7 - i];
RC2[i] = random_challenge[i];
CK[i] = card_key[7 - i];
CK[i + 8] = card_key[15 - i];
}
mbedtls_des3_context ctx;
uint8_t SK1[8];
mbedtls_des3_init(&ctx);
mbedtls_des3_set2key_enc(&ctx, CK);
mbedtls_des3_crypt_ecb(&ctx, RC1, SK1);
uint8_t SK2[8];
for(int i = 0; i < 8; i++) {
RC2[i] ^= SK1[i];
}
mbedtls_des3_crypt_ecb(&ctx, RC2, SK2);
mbedtls_des3_free(&ctx);
for(int i = 0; i < 8; i++) {
session_key[i] = SK1[7 - i];
session_key[i + 8] = SK2[7 - i];
}
}
static void felica_lite_calculate_mac(
uint8_t* random_challenge,
uint8_t* session_key,
uint8_t* block_data,
size_t block_count,
uint8_t* MAC) {
uint8_t SK[16];
for(int i = 0; i < 8; i++) {
MAC[i] = random_challenge[7 - i];
SK[i] = session_key[7 - i];
SK[i + 8] = session_key[15 - i];
}
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set3key_enc(&ctx, SK);
for(size_t block_num = 0; block_num < block_count; block_num++) {
for(int i = 0; i < 8; i++) {
MAC[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i];
}
uint8_t intermediate[8];
mbedtls_des3_crypt_ecb(&ctx, MAC, intermediate);
for(int i = 0; i < 8; i++) {
intermediate[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 15 - i];
}
mbedtls_des3_crypt_ecb(&ctx, intermediate, MAC);
}
mbedtls_des3_free(&ctx);
}
static void felica_lite_calculate_mac_a(
uint8_t* random_challenge,
uint8_t* session_key,
uint8_t* iv,
uint8_t* block_data,
size_t block_count,
uint8_t* MAC_A) {
uint8_t SK[16];
uint8_t intermediate_a[8];
uint8_t intermediate_b[8];
for(int i = 0; i < 8; i++) {
intermediate_a[i] = iv[7 - 1] ^ random_challenge[7 - i];
SK[i] = session_key[7 - i];
SK[i + 8] = session_key[15 - i];
}
mbedtls_des3_context ctx;
mbedtls_des3_init(&ctx);
mbedtls_des3_set3key_enc(&ctx, SK);
mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b);
for(size_t block_num = 0; block_num < block_count; block_num++) {
for(int i = 0; i < 8; i++) {
intermediate_b[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i];
}
mbedtls_des3_crypt_ecb(&ctx, intermediate_b, intermediate_a);
for(int i = 0; i < 8; i++) {
intermediate_a[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i];
}
mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b);
}
for(int i = 0; i < 8; i++) {
MAC_A[i] = intermediate_b[7 - 1];
}
}
static void felica_lite_calculate_mac_a_for_write(
uint8_t* random_challenge,
uint8_t* session_key,
uint32_t write_count,
uint8_t block_number,
uint8_t* block_data,
uint8_t* MAC_A) {
uint8_t iv[8];
nfc_util_num2bytes(write_count, 3, iv);
iv[3] = 0x00;
iv[4] = block_number;
iv[5] = 0x00;
iv[6] = 0x91;
iv[7] = 0x00;
uint8_t SK[16];
for(int i = 0; i < 8; i++) {
SK[i] = session_key[i + 8];
SK[i + 8] = session_key[i];
}
felica_lite_calculate_mac_a(random_challenge, SK, iv, block_data, 1, MAC_A);
}
static void felica_lite_calculate_mac_a_for_read(
uint8_t* random_challenge,
uint8_t* session_key,
uint8_t* block_list,
uint8_t block_list_count,
uint8_t* block_data,
uint8_t block_count,
uint8_t* MAC_A) {
uint8_t iv[8] = {0};
uint8_t block_list_to_write = MIN(block_list_count, 4);
for(int i = 0; i < block_list_to_write; i++) {
iv[i * 2] = block_list[i];
}
if(block_list_to_write < 4) {
iv[6] = 0xFF;
iv[7] = 0xFF;
}
if(block_list_to_write < 3) {
iv[4] = 0xFF;
iv[5] = 0xFF;
}
felica_lite_calculate_mac_a(random_challenge, session_key, iv, block_data, block_count, MAC_A);
}
/** Parse common FeliCa response headers.
*
* This parses and validates the most commonly occurring response header types.
@@ -165,7 +375,7 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) {
* @param always_succeed When set to true, skip status flags (sf1 and sf2) parsing.
* @return The number of bytes parsed, or 0 when response is invalid or status flags are set.
*/
static uint8_t felica_consume_header(
static uint8_t felica_consume_unencrypted_header(
uint8_t* buf,
uint8_t len,
FelicaReader* reader,
@@ -261,7 +471,8 @@ uint16_t felica_parse_unencrypted_read(
FelicaReader* reader,
uint8_t* out,
uint16_t out_len) {
uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false);
uint8_t consumed =
felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false);
if(!consumed) {
return 0;
}
@@ -345,7 +556,7 @@ uint8_t felica_lite_prepare_unencrypted_write(
bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) {
uint8_t consumed =
felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false);
felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false);
if(!consumed) {
return false;
}
@@ -364,7 +575,7 @@ bool felica_parse_request_system_code(
FelicaReader* reader,
FelicaSystemArray_t* systems) {
uint8_t consumed =
felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true);
felica_consume_unencrypted_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true);
if(consumed == 0) {
return false;
}
@@ -588,6 +799,7 @@ bool felica_lite_dump_data(
}
}
if(data->type == FelicaICTypeLiteS) {
lite_info->is_lite_s = true;
const uint8_t fixed_s_blocks[] = {
CARD_KEY_LITE_BLOCK,
MAC_A_LITE_BLOCK,
+1 -2
View File
@@ -206,6 +206,7 @@ typedef struct {
uint16_t card_key_version;
uint8_t memory_config[FELICA_BLOCK_SIZE];
bool is_lite_s;
// Lite-S only
uint8_t MAC_A[8];
uint32_t write_count;
@@ -294,8 +295,6 @@ uint8_t felica_lite_prepare_unencrypted_write(
const uint8_t* block_data);
bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader);
bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number);
void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t* data);
void felica_push_normal_block(FelicaService* service, uint8_t* data);
+14
View File
@@ -11,6 +11,20 @@ uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t uni
return TIME_CONSTANT_US * scale * (base_cost_factor + unit_cost_factor * units);
}
bool felica_lite_is_issued(FelicaLiteInfo* lite_info) {
// System blocks aren't writable?
if(lite_info->memory_config[2] == 0x00) {
return false;
}
// MC is not writable?
if(lite_info->memory_config[1] & 0x80) {
return false;
}
return true;
}
FuriString* felica_get_system_name(FelicaSystem* system) {
uint16_t code = system->code;
+1
View File
@@ -1,5 +1,6 @@
#include "./felica.h"
uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units);
bool felica_lite_is_issued(FelicaLiteInfo* lite_info);
FuriString* felica_get_system_name(FelicaSystem* system);
FuriString* felica_get_service_name(FelicaService* service);