mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-21 00:58:10 -07:00
Merge branch 'dev' into astra/3746-mfp-detect
This commit is contained in:
@@ -13,6 +13,14 @@
|
||||
|
||||
static const uint32_t felica_data_format_version = 1;
|
||||
|
||||
/** @brief This is used in felica_prepare_first_block to define which
|
||||
* type of block needs to be prepared.
|
||||
*/
|
||||
typedef enum {
|
||||
FelicaMACTypeRead,
|
||||
FelicaMACTypeWrite,
|
||||
} FelicaMACType;
|
||||
|
||||
const NfcDeviceBase nfc_device_felica = {
|
||||
.protocol_name = FELICA_PROTOCOL_NAME,
|
||||
.alloc = (NfcDeviceAlloc)felica_alloc,
|
||||
@@ -29,24 +37,24 @@ const NfcDeviceBase nfc_device_felica = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)felica_get_base_data,
|
||||
};
|
||||
|
||||
FelicaData* felica_alloc() {
|
||||
FelicaData* felica_alloc(void) {
|
||||
FelicaData* data = malloc(sizeof(FelicaData));
|
||||
return data;
|
||||
}
|
||||
|
||||
void felica_free(FelicaData* data) {
|
||||
furi_assert(data);
|
||||
|
||||
furi_check(data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void felica_reset(FelicaData* data) {
|
||||
furi_check(data);
|
||||
memset(data, 0, sizeof(FelicaData));
|
||||
}
|
||||
|
||||
void felica_copy(FelicaData* data, const FelicaData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
*data = *other;
|
||||
}
|
||||
@@ -59,7 +67,7 @@ bool felica_verify(FelicaData* data, const FuriString* device_type) {
|
||||
}
|
||||
|
||||
bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
@@ -77,13 +85,32 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) {
|
||||
break;
|
||||
|
||||
parsed = true;
|
||||
uint32_t blocks_total = 0;
|
||||
uint32_t blocks_read = 0;
|
||||
if(!flipper_format_read_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
||||
if(!flipper_format_read_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
||||
data->blocks_total = (uint8_t)blocks_total;
|
||||
data->blocks_read = (uint8_t)blocks_read;
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
for(uint8_t i = 0; i < data->blocks_total; i++) {
|
||||
furi_string_printf(temp_str, "Block %d", i);
|
||||
if(!flipper_format_read_hex(
|
||||
ff,
|
||||
furi_string_get_cstr(temp_str),
|
||||
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||
sizeof(FelicaBlock))) {
|
||||
parsed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
@@ -98,15 +125,33 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) {
|
||||
ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE))
|
||||
break;
|
||||
|
||||
uint32_t blocks_total = data->blocks_total;
|
||||
uint32_t blocks_read = data->blocks_read;
|
||||
if(!flipper_format_write_uint32(ff, "Blocks total", &blocks_total, 1)) break;
|
||||
if(!flipper_format_write_uint32(ff, "Blocks read", &blocks_read, 1)) break;
|
||||
|
||||
saved = true;
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
for(uint8_t i = 0; i < blocks_total; i++) {
|
||||
furi_string_printf(temp_str, "Block %d", i);
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
furi_string_get_cstr(temp_str),
|
||||
(&data->data.dump[i * sizeof(FelicaBlock)]),
|
||||
sizeof(FelicaBlock))) {
|
||||
saved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool felica_is_equal(const FelicaData* data, const FelicaData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return memcmp(data, other, sizeof(FelicaData)) == 0;
|
||||
}
|
||||
@@ -119,7 +164,7 @@ const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType nam
|
||||
}
|
||||
|
||||
const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
// Consider Manufacturer ID as UID
|
||||
if(uid_len) {
|
||||
@@ -130,7 +175,7 @@ const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) {
|
||||
}
|
||||
|
||||
bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
// Consider Manufacturer ID as UID
|
||||
const bool uid_valid = uid_len == FELICA_IDM_SIZE;
|
||||
@@ -145,3 +190,149 @@ FelicaData* felica_get_base_data(const FelicaData* data) {
|
||||
UNUSED(data);
|
||||
furi_crash("No base data");
|
||||
}
|
||||
|
||||
static void felica_reverse_copy_block(const uint8_t* array, uint8_t* reverse_array) {
|
||||
furi_assert(array);
|
||||
furi_assert(reverse_array);
|
||||
|
||||
for(int i = 0; i < 8; i++) {
|
||||
reverse_array[i] = array[7 - i];
|
||||
}
|
||||
}
|
||||
|
||||
void felica_calculate_session_key(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* ck,
|
||||
const uint8_t* rc,
|
||||
uint8_t* out) {
|
||||
furi_check(ctx);
|
||||
furi_check(ck);
|
||||
furi_check(rc);
|
||||
furi_check(out);
|
||||
|
||||
uint8_t iv[8];
|
||||
memset(iv, 0, 8);
|
||||
|
||||
uint8_t ck_reversed[16];
|
||||
felica_reverse_copy_block(ck, ck_reversed);
|
||||
felica_reverse_copy_block(ck + 8, ck_reversed + 8);
|
||||
|
||||
uint8_t rc_reversed[16];
|
||||
felica_reverse_copy_block(rc, rc_reversed);
|
||||
felica_reverse_copy_block(rc + 8, rc_reversed + 8);
|
||||
|
||||
mbedtls_des3_set2key_enc(ctx, ck_reversed);
|
||||
mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, FELICA_DATA_BLOCK_SIZE, iv, rc_reversed, out);
|
||||
}
|
||||
|
||||
static bool felica_calculate_mac(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* session_key,
|
||||
const uint8_t* rc,
|
||||
const uint8_t* first_block,
|
||||
const uint8_t* data,
|
||||
const size_t length,
|
||||
uint8_t* mac) {
|
||||
furi_check((length % 8) == 0);
|
||||
|
||||
uint8_t reverse_data[8];
|
||||
uint8_t iv[8];
|
||||
uint8_t out[8];
|
||||
mbedtls_des3_set2key_enc(ctx, session_key);
|
||||
|
||||
felica_reverse_copy_block(rc, iv);
|
||||
felica_reverse_copy_block(first_block, reverse_data);
|
||||
uint8_t i = 0;
|
||||
bool error = false;
|
||||
do {
|
||||
if(mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, 8, iv, reverse_data, out) == 0) {
|
||||
memcpy(iv, out, sizeof(iv));
|
||||
felica_reverse_copy_block(data + i, reverse_data);
|
||||
i += 8;
|
||||
} else {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
} while(i <= length);
|
||||
|
||||
if(!error) {
|
||||
felica_reverse_copy_block(out, mac);
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
static void felica_prepare_first_block(
|
||||
FelicaMACType operation_type,
|
||||
const uint8_t* blocks,
|
||||
const uint8_t block_count,
|
||||
uint8_t* out) {
|
||||
furi_check(blocks);
|
||||
furi_check(out);
|
||||
if(operation_type == FelicaMACTypeRead) {
|
||||
memset(out, 0xFF, 8);
|
||||
for(uint8_t i = 0, j = 0; i < block_count; i++, j += 2) {
|
||||
out[j] = blocks[i];
|
||||
out[j + 1] = 0;
|
||||
}
|
||||
} else {
|
||||
furi_check(block_count == 4);
|
||||
memset(out, 0, 8);
|
||||
out[0] = blocks[0];
|
||||
out[1] = blocks[1];
|
||||
out[2] = blocks[2];
|
||||
out[4] = blocks[3];
|
||||
out[6] = FELICA_BLOCK_INDEX_MAC_A;
|
||||
}
|
||||
}
|
||||
|
||||
bool felica_check_mac(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* session_key,
|
||||
const uint8_t* rc,
|
||||
const uint8_t* blocks,
|
||||
const uint8_t block_count,
|
||||
uint8_t* data) {
|
||||
furi_check(ctx);
|
||||
furi_check(session_key);
|
||||
furi_check(rc);
|
||||
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);
|
||||
|
||||
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(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* session_key,
|
||||
const uint8_t* rc,
|
||||
const uint8_t* wcnt,
|
||||
const uint8_t* data,
|
||||
uint8_t* mac) {
|
||||
furi_check(ctx);
|
||||
furi_check(session_key);
|
||||
furi_check(rc);
|
||||
furi_check(wcnt);
|
||||
furi_check(data);
|
||||
furi_check(mac);
|
||||
|
||||
const uint8_t WCNT_length = 3;
|
||||
uint8_t block_data[WCNT_length + 1];
|
||||
uint8_t first_block[8];
|
||||
|
||||
memcpy(block_data, wcnt, WCNT_length);
|
||||
block_data[3] = FELICA_BLOCK_INDEX_STATE;
|
||||
felica_prepare_first_block(FelicaMACTypeWrite, block_data, WCNT_length + 1, first_block);
|
||||
|
||||
uint8_t session_swapped[FELICA_DATA_BLOCK_SIZE];
|
||||
memcpy(session_swapped, session_key + 8, 8);
|
||||
memcpy(session_swapped + 8, session_key, 8);
|
||||
felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
#include <nfc/protocols/nfc_device_base_i.h>
|
||||
#include <mbedtls/include/mbedtls/des.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -9,6 +10,23 @@ extern "C" {
|
||||
|
||||
#define FELICA_IDM_SIZE (8U)
|
||||
#define FELICA_PMM_SIZE (8U)
|
||||
#define FELICA_DATA_BLOCK_SIZE (16U)
|
||||
|
||||
#define FELICA_BLOCKS_TOTAL_COUNT (27U)
|
||||
#define FELICA_BLOCK_INDEX_REG (0x0EU)
|
||||
#define FELICA_BLOCK_INDEX_RC (0x80U)
|
||||
#define FELICA_BLOCK_INDEX_MAC (0x81U)
|
||||
#define FELICA_BLOCK_INDEX_ID (0x82U)
|
||||
#define FELICA_BLOCK_INDEX_D_ID (0x83U)
|
||||
#define FELICA_BLOCK_INDEX_SER_C (0x84U)
|
||||
#define FELICA_BLOCK_INDEX_SYS_C (0x85U)
|
||||
#define FELICA_BLOCK_INDEX_CKV (0x86U)
|
||||
#define FELICA_BLOCK_INDEX_CK (0x87U)
|
||||
#define FELICA_BLOCK_INDEX_MC (0x88U)
|
||||
#define FELICA_BLOCK_INDEX_WCNT (0x90U)
|
||||
#define FELICA_BLOCK_INDEX_MAC_A (0x91U)
|
||||
#define FELICA_BLOCK_INDEX_STATE (0x92U)
|
||||
#define FELICA_BLOCK_INDEX_CRC_CHECK (0xA0U)
|
||||
|
||||
#define FELICA_GUARD_TIME_US (20000U)
|
||||
#define FELICA_FDT_POLL_FC (10000U)
|
||||
@@ -23,6 +41,7 @@ extern "C" {
|
||||
#define FELICA_TIME_SLOT_8 (0x07U)
|
||||
#define FELICA_TIME_SLOT_16 (0x0FU)
|
||||
|
||||
/** @brief Type of possible Felica errors */
|
||||
typedef enum {
|
||||
FelicaErrorNone,
|
||||
FelicaErrorNotPresent,
|
||||
@@ -35,22 +54,83 @@ typedef enum {
|
||||
FelicaErrorTimeout,
|
||||
} FelicaError;
|
||||
|
||||
/** @brief Separate type for card key block. Used in authentication process */
|
||||
typedef struct {
|
||||
uint8_t data[FELICA_DATA_BLOCK_SIZE];
|
||||
} FelicaCardKey;
|
||||
|
||||
/** @brief In Felica there two types of auth. Internal is the first one, after
|
||||
* which external became possible. Here are two flags representing which one
|
||||
* was passed */
|
||||
typedef struct {
|
||||
bool internal : 1;
|
||||
bool external : 1;
|
||||
} FelicaAuthenticationStatus;
|
||||
|
||||
/** @brief Struct which controls the process of authentication and can be passed as
|
||||
* a parameter to the application level. In order to force user to fill card key block data. */
|
||||
typedef struct {
|
||||
bool skip_auth; /**< By default it is true, so auth is skipped. By setting this to false several auth steps will be performed in order to pass auth*/
|
||||
FelicaCardKey
|
||||
card_key; /**< User must fill this field with known card key in order to pass auth*/
|
||||
FelicaAuthenticationStatus auth_status; /**< Authentication status*/
|
||||
} FelicaAuthenticationContext;
|
||||
|
||||
/** @brief Felica ID block */
|
||||
typedef struct {
|
||||
uint8_t data[FELICA_IDM_SIZE];
|
||||
} FelicaIDm;
|
||||
|
||||
/** @brief Felica PMm block */
|
||||
typedef struct {
|
||||
uint8_t data[FELICA_PMM_SIZE];
|
||||
} FelicaPMm;
|
||||
|
||||
/** @brief Felica block with status flags indicating last operation with it.
|
||||
* See Felica manual for more details on status codes. */
|
||||
typedef struct {
|
||||
uint8_t SF1; /**< Status flag 1, equals to 0 when success*/
|
||||
uint8_t SF2; /**< Status flag 2, equals to 0 when success*/
|
||||
uint8_t data[FELICA_DATA_BLOCK_SIZE]; /**< Block data */
|
||||
} FelicaBlock;
|
||||
|
||||
/** @brief Felica filesystem structure */
|
||||
typedef struct {
|
||||
FelicaBlock spad[14];
|
||||
FelicaBlock reg;
|
||||
FelicaBlock rc;
|
||||
FelicaBlock mac;
|
||||
FelicaBlock id;
|
||||
FelicaBlock d_id;
|
||||
FelicaBlock ser_c;
|
||||
FelicaBlock sys_c;
|
||||
FelicaBlock ckv;
|
||||
FelicaBlock ck;
|
||||
FelicaBlock mc;
|
||||
FelicaBlock wcnt;
|
||||
FelicaBlock mac_a;
|
||||
FelicaBlock state;
|
||||
FelicaBlock crc_check;
|
||||
} FelicaFileSystem;
|
||||
|
||||
/** @brief Union which represents filesystem in junction with plain data dump */
|
||||
typedef union {
|
||||
FelicaFileSystem fs;
|
||||
uint8_t dump[sizeof(FelicaFileSystem)];
|
||||
} FelicaFSUnion;
|
||||
|
||||
/** @brief Structure used to store Felica data and additional values about reading */
|
||||
typedef struct {
|
||||
FelicaIDm idm;
|
||||
FelicaPMm pmm;
|
||||
uint8_t blocks_total;
|
||||
uint8_t blocks_read;
|
||||
FelicaFSUnion data;
|
||||
} FelicaData;
|
||||
|
||||
extern const NfcDeviceBase nfc_device_felica;
|
||||
|
||||
FelicaData* felica_alloc();
|
||||
FelicaData* felica_alloc(void);
|
||||
|
||||
void felica_free(FelicaData* data);
|
||||
|
||||
@@ -74,6 +154,27 @@ bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len);
|
||||
|
||||
FelicaData* felica_get_base_data(const FelicaData* data);
|
||||
|
||||
void felica_calculate_session_key(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* ck,
|
||||
const uint8_t* rc,
|
||||
uint8_t* out);
|
||||
|
||||
bool felica_check_mac(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* session_key,
|
||||
const uint8_t* rc,
|
||||
const uint8_t* blocks,
|
||||
const uint8_t block_count,
|
||||
uint8_t* data);
|
||||
|
||||
void felica_calculate_mac_write(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* session_key,
|
||||
const uint8_t* rc,
|
||||
const uint8_t* wcnt,
|
||||
const uint8_t* data,
|
||||
uint8_t* mac);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "FelicaPoller"
|
||||
|
||||
typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance);
|
||||
|
||||
const FelicaData* felica_poller_get_data(FelicaPoller* instance) {
|
||||
furi_assert(instance);
|
||||
@@ -23,6 +28,8 @@ static FelicaPoller* felica_poller_alloc(Nfc* nfc) {
|
||||
nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US);
|
||||
nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC);
|
||||
nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US);
|
||||
|
||||
mbedtls_des3_init(&instance->auth.des_context);
|
||||
instance->data = felica_alloc();
|
||||
|
||||
instance->felica_event.data = &instance->felica_event_data;
|
||||
@@ -40,6 +47,7 @@ static void felica_poller_free(FelicaPoller* instance) {
|
||||
furi_assert(instance->rx_buffer);
|
||||
furi_assert(instance->data);
|
||||
|
||||
mbedtls_des3_free(&instance->auth.des_context);
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
bit_buffer_free(instance->rx_buffer);
|
||||
felica_free(instance->data);
|
||||
@@ -55,6 +63,212 @@ static void
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_idle(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Idle");
|
||||
felica_reset(instance->data);
|
||||
instance->state = FelicaPollerStateActivated;
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Activate");
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
FelicaError error = felica_poller_activate(instance, instance->data);
|
||||
if(error == FelicaErrorNone) {
|
||||
furi_hal_random_fill_buf(instance->data->data.fs.rc.data, FELICA_DATA_BLOCK_SIZE);
|
||||
|
||||
instance->felica_event.type = FelicaPollerEventTypeRequestAuthContext;
|
||||
instance->felica_event_data.auth_context = &instance->auth.context;
|
||||
|
||||
instance->callback(instance->general_event, instance->context);
|
||||
|
||||
bool skip_auth = instance->auth.context.skip_auth;
|
||||
instance->state = skip_auth ? FelicaPollerStateReadBlocks :
|
||||
FelicaPollerStateAuthenticateInternal;
|
||||
} else if(error != FelicaErrorTimeout) {
|
||||
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||
instance->felica_event_data.error = error;
|
||||
instance->state = FelicaPollerStateReadFailed;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Auth Internal");
|
||||
|
||||
felica_calculate_session_key(
|
||||
&instance->auth.des_context,
|
||||
instance->auth.context.card_key.data,
|
||||
instance->data->data.fs.rc.data,
|
||||
instance->auth.session_key.data);
|
||||
|
||||
instance->state = FelicaPollerStateReadBlocks;
|
||||
|
||||
uint8_t blocks[3] = {FELICA_BLOCK_INDEX_RC, 0, 0};
|
||||
FelicaPollerWriteCommandResponse* tx_resp;
|
||||
do {
|
||||
FelicaError error = felica_poller_write_blocks(
|
||||
instance, 1, blocks, instance->data->data.fs.rc.data, &tx_resp);
|
||||
if((error != FelicaErrorNone) || (tx_resp->SF1 != 0) || (tx_resp->SF2 != 0)) break;
|
||||
|
||||
blocks[0] = FELICA_BLOCK_INDEX_ID;
|
||||
blocks[1] = FELICA_BLOCK_INDEX_WCNT;
|
||||
blocks[2] = FELICA_BLOCK_INDEX_MAC_A;
|
||||
FelicaPollerReadCommandResponse* rx_resp;
|
||||
error = felica_poller_read_blocks(instance, sizeof(blocks), blocks, &rx_resp);
|
||||
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
|
||||
|
||||
if(felica_check_mac(
|
||||
&instance->auth.des_context,
|
||||
instance->auth.session_key.data,
|
||||
instance->data->data.fs.rc.data,
|
||||
blocks,
|
||||
rx_resp->block_count,
|
||||
rx_resp->data)) {
|
||||
instance->auth.context.auth_status.internal = true;
|
||||
instance->data->data.fs.wcnt.SF1 = 0;
|
||||
instance->data->data.fs.wcnt.SF2 = 0;
|
||||
memcpy(
|
||||
instance->data->data.fs.wcnt.data,
|
||||
rx_resp->data + FELICA_DATA_BLOCK_SIZE,
|
||||
FELICA_DATA_BLOCK_SIZE);
|
||||
instance->state = FelicaPollerStateAuthenticateExternal;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_auth_external(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Auth External");
|
||||
instance->state = FelicaPollerStateReadBlocks;
|
||||
uint8_t blocks[2];
|
||||
|
||||
instance->data->data.fs.state.data[0] = 1;
|
||||
FelicaAuthentication* auth = &instance->auth;
|
||||
felica_calculate_mac_write(
|
||||
&auth->des_context,
|
||||
auth->session_key.data,
|
||||
instance->data->data.fs.rc.data,
|
||||
instance->data->data.fs.wcnt.data,
|
||||
instance->data->data.fs.state.data,
|
||||
instance->data->data.fs.mac_a.data);
|
||||
|
||||
memcpy(instance->data->data.fs.mac_a.data + 8, instance->data->data.fs.wcnt.data, 3); //-V1086
|
||||
|
||||
uint8_t tx_data[FELICA_DATA_BLOCK_SIZE * 2];
|
||||
memcpy(tx_data, instance->data->data.fs.state.data, FELICA_DATA_BLOCK_SIZE);
|
||||
memcpy(
|
||||
tx_data + FELICA_DATA_BLOCK_SIZE,
|
||||
instance->data->data.fs.mac_a.data,
|
||||
FELICA_DATA_BLOCK_SIZE);
|
||||
|
||||
do {
|
||||
blocks[0] = FELICA_BLOCK_INDEX_STATE;
|
||||
blocks[1] = FELICA_BLOCK_INDEX_MAC_A;
|
||||
FelicaPollerWriteCommandResponse* tx_resp;
|
||||
FelicaError error = felica_poller_write_blocks(instance, 2, blocks, tx_data, &tx_resp);
|
||||
if(error != FelicaErrorNone || tx_resp->SF1 != 0 || tx_resp->SF2 != 0) break;
|
||||
|
||||
FelicaPollerReadCommandResponse* rx_resp;
|
||||
error = felica_poller_read_blocks(instance, 1, blocks, &rx_resp);
|
||||
if(error != FelicaErrorNone || rx_resp->SF1 != 0 || rx_resp->SF2 != 0) break;
|
||||
|
||||
instance->data->data.fs.state.SF1 = 0;
|
||||
instance->data->data.fs.state.SF2 = 0;
|
||||
memcpy(instance->data->data.fs.state.data, rx_resp->data, FELICA_DATA_BLOCK_SIZE);
|
||||
instance->auth.context.auth_status.external = instance->data->data.fs.state.data[0];
|
||||
} while(false);
|
||||
instance->state = FelicaPollerStateReadBlocks;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_read_blocks(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read Blocks");
|
||||
|
||||
uint8_t block_count = 1;
|
||||
uint8_t block_list[4] = {0, 0, 0, 0};
|
||||
block_list[0] = instance->block_index;
|
||||
|
||||
instance->block_index++;
|
||||
if(instance->block_index == FELICA_BLOCK_INDEX_REG + 1) {
|
||||
instance->block_index = FELICA_BLOCK_INDEX_RC;
|
||||
} else if(instance->block_index == FELICA_BLOCK_INDEX_MC + 1) {
|
||||
instance->block_index = FELICA_BLOCK_INDEX_WCNT;
|
||||
} else if(instance->block_index == FELICA_BLOCK_INDEX_STATE + 1) {
|
||||
instance->block_index = FELICA_BLOCK_INDEX_CRC_CHECK;
|
||||
}
|
||||
|
||||
FelicaPollerReadCommandResponse* response;
|
||||
FelicaError error = felica_poller_read_blocks(instance, block_count, block_list, &response);
|
||||
if(error == FelicaErrorNone) {
|
||||
block_count = (response->SF1 == 0) ? response->block_count : block_count;
|
||||
uint8_t* data_ptr =
|
||||
instance->data->data.dump + instance->data->blocks_total * sizeof(FelicaBlock);
|
||||
|
||||
*data_ptr++ = response->SF1;
|
||||
*data_ptr++ = response->SF2;
|
||||
|
||||
if(response->SF1 == 0) {
|
||||
uint8_t* response_data_ptr = response->data;
|
||||
instance->data->blocks_read++;
|
||||
memcpy(data_ptr, response_data_ptr, FELICA_DATA_BLOCK_SIZE);
|
||||
} else {
|
||||
memset(data_ptr, 0, FELICA_DATA_BLOCK_SIZE);
|
||||
}
|
||||
instance->data->blocks_total++;
|
||||
|
||||
if(instance->data->blocks_total == FELICA_BLOCKS_TOTAL_COUNT) {
|
||||
instance->state = FelicaPollerStateReadSuccess;
|
||||
}
|
||||
} else {
|
||||
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||
instance->felica_event_data.error = error;
|
||||
instance->state = FelicaPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read Success");
|
||||
|
||||
if(!instance->auth.context.auth_status.internal ||
|
||||
!instance->auth.context.auth_status.external) {
|
||||
instance->data->blocks_read--;
|
||||
instance->felica_event.type = FelicaPollerEventTypeIncomplete;
|
||||
} else {
|
||||
memcpy(
|
||||
instance->data->data.fs.ck.data,
|
||||
instance->auth.context.card_key.data,
|
||||
FELICA_DATA_BLOCK_SIZE);
|
||||
instance->felica_event.type = FelicaPollerEventTypeReady;
|
||||
}
|
||||
|
||||
instance->felica_event_data.error = FelicaErrorNone;
|
||||
return instance->callback(instance->general_event, instance->context);
|
||||
}
|
||||
|
||||
NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read Fail");
|
||||
instance->callback(instance->general_event, instance->context);
|
||||
|
||||
return NfcCommandStop;
|
||||
}
|
||||
|
||||
static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = {
|
||||
[FelicaPollerStateIdle] = felica_poller_state_handler_idle,
|
||||
[FelicaPollerStateActivated] = felica_poller_state_handler_activate,
|
||||
[FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal,
|
||||
[FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external,
|
||||
[FelicaPollerStateReadBlocks] = felica_poller_state_handler_read_blocks,
|
||||
[FelicaPollerStateReadSuccess] = felica_poller_state_handler_read_success,
|
||||
[FelicaPollerStateReadFailed] = felica_poller_state_handler_read_failed,
|
||||
};
|
||||
|
||||
static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol == NfcProtocolInvalid);
|
||||
@@ -65,24 +279,7 @@ static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) {
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||
if(instance->state != FelicaPollerStateActivated) {
|
||||
FelicaError error = felica_poller_activate(instance, instance->data);
|
||||
if(error == FelicaErrorNone) {
|
||||
instance->felica_event.type = FelicaPollerEventTypeReady;
|
||||
instance->felica_event_data.error = error;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
} else {
|
||||
instance->felica_event.type = FelicaPollerEventTypeError;
|
||||
instance->felica_event_data.error = error;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
// Add delay to switch context
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
} else {
|
||||
instance->felica_event.type = FelicaPollerEventTypeReady;
|
||||
instance->felica_event_data.error = FelicaErrorNone;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
}
|
||||
command = felica_poller_handler[instance->state](instance);
|
||||
}
|
||||
|
||||
return command;
|
||||
|
||||
@@ -19,14 +19,33 @@ typedef struct FelicaPoller FelicaPoller;
|
||||
*/
|
||||
typedef enum {
|
||||
FelicaPollerEventTypeError, /**< An error occured during activation procedure. */
|
||||
FelicaPollerEventTypeReady, /**< The card was activated by the poller. */
|
||||
FelicaPollerEventTypeReady, /**< The card was activated and fully read by the poller. */
|
||||
FelicaPollerEventTypeIncomplete, /**< The card was activated and partly read by the poller. */
|
||||
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.
|
||||
*/
|
||||
typedef union {
|
||||
FelicaError error; /**< Error code indicating card activation fail reason. */
|
||||
FelicaAuthenticationContext* auth_context; /**< Authentication context to be filled by user. */
|
||||
} FelicaPollerEventData;
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
#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) {
|
||||
@@ -15,8 +20,8 @@ static FelicaError felica_poller_process_error(NfcError error) {
|
||||
}
|
||||
}
|
||||
|
||||
static FelicaError felica_poller_frame_exchange(
|
||||
FelicaPoller* instance,
|
||||
FelicaError felica_poller_frame_exchange(
|
||||
const FelicaPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
@@ -93,11 +98,105 @@ FelicaError felica_poller_polling(
|
||||
return error;
|
||||
}
|
||||
|
||||
static void felica_poller_prepare_tx_buffer(
|
||||
const FelicaPoller* instance,
|
||||
const uint8_t command,
|
||||
const uint16_t service_code,
|
||||
const uint8_t block_count,
|
||||
const uint8_t* const blocks,
|
||||
const uint8_t data_block_count,
|
||||
const uint8_t* data) {
|
||||
FelicaCommandHeader cmd = {
|
||||
.code = command,
|
||||
.idm = instance->data->idm,
|
||||
.service_num = 1,
|
||||
.service_code = service_code,
|
||||
.block_count = block_count,
|
||||
};
|
||||
|
||||
FelicaBlockListElement block_list[4] = {{0}, {0}, {0}, {0}};
|
||||
for(uint8_t i = 0; i < block_count; i++) {
|
||||
block_list[i].length = 1;
|
||||
block_list[i].block_number = blocks[i];
|
||||
}
|
||||
|
||||
uint8_t block_list_count = block_count;
|
||||
uint8_t block_list_size = block_list_count * sizeof(FelicaBlockListElement);
|
||||
uint8_t total_size = sizeof(FelicaCommandHeader) + 1 + block_list_size +
|
||||
data_block_count * FELICA_DATA_BLOCK_SIZE;
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, total_size);
|
||||
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeader));
|
||||
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block_list, block_list_size);
|
||||
|
||||
if(data_block_count != 0) {
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer, data, data_block_count * FELICA_DATA_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
FelicaError felica_poller_read_blocks(
|
||||
FelicaPoller* instance,
|
||||
const uint8_t block_count,
|
||||
const uint8_t* const block_numbers,
|
||||
FelicaPollerReadCommandResponse** const response_ptr) {
|
||||
furi_assert(instance);
|
||||
furi_assert(block_count <= 4);
|
||||
furi_assert(block_numbers);
|
||||
furi_assert(response_ptr);
|
||||
|
||||
felica_poller_prepare_tx_buffer(
|
||||
instance,
|
||||
FELICA_CMD_READ_WITHOUT_ENCRYPTION,
|
||||
FELICA_SERVICE_RO_ACCESS,
|
||||
block_count,
|
||||
block_numbers,
|
||||
0,
|
||||
NULL);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
FelicaError error = felica_poller_frame_exchange(
|
||||
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
|
||||
if(error == FelicaErrorNone) {
|
||||
*response_ptr = (FelicaPollerReadCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
FelicaError felica_poller_write_blocks(
|
||||
const FelicaPoller* instance,
|
||||
const uint8_t block_count,
|
||||
const uint8_t* const block_numbers,
|
||||
const uint8_t* data,
|
||||
FelicaPollerWriteCommandResponse** const response_ptr) {
|
||||
furi_assert(instance);
|
||||
furi_assert(block_count <= 2);
|
||||
furi_assert(block_numbers);
|
||||
furi_assert(data);
|
||||
furi_assert(response_ptr);
|
||||
|
||||
felica_poller_prepare_tx_buffer(
|
||||
instance,
|
||||
FELICA_CMD_WRITE_WITHOUT_ENCRYPTION,
|
||||
FELICA_SERVICE_RW_ACCESS,
|
||||
block_count,
|
||||
block_numbers,
|
||||
block_count,
|
||||
data);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
FelicaError error = felica_poller_frame_exchange(
|
||||
instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT);
|
||||
if(error == FelicaErrorNone) {
|
||||
*response_ptr =
|
||||
(FelicaPollerWriteCommandResponse*)bit_buffer_get_data(instance->rx_buffer);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) {
|
||||
furi_assert(instance);
|
||||
|
||||
felica_reset(data);
|
||||
|
||||
FelicaError ret;
|
||||
|
||||
do {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "felica_poller.h"
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -18,11 +17,20 @@ extern "C" {
|
||||
typedef enum {
|
||||
FelicaPollerStateIdle,
|
||||
FelicaPollerStateActivated,
|
||||
FelicaPollerStateAuthenticateInternal,
|
||||
FelicaPollerStateAuthenticateExternal,
|
||||
FelicaPollerStateReadBlocks,
|
||||
FelicaPollerStateReadSuccess,
|
||||
FelicaPollerStateReadFailed,
|
||||
|
||||
FelicaPollerStateNum
|
||||
} FelicaPollerState;
|
||||
|
||||
struct FelicaPoller {
|
||||
Nfc* nfc;
|
||||
FelicaPollerState state;
|
||||
FelicaAuthentication auth;
|
||||
|
||||
FelicaData* data;
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
@@ -31,6 +39,7 @@ struct FelicaPoller {
|
||||
FelicaPollerEvent felica_event;
|
||||
FelicaPollerEventData felica_event_data;
|
||||
NfcGenericCallback callback;
|
||||
uint8_t block_index;
|
||||
void* context;
|
||||
};
|
||||
|
||||
@@ -46,13 +55,106 @@ 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);
|
||||
|
||||
/**
|
||||
* @brief Performs felica polling operation as part of the activation process
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] cmd Pointer to polling command structure
|
||||
* @param[out] resp Pointer to the response structure
|
||||
* @return FelicaErrorNone on success, an error code on failure
|
||||
*/
|
||||
FelicaError felica_poller_polling(
|
||||
FelicaPoller* instance,
|
||||
const FelicaPollerPollingCommand* cmd,
|
||||
FelicaPollerPollingResponse* resp);
|
||||
|
||||
/**
|
||||
* @brief Performs felica read operation for blocks provided as parameters
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] block_count Amount of blocks involved in reading procedure
|
||||
* @param[in] block_numbers Array with block indexes according to felica docs
|
||||
* @param[out] response_ptr Pointer to the response structure
|
||||
* @return FelicaErrorNone on success, an error code on failure.
|
||||
*/
|
||||
FelicaError felica_poller_read_blocks(
|
||||
FelicaPoller* instance,
|
||||
const uint8_t block_count,
|
||||
const uint8_t* const block_numbers,
|
||||
FelicaPollerReadCommandResponse** const response_ptr);
|
||||
|
||||
/**
|
||||
* @brief Performs felica write operation with data provided as parameters
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] block_count Amount of blocks involved in writing procedure
|
||||
* @param[in] block_numbers Array with block indexes according to felica docs
|
||||
* @param[in] data Data of blocks provided in block_numbers
|
||||
* @param[out] response_ptr Pointer to the response structure
|
||||
* @return FelicaErrorNone on success, an error code on failure.
|
||||
*/
|
||||
FelicaError felica_poller_write_blocks(
|
||||
const FelicaPoller* instance,
|
||||
const uint8_t block_count,
|
||||
const uint8_t* const block_numbers,
|
||||
const uint8_t* data,
|
||||
FelicaPollerWriteCommandResponse** const response_ptr);
|
||||
|
||||
/**
|
||||
* @brief Perform frame exchange procedure.
|
||||
*
|
||||
* Prepares data for sending by adding crc, after that performs
|
||||
* low level calls to send package data to the card
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer with data to be transmitted
|
||||
* @param[out] rx_buffer pointer to the buffer with received data from card
|
||||
* @param[in] fwt timeout window
|
||||
* @return FelicaErrorNone on success, an error code on failure.
|
||||
*/
|
||||
FelicaError felica_poller_frame_exchange(
|
||||
const FelicaPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -28,36 +28,40 @@ const NfcDeviceBase nfc_device_iso14443_3a = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data,
|
||||
};
|
||||
|
||||
Iso14443_3aData* iso14443_3a_alloc() {
|
||||
Iso14443_3aData* iso14443_3a_alloc(void) {
|
||||
Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData));
|
||||
return data;
|
||||
}
|
||||
|
||||
void iso14443_3a_free(Iso14443_3aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
void iso14443_3a_reset(Iso14443_3aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
memset(data, 0, sizeof(Iso14443_3aData));
|
||||
}
|
||||
|
||||
void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
*data = *other;
|
||||
}
|
||||
|
||||
bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
furi_check(device_type);
|
||||
|
||||
return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY);
|
||||
}
|
||||
|
||||
bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
@@ -78,7 +82,8 @@ bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version
|
||||
}
|
||||
|
||||
bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
@@ -98,8 +103,8 @@ bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return memcmp(data, other, sizeof(Iso14443_3aData)) == 0;
|
||||
}
|
||||
@@ -111,7 +116,7 @@ const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNa
|
||||
}
|
||||
|
||||
const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
if(uid_len) {
|
||||
*uid_len = data->uid_len;
|
||||
@@ -121,7 +126,8 @@ const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len)
|
||||
}
|
||||
|
||||
bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(uid);
|
||||
|
||||
const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES ||
|
||||
uid_len == ISO14443_3A_UID_7_BYTES ||
|
||||
@@ -141,7 +147,7 @@ Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) {
|
||||
}
|
||||
|
||||
uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
uint32_t cuid = 0;
|
||||
const uint8_t* cuid_start = data->uid;
|
||||
@@ -154,33 +160,33 @@ uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) {
|
||||
}
|
||||
|
||||
bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->sak & ISO14443A_ATS_BIT;
|
||||
}
|
||||
|
||||
uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->sak;
|
||||
}
|
||||
|
||||
void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]) {
|
||||
furi_assert(data);
|
||||
furi_assert(atqa);
|
||||
furi_check(data);
|
||||
furi_check(atqa);
|
||||
|
||||
memcpy(atqa, data->atqa, sizeof(data->atqa));
|
||||
}
|
||||
|
||||
void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
data->sak = sak;
|
||||
}
|
||||
|
||||
void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]) {
|
||||
furi_assert(data);
|
||||
furi_assert(atqa);
|
||||
furi_check(data);
|
||||
furi_check(atqa);
|
||||
|
||||
memcpy(data->atqa, atqa, sizeof(data->atqa));
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ typedef struct {
|
||||
uint8_t sak;
|
||||
} Iso14443_3aData;
|
||||
|
||||
Iso14443_3aData* iso14443_3a_alloc();
|
||||
Iso14443_3aData* iso14443_3a_alloc(void);
|
||||
|
||||
void iso14443_3a_free(Iso14443_3aData* data);
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange(
|
||||
}
|
||||
|
||||
Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
|
||||
NfcError error = NfcErrorNone;
|
||||
Iso14443_3aError ret = Iso14443_3aErrorNone;
|
||||
@@ -80,9 +80,9 @@ Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance)
|
||||
}
|
||||
|
||||
Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_assert(instance->tx_buffer);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
furi_check(instance->tx_buffer);
|
||||
|
||||
uint8_t halt_cmd[2] = {0x50, 0x00};
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd));
|
||||
@@ -96,10 +96,10 @@ Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) {
|
||||
|
||||
Iso14443_3aError
|
||||
iso14443_3a_poller_activate(Iso14443_3aPoller* instance, Iso14443_3aData* iso14443_3a_data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_assert(instance->tx_buffer);
|
||||
furi_assert(instance->rx_buffer);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
furi_check(instance->tx_buffer);
|
||||
furi_check(instance->rx_buffer);
|
||||
|
||||
// Reset Iso14443_3a poller state
|
||||
memset(&instance->col_res, 0, sizeof(instance->col_res));
|
||||
@@ -244,9 +244,9 @@ Iso14443_3aError iso14443_3a_poller_txrx_custom_parity(
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_buffer);
|
||||
furi_assert(rx_buffer);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError ret = Iso14443_3aErrorNone;
|
||||
NfcError error =
|
||||
@@ -263,9 +263,9 @@ Iso14443_3aError iso14443_3a_poller_txrx(
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_buffer);
|
||||
furi_assert(rx_buffer);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError ret = Iso14443_3aErrorNone;
|
||||
NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt);
|
||||
@@ -281,9 +281,9 @@ Iso14443_3aError iso14443_3a_poller_send_standard_frame(
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_buffer);
|
||||
furi_assert(rx_buffer);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError ret =
|
||||
iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
|
||||
|
||||
@@ -35,8 +35,8 @@ NfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context
|
||||
}
|
||||
|
||||
Iso14443_3aError iso14443_3a_poller_sync_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(iso14443_3a_data);
|
||||
furi_check(nfc);
|
||||
furi_check(iso14443_3a_data);
|
||||
|
||||
Iso14443_3aPollerContext poller_context = {};
|
||||
poller_context.thread_id = furi_thread_get_current_id();
|
||||
|
||||
@@ -30,24 +30,26 @@ const NfcDeviceBase nfc_device_iso14443_3b = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)iso14443_3b_get_base_data,
|
||||
};
|
||||
|
||||
Iso14443_3bData* iso14443_3b_alloc() {
|
||||
Iso14443_3bData* iso14443_3b_alloc(void) {
|
||||
Iso14443_3bData* data = malloc(sizeof(Iso14443_3bData));
|
||||
return data;
|
||||
}
|
||||
|
||||
void iso14443_3b_free(Iso14443_3bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
void iso14443_3b_reset(Iso14443_3bData* data) {
|
||||
furi_check(data);
|
||||
|
||||
memset(data, 0, sizeof(Iso14443_3bData));
|
||||
}
|
||||
|
||||
void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
*data = *other;
|
||||
}
|
||||
@@ -60,7 +62,8 @@ bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type) {
|
||||
}
|
||||
|
||||
bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
@@ -84,7 +87,8 @@ bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version
|
||||
}
|
||||
|
||||
bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
@@ -107,8 +111,8 @@ bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return memcmp(data, other, sizeof(Iso14443_3bData)) == 0;
|
||||
}
|
||||
@@ -121,15 +125,16 @@ const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNa
|
||||
}
|
||||
|
||||
const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_assert(uid_len);
|
||||
furi_check(data);
|
||||
furi_check(uid_len);
|
||||
|
||||
*uid_len = ISO14443_3B_UID_SIZE;
|
||||
return data->uid;
|
||||
}
|
||||
|
||||
bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(uid);
|
||||
|
||||
const bool uid_valid = uid_len == ISO14443_3B_UID_SIZE;
|
||||
|
||||
@@ -146,13 +151,13 @@ Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data) {
|
||||
}
|
||||
|
||||
bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->protocol_info.protocol_type == 0x01;
|
||||
}
|
||||
|
||||
bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
const uint8_t capability = data->protocol_info.bit_rate_capability;
|
||||
|
||||
@@ -177,7 +182,7 @@ bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRa
|
||||
}
|
||||
|
||||
bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
switch(option) {
|
||||
case Iso14443_3bFrameOptionNad:
|
||||
@@ -190,15 +195,15 @@ bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bF
|
||||
}
|
||||
|
||||
const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size) {
|
||||
furi_assert(data);
|
||||
furi_assert(data_size);
|
||||
furi_check(data);
|
||||
furi_check(data_size);
|
||||
|
||||
*data_size = ISO14443_3B_APP_DATA_SIZE;
|
||||
return data->app_data;
|
||||
}
|
||||
|
||||
uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
const uint8_t fs_bits = data->protocol_info.max_frame_size;
|
||||
|
||||
@@ -216,7 +221,7 @@ uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data) {
|
||||
}
|
||||
|
||||
uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
const uint8_t fwi = data->protocol_info.fwi;
|
||||
return fwi < 0x0F ? 4096UL << fwi : ISO14443_3B_FDT_POLL_DEFAULT_FC;
|
||||
|
||||
@@ -40,7 +40,7 @@ typedef struct Iso14443_3bData Iso14443_3bData;
|
||||
|
||||
// Virtual methods
|
||||
|
||||
Iso14443_3bData* iso14443_3b_alloc();
|
||||
Iso14443_3bData* iso14443_3b_alloc(void);
|
||||
|
||||
void iso14443_3b_free(Iso14443_3bData* data);
|
||||
|
||||
|
||||
@@ -64,8 +64,9 @@ static Iso14443_3bError iso14443_3b_poller_frame_exchange(
|
||||
}
|
||||
|
||||
Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3b_reset(data);
|
||||
|
||||
@@ -155,7 +156,7 @@ Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso144
|
||||
}
|
||||
|
||||
Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
@@ -188,6 +189,10 @@ Iso14443_3bError iso14443_3b_poller_send_frame(
|
||||
Iso14443_3bPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3bError ret;
|
||||
|
||||
do {
|
||||
|
||||
@@ -35,7 +35,7 @@ const NfcDeviceBase nfc_device_iso14443_4a = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data,
|
||||
};
|
||||
|
||||
Iso14443_4aData* iso14443_4a_alloc() {
|
||||
Iso14443_4aData* iso14443_4a_alloc(void) {
|
||||
Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData));
|
||||
|
||||
data->iso14443_3a_data = iso14443_3a_alloc();
|
||||
@@ -45,7 +45,7 @@ Iso14443_4aData* iso14443_4a_alloc() {
|
||||
}
|
||||
|
||||
void iso14443_4a_free(Iso14443_4aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
simple_array_free(data->ats_data.t1_tk);
|
||||
iso14443_3a_free(data->iso14443_3a_data);
|
||||
@@ -54,7 +54,7 @@ void iso14443_4a_free(Iso14443_4aData* data) {
|
||||
}
|
||||
|
||||
void iso14443_4a_reset(Iso14443_4aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3a_reset(data->iso14443_3a_data);
|
||||
|
||||
@@ -68,8 +68,8 @@ void iso14443_4a_reset(Iso14443_4aData* data) {
|
||||
}
|
||||
|
||||
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
|
||||
|
||||
@@ -91,7 +91,8 @@ bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) {
|
||||
}
|
||||
|
||||
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
@@ -145,7 +146,8 @@ bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version
|
||||
}
|
||||
|
||||
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
@@ -186,6 +188,9 @@ bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data);
|
||||
}
|
||||
|
||||
@@ -196,23 +201,26 @@ const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNa
|
||||
}
|
||||
|
||||
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) {
|
||||
furi_check(data);
|
||||
furi_check(uid_len);
|
||||
|
||||
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
|
||||
}
|
||||
|
||||
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
|
||||
}
|
||||
|
||||
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_3a_data;
|
||||
}
|
||||
|
||||
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
const uint8_t fsci = data->ats_data.t0 & 0x0F;
|
||||
|
||||
@@ -230,7 +238,7 @@ uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {
|
||||
}
|
||||
|
||||
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC;
|
||||
|
||||
@@ -248,8 +256,8 @@ uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {
|
||||
}
|
||||
|
||||
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) {
|
||||
furi_assert(data);
|
||||
furi_assert(count);
|
||||
furi_check(data);
|
||||
furi_check(count);
|
||||
|
||||
*count = simple_array_get_count(data->ats_data.t1_tk);
|
||||
const uint8_t* hist_bytes = NULL;
|
||||
@@ -261,7 +269,7 @@ const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uin
|
||||
}
|
||||
|
||||
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1))
|
||||
return bit_rate == Iso14443_4aBitRateBoth106Kbit;
|
||||
@@ -289,7 +297,7 @@ bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRa
|
||||
}
|
||||
|
||||
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
const Iso14443_4aAtsData* ats_data = &data->ats_data;
|
||||
if(!(ats_data->t0 & ISO14443_4A_ATS_T0_TC1)) return false;
|
||||
|
||||
@@ -46,7 +46,7 @@ typedef struct {
|
||||
|
||||
// Virtual methods
|
||||
|
||||
Iso14443_4aData* iso14443_4a_alloc();
|
||||
Iso14443_4aData* iso14443_4a_alloc(void);
|
||||
|
||||
void iso14443_4a_free(Iso14443_4aData* data);
|
||||
|
||||
|
||||
@@ -7,9 +7,13 @@
|
||||
#define TAG "Iso14443_4aPoller"
|
||||
|
||||
#define ISO14443_4A_FSDI_256 (0x8U)
|
||||
#define ISO14443_4A_FWT_MAX (4096UL << 14)
|
||||
#define ISO14443_4A_WTXM_MASK (0x3FU)
|
||||
#define ISO14443_4A_WTXM_MAX (0x3BU)
|
||||
#define ISO14443_4A_SWTX (0xF2U)
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||
instance->poller_state = Iso14443_4aPollerStateIdle;
|
||||
@@ -19,7 +23,8 @@ Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) {
|
||||
|
||||
Iso14443_4aError
|
||||
iso14443_4a_poller_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_CMD_READ_ATS);
|
||||
@@ -54,7 +59,9 @@ Iso14443_4aError iso14443_4a_poller_send_block(
|
||||
Iso14443_4aPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
|
||||
@@ -71,9 +78,35 @@ Iso14443_4aError iso14443_4a_poller_send_block(
|
||||
if(iso14443_3a_error != Iso14443_3aErrorNone) {
|
||||
error = iso14443_4a_process_error(iso14443_3a_error);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if(!iso14443_4_layer_decode_block(
|
||||
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
|
||||
if(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX)) {
|
||||
do {
|
||||
uint8_t wtxm = bit_buffer_get_byte(instance->rx_buffer, 1) & ISO14443_4A_WTXM_MASK;
|
||||
if(wtxm > ISO14443_4A_WTXM_MAX) {
|
||||
return Iso14443_4aErrorProtocol;
|
||||
}
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_copy_left(instance->tx_buffer, instance->rx_buffer, 1);
|
||||
bit_buffer_append_byte(instance->tx_buffer, wtxm);
|
||||
|
||||
iso14443_3a_error = iso14443_3a_poller_send_standard_frame(
|
||||
instance->iso14443_3a_poller,
|
||||
instance->tx_buffer,
|
||||
instance->rx_buffer,
|
||||
MAX(iso14443_4a_get_fwt_fc_max(instance->data) * wtxm, ISO14443_4A_FWT_MAX));
|
||||
|
||||
if(iso14443_3a_error != Iso14443_3aErrorNone) {
|
||||
error = iso14443_4a_process_error(iso14443_3a_error);
|
||||
return error;
|
||||
}
|
||||
|
||||
} while(bit_buffer_starts_with_byte(instance->rx_buffer, ISO14443_4A_SWTX));
|
||||
}
|
||||
|
||||
if(!iso14443_4_layer_decode_block(
|
||||
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
|
||||
error = Iso14443_4aErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const NfcDeviceBase nfc_device_iso14443_4b = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)iso14443_4b_get_base_data,
|
||||
};
|
||||
|
||||
Iso14443_4bData* iso14443_4b_alloc() {
|
||||
Iso14443_4bData* iso14443_4b_alloc(void) {
|
||||
Iso14443_4bData* data = malloc(sizeof(Iso14443_4bData));
|
||||
|
||||
data->iso14443_3b_data = iso14443_3b_alloc();
|
||||
@@ -30,21 +30,21 @@ Iso14443_4bData* iso14443_4b_alloc() {
|
||||
}
|
||||
|
||||
void iso14443_4b_free(Iso14443_4bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3b_free(data->iso14443_3b_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void iso14443_4b_reset(Iso14443_4bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3b_reset(data->iso14443_3b_data);
|
||||
}
|
||||
|
||||
void iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
iso14443_3b_copy(data->iso14443_3b_data, other->iso14443_3b_data);
|
||||
}
|
||||
@@ -58,16 +58,21 @@ bool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type) {
|
||||
}
|
||||
|
||||
bool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
return iso14443_3b_load(data->iso14443_3b_data, ff, version);
|
||||
}
|
||||
|
||||
bool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
return iso14443_3b_save(data->iso14443_3b_data, ff);
|
||||
}
|
||||
|
||||
bool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return iso14443_3b_is_equal(data->iso14443_3b_data, other->iso14443_3b_data);
|
||||
}
|
||||
|
||||
@@ -78,17 +83,21 @@ const char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNa
|
||||
}
|
||||
|
||||
const uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len) {
|
||||
furi_check(data);
|
||||
furi_check(uid_len);
|
||||
|
||||
return iso14443_3b_get_uid(data->iso14443_3b_data, uid_len);
|
||||
}
|
||||
|
||||
bool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(uid);
|
||||
|
||||
return iso14443_3b_set_uid(data->iso14443_3b_data, uid, uid_len);
|
||||
}
|
||||
|
||||
Iso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_3b_data;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ typedef struct Iso14443_4bData Iso14443_4bData;
|
||||
|
||||
// Virtual methods
|
||||
|
||||
Iso14443_4bData* iso14443_4b_alloc();
|
||||
Iso14443_4bData* iso14443_4b_alloc(void);
|
||||
|
||||
void iso14443_4b_free(Iso14443_4bData* data);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define TAG "Iso14443_4bPoller"
|
||||
|
||||
Iso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
iso14443_3b_poller_halt(instance->iso14443_3b_poller);
|
||||
instance->poller_state = Iso14443_4bPollerStateIdle;
|
||||
@@ -19,7 +19,9 @@ Iso14443_4bError iso14443_4b_poller_send_block(
|
||||
Iso14443_4bPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
|
||||
|
||||
@@ -36,7 +36,7 @@ const NfcDeviceBase nfc_device_iso15693_3 = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data,
|
||||
};
|
||||
|
||||
Iso15693_3Data* iso15693_3_alloc() {
|
||||
Iso15693_3Data* iso15693_3_alloc(void) {
|
||||
Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data));
|
||||
|
||||
data->block_data = simple_array_alloc(&simple_array_config_uint8_t);
|
||||
@@ -46,7 +46,7 @@ Iso15693_3Data* iso15693_3_alloc() {
|
||||
}
|
||||
|
||||
void iso15693_3_free(Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
simple_array_free(data->block_data);
|
||||
simple_array_free(data->block_security);
|
||||
@@ -54,7 +54,7 @@ void iso15693_3_free(Iso15693_3Data* data) {
|
||||
}
|
||||
|
||||
void iso15693_3_reset(Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
memset(data->uid, 0, ISO15693_3_UID_SIZE);
|
||||
memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo));
|
||||
@@ -65,8 +65,8 @@ void iso15693_3_reset(Iso15693_3Data* data) {
|
||||
}
|
||||
|
||||
void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE);
|
||||
|
||||
@@ -79,6 +79,8 @@ void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) {
|
||||
|
||||
bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
furi_check(device_type);
|
||||
|
||||
return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY);
|
||||
}
|
||||
|
||||
@@ -136,7 +138,8 @@ static inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat*
|
||||
}
|
||||
|
||||
bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
UNUSED(version);
|
||||
|
||||
bool loaded = false;
|
||||
@@ -207,7 +210,8 @@ bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version)
|
||||
}
|
||||
|
||||
bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
@@ -280,12 +284,15 @@ bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 &&
|
||||
memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 &&
|
||||
memcmp(&data->system_info, &other->system_info, sizeof(Iso15693_3SystemInfo)) == 0 &&
|
||||
memcmp( //-V1103
|
||||
&data->system_info,
|
||||
&other->system_info,
|
||||
sizeof(Iso15693_3SystemInfo)) == 0 &&
|
||||
simple_array_is_equal(data->block_data, other->block_data) &&
|
||||
simple_array_is_equal(data->block_security, other->block_security);
|
||||
}
|
||||
@@ -298,15 +305,15 @@ const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceName
|
||||
}
|
||||
|
||||
const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
if(uid_len) *uid_len = ISO15693_3_UID_SIZE;
|
||||
return data->uid;
|
||||
}
|
||||
|
||||
bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_assert(uid);
|
||||
furi_check(data);
|
||||
furi_check(uid);
|
||||
|
||||
bool uid_valid = uid_len == ISO15693_3_UID_SIZE;
|
||||
|
||||
@@ -325,33 +332,33 @@ Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) {
|
||||
}
|
||||
|
||||
bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) {
|
||||
furi_assert(data);
|
||||
furi_assert(block_index < data->system_info.block_count);
|
||||
furi_check(data);
|
||||
furi_check(block_index < data->system_info.block_count);
|
||||
|
||||
return *(const uint8_t*)simple_array_cget(data->block_security, block_index);
|
||||
}
|
||||
|
||||
uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->uid[1];
|
||||
}
|
||||
|
||||
uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->system_info.block_count;
|
||||
}
|
||||
|
||||
uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->system_info.block_size;
|
||||
}
|
||||
|
||||
const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) {
|
||||
furi_assert(data);
|
||||
furi_assert(data->system_info.block_count > block_index);
|
||||
furi_check(data);
|
||||
furi_check(data->system_info.block_count > block_index);
|
||||
|
||||
return (const uint8_t*)simple_array_cget(
|
||||
data->block_data, block_index * data->system_info.block_size);
|
||||
|
||||
@@ -122,7 +122,7 @@ typedef struct {
|
||||
SimpleArray* block_security;
|
||||
} Iso15693_3Data;
|
||||
|
||||
Iso15693_3Data* iso15693_3_alloc();
|
||||
Iso15693_3Data* iso15693_3_alloc(void);
|
||||
|
||||
void iso15693_3_free(Iso15693_3Data* data);
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ Iso15693_3Error iso15693_3_poller_send_frame(
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
#include "crypto1.h"
|
||||
|
||||
#include <lib/nfc/helpers/nfc_util.h>
|
||||
#include <lib/bit_lib/bit_lib.h>
|
||||
#include <furi.h>
|
||||
|
||||
// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git
|
||||
|
||||
#define SWAPENDIAN(x) \
|
||||
((x) = ((x) >> 8 & 0xff00ff) | ((x) & 0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
|
||||
#define LF_POLY_ODD (0x29CE5C)
|
||||
#define LF_POLY_EVEN (0x870804)
|
||||
|
||||
#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24)
|
||||
|
||||
Crypto1* crypto1_alloc() {
|
||||
Crypto1* instance = malloc(sizeof(Crypto1));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void crypto1_free(Crypto1* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void crypto1_reset(Crypto1* crypto1) {
|
||||
furi_assert(crypto1);
|
||||
crypto1->even = 0;
|
||||
crypto1->odd = 0;
|
||||
}
|
||||
|
||||
void crypto1_init(Crypto1* crypto1, uint64_t key) {
|
||||
furi_assert(crypto1);
|
||||
crypto1->even = 0;
|
||||
crypto1->odd = 0;
|
||||
for(int8_t i = 47; i > 0; i -= 2) {
|
||||
crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7);
|
||||
crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t crypto1_filter(uint32_t in) {
|
||||
uint32_t out = 0;
|
||||
out = 0xf22c0 >> (in & 0xf) & 16;
|
||||
out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8;
|
||||
out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4;
|
||||
out |= 0x1e458 >> (in >> 12 & 0xf) & 2;
|
||||
out |= 0x0d938 >> (in >> 16 & 0xf) & 1;
|
||||
return FURI_BIT(0xEC57E80A, out);
|
||||
}
|
||||
|
||||
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) {
|
||||
furi_assert(crypto1);
|
||||
uint8_t out = crypto1_filter(crypto1->odd);
|
||||
uint32_t feed = out & (!!is_encrypted);
|
||||
feed ^= !!in;
|
||||
feed ^= LF_POLY_ODD & crypto1->odd;
|
||||
feed ^= LF_POLY_EVEN & crypto1->even;
|
||||
crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed));
|
||||
|
||||
FURI_SWAP(crypto1->odd, crypto1->even);
|
||||
return out;
|
||||
}
|
||||
|
||||
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) {
|
||||
furi_assert(crypto1);
|
||||
uint8_t out = 0;
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
|
||||
furi_assert(crypto1);
|
||||
uint32_t out = 0;
|
||||
for(uint8_t i = 0; i < 32; i++) {
|
||||
out |= (uint32_t)crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t prng_successor(uint32_t x, uint32_t n) {
|
||||
SWAPENDIAN(x);
|
||||
while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
|
||||
|
||||
return SWAPENDIAN(x);
|
||||
}
|
||||
|
||||
void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out) {
|
||||
furi_assert(crypto);
|
||||
furi_assert(buff);
|
||||
furi_assert(out);
|
||||
|
||||
size_t bits = bit_buffer_get_size(buff);
|
||||
bit_buffer_set_size(out, bits);
|
||||
const uint8_t* encrypted_data = bit_buffer_get_data(buff);
|
||||
if(bits < 8) {
|
||||
uint8_t decrypted_byte = 0;
|
||||
uint8_t encrypted_byte = encrypted_data[0];
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 0)) << 0;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 1)) << 1;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 2)) << 2;
|
||||
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 3)) << 3;
|
||||
bit_buffer_set_byte(out, 0, decrypted_byte);
|
||||
} else {
|
||||
for(size_t i = 0; i < bits / 8; i++) {
|
||||
uint8_t decrypted_byte = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
|
||||
bit_buffer_set_byte(out, i, decrypted_byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out) {
|
||||
furi_assert(crypto);
|
||||
furi_assert(buff);
|
||||
furi_assert(out);
|
||||
|
||||
size_t bits = bit_buffer_get_size(buff);
|
||||
bit_buffer_set_size(out, bits);
|
||||
const uint8_t* plain_data = bit_buffer_get_data(buff);
|
||||
if(bits < 8) {
|
||||
uint8_t encrypted_byte = 0;
|
||||
for(size_t i = 0; i < bits; i++) {
|
||||
encrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
|
||||
}
|
||||
bit_buffer_set_byte(out, 0, encrypted_byte);
|
||||
} else {
|
||||
for(size_t i = 0; i < bits / 8; i++) {
|
||||
uint8_t encrypted_byte = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
|
||||
plain_data[i];
|
||||
bool parity_bit =
|
||||
((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01);
|
||||
bit_buffer_set_byte_with_parity(out, i, encrypted_byte, parity_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crypto1_encrypt_reader_nonce(
|
||||
Crypto1* crypto,
|
||||
uint64_t key,
|
||||
uint32_t cuid,
|
||||
uint8_t* nt,
|
||||
uint8_t* nr,
|
||||
BitBuffer* out,
|
||||
bool is_nested) {
|
||||
furi_assert(crypto);
|
||||
furi_assert(nt);
|
||||
furi_assert(nr);
|
||||
furi_assert(out);
|
||||
|
||||
bit_buffer_set_size_bytes(out, 8);
|
||||
uint32_t nt_num = bit_lib_bytes_to_num_be(nt, sizeof(uint32_t));
|
||||
|
||||
crypto1_init(crypto, key);
|
||||
if(is_nested) {
|
||||
nt_num = crypto1_word(crypto, nt_num ^ cuid, 1) ^ nt_num;
|
||||
} else {
|
||||
crypto1_word(crypto, nt_num ^ cuid, 0);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
uint8_t byte = crypto1_byte(crypto, nr[i], 0) ^ nr[i];
|
||||
bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01);
|
||||
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
|
||||
nr[i] = byte;
|
||||
}
|
||||
|
||||
nt_num = prng_successor(nt_num, 32);
|
||||
for(size_t i = 4; i < 8; i++) {
|
||||
nt_num = prng_successor(nt_num, 8);
|
||||
uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num);
|
||||
bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01);
|
||||
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t odd;
|
||||
uint32_t even;
|
||||
} Crypto1;
|
||||
|
||||
Crypto1* crypto1_alloc();
|
||||
|
||||
void crypto1_free(Crypto1* instance);
|
||||
|
||||
void crypto1_reset(Crypto1* crypto1);
|
||||
|
||||
void crypto1_init(Crypto1* crypto1, uint64_t key);
|
||||
|
||||
uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted);
|
||||
|
||||
uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted);
|
||||
|
||||
uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);
|
||||
|
||||
void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out);
|
||||
|
||||
void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out);
|
||||
|
||||
void crypto1_encrypt_reader_nonce(
|
||||
Crypto1* crypto,
|
||||
uint64_t key,
|
||||
uint32_t cuid,
|
||||
uint8_t* nt,
|
||||
uint8_t* nr,
|
||||
BitBuffer* out,
|
||||
bool is_nested);
|
||||
|
||||
uint32_t prng_successor(uint32_t x, uint32_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -56,28 +56,28 @@ const NfcDeviceBase nfc_device_mf_classic = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)mf_classic_get_base_data,
|
||||
};
|
||||
|
||||
MfClassicData* mf_classic_alloc() {
|
||||
MfClassicData* mf_classic_alloc(void) {
|
||||
MfClassicData* data = malloc(sizeof(MfClassicData));
|
||||
data->iso14443_3a_data = iso14443_3a_alloc();
|
||||
return data;
|
||||
}
|
||||
|
||||
void mf_classic_free(MfClassicData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3a_free(data->iso14443_3a_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void mf_classic_reset(MfClassicData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3a_reset(data->iso14443_3a_data);
|
||||
}
|
||||
|
||||
void mf_classic_copy(MfClassicData* data, const MfClassicData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
|
||||
for(size_t i = 0; i < COUNT_OF(data->block); i++) {
|
||||
@@ -92,7 +92,9 @@ void mf_classic_copy(MfClassicData* data, const MfClassicData* other) {
|
||||
}
|
||||
|
||||
bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) {
|
||||
furi_check(device_type);
|
||||
UNUSED(data);
|
||||
|
||||
return furi_string_equal_str(device_type, "Mifare Classic");
|
||||
}
|
||||
|
||||
@@ -146,7 +148,8 @@ static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, u
|
||||
}
|
||||
|
||||
bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool parsed = false;
|
||||
@@ -255,7 +258,8 @@ static void
|
||||
}
|
||||
|
||||
bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool saved = false;
|
||||
@@ -297,6 +301,9 @@ bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
bool is_equal = false;
|
||||
bool data_array_is_equal = true;
|
||||
|
||||
@@ -329,8 +336,8 @@ bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other)
|
||||
}
|
||||
|
||||
const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type) {
|
||||
furi_assert(data);
|
||||
furi_assert(data->type < MfClassicTypeNum);
|
||||
furi_check(data);
|
||||
furi_check(data->type < MfClassicTypeNum);
|
||||
|
||||
if(name_type == NfcDeviceNameTypeFull) {
|
||||
return mf_classic_features[data->type].full_name;
|
||||
@@ -340,13 +347,13 @@ const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameT
|
||||
}
|
||||
|
||||
const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
|
||||
}
|
||||
|
||||
bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool uid_valid = iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
|
||||
|
||||
@@ -370,16 +377,18 @@ bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len)
|
||||
}
|
||||
|
||||
Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_3a_data;
|
||||
}
|
||||
|
||||
uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
|
||||
furi_check(type < MfClassicTypeNum);
|
||||
return mf_classic_features[type].sectors_total;
|
||||
}
|
||||
|
||||
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
|
||||
furi_check(type < MfClassicTypeNum);
|
||||
return mf_classic_features[type].blocks_total;
|
||||
}
|
||||
|
||||
@@ -411,7 +420,7 @@ uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) {
|
||||
|
||||
MfClassicSectorTrailer*
|
||||
mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
uint8_t sec_tr_block = mf_classic_get_sector_trailer_num_by_sector(sector_num);
|
||||
MfClassicSectorTrailer* sec_trailer = (MfClassicSectorTrailer*)&data->block[sec_tr_block];
|
||||
@@ -436,7 +445,8 @@ uint8_t mf_classic_get_sector_by_block(uint8_t block) {
|
||||
}
|
||||
|
||||
bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr) {
|
||||
furi_assert(block);
|
||||
furi_check(block);
|
||||
furi_check(value);
|
||||
|
||||
uint32_t v = *(uint32_t*)&block->data[0];
|
||||
uint32_t v_inv = *(uint32_t*)&block->data[sizeof(uint32_t)];
|
||||
@@ -445,9 +455,7 @@ bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint
|
||||
bool val_checks =
|
||||
((v == v1) && (v == ~v_inv) && (block->data[12] == (~block->data[13] & 0xFF)) &&
|
||||
(block->data[14] == (~block->data[15] & 0xFF)) && (block->data[12] == block->data[14]));
|
||||
if(value) {
|
||||
*value = (int32_t)v;
|
||||
}
|
||||
*value = (int32_t)v;
|
||||
if(addr) {
|
||||
*addr = block->data[12];
|
||||
}
|
||||
@@ -455,7 +463,7 @@ bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint
|
||||
}
|
||||
|
||||
void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block) {
|
||||
furi_assert(block);
|
||||
furi_check(block);
|
||||
|
||||
uint32_t v_inv = ~((uint32_t)value);
|
||||
|
||||
@@ -473,7 +481,7 @@ bool mf_classic_is_key_found(
|
||||
const MfClassicData* data,
|
||||
uint8_t sector_num,
|
||||
MfClassicKeyType key_type) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool key_found = false;
|
||||
if(key_type == MfClassicKeyTypeA) {
|
||||
@@ -490,7 +498,7 @@ void mf_classic_set_key_found(
|
||||
uint8_t sector_num,
|
||||
MfClassicKeyType key_type,
|
||||
uint64_t key) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
uint8_t key_arr[6] = {};
|
||||
MfClassicSectorTrailer* sec_trailer =
|
||||
@@ -509,7 +517,7 @@ void mf_classic_set_key_not_found(
|
||||
MfClassicData* data,
|
||||
uint8_t sector_num,
|
||||
MfClassicKeyType key_type) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
if(key_type == MfClassicKeyTypeA) {
|
||||
FURI_BIT_CLEAR(data->key_a_mask, sector_num);
|
||||
@@ -519,13 +527,14 @@ void mf_classic_set_key_not_found(
|
||||
}
|
||||
|
||||
bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1);
|
||||
}
|
||||
|
||||
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(block_data);
|
||||
|
||||
if(mf_classic_is_sector_trailer(block_num)) {
|
||||
memcpy(&data->block[block_num].data[6], &block_data->data[6], 4);
|
||||
@@ -536,7 +545,7 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic
|
||||
}
|
||||
|
||||
uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {
|
||||
furi_assert(sector < 40);
|
||||
furi_check(sector < 40);
|
||||
|
||||
uint8_t block = 0;
|
||||
if(sector < 32) {
|
||||
@@ -549,7 +558,8 @@ uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) {
|
||||
}
|
||||
|
||||
uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) {
|
||||
furi_assert(sector < 40);
|
||||
furi_check(sector < 40);
|
||||
|
||||
return sector < 32 ? 4 : 16;
|
||||
}
|
||||
|
||||
@@ -557,9 +567,9 @@ void mf_classic_get_read_sectors_and_keys(
|
||||
const MfClassicData* data,
|
||||
uint8_t* sectors_read,
|
||||
uint8_t* keys_found) {
|
||||
furi_assert(data);
|
||||
furi_assert(sectors_read);
|
||||
furi_assert(keys_found);
|
||||
furi_check(data);
|
||||
furi_check(sectors_read);
|
||||
furi_check(keys_found);
|
||||
|
||||
*sectors_read = 0;
|
||||
*keys_found = 0;
|
||||
@@ -585,7 +595,7 @@ void mf_classic_get_read_sectors_and_keys(
|
||||
}
|
||||
|
||||
bool mf_classic_is_card_read(const MfClassicData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
|
||||
uint8_t sectors_read = 0;
|
||||
@@ -597,7 +607,7 @@ bool mf_classic_is_card_read(const MfClassicData* data) {
|
||||
}
|
||||
|
||||
bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool sector_read = false;
|
||||
do {
|
||||
@@ -662,7 +672,7 @@ bool mf_classic_is_allowed_access_data_block(
|
||||
uint8_t block_num,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAction action) {
|
||||
furi_assert(sec_tr);
|
||||
furi_check(sec_tr);
|
||||
|
||||
uint8_t* access_bits_arr = sec_tr->access_bits.data;
|
||||
|
||||
@@ -732,7 +742,7 @@ bool mf_classic_is_allowed_access(
|
||||
uint8_t block_num,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAction action) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool access_allowed = false;
|
||||
if(mf_classic_is_sector_trailer(block_num)) {
|
||||
@@ -749,7 +759,7 @@ bool mf_classic_is_allowed_access(
|
||||
}
|
||||
|
||||
bool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num) {
|
||||
furi_assert(sec_tr);
|
||||
furi_check(sec_tr);
|
||||
|
||||
// Check if key A can write, if it can, it's transport configuration, not data block
|
||||
return !mf_classic_is_allowed_access_data_block(
|
||||
|
||||
@@ -143,7 +143,7 @@ typedef struct {
|
||||
|
||||
extern const NfcDeviceBase nfc_device_mf_classic;
|
||||
|
||||
MfClassicData* mf_classic_alloc();
|
||||
MfClassicData* mf_classic_alloc(void);
|
||||
|
||||
void mf_classic_free(MfClassicData* data);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "mf_classic_listener.h"
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>
|
||||
#include <nfc/protocols/nfc_generic_event.h>
|
||||
#include "crypto1.h"
|
||||
#include <nfc/helpers/crypto1.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -315,6 +315,89 @@ MfClassicError mf_classic_poller_value_cmd(
|
||||
*/
|
||||
MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Iso14443_3a standard frames in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_standard_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Iso14443_3a frames in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Iso14443_3a frames with custom parity bits in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* Custom parity bits must be set in the tx_buffer. The rx_buffer will contain
|
||||
* the received data with the parity bits.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_custom_parity_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
/**
|
||||
* @brief Transmit and receive Mifare Classic encrypted frames with custom parity bits in poller mode.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* The rx_buffer will be filled with any data received as a response to data
|
||||
* sent from tx_buffer, with a timeout defined by the fwt parameter.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] tx_buffer pointer to the buffer containing the plain data to be transmitted.
|
||||
* @param[out] rx_buffer pointer to the buffer to be filled with decyphered received data.
|
||||
* @param[in] fwt frame wait time (response timeout), in carrier cycles.
|
||||
* @return MfClassicErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_send_encrypted_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -90,6 +90,8 @@ MfClassicError mf_classic_poller_get_nt(
|
||||
uint8_t block_num,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicNt* nt) {
|
||||
furi_check(instance);
|
||||
|
||||
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, false);
|
||||
}
|
||||
|
||||
@@ -98,6 +100,8 @@ MfClassicError mf_classic_poller_get_nt_nested(
|
||||
uint8_t block_num,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicNt* nt) {
|
||||
furi_check(instance);
|
||||
|
||||
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true);
|
||||
}
|
||||
|
||||
@@ -179,6 +183,8 @@ MfClassicError mf_classic_poller_auth(
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAuthContext* data) {
|
||||
furi_check(instance);
|
||||
furi_check(key);
|
||||
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false);
|
||||
}
|
||||
|
||||
@@ -188,10 +194,14 @@ MfClassicError mf_classic_poller_auth_nested(
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAuthContext* data) {
|
||||
furi_check(instance);
|
||||
furi_check(key);
|
||||
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, true);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -223,6 +233,9 @@ MfClassicError mf_classic_poller_read_block(
|
||||
MfClassicPoller* instance,
|
||||
uint8_t block_num,
|
||||
MfClassicBlock* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -269,6 +282,9 @@ MfClassicError mf_classic_poller_write_block(
|
||||
MfClassicPoller* instance,
|
||||
uint8_t block_num,
|
||||
MfClassicBlock* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -341,6 +357,8 @@ MfClassicError mf_classic_poller_value_cmd(
|
||||
uint8_t block_num,
|
||||
MfClassicValueCommand cmd,
|
||||
int32_t data) {
|
||||
furi_check(instance);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -407,6 +425,8 @@ MfClassicError mf_classic_poller_value_cmd(
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num) {
|
||||
furi_check(instance);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -445,3 +465,77 @@ MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_standard_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_send_standard_frame(
|
||||
instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
|
||||
|
||||
return mf_classic_process_error(error);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError error =
|
||||
iso14443_3a_poller_txrx(instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
|
||||
|
||||
return mf_classic_process_error(error);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_custom_parity_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(
|
||||
instance->iso14443_3a_poller, tx_buffer, rx_buffer, fwt_fc);
|
||||
|
||||
return mf_classic_process_error(error);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_send_encrypted_frame(
|
||||
MfClassicPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt_fc) {
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
do {
|
||||
crypto1_encrypt(instance->crypto, NULL, tx_buffer, instance->tx_encrypted_buffer);
|
||||
|
||||
Iso14443_3aError error = iso14443_3a_poller_txrx_custom_parity(
|
||||
instance->iso14443_3a_poller,
|
||||
instance->tx_encrypted_buffer,
|
||||
instance->rx_encrypted_buffer,
|
||||
fwt_fc);
|
||||
if(error != Iso14443_3aErrorNone) {
|
||||
ret = mf_classic_process_error(error);
|
||||
break;
|
||||
}
|
||||
|
||||
crypto1_decrypt(instance->crypto, instance->rx_encrypted_buffer, rx_buffer);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "mf_classic_poller.h"
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include "crypto1.h"
|
||||
#include <nfc/helpers/crypto1.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -225,7 +225,7 @@ MfClassicError mf_classic_poller_sync_collect_nt(
|
||||
uint8_t block_num,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicNt* nt) {
|
||||
furi_assert(nfc);
|
||||
furi_check(nfc);
|
||||
|
||||
MfClassicPollerContext poller_context = {
|
||||
.cmd_type = MfClassicPollerCmdTypeCollectNt,
|
||||
@@ -250,8 +250,8 @@ MfClassicError mf_classic_poller_sync_auth(
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAuthContext* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(key);
|
||||
furi_check(nfc);
|
||||
furi_check(key);
|
||||
|
||||
MfClassicPollerContext poller_context = {
|
||||
.cmd_type = MfClassicPollerCmdTypeAuth,
|
||||
@@ -277,9 +277,9 @@ MfClassicError mf_classic_poller_sync_read_block(
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicBlock* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(key);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(key);
|
||||
furi_check(data);
|
||||
|
||||
MfClassicPollerContext poller_context = {
|
||||
.cmd_type = MfClassicPollerCmdTypeReadBlock,
|
||||
@@ -303,9 +303,9 @@ MfClassicError mf_classic_poller_sync_write_block(
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicBlock* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(key);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(key);
|
||||
furi_check(data);
|
||||
|
||||
MfClassicPollerContext poller_context = {
|
||||
.cmd_type = MfClassicPollerCmdTypeWriteBlock,
|
||||
@@ -326,9 +326,9 @@ MfClassicError mf_classic_poller_sync_read_value(
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
int32_t* value) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(key);
|
||||
furi_assert(value);
|
||||
furi_check(nfc);
|
||||
furi_check(key);
|
||||
furi_check(value);
|
||||
|
||||
MfClassicPollerContext poller_context = {
|
||||
.cmd_type = MfClassicPollerCmdTypeReadValue,
|
||||
@@ -353,9 +353,9 @@ MfClassicError mf_classic_poller_sync_change_value(
|
||||
MfClassicKeyType key_type,
|
||||
int32_t data,
|
||||
int32_t* new_value) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(key);
|
||||
furi_assert(new_value);
|
||||
furi_check(nfc);
|
||||
furi_check(key);
|
||||
furi_check(new_value);
|
||||
|
||||
MfClassicValueCommand command = MfClassicValueCommandRestore;
|
||||
int32_t command_data = 0;
|
||||
@@ -459,9 +459,9 @@ NfcCommand mf_classic_poller_read_callback(NfcGenericEvent event, void* context)
|
||||
|
||||
MfClassicError
|
||||
mf_classic_poller_sync_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(keys);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(keys);
|
||||
furi_check(data);
|
||||
|
||||
MfClassicError error = MfClassicErrorNone;
|
||||
MfClassicPollerContext poller_context = {};
|
||||
@@ -493,8 +493,8 @@ MfClassicError
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_sync_detect_type(Nfc* nfc, MfClassicType* type) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(type);
|
||||
furi_check(nfc);
|
||||
furi_check(type);
|
||||
|
||||
MfClassicError error = MfClassicErrorNone;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const NfcDeviceBase nfc_device_mf_desfire = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)mf_desfire_get_base_data,
|
||||
};
|
||||
|
||||
MfDesfireData* mf_desfire_alloc() {
|
||||
MfDesfireData* mf_desfire_alloc(void) {
|
||||
MfDesfireData* data = malloc(sizeof(MfDesfireData));
|
||||
data->iso14443_4a_data = iso14443_4a_alloc();
|
||||
data->master_key_versions = simple_array_alloc(&mf_desfire_key_version_array_config);
|
||||
@@ -31,7 +31,7 @@ MfDesfireData* mf_desfire_alloc() {
|
||||
}
|
||||
|
||||
void mf_desfire_free(MfDesfireData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
mf_desfire_reset(data);
|
||||
simple_array_free(data->applications);
|
||||
@@ -42,7 +42,7 @@ void mf_desfire_free(MfDesfireData* data) {
|
||||
}
|
||||
|
||||
void mf_desfire_reset(MfDesfireData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_4a_reset(data->iso14443_4a_data);
|
||||
|
||||
@@ -55,8 +55,8 @@ void mf_desfire_reset(MfDesfireData* data) {
|
||||
}
|
||||
|
||||
void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
mf_desfire_reset(data);
|
||||
|
||||
@@ -77,7 +77,8 @@ bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type) {
|
||||
}
|
||||
|
||||
bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* prefix = furi_string_alloc();
|
||||
|
||||
@@ -143,7 +144,8 @@ bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) {
|
||||
}
|
||||
|
||||
bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* prefix = furi_string_alloc();
|
||||
|
||||
@@ -208,12 +210,15 @@ bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&
|
||||
memcmp(&data->version, &other->version, sizeof(MfDesfireVersion)) == 0 &&
|
||||
memcmp(&data->free_memory, &other->free_memory, sizeof(MfDesfireFreeMemory)) == 0 &&
|
||||
memcmp( //-V1103
|
||||
&data->free_memory,
|
||||
&other->free_memory,
|
||||
sizeof(MfDesfireFreeMemory)) == 0 &&
|
||||
memcmp(
|
||||
&data->master_key_settings,
|
||||
&other->master_key_settings,
|
||||
@@ -230,25 +235,29 @@ const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameT
|
||||
}
|
||||
|
||||
const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(uid_len);
|
||||
|
||||
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
|
||||
}
|
||||
|
||||
bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
|
||||
}
|
||||
|
||||
Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_4a_data;
|
||||
}
|
||||
|
||||
const MfDesfireApplication*
|
||||
mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id) {
|
||||
furi_check(data);
|
||||
furi_check(app_id);
|
||||
|
||||
MfDesfireApplication* app = NULL;
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) {
|
||||
@@ -263,6 +272,9 @@ const MfDesfireApplication*
|
||||
|
||||
const MfDesfireFileSettings*
|
||||
mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {
|
||||
furi_check(data);
|
||||
furi_check(file_id);
|
||||
|
||||
MfDesfireFileSettings* file_settings = NULL;
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {
|
||||
@@ -277,6 +289,9 @@ const MfDesfireFileSettings*
|
||||
|
||||
const MfDesfireFileData*
|
||||
mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id) {
|
||||
furi_check(data);
|
||||
furi_check(file_id);
|
||||
|
||||
MfDesfireFileData* file_data = NULL;
|
||||
|
||||
for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) {
|
||||
|
||||
@@ -21,8 +21,6 @@ extern "C" {
|
||||
#define MF_DESFIRE_CMD_GET_VALUE (0x6C)
|
||||
#define MF_DESFIRE_CMD_READ_RECORDS (0xBB)
|
||||
|
||||
#define MF_DESFIRE_FLAG_HAS_NEXT (0xAF)
|
||||
|
||||
#define MF_DESFIRE_MAX_KEYS (14)
|
||||
#define MF_DESFIRE_MAX_FILES (32)
|
||||
|
||||
@@ -71,11 +69,6 @@ typedef struct {
|
||||
|
||||
typedef uint8_t MfDesfireKeyVersion;
|
||||
|
||||
typedef struct {
|
||||
MfDesfireKeySettings key_settings;
|
||||
SimpleArray* key_versions;
|
||||
} MfDesfireKeyConfiguration;
|
||||
|
||||
typedef enum {
|
||||
MfDesfireFileTypeStandard = 0,
|
||||
MfDesfireFileTypeBackup = 1,
|
||||
@@ -96,7 +89,8 @@ typedef uint16_t MfDesfireFileAccessRights;
|
||||
typedef struct {
|
||||
MfDesfireFileType type;
|
||||
MfDesfireFileCommunicationSettings comm;
|
||||
MfDesfireFileAccessRights access_rights;
|
||||
MfDesfireFileAccessRights access_rights[MF_DESFIRE_MAX_KEYS];
|
||||
uint8_t access_rights_len;
|
||||
union {
|
||||
struct {
|
||||
uint32_t size;
|
||||
@@ -136,6 +130,7 @@ typedef enum {
|
||||
MfDesfireErrorNotPresent,
|
||||
MfDesfireErrorProtocol,
|
||||
MfDesfireErrorTimeout,
|
||||
MfDesfireErrorAuthentication,
|
||||
} MfDesfireError;
|
||||
|
||||
typedef struct {
|
||||
@@ -152,7 +147,7 @@ extern const NfcDeviceBase nfc_device_mf_desfire;
|
||||
|
||||
// Virtual methods
|
||||
|
||||
MfDesfireData* mf_desfire_alloc();
|
||||
MfDesfireData* mf_desfire_alloc(void);
|
||||
|
||||
void mf_desfire_free(MfDesfireData* data);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "mf_desfire_i.h"
|
||||
|
||||
#define TAG "MfDesfire"
|
||||
|
||||
#define BITS_IN_BYTE (8U)
|
||||
|
||||
#define MF_DESFIRE_FFF_VERSION_KEY \
|
||||
@@ -175,59 +177,88 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
|
||||
};
|
||||
} MfDesfireFileSettingsLayout;
|
||||
|
||||
MfDesfireFileSettings file_settings_temp = {};
|
||||
do {
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
const uint8_t* data_ptr = bit_buffer_get_data(buf);
|
||||
const size_t min_data_size =
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData);
|
||||
const size_t max_data_size =
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue);
|
||||
|
||||
if(data_size < min_data_size) break;
|
||||
if(data_size <= max_data_size) {
|
||||
MfDesfireFileSettingsLayout layout;
|
||||
bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout));
|
||||
|
||||
data->type = layout.header.type;
|
||||
data->comm = layout.header.comm;
|
||||
data->access_rights = layout.header.access_rights;
|
||||
|
||||
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
|
||||
if(data_size != min_data_size) break;
|
||||
|
||||
data->data.size = layout.data.size;
|
||||
} else if(data->type == MfDesfireFileTypeValue) {
|
||||
if(data_size !=
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue))
|
||||
break;
|
||||
|
||||
data->value.lo_limit = layout.value.lo_limit;
|
||||
data->value.hi_limit = layout.value.hi_limit;
|
||||
data->value.limited_credit_value = layout.value.limited_credit_value;
|
||||
data->value.limited_credit_enabled = layout.value.limited_credit_enabled;
|
||||
|
||||
} else if(
|
||||
data->type == MfDesfireFileTypeLinearRecord ||
|
||||
data->type == MfDesfireFileTypeCyclicRecord) {
|
||||
if(data_size !=
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord))
|
||||
break;
|
||||
|
||||
data->record.size = layout.record.size;
|
||||
data->record.max = layout.record.max;
|
||||
data->record.cur = layout.record.cur;
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO FL-3750: process HID Desfire command response here
|
||||
// Set default fields for now
|
||||
data->type = 0;
|
||||
data->comm = 0;
|
||||
data->access_rights = 0;
|
||||
data->data.size = 0;
|
||||
if(data_size < min_data_size) {
|
||||
FURI_LOG_E(
|
||||
TAG, "File settings size %zu less than minimum %zu", data_size, min_data_size);
|
||||
break;
|
||||
}
|
||||
|
||||
size_t bytes_processed = sizeof(MfDesfireFileSettingsHeader);
|
||||
MfDesfireFileSettingsLayout layout = {};
|
||||
memcpy(&layout.header, data_ptr, sizeof(MfDesfireFileSettingsHeader));
|
||||
bool has_additional_access_rights = (layout.header.comm & 0x80) != 0;
|
||||
|
||||
file_settings_temp.type = layout.header.type;
|
||||
file_settings_temp.comm = layout.header.comm & 0x03;
|
||||
file_settings_temp.access_rights_len = 1;
|
||||
file_settings_temp.access_rights[0] = layout.header.access_rights;
|
||||
|
||||
if(file_settings_temp.type == MfDesfireFileTypeStandard ||
|
||||
file_settings_temp.type == MfDesfireFileTypeBackup) {
|
||||
memcpy(
|
||||
&layout.data,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsData));
|
||||
file_settings_temp.data.size = layout.data.size;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsData);
|
||||
} else if(file_settings_temp.type == MfDesfireFileTypeValue) {
|
||||
memcpy(
|
||||
&layout.value,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsValue));
|
||||
file_settings_temp.value.lo_limit = layout.value.lo_limit;
|
||||
file_settings_temp.value.hi_limit = layout.value.hi_limit;
|
||||
file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value;
|
||||
file_settings_temp.value.limited_credit_enabled = layout.value.limited_credit_enabled;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsValue);
|
||||
} else if(
|
||||
file_settings_temp.type == MfDesfireFileTypeLinearRecord ||
|
||||
file_settings_temp.type == MfDesfireFileTypeCyclicRecord) {
|
||||
memcpy(
|
||||
&layout.record,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsRecord));
|
||||
file_settings_temp.record.size = layout.record.size;
|
||||
file_settings_temp.record.max = layout.record.max;
|
||||
file_settings_temp.record.cur = layout.record.cur;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsRecord);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type);
|
||||
break;
|
||||
}
|
||||
|
||||
if(has_additional_access_rights) {
|
||||
uint8_t additional_access_rights_len = bit_buffer_get_byte(buf, bytes_processed);
|
||||
FURI_LOG_D(TAG, "Has additional rights: %d", additional_access_rights_len);
|
||||
if(data_size != bytes_processed +
|
||||
additional_access_rights_len * sizeof(MfDesfireFileAccessRights) +
|
||||
1) {
|
||||
FURI_LOG_W(TAG, "Unexpected command length: %zu", data_size);
|
||||
for(size_t i = 0; i < bit_buffer_get_size_bytes(buf); i++) {
|
||||
printf("%02X ", bit_buffer_get_byte(buf, i));
|
||||
}
|
||||
printf("\r\n");
|
||||
break;
|
||||
}
|
||||
if(additional_access_rights_len >
|
||||
MF_DESFIRE_MAX_KEYS * sizeof(MfDesfireFileAccessRights))
|
||||
break;
|
||||
|
||||
memcpy(
|
||||
&file_settings_temp.access_rights[1],
|
||||
&data_ptr[bytes_processed],
|
||||
additional_access_rights_len * sizeof(MfDesfireFileAccessRights));
|
||||
file_settings_temp.access_rights_len += additional_access_rights_len;
|
||||
}
|
||||
|
||||
*data = file_settings_temp;
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
@@ -392,18 +423,19 @@ bool mf_desfire_file_settings_load(
|
||||
break;
|
||||
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY);
|
||||
if(!flipper_format_read_hex(
|
||||
ff,
|
||||
furi_string_get_cstr(key),
|
||||
(uint8_t*)&data->access_rights,
|
||||
sizeof(MfDesfireFileAccessRights)))
|
||||
uint32_t access_rights_len = 0;
|
||||
if(!flipper_format_get_value_count(ff, furi_string_get_cstr(key), &access_rights_len))
|
||||
break;
|
||||
if((access_rights_len == 0) || ((access_rights_len % 2) != 0)) break;
|
||||
if(!flipper_format_read_hex(
|
||||
ff, furi_string_get_cstr(key), (uint8_t*)&data->access_rights, access_rights_len))
|
||||
break;
|
||||
data->access_rights_len = access_rights_len / sizeof(MfDesfireFileAccessRights);
|
||||
|
||||
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY);
|
||||
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1))
|
||||
break;
|
||||
|
||||
} else if(data->type == MfDesfireFileTypeValue) {
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY);
|
||||
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1))
|
||||
@@ -641,8 +673,8 @@ bool mf_desfire_file_settings_save(
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
furi_string_get_cstr(key),
|
||||
(const uint8_t*)&data->access_rights,
|
||||
sizeof(MfDesfireFileAccessRights)))
|
||||
(const uint8_t*)data->access_rights,
|
||||
data->access_rights_len * sizeof(MfDesfireFileAccessRights)))
|
||||
break;
|
||||
|
||||
if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) {
|
||||
@@ -737,8 +769,11 @@ bool mf_desfire_application_save(
|
||||
if(i != key_version_count) break;
|
||||
|
||||
const uint32_t file_count = simple_array_get_count(data->file_ids);
|
||||
if(!mf_desfire_file_ids_save(simple_array_get_data(data->file_ids), file_count, prefix, ff))
|
||||
break;
|
||||
if(file_count > 0) {
|
||||
if(!mf_desfire_file_ids_save(
|
||||
simple_array_get_data(data->file_ids), file_count, prefix, ff))
|
||||
break;
|
||||
}
|
||||
|
||||
for(i = 0; i < file_count; ++i) {
|
||||
const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i);
|
||||
|
||||
@@ -5,6 +5,52 @@
|
||||
#define MF_DESFIRE_FFF_PICC_PREFIX "PICC"
|
||||
#define MF_DESFIRE_FFF_APP_PREFIX "Application"
|
||||
|
||||
// Successful operation
|
||||
#define MF_DESFIRE_STATUS_OPERATION_OK (0x00)
|
||||
// No changes done to backup files, CommitTransaction / AbortTransaction not necessary
|
||||
#define MF_DESFIRE_STATUS_NO_CHANGES (0x0C)
|
||||
// Insufficient NV-Memory to complete command
|
||||
#define MF_DESFIRE_STATUS_OUT_OF_EEPROM_ERROR (0x0E)
|
||||
// Command code not supported
|
||||
#define MF_DESFIRE_STATUS_ILLEGAL_COMMAND_CODE (0x1C)
|
||||
// CRC or MAC does not match data Padding bytes not valid
|
||||
#define MF_DESFIRE_STATUS_INTEGRITY_ERROR (0x1E)
|
||||
// Invalid key number specified
|
||||
#define MF_DESFIRE_STATUS_NO_SUCH_KEY (0x40)
|
||||
// Length of command string invalid
|
||||
#define MF_DESFIRE_STATUS_LENGTH_ERROR (0x7E)
|
||||
// Current configuration / status does not allow the requested command
|
||||
#define MF_DESFIRE_STATUS_PERMISSION_DENIED (0x9D)
|
||||
// Value of the parameter(s) invalid
|
||||
#define MF_DESFIRE_STATUS_PARAMETER_ERROR (0x9E)
|
||||
// Requested AID not present on PICC
|
||||
#define MF_DESFIRE_STATUS_APPLICATION_NOT_FOUND (0xA0)
|
||||
// Unrecoverable error within application, application will be disabled
|
||||
#define MF_DESFIRE_STATUS_APPL_INTEGRITY_ERROR (0xA1)
|
||||
// Current authentication status does not allow the requested command
|
||||
#define MF_DESFIRE_STATUS_AUTHENTICATION_ERROR (0xAE)
|
||||
// Additional data frame is expected to be sent
|
||||
#define MF_DESFIRE_STATUS_ADDITIONAL_FRAME (0xAF)
|
||||
// Attempt to read/write data from/to beyond the file's/record's limits
|
||||
// Attempt to exceed the limits of a value file.
|
||||
#define MF_DESFIRE_STATUS_BOUNDARY_ERROR (0xBE)
|
||||
// Unrecoverable error within PICC, PICC will be disabled
|
||||
#define MF_DESFIRE_STATUS_PICC_INTEGRITY_ERROR (0xC1)
|
||||
// Previous Command was not fully completed. Not all Frames were requested or provided by the PCD
|
||||
#define MF_DESFIRE_STATUS_COMMAND_ABORTED (0xCA)
|
||||
// PICC was disabled by an unrecoverable error
|
||||
#define MF_DESFIRE_STATUS_PICC_DISABLED_ERROR (0xCD)
|
||||
// Number of Applications limited to 28, no additional CreateApplication possible
|
||||
#define MF_DESFIRE_STATUS_COUNT_ERROR (0xCE)
|
||||
// Creation of file/application failed because file/application with same number already exists
|
||||
#define MF_DESFIRE_STATUS_DUBLICATE_ERROR (0xDE)
|
||||
// Could not complete NV-write operation due to loss of power, internal backup/rollback mechanism activated
|
||||
#define MF_DESFIRE_STATUS_EEPROM_ERROR (0xEE)
|
||||
// Specified file number does not exist
|
||||
#define MF_DESFIRE_STATUS_FILE_NOT_FOUND (0xF0)
|
||||
// Unrecoverable error within file, file will be disabled
|
||||
#define MF_DESFIRE_STATUS_FILE_INTEGRITY_ERROR (0xF1)
|
||||
|
||||
// SimpleArray configurations
|
||||
|
||||
extern const SimpleArrayConfig mf_desfire_key_version_array_config;
|
||||
|
||||
@@ -75,17 +75,23 @@ static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instan
|
||||
}
|
||||
|
||||
static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) {
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
instance->error = mf_desfire_poller_read_free_memory(instance, &instance->data->free_memory);
|
||||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read free memory success");
|
||||
instance->state = MfDesfirePollerStateReadMasterKeySettings;
|
||||
} else if(instance->error == MfDesfireErrorNotPresent) {
|
||||
FURI_LOG_D(TAG, "Read free memoty is unsupported");
|
||||
instance->state = MfDesfirePollerStateReadMasterKeySettings;
|
||||
command = NfcCommandReset;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read free memory");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->state = MfDesfirePollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) {
|
||||
@@ -94,6 +100,11 @@ static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePo
|
||||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read master key settings success");
|
||||
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
|
||||
} else if(instance->error == MfDesfireErrorAuthentication) {
|
||||
FURI_LOG_D(TAG, "Auth is required to read master key settings and app ids");
|
||||
instance->data->master_key_settings.is_free_directory_list = false;
|
||||
instance->data->master_key_settings.max_keys = 1;
|
||||
instance->state = MfDesfirePollerStateReadMasterKeyVersion;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read master key settings");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
@@ -110,7 +121,11 @@ static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePol
|
||||
instance->data->master_key_settings.max_keys);
|
||||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read master key version success");
|
||||
instance->state = MfDesfirePollerStateReadApplicationIds;
|
||||
if(instance->data->master_key_settings.is_free_directory_list) {
|
||||
instance->state = MfDesfirePollerStateReadApplicationIds;
|
||||
} else {
|
||||
instance->state = MfDesfirePollerStateReadSuccess;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read master key version");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
@@ -126,6 +141,9 @@ static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller
|
||||
if(instance->error == MfDesfireErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read application ids success");
|
||||
instance->state = MfDesfirePollerStateReadApplications;
|
||||
} else if(instance->error == MfDesfireErrorAuthentication) {
|
||||
FURI_LOG_D(TAG, "Read application ids impossible without authentication");
|
||||
instance->state = MfDesfirePollerStateReadSuccess;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read application ids");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
@@ -225,9 +243,17 @@ static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) {
|
||||
bool protocol_detected = false;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
MfDesfireVersion version = {};
|
||||
const MfDesfireError error = mf_desfire_poller_read_version(instance, &version);
|
||||
protocol_detected = (error == MfDesfireErrorNone);
|
||||
do {
|
||||
MfDesfireKeyVersion key_version = 0;
|
||||
MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
MfDesfireVersion version = {};
|
||||
error = mf_desfire_poller_read_version(instance, &version);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
protocol_detected = true;
|
||||
} while(false);
|
||||
}
|
||||
|
||||
return protocol_detected;
|
||||
|
||||
@@ -91,6 +91,21 @@ MfDesfireError
|
||||
MfDesfireError
|
||||
mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data);
|
||||
|
||||
/**
|
||||
* @brief Read key version on MfDesfire card.
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[out] key_num key number.
|
||||
* @param[in] data pointer to the MfDesfireKeyVersion structure to be filled with key version data.
|
||||
* @return MfDesfireErrorNone on success, an error code on failure.
|
||||
*/
|
||||
MfDesfireError mf_desfire_poller_read_key_version(
|
||||
MfDesfirePoller* instance,
|
||||
uint8_t key_num,
|
||||
MfDesfireKeyVersion* data);
|
||||
|
||||
/**
|
||||
* @brief Read key versions on MfDesfire card.
|
||||
*
|
||||
|
||||
@@ -19,16 +19,27 @@ MfDesfireError mf_desfire_process_error(Iso14443_4aError error) {
|
||||
}
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_process_status_code(uint8_t status_code) {
|
||||
switch(status_code) {
|
||||
case MF_DESFIRE_STATUS_OPERATION_OK:
|
||||
return MfDesfireErrorNone;
|
||||
case MF_DESFIRE_STATUS_AUTHENTICATION_ERROR:
|
||||
return MfDesfireErrorAuthentication;
|
||||
default:
|
||||
return MfDesfireErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_send_chunks(
|
||||
MfDesfirePoller* 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);
|
||||
furi_check(instance);
|
||||
furi_check(instance->iso14443_4a_poller);
|
||||
furi_check(instance->tx_buffer);
|
||||
furi_check(instance->rx_buffer);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
MfDesfireError error = MfDesfireErrorNone;
|
||||
|
||||
@@ -42,7 +53,7 @@ MfDesfireError mf_desfire_send_chunks(
|
||||
}
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT);
|
||||
bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME);
|
||||
|
||||
if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) {
|
||||
bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t));
|
||||
@@ -50,7 +61,8 @@ MfDesfireError mf_desfire_send_chunks(
|
||||
bit_buffer_reset(rx_buffer);
|
||||
}
|
||||
|
||||
while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) {
|
||||
while(
|
||||
bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_STATUS_ADDITIONAL_FRAME)) {
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
@@ -71,11 +83,16 @@ MfDesfireError mf_desfire_send_chunks(
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(error == MfDesfireErrorNone) {
|
||||
uint8_t err_code = bit_buffer_get_byte(instance->rx_buffer, 0);
|
||||
error = mf_desfire_process_status_code(err_code);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION);
|
||||
@@ -97,7 +114,7 @@ MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfi
|
||||
|
||||
MfDesfireError
|
||||
mf_desfire_poller_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY);
|
||||
@@ -110,7 +127,7 @@ MfDesfireError
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) {
|
||||
error = MfDesfireErrorProtocol;
|
||||
error = MfDesfireErrorNotPresent;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
@@ -119,7 +136,7 @@ MfDesfireError
|
||||
|
||||
MfDesfireError
|
||||
mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS);
|
||||
@@ -139,31 +156,42 @@ MfDesfireError
|
||||
return error;
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_key_version(
|
||||
MfDesfirePoller* instance,
|
||||
uint8_t key_num,
|
||||
MfDesfireKeyVersion* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);
|
||||
bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);
|
||||
bit_buffer_set_byte(instance->input_buffer, 1, key_num);
|
||||
|
||||
MfDesfireError error =
|
||||
mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
|
||||
if(error == MfDesfireErrorNone) {
|
||||
if(!mf_desfire_key_version_parse(data, instance->result_buffer)) {
|
||||
error = MfDesfireErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_key_versions(
|
||||
MfDesfirePoller* instance,
|
||||
SimpleArray* data,
|
||||
uint32_t count) {
|
||||
furi_assert(instance);
|
||||
furi_assert(count > 0);
|
||||
furi_check(instance);
|
||||
furi_check(count > 0);
|
||||
|
||||
simple_array_init(data, count);
|
||||
|
||||
bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2);
|
||||
bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION);
|
||||
|
||||
MfDesfireError error = MfDesfireErrorNone;
|
||||
|
||||
for(uint32_t i = 0; i < count; ++i) {
|
||||
bit_buffer_set_byte(instance->input_buffer, 1, i);
|
||||
|
||||
error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer);
|
||||
|
||||
error = mf_desfire_poller_read_key_version(instance, i, simple_array_get(data, i));
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
if(!mf_desfire_key_version_parse(simple_array_get(data, i), instance->result_buffer)) {
|
||||
error = MfDesfireErrorProtocol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
@@ -171,7 +199,8 @@ MfDesfireError mf_desfire_poller_read_key_versions(
|
||||
|
||||
MfDesfireError
|
||||
mf_desfire_poller_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS);
|
||||
@@ -204,7 +233,7 @@ MfDesfireError
|
||||
MfDesfireError mf_desfire_poller_select_application(
|
||||
MfDesfirePoller* instance,
|
||||
const MfDesfireApplicationId* id) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION);
|
||||
@@ -218,7 +247,8 @@ MfDesfireError mf_desfire_poller_select_application(
|
||||
}
|
||||
|
||||
MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS);
|
||||
@@ -251,7 +281,8 @@ MfDesfireError mf_desfire_poller_read_file_settings(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
MfDesfireFileSettings* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS);
|
||||
@@ -276,7 +307,9 @@ MfDesfireError mf_desfire_poller_read_file_settings_multi(
|
||||
MfDesfirePoller* instance,
|
||||
const SimpleArray* file_ids,
|
||||
SimpleArray* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(file_ids);
|
||||
furi_check(data);
|
||||
|
||||
MfDesfireError error = MfDesfireErrorNone;
|
||||
|
||||
@@ -300,7 +333,8 @@ MfDesfireError mf_desfire_poller_read_file_data(
|
||||
uint32_t offset,
|
||||
size_t size,
|
||||
MfDesfireFileData* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA);
|
||||
@@ -327,7 +361,8 @@ MfDesfireError mf_desfire_poller_read_file_value(
|
||||
MfDesfirePoller* instance,
|
||||
MfDesfireFileId id,
|
||||
MfDesfireFileData* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE);
|
||||
@@ -354,7 +389,8 @@ MfDesfireError mf_desfire_poller_read_file_records(
|
||||
uint32_t offset,
|
||||
size_t size,
|
||||
MfDesfireFileData* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
bit_buffer_reset(instance->input_buffer);
|
||||
bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_RECORDS);
|
||||
@@ -382,8 +418,11 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
||||
const SimpleArray* file_ids,
|
||||
const SimpleArray* file_settings,
|
||||
SimpleArray* data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(simple_array_get_count(file_ids) == simple_array_get_count(file_settings));
|
||||
furi_check(instance);
|
||||
furi_check(file_ids);
|
||||
furi_check(file_settings);
|
||||
furi_check(data);
|
||||
furi_check(simple_array_get_count(file_ids) == simple_array_get_count(file_settings));
|
||||
|
||||
MfDesfireError error = MfDesfireErrorNone;
|
||||
|
||||
@@ -392,13 +431,25 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
||||
simple_array_init(data, file_id_count);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < file_id_count; ++i) {
|
||||
for(size_t i = 0; i < file_id_count; ++i) {
|
||||
const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i);
|
||||
const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i);
|
||||
const MfDesfireFileType file_type = file_settings_cur->type;
|
||||
|
||||
MfDesfireFileData* file_data = simple_array_get(data, i);
|
||||
|
||||
bool can_read_data = false;
|
||||
for(size_t j = 0; j < file_settings_cur->access_rights_len; j++) {
|
||||
uint8_t read_access = (file_settings_cur->access_rights[j] >> 12) & 0x0f;
|
||||
uint8_t read_write_access = (file_settings_cur->access_rights[j] >> 4) & 0x0f;
|
||||
can_read_data = (read_access == 0x0e) || (read_write_access == 0x0e);
|
||||
if(can_read_data) break;
|
||||
}
|
||||
if(!can_read_data) {
|
||||
FURI_LOG_D(TAG, "Can't read file %zu data without authentication", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) {
|
||||
error = mf_desfire_poller_read_file_data(
|
||||
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
||||
@@ -410,8 +461,6 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
||||
error = mf_desfire_poller_read_file_records(
|
||||
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
||||
}
|
||||
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
}
|
||||
|
||||
return error;
|
||||
@@ -419,29 +468,43 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
||||
|
||||
MfDesfireError
|
||||
mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfDesfireError error;
|
||||
|
||||
do {
|
||||
error = mf_desfire_poller_read_key_settings(instance, &data->key_settings);
|
||||
if(error == MfDesfireErrorAuthentication) {
|
||||
FURI_LOG_D(TAG, "Auth is required to read master key settings and app ids");
|
||||
data->key_settings.is_free_directory_list = false;
|
||||
error = MfDesfireErrorNone;
|
||||
break;
|
||||
}
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
error = mf_desfire_poller_read_key_versions(
|
||||
instance, data->key_versions, data->key_settings.max_keys);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
if(error != MfDesfireErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read key version: %d", error);
|
||||
break;
|
||||
}
|
||||
|
||||
error = mf_desfire_poller_read_file_ids(instance, data->file_ids);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
if(error != MfDesfireErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read file ids: %d", error);
|
||||
break;
|
||||
}
|
||||
|
||||
error = mf_desfire_poller_read_file_settings_multi(
|
||||
instance, data->file_ids, data->file_settings);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
if(error != MfDesfireErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read file settings: %d", error);
|
||||
break;
|
||||
}
|
||||
|
||||
error = mf_desfire_poller_read_file_data_multi(
|
||||
instance, data->file_ids, data->file_settings, data->file_data);
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
} while(false);
|
||||
|
||||
@@ -452,7 +515,8 @@ MfDesfireError mf_desfire_poller_read_applications(
|
||||
MfDesfirePoller* instance,
|
||||
const SimpleArray* app_ids,
|
||||
SimpleArray* data) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfDesfireError error = MfDesfireErrorNone;
|
||||
|
||||
@@ -461,11 +525,13 @@ MfDesfireError mf_desfire_poller_read_applications(
|
||||
simple_array_init(data, app_id_count);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < app_id_count; ++i) {
|
||||
for(size_t i = 0; i < app_id_count; ++i) {
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Selecting app %zu", i);
|
||||
error = mf_desfire_poller_select_application(instance, simple_array_cget(app_ids, i));
|
||||
if(error != MfDesfireErrorNone) break;
|
||||
|
||||
FURI_LOG_D(TAG, "Reading app %zu", i);
|
||||
MfDesfireApplication* current_app = simple_array_get(data, i);
|
||||
error = mf_desfire_poller_read_application(instance, current_app);
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ struct MfDesfirePoller {
|
||||
|
||||
MfDesfireError mf_desfire_process_error(Iso14443_4aError error);
|
||||
|
||||
MfDesfireError mf_desfire_process_status_code(uint8_t status_code);
|
||||
|
||||
const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -168,28 +168,28 @@ const NfcDeviceBase nfc_device_mf_ultralight = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)mf_ultralight_get_base_data,
|
||||
};
|
||||
|
||||
MfUltralightData* mf_ultralight_alloc() {
|
||||
MfUltralightData* mf_ultralight_alloc(void) {
|
||||
MfUltralightData* data = malloc(sizeof(MfUltralightData));
|
||||
data->iso14443_3a_data = iso14443_3a_alloc();
|
||||
return data;
|
||||
}
|
||||
|
||||
void mf_ultralight_free(MfUltralightData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3a_free(data->iso14443_3a_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void mf_ultralight_reset(MfUltralightData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso14443_3a_reset(data->iso14443_3a_data);
|
||||
}
|
||||
|
||||
void mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
|
||||
for(size_t i = 0; i < COUNT_OF(data->counter); i++) {
|
||||
@@ -222,7 +222,8 @@ static const char*
|
||||
}
|
||||
|
||||
bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(device_type);
|
||||
|
||||
bool verified = false;
|
||||
|
||||
@@ -239,7 +240,8 @@ bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type)
|
||||
}
|
||||
|
||||
bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool parsed = false;
|
||||
@@ -338,7 +340,8 @@ bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t vers
|
||||
}
|
||||
|
||||
bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool saved = false;
|
||||
@@ -419,6 +422,9 @@ bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
bool is_equal = false;
|
||||
bool data_array_is_equal = true;
|
||||
|
||||
@@ -467,20 +473,20 @@ bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData
|
||||
|
||||
const char*
|
||||
mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type) {
|
||||
furi_assert(data);
|
||||
furi_assert(data->type < MfUltralightTypeNum);
|
||||
furi_check(data);
|
||||
furi_check(data->type < MfUltralightTypeNum);
|
||||
|
||||
return mf_ultralight_get_device_name_by_type(data->type, name_type);
|
||||
}
|
||||
|
||||
const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
|
||||
}
|
||||
|
||||
bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool uid_valid = iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
|
||||
|
||||
@@ -498,13 +504,13 @@ bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t ui
|
||||
}
|
||||
|
||||
Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_3a_data;
|
||||
}
|
||||
|
||||
MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version) {
|
||||
furi_assert(version);
|
||||
furi_check(version);
|
||||
|
||||
MfUltralightType type = MfUltralightTypeUnknown;
|
||||
|
||||
@@ -538,15 +544,19 @@ MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version)
|
||||
}
|
||||
|
||||
uint16_t mf_ultralight_get_pages_total(MfUltralightType type) {
|
||||
furi_check(type < MfUltralightTypeNum);
|
||||
|
||||
return mf_ultralight_features[type].total_pages;
|
||||
}
|
||||
|
||||
uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type) {
|
||||
furi_check(type < MfUltralightTypeNum);
|
||||
|
||||
return mf_ultralight_features[type].feature_set;
|
||||
}
|
||||
|
||||
bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data) {
|
||||
furi_assert(iso14443_3a_data);
|
||||
furi_check(iso14443_3a_data);
|
||||
|
||||
bool mfu_detected = (iso14443_3a_data->atqa[0] == 0x44) &&
|
||||
(iso14443_3a_data->atqa[1] == 0x00) && (iso14443_3a_data->sak == 0x00);
|
||||
@@ -555,10 +565,14 @@ bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data) {
|
||||
}
|
||||
|
||||
uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) {
|
||||
furi_check(type < MfUltralightTypeNum);
|
||||
|
||||
return mf_ultralight_features[type].config_page;
|
||||
}
|
||||
|
||||
uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type) {
|
||||
furi_check(type < MfUltralightTypeNum);
|
||||
|
||||
uint8_t config_page = mf_ultralight_features[type].config_page;
|
||||
return (config_page != 0) ? config_page + 2 : 0;
|
||||
}
|
||||
@@ -574,8 +588,8 @@ bool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t fe
|
||||
}
|
||||
|
||||
bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config) {
|
||||
furi_assert(data);
|
||||
furi_assert(config);
|
||||
furi_check(data);
|
||||
furi_check(config);
|
||||
|
||||
bool config_pages_found = false;
|
||||
|
||||
@@ -589,7 +603,7 @@ bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightCon
|
||||
}
|
||||
|
||||
bool mf_ultralight_is_all_data_read(const MfUltralightData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
bool all_read = false;
|
||||
if(data->pages_read == data->pages_total ||
|
||||
@@ -616,7 +630,7 @@ bool mf_ultralight_is_all_data_read(const MfUltralightData* data) {
|
||||
}
|
||||
|
||||
bool mf_ultralight_is_counter_configured(const MfUltralightData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightConfigPages* config = NULL;
|
||||
bool configured = false;
|
||||
|
||||
@@ -177,7 +177,7 @@ typedef struct {
|
||||
|
||||
extern const NfcDeviceBase nfc_device_mf_ultralight;
|
||||
|
||||
MfUltralightData* mf_ultralight_alloc();
|
||||
MfUltralightData* mf_ultralight_alloc(void);
|
||||
|
||||
void mf_ultralight_free(MfUltralightData* data);
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ MfUltralightError mf_ultralight_process_error(Iso14443_3aError error) {
|
||||
MfUltralightError mf_ultralight_poller_auth_pwd(
|
||||
MfUltralightPoller* instance,
|
||||
MfUltralightPollerAuthContext* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
uint8_t auth_cmd[5] = {MF_ULTRALIGHT_CMD_PWD_AUTH}; //-V1009
|
||||
memccpy(&auth_cmd[1], data->password.data, 0, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE);
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));
|
||||
@@ -60,6 +63,8 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00};
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));
|
||||
|
||||
@@ -91,6 +96,9 @@ MfUltralightError mf_ultralight_poller_read_page_from_sector(
|
||||
uint8_t sector,
|
||||
uint8_t tag,
|
||||
MfUltralightPageReadCommandData* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -132,6 +140,9 @@ MfUltralightError mf_ultralight_poller_read_page(
|
||||
MfUltralightPoller* instance,
|
||||
uint8_t start_page,
|
||||
MfUltralightPageReadCommandData* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -162,6 +173,9 @@ MfUltralightError mf_ultralight_poller_write_page(
|
||||
MfUltralightPoller* instance,
|
||||
uint8_t page,
|
||||
const MfUltralightPage* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -193,6 +207,9 @@ MfUltralightError mf_ultralight_poller_write_page(
|
||||
|
||||
MfUltralightError
|
||||
mf_ultralight_poller_read_version(MfUltralightPoller* instance, MfUltralightVersion* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -222,6 +239,9 @@ MfUltralightError
|
||||
|
||||
MfUltralightError
|
||||
mf_ultralight_poller_read_signature(MfUltralightPoller* instance, MfUltralightSignature* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -251,6 +271,9 @@ MfUltralightError mf_ultralight_poller_read_counter(
|
||||
MfUltralightPoller* instance,
|
||||
uint8_t counter_num,
|
||||
MfUltralightCounter* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -280,6 +303,9 @@ MfUltralightError mf_ultralight_poller_read_tearing_flag(
|
||||
MfUltralightPoller* instance,
|
||||
uint8_t tearing_falg_num,
|
||||
MfUltralightTearingFlag* data) {
|
||||
furi_check(instance);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ static MfUltralightError
|
||||
|
||||
MfUltralightError
|
||||
mf_ultralight_poller_sync_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {
|
||||
.cmd_type = MfUltralightPollerCmdTypeReadPage,
|
||||
@@ -138,8 +138,8 @@ MfUltralightError
|
||||
|
||||
MfUltralightError
|
||||
mf_ultralight_poller_sync_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {
|
||||
.cmd_type = MfUltralightPollerCmdTypeWritePage,
|
||||
@@ -156,8 +156,8 @@ MfUltralightError
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_sync_read_version(Nfc* nfc, MfUltralightVersion* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {
|
||||
.cmd_type = MfUltralightPollerCmdTypeReadVersion,
|
||||
@@ -173,8 +173,8 @@ MfUltralightError mf_ultralight_poller_sync_read_version(Nfc* nfc, MfUltralightV
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_sync_read_signature(Nfc* nfc, MfUltralightSignature* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {
|
||||
.cmd_type = MfUltralightPollerCmdTypeReadSignature,
|
||||
@@ -193,8 +193,8 @@ MfUltralightError mf_ultralight_poller_sync_read_counter(
|
||||
Nfc* nfc,
|
||||
uint8_t counter_num,
|
||||
MfUltralightCounter* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {
|
||||
.cmd_type = MfUltralightPollerCmdTypeReadCounter,
|
||||
@@ -214,8 +214,8 @@ MfUltralightError mf_ultralight_poller_sync_read_tearing_flag(
|
||||
Nfc* nfc,
|
||||
uint8_t flag_num,
|
||||
MfUltralightTearingFlag* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {
|
||||
.cmd_type = MfUltralightPollerCmdTypeReadTearingFlag,
|
||||
@@ -261,8 +261,8 @@ static NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_sync_read_card(Nfc* nfc, MfUltralightData* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
MfUltralightPollerContext poller_context = {};
|
||||
poller_context.thread_id = furi_thread_get_current_id();
|
||||
|
||||
@@ -20,7 +20,7 @@ extern "C" {
|
||||
*
|
||||
* @returns pointer to the allocated instance.
|
||||
*/
|
||||
typedef NfcDeviceData* (*NfcDeviceAlloc)();
|
||||
typedef NfcDeviceData* (*NfcDeviceAlloc)(void);
|
||||
|
||||
/**
|
||||
* @brief Delete the protocol-specific NFC device data instance.
|
||||
|
||||
@@ -157,14 +157,14 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {
|
||||
};
|
||||
|
||||
NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol) {
|
||||
furi_assert(protocol < NfcProtocolNum);
|
||||
furi_check(protocol < NfcProtocolNum);
|
||||
|
||||
return nfc_protocol_nodes[protocol].parent_protocol;
|
||||
}
|
||||
|
||||
bool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol) {
|
||||
furi_assert(protocol < NfcProtocolNum);
|
||||
furi_assert(parent_protocol < NfcProtocolNum);
|
||||
furi_check(protocol < NfcProtocolNum);
|
||||
furi_check(parent_protocol < NfcProtocolNum);
|
||||
|
||||
bool parent_found = false;
|
||||
const NfcProtocolTreeNode* iter = &nfc_protocol_nodes[protocol];
|
||||
|
||||
@@ -78,8 +78,8 @@ typedef struct {
|
||||
static const SlixPasswordConfig slix_password_configs[] = {
|
||||
[SlixPasswordTypeRead] = {SLIX_PASSWORD_READ_KEY, SLIX_TYPE_FEATURE_READ, 0x00000000U},
|
||||
[SlixPasswordTypeWrite] = {SLIX_PASSWORD_WRITE_KEY, SLIX_TYPE_FEATURE_WRITE, 0x00000000U},
|
||||
[SlixPasswordTypePrivacy] = {SLIX_PASSWORD_PRIVACY_KEY, SLIX_TYPE_FEATURE_PRIVACY, 0xFFFFFFFFU},
|
||||
[SlixPasswordTypeDestroy] = {SLIX_PASSWORD_DESTROY_KEY, SLIX_TYPE_FEATURE_DESTROY, 0xFFFFFFFFU},
|
||||
[SlixPasswordTypePrivacy] = {SLIX_PASSWORD_PRIVACY_KEY, SLIX_TYPE_FEATURE_PRIVACY, 0x0F0F0F0FU},
|
||||
[SlixPasswordTypeDestroy] = {SLIX_PASSWORD_DESTROY_KEY, SLIX_TYPE_FEATURE_DESTROY, 0x0F0F0F0FU},
|
||||
[SlixPasswordTypeEasAfi] = {SLIX_PASSWORD_EAS_KEY, SLIX_TYPE_FEATURE_EAS, 0x00000000U},
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ static void slix_password_set_defaults(SlixPassword* passwords) {
|
||||
}
|
||||
}
|
||||
|
||||
SlixData* slix_alloc() {
|
||||
SlixData* slix_alloc(void) {
|
||||
SlixData* data = malloc(sizeof(SlixData));
|
||||
|
||||
data->iso15693_3_data = iso15693_3_alloc();
|
||||
@@ -99,7 +99,7 @@ SlixData* slix_alloc() {
|
||||
}
|
||||
|
||||
void slix_free(SlixData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso15693_3_free(data->iso15693_3_data);
|
||||
|
||||
@@ -107,7 +107,7 @@ void slix_free(SlixData* data) {
|
||||
}
|
||||
|
||||
void slix_reset(SlixData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
iso15693_3_reset(data->iso15693_3_data);
|
||||
slix_password_set_defaults(data->passwords);
|
||||
@@ -119,8 +119,8 @@ void slix_reset(SlixData* data) {
|
||||
}
|
||||
|
||||
void slix_copy(SlixData* data, const SlixData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data);
|
||||
|
||||
@@ -160,7 +160,8 @@ static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, Fli
|
||||
}
|
||||
|
||||
bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
@@ -238,7 +239,8 @@ static bool
|
||||
}
|
||||
|
||||
bool slix_save(const SlixData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
@@ -303,6 +305,9 @@ bool slix_save(const SlixData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool slix_is_equal(const SlixData* data, const SlixData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return iso15693_3_is_equal(data->iso15693_3_data, other->iso15693_3_data) &&
|
||||
memcmp(&data->system_info, &other->system_info, sizeof(SlixSystemInfo)) == 0 &&
|
||||
memcmp(
|
||||
@@ -313,6 +318,7 @@ bool slix_is_equal(const SlixData* data, const SlixData* other) {
|
||||
}
|
||||
|
||||
const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type) {
|
||||
furi_check(data);
|
||||
UNUSED(name_type);
|
||||
|
||||
const SlixType slix_type = slix_get_type(data);
|
||||
@@ -322,22 +328,25 @@ const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_ty
|
||||
}
|
||||
|
||||
const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len) {
|
||||
furi_check(data);
|
||||
return iso15693_3_get_uid(data->iso15693_3_data, uid_len);
|
||||
}
|
||||
|
||||
bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return iso15693_3_set_uid(data->iso15693_3_data, uid, uid_len);
|
||||
}
|
||||
|
||||
const Iso15693_3Data* slix_get_base_data(const SlixData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->iso15693_3_data;
|
||||
}
|
||||
|
||||
SlixType slix_get_type(const SlixData* data) {
|
||||
furi_check(data);
|
||||
|
||||
SlixType type = SlixTypeUnknown;
|
||||
|
||||
do {
|
||||
@@ -364,14 +373,14 @@ SlixType slix_get_type(const SlixData* data) {
|
||||
}
|
||||
|
||||
SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type) {
|
||||
furi_assert(data);
|
||||
furi_assert(password_type < SlixPasswordTypeCount);
|
||||
furi_check(data);
|
||||
furi_check(password_type < SlixPasswordTypeCount);
|
||||
|
||||
return data->passwords[password_type];
|
||||
}
|
||||
|
||||
uint16_t slix_get_counter(const SlixData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data(
|
||||
data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);
|
||||
|
||||
@@ -379,7 +388,7 @@ uint16_t slix_get_counter(const SlixData* data) {
|
||||
}
|
||||
|
||||
bool slix_is_privacy_mode(const SlixData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
return data->privacy;
|
||||
}
|
||||
@@ -388,8 +397,8 @@ bool slix_is_block_protected(
|
||||
const SlixData* data,
|
||||
SlixPasswordType password_type,
|
||||
uint8_t block_num) {
|
||||
furi_assert(data);
|
||||
furi_assert(password_type < SlixPasswordTypeCount);
|
||||
furi_check(data);
|
||||
furi_check(password_type < SlixPasswordTypeCount);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
@@ -411,7 +420,7 @@ bool slix_is_block_protected(
|
||||
}
|
||||
|
||||
bool slix_is_counter_increment_protected(const SlixData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data(
|
||||
data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM);
|
||||
@@ -420,14 +429,14 @@ bool slix_is_counter_increment_protected(const SlixData* data) {
|
||||
}
|
||||
|
||||
bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features) {
|
||||
furi_assert(slix_type < SlixTypeCount);
|
||||
furi_check(slix_type < SlixTypeCount);
|
||||
|
||||
return (slix_type_features[slix_type] & features) == features;
|
||||
}
|
||||
|
||||
bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type) {
|
||||
furi_assert(slix_type < SlixTypeCount);
|
||||
furi_assert(password_type < SlixPasswordTypeCount);
|
||||
furi_check(slix_type < SlixTypeCount);
|
||||
furi_check(password_type < SlixPasswordTypeCount);
|
||||
|
||||
return slix_type_features[slix_type] & slix_password_configs[password_type].feature_flag;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ typedef struct {
|
||||
SlixPrivacy privacy;
|
||||
} SlixData;
|
||||
|
||||
SlixData* slix_alloc();
|
||||
SlixData* slix_alloc(void);
|
||||
|
||||
void slix_free(SlixData* data);
|
||||
|
||||
|
||||
@@ -73,17 +73,62 @@ static NfcCommand slix_poller_handler_read_signature(SlixPoller* instance) {
|
||||
if(slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_SIGNATURE)) {
|
||||
instance->error = slix_poller_read_signature(instance, &instance->data->signature);
|
||||
if(instance->error == SlixErrorNone) {
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
instance->poller_state = SlixPollerStateCheckPrivacyPassword;
|
||||
} else {
|
||||
instance->poller_state = SlixPollerStateError;
|
||||
}
|
||||
} else {
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
instance->poller_state = SlixPollerStateCheckPrivacyPassword;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand slix_poller_handler_check_privacy_password(SlixPoller* instance) {
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
do {
|
||||
if(!slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_PRIVACY)) {
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
break;
|
||||
}
|
||||
if(instance->privacy_password_checked) {
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->slix_event.type = SlixPollerEventTypePrivacyUnlockRequest;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
|
||||
if(!instance->slix_event_data.privacy_password.password_set) {
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
break;
|
||||
}
|
||||
|
||||
SlixPassword pwd = instance->slix_event_data.privacy_password.password;
|
||||
FURI_LOG_I(TAG, "Trying to check privacy password: %08lX", pwd);
|
||||
|
||||
instance->error = slix_poller_get_random_number(instance, &instance->random_number);
|
||||
if(instance->error != SlixErrorNone) {
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd);
|
||||
if(instance->error != SlixErrorNone) {
|
||||
command = NfcCommandReset;
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Found privacy password");
|
||||
instance->data->passwords[SlixPasswordTypePrivacy] = pwd;
|
||||
instance->privacy_password_checked = true;
|
||||
instance->poller_state = SlixPollerStateReady;
|
||||
} while(false);
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) {
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
instance->poller_state = SlixPollerStateError;
|
||||
@@ -108,6 +153,7 @@ static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) {
|
||||
|
||||
FURI_LOG_I(TAG, "Privacy mode disabled");
|
||||
instance->data->passwords[SlixPasswordTypePrivacy] = pwd;
|
||||
instance->privacy_password_checked = true;
|
||||
instance->poller_state = SlixPollerStateIdle;
|
||||
slix_unlocked = true;
|
||||
} while(false);
|
||||
@@ -140,6 +186,7 @@ static const SlixPollerStateHandler slix_poller_state_handler[SlixPollerStateNum
|
||||
[SlixPollerStateError] = slix_poller_handler_error,
|
||||
[SlixPollerStateGetNxpSysInfo] = slix_poller_handler_get_nfc_system_info,
|
||||
[SlixPollerStateReadSignature] = slix_poller_handler_read_signature,
|
||||
[SlixPollerStateCheckPrivacyPassword] = slix_poller_handler_check_privacy_password,
|
||||
[SlixPollerStatePrivacyUnlock] = slix_poller_handler_privacy_unlock,
|
||||
[SlixPollerStateReady] = slix_poller_handler_ready,
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ typedef enum {
|
||||
SlixPollerStateIdle,
|
||||
SlixPollerStateGetNxpSysInfo,
|
||||
SlixPollerStateReadSignature,
|
||||
SlixPollerStateCheckPrivacyPassword,
|
||||
SlixPollerStatePrivacyUnlock,
|
||||
SlixPollerStateReady,
|
||||
SlixPollerStateError,
|
||||
@@ -27,6 +28,7 @@ struct SlixPoller {
|
||||
SlixPollerState poller_state;
|
||||
SlixError error;
|
||||
SlixRandomNumber random_number;
|
||||
bool privacy_password_checked;
|
||||
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
|
||||
@@ -79,35 +79,39 @@ const NfcDeviceBase nfc_device_st25tb = {
|
||||
.get_base_data = (NfcDeviceGetBaseData)st25tb_get_base_data,
|
||||
};
|
||||
|
||||
St25tbData* st25tb_alloc() {
|
||||
St25tbData* st25tb_alloc(void) {
|
||||
St25tbData* data = malloc(sizeof(St25tbData));
|
||||
return data;
|
||||
}
|
||||
|
||||
void st25tb_free(St25tbData* data) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
void st25tb_reset(St25tbData* data) {
|
||||
furi_check(data);
|
||||
memset(data, 0, sizeof(St25tbData));
|
||||
}
|
||||
|
||||
void st25tb_copy(St25tbData* data, const St25tbData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
*data = *other;
|
||||
}
|
||||
|
||||
bool st25tb_verify(St25tbData* data, const FuriString* device_type) {
|
||||
furi_check(device_type);
|
||||
UNUSED(data);
|
||||
|
||||
return furi_string_equal_str(device_type, ST25TB_PROTOCOL_NAME);
|
||||
}
|
||||
|
||||
bool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
@@ -150,7 +154,8 @@ bool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version) {
|
||||
}
|
||||
|
||||
bool st25tb_save(const St25tbData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool saved = false;
|
||||
@@ -184,19 +189,21 @@ bool st25tb_save(const St25tbData* data, FlipperFormat* ff) {
|
||||
}
|
||||
|
||||
bool st25tb_is_equal(const St25tbData* data, const St25tbData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return memcmp(data, other, sizeof(St25tbData)) == 0;
|
||||
return memcmp(data, other, sizeof(St25tbData)) == 0; //-V1103
|
||||
}
|
||||
|
||||
uint8_t st25tb_get_block_count(St25tbType type) {
|
||||
furi_check(type < St25tbTypeNum);
|
||||
|
||||
return st25tb_features[type].blocks_total;
|
||||
}
|
||||
|
||||
const char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type) {
|
||||
furi_assert(data);
|
||||
furi_assert(data->type < St25tbTypeNum);
|
||||
furi_check(data);
|
||||
furi_check(data->type < St25tbTypeNum);
|
||||
|
||||
if(name_type == NfcDeviceNameTypeFull) {
|
||||
return st25tb_features[data->type].full_name;
|
||||
@@ -206,7 +213,7 @@ const char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType nam
|
||||
}
|
||||
|
||||
const uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
|
||||
if(uid_len) {
|
||||
*uid_len = ST25TB_UID_SIZE;
|
||||
@@ -216,7 +223,8 @@ const uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len) {
|
||||
}
|
||||
|
||||
bool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_check(data);
|
||||
furi_check(uid);
|
||||
|
||||
const bool uid_valid = uid_len == ST25TB_UID_SIZE;
|
||||
|
||||
@@ -233,6 +241,8 @@ St25tbData* st25tb_get_base_data(const St25tbData* data) {
|
||||
}
|
||||
|
||||
St25tbType st25tb_get_type_from_uid(const uint8_t* uid) {
|
||||
furi_check(uid);
|
||||
|
||||
switch(uid[2] >> 2) {
|
||||
case 0x0:
|
||||
case 0x3:
|
||||
|
||||
@@ -48,7 +48,7 @@ typedef struct {
|
||||
|
||||
extern const NfcDeviceBase nfc_device_st25tb;
|
||||
|
||||
St25tbData* st25tb_alloc();
|
||||
St25tbData* st25tb_alloc(void);
|
||||
|
||||
void st25tb_free(St25tbData* data);
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@ St25tbError st25tb_poller_send_frame(
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
|
||||
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
|
||||
furi_assert(
|
||||
@@ -54,8 +56,8 @@ St25tbError st25tb_poller_send_frame(
|
||||
|
||||
St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr) {
|
||||
// Send Initiate()
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
@@ -86,8 +88,8 @@ St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr)
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
|
||||
St25tbError ret;
|
||||
|
||||
@@ -169,8 +171,9 @@ St25tbError st25tb_poller_read(St25tbPoller* instance, St25tbData* data) {
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
furi_check(uid);
|
||||
|
||||
St25tbError ret;
|
||||
|
||||
@@ -213,10 +216,10 @@ St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) {
|
||||
|
||||
St25tbError
|
||||
st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_assert(block);
|
||||
furi_assert(
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
furi_check(block);
|
||||
furi_check(
|
||||
(block_number <= st25tb_get_block_count(instance->data->type)) ||
|
||||
block_number == ST25TB_SYSTEM_OTP_BLOCK);
|
||||
FURI_LOG_T(TAG, "reading block %d", block_number);
|
||||
@@ -248,9 +251,9 @@ St25tbError
|
||||
|
||||
St25tbError
|
||||
st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_assert(
|
||||
furi_check(instance);
|
||||
furi_check(instance->nfc);
|
||||
furi_check(
|
||||
(block_number <= st25tb_get_block_count(instance->data->type)) ||
|
||||
block_number == ST25TB_SYSTEM_OTP_BLOCK);
|
||||
FURI_LOG_T(TAG, "writing block %d", block_number);
|
||||
@@ -292,7 +295,7 @@ St25tbError
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_halt(St25tbPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
@@ -109,7 +109,8 @@ static St25tbError st25tb_poller_cmd_execute(Nfc* nfc, St25tbPollerSyncContext*
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block) {
|
||||
furi_assert(block);
|
||||
furi_check(nfc);
|
||||
furi_check(block);
|
||||
St25tbPollerSyncContext poller_context = {
|
||||
.cmd_type = St25tbPollerCmdTypeReadBlock,
|
||||
.cmd_data =
|
||||
@@ -125,6 +126,7 @@ St25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t*
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block) {
|
||||
furi_check(nfc);
|
||||
St25tbPollerSyncContext poller_context = {
|
||||
.cmd_type = St25tbPollerCmdTypeWriteBlock,
|
||||
.cmd_data =
|
||||
@@ -140,7 +142,8 @@ St25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type) {
|
||||
furi_assert(type);
|
||||
furi_check(nfc);
|
||||
furi_check(type);
|
||||
St25tbPollerSyncContext poller_context = {
|
||||
.cmd_type = St25tbPollerCmdTypeDetectType,
|
||||
.cmd_data =
|
||||
@@ -185,8 +188,8 @@ static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, v
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data) {
|
||||
furi_assert(nfc);
|
||||
furi_assert(data);
|
||||
furi_check(nfc);
|
||||
furi_check(data);
|
||||
|
||||
St25tbPollerSyncContext poller_context = {
|
||||
.thread_id = furi_thread_get_current_id(),
|
||||
|
||||
Reference in New Issue
Block a user