Merge branch 'dev' into zlo/tlsf-and-a-temple-of-memcorrupt

This commit is contained in:
SG
2024-06-11 14:38:09 +01:00
249 changed files with 5531 additions and 2631 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
#include "hid_service.h"
#include "app_common.h"
#include "app_common.h" // IWYU pragma: keep
#include <ble/ble.h>
#include <furi_ble/event_dispatcher.h>
#include <furi_ble/gatt.h>
-2
View File
@@ -3,8 +3,6 @@
#include "lp5562_reg.h"
#include <furi_hal.h>
#include <stdio.h>
void lp5562_reset(FuriHalI2cBusHandle* handle) {
Reg0D_Reset reg = {.value = 0xFF};
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)&reg, LP5562_I2C_TIMEOUT);
+1 -5
View File
@@ -19,17 +19,13 @@
#define TAG "FapAssets"
#pragma pack(push, 1)
typedef struct {
typedef struct FURI_PACKED {
uint32_t magic;
uint32_t version;
uint32_t dirs_count;
uint32_t files_count;
} FlipperApplicationAssetsHeader;
#pragma pack(pop)
typedef enum {
AssetsSignatureResultEqual,
AssetsSignatureResultNotEqual,
@@ -18,7 +18,7 @@ struct FlipperApplication {
/********************** Debugger access to loader state **********************/
LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST);
LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST); // NOLINT
FlipperApplicationList_t flipper_application_loaded_app_list = {0};
static bool flipper_application_loaded_app_list_initialized = false;
@@ -277,8 +277,10 @@ static const char* preload_status_strings[] = {
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
[FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
[FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
[FlipperApplicationPreloadStatusApiTooOld] = "Update Application to use with this Firmware (ApiTooOld)",
[FlipperApplicationPreloadStatusApiTooNew] = "Update Firmware to use with this Application (ApiTooNew)",
[FlipperApplicationPreloadStatusApiTooOld] =
"Update Application to use with this Firmware (ApiTooOld)",
[FlipperApplicationPreloadStatusApiTooNew] =
"Update Firmware to use with this Application (ApiTooNew)",
[FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
};
@@ -286,7 +288,8 @@ static const char* load_status_strings[] = {
[FlipperApplicationLoadStatusSuccess] = "Success",
[FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
[FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
[FlipperApplicationLoadStatusMissingImports] = "Update Firmware to use with this Application (MissingImports)",
[FlipperApplicationLoadStatusMissingImports] =
"Update Firmware to use with this Application (MissingImports)",
};
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
@@ -4,7 +4,7 @@
#include <m-list.h>
#include <m-algo.h>
LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST)
LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) // NOLINT
#define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST)
struct CompositeApiResolver {
@@ -11,7 +11,7 @@
#define TAG "PluginManager"
ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST)
ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) // NOLINT
#define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST)
struct PluginManager {
+1 -1
View File
@@ -96,7 +96,7 @@ bool protocol_pac_stanley_decoder_feed(ProtocolPACStanley* protocol, bool level,
if(duration > PAC_STANLEY_MAX_TIME) return false;
uint8_t pulses = (uint8_t)round((float)duration / PAC_STANLEY_CYCLE_LENGTH);
uint8_t pulses = (uint8_t)roundf((float)duration / PAC_STANLEY_CYCLE_LENGTH);
// Handle last stopbit & preamble (1 sb, 8 bit preamble)
if(pulses >= 9 && !protocol->got_preamble) {
+4
View File
@@ -21,6 +21,7 @@ env.Append(
File("protocols/iso14443_4b/iso14443_4b.h"),
File("protocols/mf_ultralight/mf_ultralight.h"),
File("protocols/mf_classic/mf_classic.h"),
File("protocols/mf_plus/mf_plus.h"),
File("protocols/mf_desfire/mf_desfire.h"),
File("protocols/slix/slix.h"),
File("protocols/st25tb/st25tb.h"),
@@ -32,6 +33,7 @@ env.Append(
File("protocols/iso14443_4b/iso14443_4b_poller.h"),
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
File("protocols/mf_classic/mf_classic_poller.h"),
File("protocols/mf_plus/mf_plus_poller.h"),
File("protocols/mf_desfire/mf_desfire_poller.h"),
File("protocols/slix/slix_poller.h"),
File("protocols/st25tb/st25tb_poller.h"),
@@ -41,11 +43,13 @@ env.Append(
File("protocols/iso14443_4a/iso14443_4a_listener.h"),
File("protocols/mf_ultralight/mf_ultralight_listener.h"),
File("protocols/mf_classic/mf_classic_listener.h"),
File("protocols/felica/felica_listener.h"),
# Sync API
File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"),
File("protocols/mf_classic/mf_classic_poller_sync.h"),
File("protocols/st25tb/st25tb_poller_sync.h"),
File("protocols/felica/felica_poller_sync.h"),
# Misc
File("helpers/nfc_util.h"),
File("helpers/iso14443_crc.h"),
+5
View File
@@ -6,6 +6,8 @@
#define FELICA_CRC_INIT (0x0000U)
uint16_t felica_crc_calculate(const uint8_t* data, size_t length) {
furi_check(data);
uint16_t crc = FELICA_CRC_INIT;
for(size_t i = 0; i < length; i++) {
@@ -24,6 +26,7 @@ uint16_t felica_crc_calculate(const uint8_t* data, size_t length) {
}
void felica_crc_append(BitBuffer* buf) {
furi_check(buf);
const uint8_t* data = bit_buffer_get_data(buf);
const size_t data_size = bit_buffer_get_size_bytes(buf);
@@ -32,6 +35,7 @@ void felica_crc_append(BitBuffer* buf) {
}
bool felica_crc_check(const BitBuffer* buf) {
furi_check(buf);
const size_t data_size = bit_buffer_get_size_bytes(buf);
if(data_size <= FELICA_CRC_SIZE) return false;
@@ -45,6 +49,7 @@ bool felica_crc_check(const BitBuffer* buf) {
}
void felica_crc_trim(BitBuffer* buf) {
furi_check(buf);
const size_t data_size = bit_buffer_get_size_bytes(buf);
furi_assert(data_size > FELICA_CRC_SIZE);
+44
View File
@@ -3,6 +3,9 @@
#include <lib/nfc/nfc.h>
#include <lib/nfc/helpers/iso14443_crc.h>
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <lib/nfc/protocols/felica/felica.h>
#include <lib/nfc/helpers/felica_crc.h>
#include <lib/nfc/protocols/felica/felica_poller_sync.h>
#include <furi/furi.h>
@@ -50,11 +53,31 @@ typedef struct {
Iso14443_3aSelResp sel_resp[2];
} Iso14443_3aColResData;
typedef struct {
uint8_t length;
uint8_t polling_cmd;
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
} FelicaPollingRequest;
typedef struct {
uint8_t code;
FelicaIDm idm;
FelicaPMm pmm;
} FelicaSensfResData;
typedef struct {
uint16_t system_code;
FelicaSensfResData sens_res;
} FelicaPTMemory;
struct Nfc {
NfcState state;
Iso14443_3aColResStatus col_res_status;
Iso14443_3aColResData col_res_data;
FelicaPTMemory pt_memory;
bool software_col_res_required;
NfcEventCallback callback;
@@ -243,6 +266,21 @@ static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, ui
NfcEvent event = {.type = NfcEventTypeListenerActivated};
instance->callback(event, instance->context);
processed = true;
}
} else if(rx_bits == 8 * 8) {
FelicaPollingRequest* request = (FelicaPollingRequest*)rx_data;
if(request->system_code == instance->pt_memory.system_code) {
uint8_t response_size = sizeof(FelicaSensfResData) + 1;
bit_buffer_reset(tx_buffer);
bit_buffer_append_byte(tx_buffer, response_size);
bit_buffer_append_bytes(
tx_buffer, (uint8_t*)&instance->pt_memory.sens_res, sizeof(FelicaSensfResData));
felica_crc_append(tx_buffer);
nfc_listener_tx(instance, tx_buffer);
instance->col_res_status = Iso14443_3aColResStatusDone;
NfcEvent event = {.type = NfcEventTypeListenerActivated};
instance->callback(event, instance->context);
processed = true;
}
}
@@ -470,6 +508,12 @@ NfcError nfc_felica_listener_set_sensf_res_data(
furi_assert(idm_len == 8);
furi_assert(pmm_len == 8);
instance->pt_memory.system_code = 0xFFFF;
instance->pt_memory.sens_res.code = 0x01;
instance->software_col_res_required = true;
memcpy(instance->pt_memory.sens_res.idm.data, idm, idm_len);
memcpy(instance->pt_memory.sens_res.pmm.data, pmm, pmm_len);
return NfcErrorNone;
}
+23 -5
View File
@@ -298,15 +298,33 @@ bool felica_check_mac(
furi_check(blocks);
furi_check(data);
uint8_t first_block[8];
uint8_t mac[8];
felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);
felica_calculate_mac_read(ctx, session_key, rc, blocks, block_count, data, mac);
uint8_t mac_offset = FELICA_DATA_BLOCK_SIZE * (block_count - 1);
uint8_t* mac_ptr = data + mac_offset;
return !memcmp(mac, mac_ptr, 8);
}
void felica_calculate_mac_read(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* blocks,
const uint8_t block_count,
const uint8_t* data,
uint8_t* mac) {
furi_check(ctx);
furi_check(session_key);
furi_check(rc);
furi_check(blocks);
furi_check(data);
furi_check(mac);
uint8_t first_block[8];
felica_prepare_first_block(FelicaMACTypeRead, blocks, block_count, first_block);
uint8_t data_size_without_mac = FELICA_DATA_BLOCK_SIZE * (block_count - 1);
felica_calculate_mac(ctx, session_key, rc, first_block, data, data_size_without_mac, mac);
uint8_t* mac_ptr = data + data_size_without_mac;
return !memcmp(mac, mac_ptr, 8);
}
void felica_calculate_mac_write(
+81 -1
View File
@@ -12,7 +12,13 @@ extern "C" {
#define FELICA_PMM_SIZE (8U)
#define FELICA_DATA_BLOCK_SIZE (16U)
#define FELICA_BLOCKS_TOTAL_COUNT (27U)
#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06U)
#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U)
#define FELICA_SERVICE_RW_ACCESS (0x0009U)
#define FELICA_SERVICE_RO_ACCESS (0x000BU)
#define FELICA_BLOCKS_TOTAL_COUNT (28U)
#define FELICA_BLOCK_INDEX_REG (0x0EU)
#define FELICA_BLOCK_INDEX_RC (0x80U)
#define FELICA_BLOCK_INDEX_MAC (0x81U)
@@ -54,6 +60,10 @@ typedef enum {
FelicaErrorTimeout,
} FelicaError;
typedef struct {
uint8_t data[FELICA_DATA_BLOCK_SIZE];
} FelicaBlockData;
/** @brief Separate type for card key block. Used in authentication process */
typedef struct {
uint8_t data[FELICA_DATA_BLOCK_SIZE];
@@ -76,6 +86,22 @@ typedef struct {
FelicaAuthenticationStatus auth_status; /**< Authentication status*/
} FelicaAuthenticationContext;
/**
* @brief Stucture for holding Felica session key which is calculated from rc and ck.
*/
typedef struct {
uint8_t data[FELICA_DATA_BLOCK_SIZE];
} FelicaSessionKey;
/**
* @brief Structure used to hold authentication related fields.
*/
typedef struct {
mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */
FelicaSessionKey session_key; /**< Calculated session key. */
FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */
} FelicaAuthentication;
/** @brief Felica ID block */
typedef struct {
uint8_t data[FELICA_IDM_SIZE];
@@ -128,6 +154,51 @@ typedef struct {
FelicaFSUnion data;
} FelicaData;
#pragma pack(push, 1)
typedef struct {
uint8_t code;
FelicaIDm idm;
uint8_t service_num;
uint16_t service_code;
uint8_t block_count;
} FelicaCommandHeader;
#pragma pack(pop)
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
uint8_t SF1;
uint8_t SF2;
} FelicaCommandResponseHeader;
typedef struct {
uint8_t service_code : 4;
uint8_t access_mode : 3;
uint8_t length : 1;
uint8_t block_number;
} FelicaBlockListElement;
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
uint8_t SF1;
uint8_t SF2;
uint8_t block_count;
uint8_t data[];
} FelicaPollerReadCommandResponse;
typedef struct {
FelicaCommandResponseHeader header;
uint8_t block_count;
uint8_t data[];
} FelicaListenerReadCommandResponse;
typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse;
typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse;
extern const NfcDeviceBase nfc_device_felica;
FelicaData* felica_alloc(void);
@@ -168,6 +239,15 @@ bool felica_check_mac(
const uint8_t block_count,
uint8_t* data);
void felica_calculate_mac_read(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
const uint8_t* rc,
const uint8_t* blocks,
const uint8_t block_count,
const uint8_t* data,
uint8_t* mac);
void felica_calculate_mac_write(
mbedtls_des3_context* ctx,
const uint8_t* session_key,
+134 -3
View File
@@ -1,9 +1,14 @@
#include "felica_listener_i.h"
#include "nfc/protocols/nfc_listener_base.h"
#include <nfc/helpers/felica_crc.h>
#include <furi_hal_nfc.h>
#define FELICA_LISTENER_MAX_BUFFER_SIZE (64)
#define TAG "Felica"
#define FELICA_LISTENER_MAX_BUFFER_SIZE (128)
#define FELICA_LISTENER_RESPONSE_CODE_READ (0x07)
#define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09)
#define TAG "FelicaListener"
FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) {
furi_assert(nfc);
@@ -15,8 +20,11 @@ FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) {
instance->tx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE);
mbedtls_des3_init(&instance->auth.des_context);
nfc_set_fdt_listen_fc(instance->nfc, FELICA_FDT_LISTEN_FC);
memcpy(instance->mc_shadow.data, instance->data->data.fs.mc.data, FELICA_DATA_BLOCK_SIZE);
instance->data->data.fs.state.data[0] = 0;
nfc_config(instance->nfc, NfcModeListener, NfcTechFelica);
nfc_felica_listener_set_sensf_res_data(
nfc, data->idm.data, sizeof(data->idm), data->pmm.data, sizeof(data->pmm));
@@ -49,6 +57,99 @@ const FelicaData* felica_listener_get_data(const FelicaListener* instance) {
return instance->data;
}
static FelicaError felica_listener_command_handler_read(
FelicaListener* instance,
const FelicaListenerGenericRequest* const generic_request) {
const FelicaListenerReadRequest* request = (FelicaListenerReadRequest*)generic_request;
FURI_LOG_D(TAG, "Read cmd");
FelicaListenerReadCommandResponse* resp = malloc(
sizeof(FelicaCommandResponseHeader) + 1 +
FELICA_LISTENER_READ_BLOCK_COUNT_MAX * FELICA_DATA_BLOCK_SIZE);
resp->header.response_code = FELICA_LISTENER_RESPONSE_CODE_READ;
resp->header.idm = request->base.header.idm;
resp->header.length = sizeof(FelicaCommandResponseHeader);
if(felica_listener_validate_read_request_and_set_sf(instance, request, &resp->header)) {
resp->block_count = request->base.header.block_count;
resp->header.length++;
} else {
resp->block_count = 0;
}
instance->mac_calc_start = 0;
memset(instance->requested_blocks, 0, sizeof(instance->requested_blocks));
const FelicaBlockListElement* item =
felica_listener_block_list_item_get_first(instance, request);
for(uint8_t i = 0; i < resp->block_count; i++) {
instance->requested_blocks[i] = item->block_number;
FelicaCommanReadBlockHandler handler =
felica_listener_get_read_block_handler(item->block_number);
handler(instance, item->block_number, i, resp);
item = felica_listener_block_list_item_get_next(instance, item);
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length);
free(resp);
return felica_listener_frame_exchange(instance, instance->tx_buffer);
}
static FelicaError felica_listener_command_handler_write(
FelicaListener* instance,
const FelicaListenerGenericRequest* const generic_request) {
FURI_LOG_D(TAG, "Write cmd");
const FelicaListenerWriteRequest* request = (FelicaListenerWriteRequest*)generic_request;
const FelicaListenerWriteBlockData* data_ptr =
felica_listener_get_write_request_data_pointer(instance, generic_request);
FelicaListenerWriteCommandResponse* resp = malloc(sizeof(FelicaListenerWriteCommandResponse));
resp->response_code = FELICA_LISTENER_RESPONSE_CODE_WRITE;
resp->idm = request->base.header.idm;
resp->length = sizeof(FelicaListenerWriteCommandResponse);
if(felica_listener_validate_write_request_and_set_sf(instance, request, data_ptr, resp)) {
const FelicaBlockListElement* item =
felica_listener_block_list_item_get_first(instance, request);
for(uint8_t i = 0; i < request->base.header.block_count; i++) {
FelicaCommandWriteBlockHandler handler =
felica_listener_get_write_block_handler(item->block_number);
handler(instance, item->block_number, &data_ptr->blocks[i]);
item = felica_listener_block_list_item_get_next(instance, item);
}
felica_wcnt_increment(instance->data);
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->length);
free(resp);
return felica_listener_frame_exchange(instance, instance->tx_buffer);
}
static FelicaError felica_listener_process_request(
FelicaListener* instance,
const FelicaListenerGenericRequest* generic_request) {
const uint8_t cmd_code = generic_request->header.code;
switch(cmd_code) {
case FELICA_CMD_READ_WITHOUT_ENCRYPTION:
return felica_listener_command_handler_read(instance, generic_request);
case FELICA_CMD_WRITE_WITHOUT_ENCRYPTION:
return felica_listener_command_handler_write(instance, generic_request);
default:
FURI_LOG_E(TAG, "FeliCa incorrect command");
return FelicaErrorNotPresent;
}
}
NfcCommand felica_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid);
@@ -58,14 +159,44 @@ NfcCommand felica_listener_run(NfcGenericEvent event, void* context) {
NfcEvent* nfc_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypeListenerActivated) {
if(nfc_event->type == NfcEventTypeFieldOn) {
FURI_LOG_D(TAG, "Field On");
} else if(nfc_event->type == NfcEventTypeListenerActivated) {
instance->state = Felica_ListenerStateActivated;
FURI_LOG_D(TAG, "Activated");
} else if(nfc_event->type == NfcEventTypeFieldOff) {
instance->state = Felica_ListenerStateIdle;
FURI_LOG_D(TAG, "Field Off");
felica_listener_reset(instance);
} else if(nfc_event->type == NfcEventTypeRxEnd) {
FURI_LOG_D(TAG, "Rx Done");
do {
if(!felica_crc_check(nfc_event->data.buffer)) {
FURI_LOG_E(TAG, "Wrong CRC");
break;
}
FelicaListenerGenericRequest* request =
(FelicaListenerGenericRequest*)bit_buffer_get_data(nfc_event->data.buffer);
uint8_t size = bit_buffer_get_size_bytes(nfc_event->data.buffer) - 2;
if((request->length != size) ||
(!felica_listener_check_block_list_size(instance, request))) {
FURI_LOG_E(TAG, "Wrong request length");
break;
}
if(!felica_listener_check_idm(instance, &request->header.idm)) {
FURI_LOG_E(TAG, "Wrong IDm");
break;
}
FelicaError error = felica_listener_process_request(instance, request);
if(error != FelicaErrorNone) {
FURI_LOG_E(TAG, "Processing error: %2X", error);
}
} while(false);
bit_buffer_reset(nfc_event->data.buffer);
}
return command;
}
@@ -0,0 +1,738 @@
#include "felica_listener_i.h"
#include <nfc/helpers/felica_crc.h>
#define FELICA_WCNT_MC2_FF_MAX_VALUE (0x00FFFFFFU)
#define FELICA_WCNT_MC2_00_MAX_VALUE (0x00FFFE00U)
#define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U)
#define FELICA_WCNT_MC2_00_WARNING_END_VALUE (0x00FFFDFFU)
#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U)
#define FELICA_MC_ALL_BYTE (2U)
#define FELICA_MC_SYS_OP (3U)
#define FELICA_MC_RF_PRM (4U)
#define FELICA_MC_CKCKV_W_MAC_A (5U)
#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U)
#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U)
#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U)
#define FELICA_MC_STATE_W_MAC_A (12U)
#define FELICA_MC_RESERVED_13 (13U)
#define FELICA_MC_RESERVED_14 (14U)
#define FELICA_MC_RESERVED_15 (15U)
#define FELICA_MC_BYTE_GET(data, byte) (data->data.fs.mc.data[byte])
#define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00)
#define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF)
#define FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN ((uint8_t)sizeof(FelicaBlockListElement))
#define FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(item) \
((item->length == 1) ? FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN : \
(FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN + 1))
static uint32_t felica_wcnt_get_max_value(const FelicaData* data) {
if(!FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && !FELICA_SYSTEM_BLOCK_RW_ACCESS(data)) {
furi_crash("MC[2] reserved values are forbidden");
}
return (FELICA_SYSTEM_BLOCK_RW_ACCESS(data)) ? FELICA_WCNT_MC2_FF_MAX_VALUE :
FELICA_WCNT_MC2_00_MAX_VALUE;
}
static bool felica_wcnt_check_warning_boundary(const FelicaData* data) {
const uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data;
return (
FELICA_SYSTEM_BLOCK_RO_ACCESS(data) &&
((*wcnt_ptr > FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE) &&
(*wcnt_ptr < FELICA_WCNT_MC2_00_WARNING_END_VALUE)));
}
static bool felica_wcnt_check_error_boundary(const FelicaData* data) {
const uint32_t wcnt_max = felica_wcnt_get_max_value(data);
const uint32_t* wcnt_ptr = (const uint32_t*)data->data.fs.wcnt.data;
return *wcnt_ptr != wcnt_max;
}
void felica_wcnt_increment(FelicaData* data) {
furi_assert(data);
const uint32_t wcnt_max = felica_wcnt_get_max_value(data);
uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data;
if(*wcnt_ptr < wcnt_max) {
*wcnt_ptr += 1;
}
}
static void felica_wcnt_post_process(FelicaData* data) {
uint32_t* wcnt_ptr = (uint32_t*)data->data.fs.wcnt.data;
if(FELICA_SYSTEM_BLOCK_RO_ACCESS(data) && (*wcnt_ptr > FELICA_WCNT_MC2_00_MAX_VALUE)) {
*wcnt_ptr = 0;
}
}
static bool
felica_listener_block_list_item_validate_block_number(const FelicaBlockListElement* item) {
bool valid = true;
if(item->length == 0) {
uint8_t D2_block_number = *(&item->block_number + 1);
valid = D2_block_number == 0;
}
return valid;
}
static const FelicaBlockListElement* felica_listener_block_list_iterate(
FelicaListener* instance,
const FelicaBlockListElement* prev_item,
const uint8_t item_size) {
FelicaBlockListElement* next_item = NULL;
if(instance->request_size_buf >= FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE_MIN) {
next_item = (FelicaBlockListElement*)((uint8_t*)prev_item + item_size);
uint8_t next_item_size = FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(next_item);
if(instance->request_size_buf < next_item_size) {
next_item = NULL;
instance->request_size_buf = 0;
} else {
instance->request_size_buf -= next_item_size;
}
}
return next_item;
}
const FelicaBlockListElement* felica_listener_block_list_item_get_first(
FelicaListener* instance,
const FelicaListenerRequest* request) {
furi_assert(instance);
furi_assert(request);
instance->request_size_buf = request->base.length - sizeof(FelicaListenerGenericRequest);
return felica_listener_block_list_iterate(instance, request->list, 0);
}
const FelicaBlockListElement* felica_listener_block_list_item_get_next(
FelicaListener* instance,
const FelicaBlockListElement* prev_item) {
furi_assert(instance);
furi_assert(prev_item);
return felica_listener_block_list_iterate(
instance, prev_item, FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(prev_item));
}
const FelicaListenerWriteBlockData* felica_listener_get_write_request_data_pointer(
const FelicaListener* const instance,
const FelicaListenerGenericRequest* const generic_request) {
furi_assert(instance);
furi_assert(generic_request);
return (const FelicaListenerWriteBlockData*)((uint8_t*)generic_request +
sizeof(FelicaListenerGenericRequest) +
instance->block_list_size);
}
static bool felica_listener_check_write_request_data_size(
const FelicaListener* const instance,
const FelicaListenerRequest* request,
const uint8_t fact_item_cnt) {
uint8_t possible_data_size = fact_item_cnt * FELICA_DATA_BLOCK_SIZE;
uint8_t fact_data_size =
request->base.length - sizeof(FelicaListenerGenericRequest) - instance->block_list_size;
return (possible_data_size <= fact_data_size);
}
static bool felica_listener_test_block_list_size_bounds(const FelicaListenerGenericRequest* req) {
bool valid = false;
if(req->header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) {
valid =
(req->header.block_count >= FELICA_LISTENER_READ_BLOCK_COUNT_MIN &&
req->header.block_count <= FELICA_LISTENER_READ_BLOCK_COUNT_MAX);
} else if(req->header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) {
valid =
(req->header.block_count >= FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN &&
req->header.block_count <= FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX);
}
return valid;
}
static uint8_t felica_listener_get_block_list_item_count_size(
FelicaListener* instance,
FelicaListenerRequest* request) {
uint8_t list_size = 0;
uint8_t item_cnt = 0;
uint8_t max_item_cnt = (request->base.header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) ?
FELICA_LISTENER_READ_BLOCK_COUNT_MAX :
FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX;
const FelicaBlockListElement* item =
felica_listener_block_list_item_get_first(instance, request);
while((item != NULL) && (item_cnt < max_item_cnt)) {
item_cnt++;
if(request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) {
if(instance->request_size_buf >= FELICA_DATA_BLOCK_SIZE) {
instance->request_size_buf -= FELICA_DATA_BLOCK_SIZE;
} else {
instance->request_size_buf = 0;
}
}
list_size += FELICA_LISTENER_BLOCK_LIST_ITEM_SIZE(item);
item = felica_listener_block_list_item_get_next(instance, item);
}
instance->block_list_size = list_size;
return item_cnt;
}
bool felica_listener_check_block_list_size(
FelicaListener* instance,
FelicaListenerGenericRequest* req) {
furi_assert(instance);
furi_assert(req);
FelicaListenerRequest* request = (FelicaListenerRequest*)req;
bool valid = true;
uint8_t item_cnt = felica_listener_get_block_list_item_count_size(instance, request);
valid = (item_cnt > 0);
if(felica_listener_test_block_list_size_bounds(req) &&
(item_cnt < request->base.header.block_count)) {
valid = false;
}
if(valid && request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) {
valid &= felica_listener_check_write_request_data_size(instance, request, item_cnt);
}
return valid;
}
bool felica_listener_check_idm(const FelicaListener* instance, const FelicaIDm* request_idm) {
furi_assert(instance);
furi_assert(request_idm);
const FelicaIDm* idm = &instance->data->idm;
return memcmp(idm->data, request_idm->data, 8) == 0;
}
void felica_listener_reset(FelicaListener* instance) {
furi_assert(instance);
instance->auth.context.auth_status.internal = false;
instance->auth.context.auth_status.external = false;
instance->data->data.fs.state.data[0] = 0;
instance->rc_written = false;
memset(instance->auth.session_key.data, 0, FELICA_DATA_BLOCK_SIZE);
memcpy(instance->data->data.fs.mc.data, instance->mc_shadow.data, FELICA_DATA_BLOCK_SIZE);
felica_wcnt_post_process(instance->data);
}
static uint8_t felica_listener_get_block_index(uint8_t number) {
if(number >= FELICA_BLOCK_INDEX_RC && number < FELICA_BLOCK_INDEX_WCNT) {
return number - 0x80 + FELICA_BLOCK_INDEX_REG + 1;
} else if(number >= FELICA_BLOCK_INDEX_WCNT && number <= FELICA_BLOCK_INDEX_STATE) {
return number - 0x90 + 9 + FELICA_BLOCK_INDEX_REG + 1;
} else if(number == FELICA_BLOCK_INDEX_CRC_CHECK) {
return number - 0x90 + 9 + FELICA_BLOCK_INDEX_REG + 2;
}
return number;
}
static bool felica_block_exists(uint8_t number) {
return !(
(number > FELICA_BLOCK_INDEX_REG && number < FELICA_BLOCK_INDEX_RC) ||
(number > FELICA_BLOCK_INDEX_MC && number < FELICA_BLOCK_INDEX_WCNT) ||
(number > FELICA_BLOCK_INDEX_STATE && number < FELICA_BLOCK_INDEX_CRC_CHECK) ||
(number > FELICA_BLOCK_INDEX_CRC_CHECK));
}
static bool
felica_get_mc_bit(const FelicaListener* instance, uint8_t byte_index, uint8_t bit_number) {
uint8_t* mc = instance->data->data.fs.mc.data;
uint16_t flags = *((uint16_t*)&mc[byte_index]);
bool bit = (flags & (1 << bit_number)) != 0;
return bit;
}
static bool felica_block_requires_auth(
const FelicaListener* instance,
uint8_t command,
uint8_t block_number) {
bool need_auth = false;
if(block_number <= FELICA_BLOCK_INDEX_REG) {
uint8_t mc_flag_index = command;
need_auth = felica_get_mc_bit(instance, mc_flag_index, block_number);
}
return need_auth;
}
static bool felica_block_is_readonly(const FelicaListener* instance, uint8_t block_number) {
bool readonly = false;
if(block_number <= FELICA_BLOCK_INDEX_REG) {
readonly = !felica_get_mc_bit(instance, FELICA_MC_SP_REG_ALL_RW_BYTES_0_1, block_number);
} else if(
((block_number == FELICA_BLOCK_INDEX_ID) || (block_number == FELICA_BLOCK_INDEX_SER_C)) ||
(block_number >= FELICA_BLOCK_INDEX_CKV && block_number <= FELICA_BLOCK_INDEX_CK)) {
const FelicaData* data = instance->data;
readonly = FELICA_SYSTEM_BLOCK_RO_ACCESS(data);
} else if(
(block_number == FELICA_BLOCK_INDEX_MAC) || (block_number == FELICA_BLOCK_INDEX_WCNT) ||
(block_number == FELICA_BLOCK_INDEX_D_ID) ||
(block_number == FELICA_BLOCK_INDEX_CRC_CHECK)) {
readonly = true;
}
return readonly;
}
static bool felica_block_requires_mac(const FelicaListener* instance, uint8_t block_number) {
bool need_mac = false;
if(block_number <= FELICA_BLOCK_INDEX_REG) {
need_mac = felica_get_mc_bit(instance, FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11, block_number);
} else if(block_number == FELICA_BLOCK_INDEX_CK || block_number == FELICA_BLOCK_INDEX_CKV) {
need_mac = felica_get_mc_bit(instance, FELICA_MC_CKCKV_W_MAC_A, 0);
} else if(block_number == FELICA_BLOCK_INDEX_STATE) {
need_mac = felica_get_mc_bit(instance, FELICA_MC_STATE_W_MAC_A, 0);
}
return need_mac;
}
static void felica_handler_read_block(
FelicaListener* instance,
const uint8_t block_number,
const uint8_t resp_data_index,
FelicaListenerReadCommandResponse* response) {
uint8_t num = felica_listener_get_block_index(block_number);
memcpy(
&response->data[resp_data_index * FELICA_DATA_BLOCK_SIZE],
&instance->data->data.dump[num * (FELICA_DATA_BLOCK_SIZE + 2) + 2],
FELICA_DATA_BLOCK_SIZE);
response->header.length += FELICA_DATA_BLOCK_SIZE;
}
static void felica_handler_read_all_zeros(
FelicaListener* instance,
const uint8_t block_number,
const uint8_t resp_data_index,
FelicaListenerReadCommandResponse* response) {
UNUSED(instance);
UNUSED(block_number);
memset(&response->data[resp_data_index * FELICA_DATA_BLOCK_SIZE], 0, FELICA_DATA_BLOCK_SIZE);
response->header.length += FELICA_DATA_BLOCK_SIZE;
}
static void felica_handler_read_mac_a_block(
FelicaListener* instance,
const uint8_t block_number,
const uint8_t resp_data_index,
FelicaListenerReadCommandResponse* response) {
if((resp_data_index != response->block_count - 1) || (resp_data_index == 0)) {
felica_handler_read_all_zeros(instance, block_number, resp_data_index, response);
instance->mac_calc_start = resp_data_index + 1;
} else {
felica_calculate_mac_read(
&instance->auth.des_context,
instance->auth.session_key.data,
instance->data->data.fs.rc.data,
&instance->requested_blocks[instance->mac_calc_start],
response->block_count - instance->mac_calc_start,
&response->data[instance->mac_calc_start * FELICA_DATA_BLOCK_SIZE],
instance->data->data.fs.mac_a.data);
felica_handler_read_block(instance, block_number, resp_data_index, response);
}
}
FelicaCommanReadBlockHandler felica_listener_get_read_block_handler(const uint8_t block_number) {
FelicaCommanReadBlockHandler handler = felica_handler_read_block;
if(block_number == FELICA_BLOCK_INDEX_RC || block_number == FELICA_BLOCK_INDEX_CK) {
handler = felica_handler_read_all_zeros;
} else if(block_number == FELICA_BLOCK_INDEX_MAC_A) {
handler = felica_handler_read_mac_a_block;
}
return handler;
}
static bool felica_validate_read_block_list(
FelicaListener* instance,
const FelicaListenerReadRequest* const request,
FelicaCommandResponseHeader* response) {
uint8_t mac_a_pos = 0;
bool mac_a_present = false, mac_present = false;
const FelicaBlockListElement* item =
felica_listener_block_list_item_get_first(instance, request);
for(uint8_t i = 0; i < request->base.header.block_count; i++) {
if(item->service_code != 0) {
response->SF1 = (1 << i);
response->SF2 = 0xA3;
return false;
} else if(item->access_mode != 0) {
response->SF1 = (1 << i);
response->SF2 = 0xA7;
return false;
} else if(
!felica_listener_block_list_item_validate_block_number(item) ||
!felica_block_exists(item->block_number) ||
(felica_block_is_readonly(instance, item->block_number) &&
request->base.header.service_code != FELICA_SERVICE_RO_ACCESS)) {
response->SF1 = (1 << i);
response->SF2 = 0xA8;
return false;
} else if(item->block_number == FELICA_BLOCK_INDEX_MAC) {
mac_present = true;
} else if(item->block_number == FELICA_BLOCK_INDEX_MAC_A) {
if(!instance->rc_written) {
response->SF1 = (1 << i);
response->SF2 = 0xB2;
return false;
}
if(!mac_a_present) {
mac_a_present = true;
mac_a_pos = i;
}
} else if(
felica_block_requires_auth(instance, request->base.header.code, item->block_number) &&
!instance->auth.context.auth_status.external) {
response->SF1 = (1 << i);
response->SF2 = 0xB1;
return false;
}
if(mac_a_present && mac_present) {
response->SF1 = (1 << mac_a_pos);
response->SF2 = 0xB0;
return false;
}
item = felica_listener_block_list_item_get_next(instance, item);
}
return true;
}
bool felica_listener_validate_read_request_and_set_sf(
FelicaListener* instance,
const FelicaListenerReadRequest* const request,
FelicaCommandResponseHeader* resp_header) {
furi_assert(instance);
furi_assert(request);
furi_assert(resp_header);
bool valid = false;
do {
if(request->base.header.service_num != 0x01) {
resp_header->SF1 = 0xFF;
resp_header->SF2 = 0xA1;
break;
}
if((request->base.header.code == FELICA_CMD_READ_WITHOUT_ENCRYPTION) &&
(request->base.header.block_count < FELICA_LISTENER_READ_BLOCK_COUNT_MIN ||
request->base.header.block_count > FELICA_LISTENER_READ_BLOCK_COUNT_MAX)) {
resp_header->SF1 = 0xFF;
resp_header->SF2 = 0xA2;
break;
}
if(request->base.header.service_code != FELICA_SERVICE_RO_ACCESS &&
request->base.header.service_code != FELICA_SERVICE_RW_ACCESS) {
resp_header->SF1 = 0x01;
resp_header->SF2 = 0xA6;
break;
}
if(!felica_validate_read_block_list(instance, request, resp_header)) break;
resp_header->SF1 = 0x00;
resp_header->SF2 = 0x00;
valid = true;
} while(false);
return valid;
}
static bool felica_validate_write_block_list(
FelicaListener* instance,
const FelicaListenerWriteRequest* const request,
const FelicaListenerWriteBlockData* const data,
FelicaListenerWriteCommandResponse* response) {
const FelicaBlockListElement* items[FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX] = {};
items[0] = felica_listener_block_list_item_get_first(instance, request);
items[1] = felica_listener_block_list_item_get_next(instance, items[0]);
bool write_with_mac = false;
if(request->base.header.block_count == FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX &&
items[1]->block_number == FELICA_BLOCK_INDEX_MAC_A) {
write_with_mac = true;
} else if(
request->base.header.block_count < FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN ||
request->base.header.block_count > FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX) {
response->SF1 = 0x02;
response->SF2 = 0xB2;
return false;
}
for(uint8_t i = 0; i < request->base.header.block_count; i++) {
const FelicaBlockListElement* item = items[i];
if(!felica_listener_block_list_item_validate_block_number(item) ||
!felica_block_exists(item->block_number) ||
((i == 1) && (item->block_number != FELICA_BLOCK_INDEX_MAC_A))) {
response->SF1 = (1 << i);
response->SF2 = 0xA8;
return false;
}
if(felica_block_is_readonly(instance, item->block_number) ||
(felica_block_requires_mac(instance, item->block_number) && !write_with_mac) ||
((i == 0) && (item->block_number == FELICA_BLOCK_INDEX_MAC_A))) {
response->SF1 = 0x01;
response->SF2 = 0xA8;
return false;
}
if(item->service_code != 0) {
response->SF1 = (1 << i);
response->SF2 = 0xA3;
return false;
} else if(item->access_mode != 0) {
response->SF1 = (1 << i);
response->SF2 = 0xA7;
return false;
} else if((i == 1) && (item->block_number == FELICA_BLOCK_INDEX_MAC_A)) {
uint8_t calculated_mac[8];
felica_calculate_mac_write(
&instance->auth.des_context,
instance->auth.session_key.data,
instance->data->data.fs.rc.data,
data->blocks[1].data + 8,
data->blocks[0].data,
calculated_mac);
if(!instance->rc_written || (memcmp(calculated_mac, data->blocks[1].data, 8) != 0) ||
!felica_wcnt_check_error_boundary(instance->data)) {
response->SF1 = 0x02;
response->SF2 = 0xB2;
return false;
}
} else if(
felica_block_requires_auth(instance, request->base.header.code, item->block_number) &&
!instance->auth.context.auth_status.external) {
response->SF1 = 0x01;
response->SF2 = 0xB1;
return false;
}
}
return true;
}
bool felica_listener_validate_write_request_and_set_sf(
FelicaListener* instance,
const FelicaListenerWriteRequest* const request,
const FelicaListenerWriteBlockData* const data,
FelicaListenerWriteCommandResponse* response) {
furi_assert(instance);
furi_assert(request);
furi_assert(data);
furi_assert(response);
bool valid = false;
do {
if(request->base.header.service_num != 0x01) {
response->SF1 = 0xFF;
response->SF2 = 0xA1;
break;
}
if((request->base.header.code == FELICA_CMD_WRITE_WITHOUT_ENCRYPTION) &&
(request->base.header.block_count < 0x01 || request->base.header.block_count > 0x02)) {
response->SF1 = 0xFF;
response->SF2 = 0xA2;
break;
}
if(request->base.header.service_code != FELICA_SERVICE_RW_ACCESS) {
response->SF1 = 0x01;
response->SF2 = 0xA6;
break;
}
if(!felica_validate_write_block_list(instance, request, data, response)) break;
if(felica_wcnt_check_warning_boundary(instance->data)) {
response->SF1 = 0xFF;
response->SF2 = 0x71;
} else {
response->SF1 = 0x00;
response->SF2 = 0x00;
}
valid = true;
} while(false);
return valid;
}
static void felica_handler_write_block(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block) {
uint8_t num = felica_listener_get_block_index(block_number);
memcpy(
&instance->data->data.dump[num * (FELICA_DATA_BLOCK_SIZE + 2) + 2],
data_block->data,
FELICA_DATA_BLOCK_SIZE);
}
static void felica_handler_write_rc_block(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block) {
felica_handler_write_block(instance, block_number, data_block);
felica_calculate_session_key(
&instance->auth.des_context,
instance->data->data.fs.ck.data,
instance->data->data.fs.rc.data,
instance->auth.session_key.data);
instance->rc_written = true;
}
static void felica_handler_write_reg_block(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block) {
UNUSED(block_number);
typedef struct {
uint32_t A;
uint32_t B;
uint64_t C;
} FELICA_REG_BLOCK;
FELICA_REG_BLOCK* Reg = (FELICA_REG_BLOCK*)instance->data->data.fs.reg.data;
FELICA_REG_BLOCK* RegNew = (FELICA_REG_BLOCK*)data_block->data;
if(Reg->A >= RegNew->A) {
Reg->A = RegNew->A;
}
if(Reg->B >= RegNew->B) {
Reg->B = RegNew->B;
}
if((Reg->A >= RegNew->A) && (Reg->B >= RegNew->B)) {
Reg->C = RegNew->C;
}
}
static void felica_handler_write_mc_block(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block) {
UNUSED(block_number);
bool mc_bits_permission = felica_get_mc_bit(instance, FELICA_MC_SP_REG_ALL_RW_BYTES_0_1, 15);
const FelicaData* data = instance->data;
bool mc_system_block_permission = FELICA_SYSTEM_BLOCK_RW_ACCESS(data);
for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++) {
if((i <= 1) && mc_bits_permission) {
instance->mc_shadow.data[i] &= data_block->data[i];
} else if(
(i >= FELICA_MC_ALL_BYTE && i <= FELICA_MC_CKCKV_W_MAC_A) &&
(mc_system_block_permission)) {
instance->mc_shadow.data[i] = data_block->data[i];
} else if(
(i >= FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 && i <= FELICA_MC_STATE_W_MAC_A) &&
(mc_bits_permission)) {
instance->mc_shadow.data[i] |= data_block->data[i];
} else if(i >= FELICA_MC_RESERVED_13) {
instance->mc_shadow.data[i] = 0;
}
}
}
static void felica_handler_write_state_block(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block) {
felica_handler_write_block(instance, block_number, data_block);
bool state = instance->data->data.fs.state.data[0] == 0x01;
instance->auth.context.auth_status.external = state;
instance->auth.context.auth_status.internal = state;
}
static void felica_handler_write_id_block(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block) {
UNUSED(block_number);
memcpy(&instance->data->data.fs.id.data[8], &data_block->data[8], 8);
}
FelicaCommandWriteBlockHandler
felica_listener_get_write_block_handler(const uint8_t block_number) {
FelicaCommandWriteBlockHandler handler = felica_handler_write_block;
switch(block_number) {
case FELICA_BLOCK_INDEX_RC:
handler = felica_handler_write_rc_block;
break;
case FELICA_BLOCK_INDEX_REG:
handler = felica_handler_write_reg_block;
break;
case FELICA_BLOCK_INDEX_MC:
handler = felica_handler_write_mc_block;
break;
case FELICA_BLOCK_INDEX_STATE:
handler = felica_handler_write_state_block;
break;
case FELICA_BLOCK_INDEX_ID:
handler = felica_handler_write_id_block;
break;
default:
handler = felica_handler_write_block;
break;
}
return handler;
}
static FelicaError felica_listener_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return FelicaErrorNone;
case NfcErrorTimeout:
return FelicaErrorTimeout;
default:
return FelicaErrorNotPresent;
}
}
FelicaError
felica_listener_frame_exchange(const FelicaListener* instance, const BitBuffer* tx_buffer) {
furi_assert(instance);
furi_assert(tx_buffer);
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_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
ret = felica_listener_process_error(error);
break;
}
} while(false);
return ret;
}
+224 -1
View File
@@ -2,15 +2,72 @@
#include <nfc/protocols/nfc_generic_event.h>
#define FELICA_LISTENER_READ_BLOCK_COUNT_MAX (4U)
#define FELICA_LISTENER_READ_BLOCK_COUNT_MIN (1U)
#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U)
#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U)
typedef enum {
Felica_ListenerStateIdle,
Felica_ListenerStateActivated,
} FelicaListenerState;
/** Generic Felica request same for both read and write operations. */
typedef struct {
uint8_t length;
FelicaCommandHeader header;
} FelicaListenerGenericRequest;
/** Generic request but with list of requested elements. */
typedef struct {
FelicaListenerGenericRequest base;
FelicaBlockListElement list[];
} FelicaListenerRequest;
typedef FelicaListenerRequest FelicaListenerReadRequest;
typedef FelicaListenerRequest FelicaListenerWriteRequest;
/** Struct for write request data.
*
* All data are placed right after @ref FelicaListenerRequest data.
*/
typedef struct {
FelicaBlockData blocks[FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX];
} FelicaListenerWriteBlockData;
/** Write command handler signature.
*
* Depending on requested block write behaviour can be different, therefore
* different handlers are used.
*/
typedef void (*FelicaCommandWriteBlockHandler)(
FelicaListener* instance,
const uint8_t block_number,
const FelicaBlockData* data_block);
/** Read command handler signature.
*
* Depending on requested block read behaviour can be different, therefore
* different handlers are used.
*/
typedef void (*FelicaCommanReadBlockHandler)(
FelicaListener* instance,
const uint8_t block_number,
const uint8_t resp_data_index,
FelicaListenerReadCommandResponse* response);
struct FelicaListener {
Nfc* nfc;
FelicaData* data;
FelicaListenerState state;
FelicaAuthentication auth;
FelicaBlockData mc_shadow;
uint8_t request_size_buf;
uint8_t block_list_size;
uint8_t requested_blocks[FELICA_LISTENER_READ_BLOCK_COUNT_MAX];
uint8_t mac_calc_start;
bool rc_written;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
@@ -18,4 +75,170 @@ struct FelicaListener {
NfcGenericEvent generic_event;
NfcGenericCallback callback;
void* context;
};
};
/** Resets card authentication state after field off, also resets session key and
* perform post process operations with some blocks.
*
* @param instance pointer to the listener instance to be used.
*/
void felica_listener_reset(FelicaListener* instance);
/** Performs WCNT increasing in case of write operation.
*
* @param data pointer to Felica card data.
*/
void felica_wcnt_increment(FelicaData* data);
/** Compares IDm of card loaded for emulation with IDm from request.
*
* @param instance pointer to the listener instance to be used.
* @param request_idm pointer to IDm block from request structure.
*
* @return True if IDms' are equal, otherwise false.
*/
bool felica_listener_check_idm(const FelicaListener* instance, const FelicaIDm* request_idm);
/** This is the first request validation function.
*
* Its main aim is to check whether the input request is valid in general by
* counting the amount of data in request. Function iterates through block list
* elements and data, counts real amount of elements and real amount of
* coresponding elements data (in case of write operation). If block list
* element amount is invalid or request data is missing, such request will be
* ignored.
*
* @param instance pointer to the listener instance to be used.
* @param request pointer to received request.
*
* @return True if block element list contains valid amount of data,
* otherwise false.
*/
bool felica_listener_check_block_list_size(
FelicaListener* instance,
FelicaListenerGenericRequest* request);
/** Used to take first element from block element list.
*
* Each element in block list can be 2 or 3-bytes length and they can be mixed
* together. Therefore before returning the element, function checks whether
* there is enough bytes in the request for this element, in order to avoid
* memory casting behind the request block.
*
* @param instance pointer to the listener instance to be used.
* @param request pointer to received request.
*
* @return Pointer to the first element of the list or NULL if there is not
* enough bytes in the request.
*/
const FelicaBlockListElement* felica_listener_block_list_item_get_first(
FelicaListener* instance,
const FelicaListenerRequest* request);
/** Used to take next element from block element list.
*
* It uses pointer to the previous element, takes its length and calculates
* pointer to the next element if there is enough bytes left.
*
* @param instance pointer to the listener instance to be used.
* @param prev_item pointer to the previous item taken.
*
* @return Pointer to the next element of the list or NULL if there is not
* enough bytes in the request.
*/
const FelicaBlockListElement* felica_listener_block_list_item_get_next(
FelicaListener* instance,
const FelicaBlockListElement* prev_item);
/** Calculates pointer to data blocks in case of write operation, because block
* list elements size can vary.
*
* For calculation it uses previously calculated block list size and
* FelicaListenerGenericRequest size.
*
* @param instance pointer to the listener instance to be used.
* @param generic_request pointer to received request.
*
* @return Pointer to data blocks for write operation.
*/
const FelicaListenerWriteBlockData* felica_listener_get_write_request_data_pointer(
const FelicaListener* const instance,
const FelicaListenerGenericRequest* const generic_request);
/** Function validates write request data and sets Felica SF1 and SF2 flags
* directly to the response.
*
* When something is wrong with data in the request (for example non-existing
* block is requested) this function sets proper SF1 and SF2 flags which will be
* then send to reader. When every thing is fine SF1 and SF2 are 0. Also this
* function performs authentiocation checks by MAC_A calculation if request
* contains MAC_A.
*
* @param instance pointer to the listener instance to be used.
* @param request pointer to received write request.
* @param data pointer to data which request wants to write.
* @param response pointer to the response to where SF1 and SF2 flags will
* be populated.
*
* @return True if request is fine also SF1 and SF2 will be 0. False if
* something is wrong and SF1, SF2 will provide more detailed
* information about the error.
*/
bool felica_listener_validate_write_request_and_set_sf(
FelicaListener* instance,
const FelicaListenerWriteRequest* const request,
const FelicaListenerWriteBlockData* const data,
FelicaListenerWriteCommandResponse* response);
/** Function validates read request data and sets Felica SF1 and SF2 flags
* directly to the response.
*
* When something is wrong with data in the request (for example non-existing
* block is requested) this function sets proper SF1 and SF2 flags which will be
* then send to reader. In such case there will be no any data in the request,
* only flags. In case when request is fine SF1 and SF2 will be 0 and data will
* be populated to the response after them. Attention! This function doesn't
* populate any data, it only allows further processing where those data will be
* populated.
*
* @param instance pointer to the listener instance to be used.
* @param request pointer to received write request.
* @param resp_header pointer to the response to where SF1 and SF2 and
* data flags will be populated.
*
* @return True if request is fine also SF1 and SF2 will be 0. False if
* something is wrong and SF1, SF2 will provide more detailed
* information about the error.
*/
bool felica_listener_validate_read_request_and_set_sf(
FelicaListener* instance,
const FelicaListenerReadRequest* const request,
FelicaCommandResponseHeader* resp_header);
/** Function returns appropiate block handler for processing read operation
* depending on number of block.
*
* @param block_number number of block.
*
* @return pointer to block handler function.
*/
FelicaCommanReadBlockHandler felica_listener_get_read_block_handler(const uint8_t block_number);
/** Function returns appropiate block handler for processing write operation
* depending on number of block.
*
* @param block_number number of block.
*
* @return pointer to block handler function.
*/
FelicaCommandWriteBlockHandler felica_listener_get_write_block_handler(const uint8_t block_number);
/** Sends response data from buffer to reader.
*
* @param instance pointer to the listener instance to be used.
* @param tx_buffer buffer with response data.
*
* @return error code or FelicaErrorNone.
*/
FelicaError
felica_listener_frame_exchange(const FelicaListener* instance, const BitBuffer* tx_buffer);
-16
View File
@@ -24,22 +24,6 @@ typedef enum {
FelicaPollerEventTypeRequestAuthContext, /**< Authentication context was requested by poller. */
} FelicaPollerEventType;
/**
* @brief Stucture for holding Felica session key which is calculated from rc and ck.
*/
typedef struct {
uint8_t data[FELICA_DATA_BLOCK_SIZE];
} FelicaSessionKey;
/**
* @brief Structure used to hold authentication related fields.
*/
typedef struct {
mbedtls_des3_context des_context; /**< Context for mbedtls des functions. */
FelicaSessionKey session_key; /**< Calculated session key. */
FelicaAuthenticationContext context; /**< Public auth context provided to upper levels. */
} FelicaAuthentication;
/**
* @brief Felica poller event data.
*/
@@ -3,11 +3,6 @@
#include <nfc/helpers/felica_crc.h>
#define TAG "FelicaPoller"
#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06U)
#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08U)
#define FELICA_SERVICE_RW_ACCESS (0x0009U)
#define FELICA_SERVICE_RO_ACCESS (0x000BU)
static FelicaError felica_poller_process_error(NfcError error) {
switch(error) {
@@ -55,41 +55,6 @@ typedef struct {
uint8_t request_data[2];
} FelicaPollerPollingResponse;
typedef struct {
uint8_t service_code : 4;
uint8_t access_mode : 3;
uint8_t length : 1;
uint8_t block_number;
} FelicaBlockListElement;
#pragma pack(push, 1)
typedef struct {
uint8_t code;
FelicaIDm idm;
uint8_t service_num;
uint16_t service_code;
uint8_t block_count;
} FelicaCommandHeader;
#pragma pack(pop)
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
uint8_t SF1;
uint8_t SF2;
uint8_t block_count;
uint8_t data[];
} FelicaPollerReadCommandResponse;
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
uint8_t SF1;
uint8_t SF2;
} FelicaPollerWriteCommandResponse;
const FelicaData* felica_poller_get_data(FelicaPoller* instance);
/**
@@ -0,0 +1,70 @@
#include "felica_poller_sync.h"
#include "felica_poller_i.h"
#include <nfc/nfc_poller.h>
#include <furi/furi.h>
#define FELICA_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0)
typedef struct {
FelicaAuthenticationContext auth_ctx;
FuriThreadId thread_id;
FelicaError error;
FelicaData data;
} Felica_PollerContext;
NfcCommand felica_poller_read_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.event_data);
furi_assert(event.instance);
furi_assert(event.protocol == NfcProtocolFelica);
Felica_PollerContext* poller_context = context;
FelicaPoller* felica_poller = event.instance;
FelicaPollerEvent* felica_event = event.event_data;
if(felica_event->type == FelicaPollerEventTypeReady ||
felica_event->type == FelicaPollerEventTypeIncomplete) {
felica_copy(&poller_context->data, felica_poller->data);
} else if(felica_event->type == FelicaPollerEventTypeRequestAuthContext) {
felica_event->data->auth_context->skip_auth = poller_context->auth_ctx.skip_auth;
memcpy(
felica_event->data->auth_context->card_key.data,
poller_context->auth_ctx.card_key.data,
FELICA_DATA_BLOCK_SIZE);
}
furi_thread_flags_set(poller_context->thread_id, FELICA_POLLER_FLAG_COMMAND_COMPLETE);
return NfcCommandStop;
}
FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCardKey* card_key) {
furi_check(nfc);
furi_check(data);
Felica_PollerContext poller_context = {};
if(card_key == NULL) {
poller_context.auth_ctx.skip_auth = true;
} else {
poller_context.auth_ctx.skip_auth = false;
memcpy(poller_context.auth_ctx.card_key.data, card_key->data, FELICA_DATA_BLOCK_SIZE);
}
poller_context.thread_id = furi_thread_get_current_id();
NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolFelica);
nfc_poller_start(poller, felica_poller_read_callback, &poller_context);
furi_thread_flags_wait(FELICA_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
furi_thread_flags_clear(FELICA_POLLER_FLAG_COMMAND_COMPLETE);
nfc_poller_stop(poller);
nfc_poller_free(poller);
if(poller_context.error == FelicaErrorNone) {
*data = poller_context.data;
}
return poller_context.error;
}
@@ -0,0 +1,14 @@
#pragma once
#include "felica.h"
#include <nfc/nfc.h>
#ifdef __cplusplus
extern "C" {
#endif
FelicaError felica_poller_sync_read(Nfc* nfc, FelicaData* data, const FelicaCardKey* card_key);
#ifdef __cplusplus
}
#endif
@@ -1,6 +1,6 @@
#include "iso14443_3a_poller_sync.h"
#include "iso14443_3a_poller_i.h"
#include "iso14443_3a_poller_i.h" // IWYU pragma: keep
#include <nfc/nfc_poller.h>
#include <furi/furi.h>
@@ -1 +0,0 @@
#include "iso14443_3b_i.h"
+1 -1
View File
@@ -1,4 +1,4 @@
#include "iso14443_4b_i.h"
#include "iso14443_4b_i.h" // IWYU pragma: keep
#include <furi.h>
#include <nfc/protocols/nfc_device_base_i.h>
+180
View File
@@ -0,0 +1,180 @@
#include "mf_plus_i.h"
#include <bit_lib/bit_lib.h>
#include <furi.h>
#define MF_PLUS_PROTOCOL_NAME "Mifare Plus"
static const char* mf_plus_type_strings[] = {
[MfPlusTypeS] = "Plus S",
[MfPlusTypeX] = "Plus X",
[MfPlusTypeSE] = "Plus SE",
[MfPlusTypeEV1] = "Plus EV1",
[MfPlusTypeEV2] = "Plus EV2",
[MfPlusTypePlus] = "Plus",
[MfPlusTypeUnknown] = "Unknown",
};
static const char* mf_plus_size_strings[] = {
[MfPlusSize1K] = "1K",
[MfPlusSize2K] = "2K",
[MfPlusSize4K] = "4K",
[MfPlusSizeUnknown] = "Unknown",
};
static const char* mf_plus_security_level_strings[] = {
[MfPlusSecurityLevel0] = "SL0",
[MfPlusSecurityLevel1] = "SL1",
[MfPlusSecurityLevel2] = "SL2",
[MfPlusSecurityLevel3] = "SL3",
[MfPlusSecurityLevelUnknown] = "Unknown",
};
const NfcDeviceBase nfc_device_mf_plus = {
.protocol_name = MF_PLUS_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)mf_plus_alloc,
.free = (NfcDeviceFree)mf_plus_free,
.reset = (NfcDeviceReset)mf_plus_reset,
.copy = (NfcDeviceCopy)mf_plus_copy,
.verify = (NfcDeviceVerify)mf_plus_verify,
.load = (NfcDeviceLoad)mf_plus_load,
.save = (NfcDeviceSave)mf_plus_save,
.is_equal = (NfcDeviceEqual)mf_plus_is_equal,
.get_name = (NfcDeviceGetName)mf_plus_get_device_name,
.get_uid = (NfcDeviceGetUid)mf_plus_get_uid,
.set_uid = (NfcDeviceSetUid)mf_plus_set_uid,
.get_base_data = (NfcDeviceGetBaseData)mf_plus_get_base_data,
};
MfPlusData* mf_plus_alloc(void) {
MfPlusData* data = malloc(sizeof(MfPlusData));
data->device_name = furi_string_alloc();
data->iso14443_4a_data = iso14443_4a_alloc();
data->type = MfPlusTypeUnknown;
data->security_level = MfPlusSecurityLevelUnknown;
data->size = MfPlusSizeUnknown;
return data;
}
void mf_plus_free(MfPlusData* data) {
furi_check(data);
furi_string_free(data->device_name);
iso14443_4a_free(data->iso14443_4a_data);
free(data);
}
void mf_plus_reset(MfPlusData* data) {
furi_check(data);
iso14443_4a_reset(data->iso14443_4a_data);
memset(&data->version, 0, sizeof(data->version));
furi_string_reset(data->device_name);
data->type = MfPlusTypeUnknown;
data->security_level = MfPlusSecurityLevelUnknown;
data->size = MfPlusSizeUnknown;
}
void mf_plus_copy(MfPlusData* data, const MfPlusData* other) {
furi_check(data);
furi_check(other);
iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);
data->version = other->version;
data->type = other->type;
data->security_level = other->security_level;
data->size = other->size;
}
bool mf_plus_verify(MfPlusData* data, const FuriString* device_type) {
UNUSED(data);
return furi_string_equal_str(device_type, MF_PLUS_PROTOCOL_NAME);
}
bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version) {
furi_check(data);
bool success = false;
do {
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
if(!mf_plus_version_load(&data->version, ff)) break;
if(!mf_plus_type_load(&data->type, ff)) break;
if(!mf_plus_security_level_load(&data->security_level, ff)) break;
if(!mf_plus_size_load(&data->size, ff)) break;
success = true;
} while(false);
return success;
}
bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff) {
furi_check(data);
bool success = false;
do {
if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, MF_PLUS_PROTOCOL_NAME " specific data")) break;
if(!mf_plus_version_save(&data->version, ff)) break;
if(!mf_plus_type_save(&data->type, ff)) break;
if(!mf_plus_security_level_save(&data->security_level, ff)) break;
if(!mf_plus_size_save(&data->size, ff)) break;
success = true;
} while(false);
return success;
}
bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other) {
furi_check(data);
furi_check(other);
bool equal = false;
do {
if(!iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data)) break;
if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break;
if(data->security_level != other->security_level) break;
if(data->type != other->type) break;
if(data->size != other->size) break;
equal = true;
} while(false);
return equal;
}
const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type) {
furi_check(data);
if(name_type == NfcDeviceNameTypeFull) {
furi_string_printf(
data->device_name,
"Mifare %s %s %s",
mf_plus_type_strings[data->type], // Includes "Plus" for regular Mifare Plus cards
mf_plus_size_strings[data->size],
mf_plus_security_level_strings[data->security_level]);
} else if(name_type == NfcDeviceNameTypeShort) {
furi_string_set_str(data->device_name, MF_PLUS_PROTOCOL_NAME);
} else {
furi_crash("Unexpected name type");
}
return furi_string_get_cstr(data->device_name);
}
const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len) {
furi_check(data);
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
}
bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len) {
furi_check(data);
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
}
Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data) {
furi_check(data);
return data->iso14443_4a_data;
}
+115
View File
@@ -0,0 +1,115 @@
#pragma once
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MF_PLUS_UID_SIZE_MAX (7)
#define MF_PLUS_BATCH_SIZE (5)
#define MF_PLUS_CMD_GET_VERSION (0x60)
typedef enum {
MfPlusErrorNone,
MfPlusErrorUnknown,
MfPlusErrorNotPresent,
MfPlusErrorProtocol,
MfPlusErrorAuth,
MfPlusErrorPartialRead,
MfPlusErrorTimeout,
} MfPlusError;
typedef enum {
MfPlusTypePlus,
MfPlusTypeEV1,
MfPlusTypeEV2,
MfPlusTypeS,
MfPlusTypeSE,
MfPlusTypeX,
MfPlusTypeUnknown,
MfPlusTypeNum,
} MfPlusType;
typedef enum {
MfPlusSize1K,
MfPlusSize2K,
MfPlusSize4K,
MfPlusSizeUnknown,
MfPlusSizeNum,
} MfPlusSize;
typedef enum {
MfPlusSecurityLevel0,
MfPlusSecurityLevel1,
MfPlusSecurityLevel2,
MfPlusSecurityLevel3,
MfPlusSecurityLevelUnknown,
MfPlusSecurityLevelNum,
} MfPlusSecurityLevel;
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_PLUS_UID_SIZE_MAX];
uint8_t batch[MF_PLUS_BATCH_SIZE];
uint8_t prod_week;
uint8_t prod_year;
} MfPlusVersion;
typedef struct {
Iso14443_4aData* iso14443_4a_data;
MfPlusVersion version;
MfPlusType type;
MfPlusSize size;
MfPlusSecurityLevel security_level;
FuriString* device_name;
} MfPlusData;
extern const NfcDeviceBase nfc_device_mf_plus;
MfPlusData* mf_plus_alloc(void);
void mf_plus_free(MfPlusData* data);
void mf_plus_reset(MfPlusData* data);
void mf_plus_copy(MfPlusData* data, const MfPlusData* other);
bool mf_plus_verify(MfPlusData* data, const FuriString* device_type);
bool mf_plus_load(MfPlusData* data, FlipperFormat* ff, uint32_t version);
bool mf_plus_save(const MfPlusData* data, FlipperFormat* ff);
bool mf_plus_is_equal(const MfPlusData* data, const MfPlusData* other);
const char* mf_plus_get_device_name(const MfPlusData* data, NfcDeviceNameType name_type);
const uint8_t* mf_plus_get_uid(const MfPlusData* data, size_t* uid_len);
bool mf_plus_set_uid(MfPlusData* data, const uint8_t* uid, size_t uid_len);
Iso14443_4aData* mf_plus_get_base_data(const MfPlusData* data);
#ifdef __cplusplus
}
#endif
+406
View File
@@ -0,0 +1,406 @@
#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
};
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_major == 0x02 || mf_plus_data->version.hw_major == 0x82) {
error = MfPlusErrorNone;
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 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;
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 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) {
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 ? 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;
}
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#include "mf_plus.h"
#define MF_PLUS_FFF_PICC_PREFIX "PICC"
MfPlusError mf_plus_get_type_from_version(
const Iso14443_4aData* iso14443_4a_data,
MfPlusData* mf_plus_data);
MfPlusError mf_plus_get_type_from_iso4(const Iso14443_4aData* iso4_data, MfPlusData* mf_plus_data);
MfPlusError mf_plus_version_parse(MfPlusVersion* data, const BitBuffer* buf);
bool mf_plus_version_load(MfPlusVersion* data, FlipperFormat* ff);
bool mf_plus_security_level_load(MfPlusSecurityLevel* data, FlipperFormat* ff);
bool mf_plus_type_load(MfPlusType* data, FlipperFormat* ff);
bool mf_plus_size_load(MfPlusSize* data, FlipperFormat* ff);
bool mf_plus_version_save(const MfPlusVersion* data, FlipperFormat* ff);
bool mf_plus_security_level_save(const MfPlusSecurityLevel* data, FlipperFormat* ff);
bool mf_plus_type_save(const MfPlusType* data, FlipperFormat* ff);
bool mf_plus_size_save(const MfPlusSize* data, FlipperFormat* ff);
+210
View File
@@ -0,0 +1,210 @@
#include "mf_plus_poller_i.h"
#include "mf_plus_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "MfPlusPoller"
#define MF_PLUS_BUF_SIZE (64U)
#define MF_PLUS_RESULT_BUF_SIZE (512U)
typedef NfcCommand (*MfPlusPollerReadHandler)(MfPlusPoller* instance);
const MfPlusData* mf_plus_poller_get_data(MfPlusPoller* instance) {
furi_assert(instance);
return instance->data;
}
MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
furi_assert(iso14443_4a_poller);
MfPlusPoller* instance = malloc(sizeof(MfPlusPoller));
instance->iso14443_4a_poller = iso14443_4a_poller;
instance->data = mf_plus_alloc();
instance->tx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);
instance->rx_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);
instance->input_buffer = bit_buffer_alloc(MF_PLUS_BUF_SIZE);
instance->result_buffer = bit_buffer_alloc(MF_PLUS_RESULT_BUF_SIZE);
instance->general_event.protocol = NfcProtocolMfPlus;
instance->general_event.event_data = &instance->mfp_event;
instance->general_event.instance = instance;
instance->mfp_event.data = &instance->mfp_event_data;
return instance;
}
static NfcCommand mf_plus_poller_handler_idle(MfPlusPoller* instance) {
furi_assert(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 = MfPlusPollerStateReadVersion;
return NfcCommandContinue;
}
static NfcCommand mf_plus_poller_handler_read_version(MfPlusPoller* instance) {
MfPlusError error = mf_plus_poller_read_version(instance, &instance->data->version);
if(error == MfPlusErrorNone) {
instance->state = MfPlusPollerStateParseVersion;
} else {
instance->state = MfPlusPollerStateParseIso4;
}
return NfcCommandContinue;
}
static NfcCommand mf_plus_poller_handler_parse_version(MfPlusPoller* instance) {
furi_assert(instance);
MfPlusError error = mf_plus_get_type_from_version(
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
if(error == MfPlusErrorNone) {
instance->state = MfPlusPollerStateReadSuccess;
} else {
instance->error = error;
instance->state = MfPlusPollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_plus_poller_handler_parse_iso4(MfPlusPoller* instance) {
furi_assert(instance);
MfPlusError error = mf_plus_get_type_from_iso4(
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
if(error == MfPlusErrorNone) {
instance->state = MfPlusPollerStateReadSuccess;
} else {
instance->error = error;
instance->state = MfPlusPollerStateReadFailed;
}
return NfcCommandContinue;
}
static NfcCommand mf_plus_poller_handler_read_failed(MfPlusPoller* instance) {
furi_assert(instance);
FURI_LOG_D(TAG, "Read failed");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->mfp_event.type = MfPlusPollerEventTypeReadFailed;
instance->mfp_event.data->error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->state = MfPlusPollerStateIdle;
return command;
}
static NfcCommand mf_plus_poller_handler_read_success(MfPlusPoller* instance) {
furi_assert(instance);
FURI_LOG_D(TAG, "Read success");
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
instance->mfp_event.type = MfPlusPollerEventTypeReadSuccess;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const MfPlusPollerReadHandler mf_plus_poller_read_handler[MfPlusPollerStateNum] = {
[MfPlusPollerStateIdle] = mf_plus_poller_handler_idle,
[MfPlusPollerStateReadVersion] = mf_plus_poller_handler_read_version,
[MfPlusPollerStateParseVersion] = mf_plus_poller_handler_parse_version,
[MfPlusPollerStateParseIso4] = mf_plus_poller_handler_parse_iso4,
[MfPlusPollerStateReadFailed] = mf_plus_poller_handler_read_failed,
[MfPlusPollerStateReadSuccess] = mf_plus_poller_handler_read_success,
};
static void mf_plus_poller_set_callback(
MfPlusPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol = NfcProtocolIso14443_4a);
furi_assert(event.event_data);
MfPlusPoller* instance = context;
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
NfcCommand command = NfcCommandContinue;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
command = mf_plus_poller_read_handler[instance->state](instance);
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
instance->mfp_event.type = MfPlusPollerEventTypeReadFailed;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
void mf_plus_poller_free(MfPlusPoller* instance) {
furi_assert(instance);
furi_assert(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);
mf_plus_free(instance->data);
free(instance);
}
static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol = NfcProtocolIso14443_4a);
furi_assert(event.event_data);
MfPlusPoller* instance = context;
Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
MfPlusError error = MfPlusErrorUnknown;
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
error = mf_plus_poller_read_version(instance, &instance->data->version);
if(error == MfPlusErrorNone) {
error = mf_plus_get_type_from_version(
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
} else {
error = mf_plus_get_type_from_iso4(
iso14443_4a_poller_get_data(instance->iso14443_4a_poller), instance->data);
}
}
return (error == MfPlusErrorNone);
}
const NfcPollerBase mf_plus_poller = {
.alloc = (NfcPollerAlloc)mf_plus_poller_alloc,
.free = (NfcPollerFree)mf_plus_poller_free,
.set_callback = (NfcPollerSetCallback)mf_plus_poller_set_callback,
.run = (NfcPollerRun)mf_plus_poller_run,
.detect = (NfcPollerDetect)mf_plus_poller_detect,
.get_data = (NfcPollerGetData)mf_plus_poller_get_data,
};
@@ -0,0 +1,55 @@
#pragma once
#include "mf_plus.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief MIFARE Plus poller opaque type definition.
*/
typedef struct MfPlusPoller MfPlusPoller;
/**
* @brief Enumeration of possible MfPlus poller event types.
*/
typedef enum {
MfPlusPollerEventTypeReadSuccess, /**< Card was read successfully. */
MfPlusPollerEventTypeReadFailed, /**< Poller failed to read the card. */
} MfPlusPollerEventType;
/**
* @brief MIFARE Plus poller event data.
*/
typedef union {
MfPlusError error; /**< Error code indicating card reading fail reason. */
} MfPlusPollerEventData;
/**
* @brief MIFARE Plus poller event structure.
*
* Upon emission of an event, an instance of this struct will be passed to the callback.
*/
typedef struct {
MfPlusPollerEventType type; /**< Type of emitted event. */
MfPlusPollerEventData* data; /**< Pointer to event specific data. */
} MfPlusPollerEvent;
/**
* @brief Read MfPlus card version.
*
* Must ONLY be used inside the callback function.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[out] data pointer to the MfPlusVersion structure to be filled with version data.
* @return MfPlusErrorNone on success, an error code on failure.
*/
MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase mf_plus_poller;
@@ -0,0 +1,59 @@
#include "mf_plus_poller_i.h"
#include <furi.h>
#include "mf_plus_i.h"
#define TAG "MfPlusPoller"
MfPlusError mf_plus_process_error(Iso14443_4aError error) {
switch(error) {
case Iso14443_4aErrorNone:
return MfPlusErrorNone;
case Iso14443_4aErrorNotPresent:
return MfPlusErrorNotPresent;
case Iso14443_4aErrorTimeout:
return MfPlusErrorTimeout;
default:
return MfPlusErrorProtocol;
}
}
MfPlusError mf_plus_poller_send_chunk(
MfPlusPoller* 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);
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer);
MfPlusError error = mf_plus_process_error(iso14443_4a_error);
if(error == MfPlusErrorNone) {
bit_buffer_copy(rx_buffer, instance->rx_buffer);
}
bit_buffer_reset(instance->tx_buffer);
return error;
}
MfPlusError mf_plus_poller_read_version(MfPlusPoller* instance, MfPlusVersion* data) {
furi_check(instance);
bit_buffer_reset(instance->input_buffer);
bit_buffer_append_byte(instance->input_buffer, MF_PLUS_CMD_GET_VERSION);
MfPlusError error =
mf_plus_poller_send_chunk(instance, instance->input_buffer, instance->result_buffer);
if(error == MfPlusErrorNone) {
error = mf_plus_version_parse(data, instance->result_buffer);
}
return error;
}
@@ -0,0 +1,56 @@
#pragma once
#include "mf_plus_poller.h"
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MF_PLUS_FWT_FC (60000)
typedef enum {
MfPlusCardStateDetected,
MfPlusCardStateLost,
} MfPlusCardState;
typedef enum {
MfPlusPollerStateIdle,
MfPlusPollerStateReadVersion,
MfPlusPollerStateParseVersion,
MfPlusPollerStateParseIso4,
MfPlusPollerStateReadFailed,
MfPlusPollerStateReadSuccess,
MfPlusPollerStateNum,
} MfPlusPollerState;
struct MfPlusPoller {
Iso14443_4aPoller* iso14443_4a_poller;
MfPlusData* data;
MfPlusPollerState state;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
BitBuffer* input_buffer;
BitBuffer* result_buffer;
MfPlusError error;
NfcGenericEvent general_event;
MfPlusPollerEvent mfp_event;
MfPlusPollerEventData mfp_event_data;
NfcGenericCallback callback;
void* context;
};
MfPlusError mf_plus_process_error(Iso14443_4aError error);
MfPlusPoller* mf_plus_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller);
void mf_plus_poller_free(MfPlusPoller* instance);
#ifdef __cplusplus
}
#endif
@@ -570,6 +570,22 @@ uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) {
return mf_ultralight_features[type].config_page;
}
uint8_t mf_ultralight_get_write_end_page(MfUltralightType type) {
furi_check(type < MfUltralightTypeNum);
furi_assert(
type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21 ||
type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 ||
type == MfUltralightTypeNTAG216);
uint8_t end_page = mf_ultralight_get_config_page_num(type);
if(type == MfUltralightTypeNTAG213 || type == MfUltralightTypeNTAG215 ||
type == MfUltralightTypeNTAG216) {
end_page -= 1;
}
return end_page;
}
uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type) {
furi_check(type < MfUltralightTypeNum);
@@ -210,6 +210,8 @@ uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type);
uint16_t mf_ultralight_get_config_page_num(MfUltralightType type);
uint8_t mf_ultralight_get_write_end_page(MfUltralightType type);
uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type);
bool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page_num);
@@ -616,7 +616,7 @@ static NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* i
do {
const MfUltralightData* write_data = instance->mfu_event.data->write_data;
uint8_t end_page = mf_ultralight_get_config_page_num(write_data->type) - 1;
uint8_t end_page = mf_ultralight_get_write_end_page(write_data->type);
if(instance->current_page == end_page) {
instance->state = MfUltralightPollerStateWriteSuccess;
break;
+2
View File
@@ -20,6 +20,7 @@
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_classic/mf_classic.h>
#include <nfc/protocols/mf_plus/mf_plus.h>
#include <nfc/protocols/mf_desfire/mf_desfire.h>
#include <nfc/protocols/slix/slix_device_defs.h>
#include <nfc/protocols/st25tb/st25tb.h>
@@ -39,6 +40,7 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = {
[NfcProtocolFelica] = &nfc_device_felica,
[NfcProtocolMfUltralight] = &nfc_device_mf_ultralight,
[NfcProtocolMfClassic] = &nfc_device_mf_classic,
[NfcProtocolMfPlus] = &nfc_device_mf_plus,
[NfcProtocolMfDesfire] = &nfc_device_mf_desfire,
[NfcProtocolSlix] = &nfc_device_slix,
[NfcProtocolSt25tb] = &nfc_device_st25tb,
+2
View File
@@ -8,6 +8,7 @@
#include <nfc/protocols/felica/felica_poller_defs.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_defs.h>
#include <nfc/protocols/mf_plus/mf_plus_poller_defs.h>
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
#include <nfc/protocols/slix/slix_poller_defs.h>
#include <nfc/protocols/st25tb/st25tb_poller_defs.h>
@@ -21,6 +22,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = {
[NfcProtocolFelica] = &nfc_poller_felica,
[NfcProtocolMfUltralight] = &mf_ultralight_poller,
[NfcProtocolMfClassic] = &mf_classic_poller,
[NfcProtocolMfPlus] = &mf_plus_poller,
[NfcProtocolMfDesfire] = &mf_desfire_poller,
[NfcProtocolSlix] = &nfc_poller_slix,
/* Add new pollers here */
+7
View File
@@ -61,6 +61,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = {
/** List of ISO14443-4A child protocols. */
static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = {
NfcProtocolMfDesfire,
NfcProtocolMfPlus,
};
/** List of ISO115693-3 child protocols. */
@@ -128,6 +129,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {
.children_num = 0,
.children_protocol = NULL,
},
[NfcProtocolMfPlus] =
{
.parent_protocol = NfcProtocolIso14443_4a,
.children_num = 0,
.children_protocol = NULL,
},
[NfcProtocolMfDesfire] =
{
.parent_protocol = NfcProtocolIso14443_4a,
+1
View File
@@ -184,6 +184,7 @@ typedef enum {
NfcProtocolFelica,
NfcProtocolMfUltralight,
NfcProtocolMfClassic,
NfcProtocolMfPlus,
NfcProtocolMfDesfire,
NfcProtocolSlix,
NfcProtocolSt25tb,
-1
View File
@@ -1,6 +1,5 @@
#include "pulse_reader.h"
#include <limits.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_gpio.h>
-1
View File
@@ -1,6 +1,5 @@
#include "signal_reader.h"
#include <limits.h>
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_gpio.h>
-1
View File
@@ -1 +0,0 @@
#include "const.h"
+7 -6
View File
@@ -529,7 +529,8 @@ static bool
bin_raw_type = BinRAWTypeGap;
//looking for the last occurrence of gap
ind = instance->data_raw_ind - 1;
while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) {
while((ind > 0) &&
(DURATION_DIFF(abs(instance->data_raw[ind]), (int32_t)gap) > gap_delta)) {
ind--;
}
gap_ind = ind;
@@ -544,10 +545,10 @@ static bool
uint16_t bit_count = 0;
do {
gap_ind--;
data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te));
data_temp = (int)(roundf((float)(instance->data_raw[gap_ind]) / instance->te));
bin_raw_debug("%d ", data_temp);
if(data_temp == 0) bit_count++; //there is noise in the package
for(size_t i = 0; i < abs(data_temp); i++) {
for(size_t i = 0; i < (size_t)abs(data_temp); i++) {
bit_count++;
if(ind) {
ind--;
@@ -563,7 +564,7 @@ static bool
}
}
//split into full bytes if gap is caught
if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) {
if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), (int32_t)gap) < gap_delta) {
instance->data_markup[data_markup_ind].byte_bias = ind >> 3;
instance->data_markup[data_markup_ind++].bit_count = bit_count;
bit_count = 0;
@@ -807,11 +808,11 @@ static bool
bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n");
ind = 0;
for(size_t i = 0; i < instance->data_raw_ind; i++) {
int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te));
int data_temp = (int)(roundf((float)(instance->data_raw[i]) / instance->te));
if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise
bin_raw_debug("%d ", data_temp);
for(size_t k = 0; k < abs(data_temp); k++) {
for(size_t k = 0; k < (size_t)abs(data_temp); k++) {
if(data_temp > 0) {
subghz_protocol_blocks_set_bit_array(
true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE);
+1 -1
View File
@@ -470,6 +470,6 @@ void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString
output,
"Ch:" CH_PATTERN " Dimm:%d%%\r\n",
CNT_TO_CH(instance->generic.cnt),
(int)(6.67 * (float)instance->generic.btn));
(int)(6.67f * (float)instance->generic.btn));
}
}
+1 -1
View File
@@ -267,7 +267,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
instance->guard_time = round((float)duration / instance->te);
instance->guard_time = roundf((float)duration / instance->te);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
+1 -1
View File
@@ -1,4 +1,4 @@
#include "protocol_items.h"
#include "protocol_items.h" // IWYU pragma: keep
const SubGhzProtocol* subghz_protocol_registry_items[] = {
&subghz_protocol_gate_tx,
-3
View File
@@ -3,10 +3,7 @@
#include "../subghz_file_encoder_worker.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include <flipper_format/flipper_format_i.h>
#include <lib/toolbox/stream/stream.h>
-1
View File
@@ -1,7 +1,6 @@
#include "receiver.h"
#include "registry.h"
#include "protocols/protocol_items.h"
#include <m-array.h>
+1 -2
View File
@@ -1,6 +1,5 @@
#include "subghz_setting.h"
#include "types.h"
//#include "subghz_i.h"
#include "types.h" // IWYU pragma: keep
#include <furi.h>
#include <m-list.h>
-1
View File
@@ -2,7 +2,6 @@
#include "protocols/base.h"
#include "registry.h"
#include "protocols/protocol_items.h"
struct SubGhzTransmitter {
const SubGhzProtocol* protocol;
+1 -1
View File
@@ -11,7 +11,7 @@
#include <furi.h>
#include <furi_hal.h>
#define SUBGHZ_APP_FOLDER ANY_PATH("subghz")
#define SUBGHZ_APP_FOLDER EXT_PATH("subghz")
#define SUBGHZ_RAW_FOLDER EXT_PATH("subghz")
#define SUBGHZ_APP_FILENAME_PREFIX "SubGHz"
#define SUBGHZ_APP_FILENAME_EXTENSION ".sub"
-1
View File
@@ -1,5 +1,4 @@
#include "manchester_encoder.h"
#include <stdio.h>
#include <furi.h>
void manchester_encoder_reset(ManchesterEncoderState* state) {
+5
View File
@@ -224,6 +224,11 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header,
FuriString* full_extracted_fname;
if(header->type == MTAR_TDIR) {
// Skip "/" entry since concat would leave it dangling, also want caller to mkdir destination
if(strcmp(header->name, "/") == 0) {
return 0;
}
full_extracted_fname = furi_string_alloc();
path_concat(op_params->work_dir, header->name, full_extracted_fname);