mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
Merge remote-tracking branch 'OFW/dev' into dev [ci skip]
This commit is contained in:
@@ -55,7 +55,6 @@ struct DigitalSequence {
|
||||
|
||||
uint32_t size;
|
||||
uint32_t max_size;
|
||||
uint8_t* data;
|
||||
|
||||
LL_DMA_InitTypeDef dma_config_gpio;
|
||||
LL_DMA_InitTypeDef dma_config_timer;
|
||||
@@ -64,19 +63,19 @@ struct DigitalSequence {
|
||||
DigitalSequenceRingBuffer timer_buf;
|
||||
DigitalSequenceSignalBank signals;
|
||||
DigitalSequenceState state;
|
||||
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
|
||||
furi_assert(size);
|
||||
furi_assert(gpio);
|
||||
|
||||
DigitalSequence* sequence = malloc(sizeof(DigitalSequence));
|
||||
DigitalSequence* sequence = malloc(sizeof(DigitalSequence) + size);
|
||||
|
||||
sequence->gpio = gpio;
|
||||
sequence->max_size = size;
|
||||
|
||||
sequence->data = malloc(sequence->max_size);
|
||||
|
||||
sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR;
|
||||
sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buf;
|
||||
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
@@ -107,7 +106,6 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
|
||||
void digital_sequence_free(DigitalSequence* sequence) {
|
||||
furi_assert(sequence);
|
||||
|
||||
free(sequence->data);
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
#define TAG "DigitalSignal"
|
||||
|
||||
DigitalSignal* digital_signal_alloc(uint32_t max_size) {
|
||||
DigitalSignal* signal = malloc(sizeof(DigitalSignal));
|
||||
DigitalSignal* signal = malloc(sizeof(DigitalSignal) + (max_size * sizeof(uint32_t)));
|
||||
|
||||
signal->max_size = max_size;
|
||||
signal->data = malloc(max_size * sizeof(uint32_t));
|
||||
|
||||
return signal;
|
||||
}
|
||||
@@ -17,7 +16,6 @@ DigitalSignal* digital_signal_alloc(uint32_t max_size) {
|
||||
void digital_signal_free(DigitalSignal* signal) {
|
||||
furi_check(signal);
|
||||
|
||||
free(signal->data);
|
||||
free(signal);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,6 @@ struct DigitalSignal {
|
||||
bool start_level; /**< The level to begin the signal with. */
|
||||
uint32_t size; /**< Current period count contained in the instance. */
|
||||
uint32_t max_size; /**< Maximum period count this instance can hold. */
|
||||
uint32_t* data; /**< Pointer to the array of time periods. */
|
||||
int32_t remainder; /**< Remainder left after converting all periods into timer ticks. */
|
||||
uint32_t data[]; /**< The array of time periods. */
|
||||
};
|
||||
|
||||
@@ -624,15 +624,19 @@ bool mf_ultralight_is_all_data_read(const MfUltralightData* data) {
|
||||
furi_check(data);
|
||||
|
||||
bool all_read = false;
|
||||
if(data->pages_read == data->pages_total ||
|
||||
(data->type == MfUltralightTypeMfulC && data->pages_read == data->pages_total - 4)) {
|
||||
// Having read all the pages doesn't mean that we've got everything.
|
||||
// By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000,
|
||||
// so a default read on an auth-supported NTAG is never complete.
|
||||
|
||||
if(data->pages_read == data->pages_total) {
|
||||
uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type);
|
||||
if(!mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportPasswordAuth)) {
|
||||
if((data->type == MfUltralightTypeMfulC) &&
|
||||
mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
||||
all_read = true;
|
||||
} else if(!mf_ultralight_support_feature(
|
||||
feature_set, MfUltralightFeatureSupportPasswordAuth)) {
|
||||
all_read = true;
|
||||
} else {
|
||||
// Having read all the pages doesn't mean that we've got everything.
|
||||
// By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000,
|
||||
// so a default read on an auth-supported NTAG is never complete.
|
||||
MfUltralightConfigPages* config = NULL;
|
||||
if(mf_ultralight_get_config_page(data, &config)) {
|
||||
uint32_t pass = bit_lib_bytes_to_num_be(
|
||||
@@ -669,3 +673,61 @@ bool mf_ultralight_is_counter_configured(const MfUltralightData* data) {
|
||||
|
||||
return configured;
|
||||
}
|
||||
|
||||
void mf_ultralight_3des_shift_data(uint8_t* const data) {
|
||||
furi_check(data);
|
||||
|
||||
uint8_t buf = data[0];
|
||||
for(uint8_t i = 1; i < MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE; i++) {
|
||||
data[i - 1] = data[i];
|
||||
}
|
||||
data[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE - 1] = buf;
|
||||
}
|
||||
|
||||
bool mf_ultralight_3des_key_valid(const MfUltralightData* data) {
|
||||
furi_check(data);
|
||||
furi_check(data->type == MfUltralightTypeMfulC);
|
||||
|
||||
return !(data->pages_read == data->pages_total - 4);
|
||||
}
|
||||
|
||||
const uint8_t* mf_ultralight_3des_get_key(const MfUltralightData* data) {
|
||||
furi_check(data);
|
||||
furi_check(data->type == MfUltralightTypeMfulC);
|
||||
|
||||
return data->page[44].data;
|
||||
}
|
||||
|
||||
void mf_ultralight_3des_encrypt(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* ck,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* input,
|
||||
const uint8_t length,
|
||||
uint8_t* out) {
|
||||
furi_check(ctx);
|
||||
furi_check(ck);
|
||||
furi_check(iv);
|
||||
furi_check(input);
|
||||
furi_check(out);
|
||||
|
||||
mbedtls_des3_set2key_enc(ctx, ck);
|
||||
mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, length, (uint8_t*)iv, input, out);
|
||||
}
|
||||
|
||||
void mf_ultralight_3des_decrypt(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* ck,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* input,
|
||||
const uint8_t length,
|
||||
uint8_t* out) {
|
||||
furi_check(ctx);
|
||||
furi_check(ck);
|
||||
furi_check(iv);
|
||||
furi_check(input);
|
||||
furi_check(out);
|
||||
|
||||
mbedtls_des3_set2key_dec(ctx, ck);
|
||||
mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_DECRYPT, length, (uint8_t*)iv, input, out);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <mbedtls/include/mbedtls/des.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -37,7 +38,15 @@ extern "C" {
|
||||
#define MF_ULTRALIGHT_TEARING_FLAG_NUM (3)
|
||||
#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4)
|
||||
#define MF_ULTRALIGHT_AUTH_PACK_SIZE (2)
|
||||
#define MF_ULTRALIGHT_AUTH_RESPONSE_SIZE (9)
|
||||
|
||||
#define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE (9)
|
||||
#define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE (16)
|
||||
#define MF_ULTRALIGHT_C_AUTH_DATA_SIZE (MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE)
|
||||
#define MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE (8)
|
||||
#define MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE (8)
|
||||
#define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0)
|
||||
#define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8)
|
||||
#define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1)
|
||||
|
||||
typedef enum {
|
||||
MfUltralightErrorNone,
|
||||
@@ -119,6 +128,10 @@ typedef struct {
|
||||
uint8_t data[MF_ULTRALIGHT_AUTH_PASSWORD_SIZE];
|
||||
} MfUltralightAuthPassword;
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE];
|
||||
} MfUltralightC3DesAuthKey;
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[MF_ULTRALIGHT_AUTH_PACK_SIZE];
|
||||
} MfUltralightAuthPack;
|
||||
@@ -226,6 +239,28 @@ bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data);
|
||||
|
||||
bool mf_ultralight_is_counter_configured(const MfUltralightData* data);
|
||||
|
||||
void mf_ultralight_3des_shift_data(uint8_t* const arr);
|
||||
|
||||
bool mf_ultralight_3des_key_valid(const MfUltralightData* data);
|
||||
|
||||
const uint8_t* mf_ultralight_3des_get_key(const MfUltralightData* data);
|
||||
|
||||
void mf_ultralight_3des_encrypt(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* ck,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* input,
|
||||
const uint8_t length,
|
||||
uint8_t* out);
|
||||
|
||||
void mf_ultralight_3des_decrypt(
|
||||
mbedtls_des3_context* ctx,
|
||||
const uint8_t* ck,
|
||||
const uint8_t* iv,
|
||||
const uint8_t* input,
|
||||
const uint8_t length,
|
||||
uint8_t* out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,16 +4,12 @@
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "MfUltralightListener"
|
||||
|
||||
#define MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE (256)
|
||||
|
||||
typedef enum {
|
||||
MfUltralightListenerAccessTypeRead,
|
||||
MfUltralightListenerAccessTypeWrite,
|
||||
} MfUltralightListenerAccessType;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
size_t cmd_len_bits;
|
||||
@@ -24,31 +20,15 @@ static bool mf_ultralight_listener_check_access(
|
||||
MfUltralightListener* instance,
|
||||
uint16_t start_page,
|
||||
MfUltralightListenerAccessType access_type) {
|
||||
bool access_success = false;
|
||||
bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite);
|
||||
|
||||
do {
|
||||
if(!mf_ultralight_support_feature(
|
||||
instance->features, MfUltralightFeatureSupportPasswordAuth)) {
|
||||
access_success = true;
|
||||
break;
|
||||
}
|
||||
if(instance->auth_state != MfUltralightListenerAuthStateSuccess) {
|
||||
if((instance->config->auth0 <= start_page) &&
|
||||
(instance->config->access.prot || is_write_op)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(instance->config->access.cfglck && is_write_op) {
|
||||
uint16_t config_page_start = instance->data->pages_total - 4;
|
||||
if((start_page == config_page_start) || (start_page == config_page_start + 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
access_success = true;
|
||||
} while(false);
|
||||
bool access_success = true;
|
||||
|
||||
if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportAuthenticate)) {
|
||||
access_success = mf_ultralight_c_check_access(
|
||||
instance->data, start_page, access_type, instance->auth_state);
|
||||
} else if(mf_ultralight_support_feature(
|
||||
instance->features, MfUltralightFeatureSupportPasswordAuth)) {
|
||||
access_success = mf_ultralight_common_check_access(instance, start_page, access_type);
|
||||
}
|
||||
return access_success;
|
||||
}
|
||||
|
||||
@@ -565,6 +545,82 @@ static MfUltralightCommand
|
||||
return command;
|
||||
}
|
||||
|
||||
static MfUltralightCommand
|
||||
mf_ultralight_c_authenticate_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) {
|
||||
MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;
|
||||
FURI_LOG_T(TAG, "CMD_ULC_AUTH_2");
|
||||
UNUSED(instance);
|
||||
do {
|
||||
if(bit_buffer_get_byte(buffer, 0) != 0xAF ||
|
||||
bit_buffer_get_size_bytes(buffer) != MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE ||
|
||||
!mf_ultralight_3des_key_valid(instance->data))
|
||||
break;
|
||||
|
||||
const uint8_t* data = bit_buffer_get_data(buffer) + 1;
|
||||
const uint8_t* iv = data + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||
|
||||
uint8_t out[MF_ULTRALIGHT_C_AUTH_DATA_SIZE] = {0};
|
||||
|
||||
const uint8_t* ck = mf_ultralight_3des_get_key(instance->data);
|
||||
mf_ultralight_3des_decrypt(
|
||||
&instance->des_context, ck, instance->encB, data, sizeof(out), out);
|
||||
|
||||
uint8_t* rndA = out;
|
||||
const uint8_t* decoded_shifted_rndB = out + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||
|
||||
mf_ultralight_3des_shift_data(rndA);
|
||||
mf_ultralight_3des_shift_data(instance->rndB);
|
||||
if(memcmp(decoded_shifted_rndB, instance->rndB, sizeof(instance->rndB)) == 0) {
|
||||
instance->auth_state = MfUltralightListenerAuthStateSuccess;
|
||||
}
|
||||
|
||||
mf_ultralight_3des_encrypt(
|
||||
&instance->des_context, ck, iv, rndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE, rndA);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x00);
|
||||
bit_buffer_append_bytes(instance->tx_buffer, rndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE);
|
||||
|
||||
iso14443_3a_listener_send_standard_frame(
|
||||
instance->iso14443_3a_listener, instance->tx_buffer);
|
||||
|
||||
command = MfUltralightCommandProcessed;
|
||||
} while(false);
|
||||
return command;
|
||||
}
|
||||
|
||||
static MfUltralightCommand
|
||||
mf_ultralight_c_authenticate_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) {
|
||||
MfUltralightCommand command = MfUltralightCommandNotProcessedNAK;
|
||||
FURI_LOG_T(TAG, "CMD_ULC_AUTH_1");
|
||||
do {
|
||||
if(!mf_ultralight_support_feature(
|
||||
instance->features, MfUltralightFeatureSupportAuthenticate) &&
|
||||
bit_buffer_get_byte(buffer, 1) == 0x00)
|
||||
break;
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0xAF);
|
||||
|
||||
furi_hal_random_fill_buf(instance->rndB, sizeof(instance->rndB));
|
||||
|
||||
const uint8_t iv[MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE] = {0};
|
||||
const uint8_t* ck = mf_ultralight_3des_get_key(instance->data);
|
||||
|
||||
mf_ultralight_3des_encrypt(
|
||||
&instance->des_context, ck, iv, instance->rndB, sizeof(instance->rndB), instance->encB);
|
||||
|
||||
bit_buffer_append_bytes(instance->tx_buffer, instance->encB, sizeof(instance->encB));
|
||||
|
||||
iso14443_3a_listener_send_standard_frame(
|
||||
instance->iso14443_3a_listener, instance->tx_buffer);
|
||||
command = MfUltralightCommandProcessed;
|
||||
mf_ultralight_composite_command_set_next(
|
||||
instance, mf_ultralight_c_authenticate_handler_p2);
|
||||
} while(false);
|
||||
return command;
|
||||
}
|
||||
|
||||
static const MfUltralightListenerCmdHandler mf_ultralight_command[] = {
|
||||
{
|
||||
.cmd = MF_ULTRALIGHT_CMD_READ_PAGE,
|
||||
@@ -631,7 +687,11 @@ static const MfUltralightListenerCmdHandler mf_ultralight_command[] = {
|
||||
.cmd_len_bits = 21 * 8,
|
||||
.callback = mf_ultralight_listener_vcsl_handler,
|
||||
},
|
||||
};
|
||||
{
|
||||
.cmd = MF_ULTRALIGHT_CMD_AUTH,
|
||||
.cmd_len_bits = 2 * 8,
|
||||
.callback = mf_ultralight_c_authenticate_handler_p1,
|
||||
}};
|
||||
|
||||
static void mf_ultralight_listener_prepare_emulation(MfUltralightListener* instance) {
|
||||
MfUltralightData* data = instance->data;
|
||||
@@ -695,6 +755,7 @@ MfUltralightListener* mf_ultralight_listener_alloc(
|
||||
instance->generic_event.protocol = NfcProtocolMfUltralight;
|
||||
instance->generic_event.instance = instance;
|
||||
instance->generic_event.event_data = &instance->mfu_event;
|
||||
mbedtls_des3_init(&instance->des_context);
|
||||
|
||||
return instance;
|
||||
}
|
||||
@@ -706,6 +767,7 @@ void mf_ultralight_listener_free(MfUltralightListener* instance) {
|
||||
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
furi_string_free(instance->mirror.ascii_mirror_data);
|
||||
mbedtls_des3_free(&instance->des_context);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
|
||||
@@ -576,4 +576,61 @@ bool mf_ultralight_auth_check_password(
|
||||
const MfUltralightAuthPassword* config_pass,
|
||||
const MfUltralightAuthPassword* auth_pass) {
|
||||
return memcmp(config_pass->data, auth_pass->data, sizeof(MfUltralightAuthPassword)) == 0;
|
||||
}
|
||||
|
||||
bool mf_ultralight_common_check_access(
|
||||
const MfUltralightListener* instance,
|
||||
const uint16_t start_page,
|
||||
const MfUltralightListenerAccessType access_type) {
|
||||
bool access_success = false;
|
||||
bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite);
|
||||
|
||||
do {
|
||||
if(instance->auth_state != MfUltralightListenerAuthStateSuccess) {
|
||||
if((instance->config->auth0 <= start_page) &&
|
||||
(instance->config->access.prot || is_write_op)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(instance->config->access.cfglck && is_write_op) {
|
||||
uint16_t config_page_start = instance->data->pages_total - 4;
|
||||
if((start_page == config_page_start) || (start_page == config_page_start + 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
access_success = true;
|
||||
} while(false);
|
||||
|
||||
return access_success;
|
||||
}
|
||||
|
||||
bool mf_ultralight_c_check_access(
|
||||
const MfUltralightData* data,
|
||||
const uint16_t start_page,
|
||||
const MfUltralightListenerAccessType access_type,
|
||||
const MfUltralightListenerAuthState auth_state) {
|
||||
bool access_success = false;
|
||||
bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite);
|
||||
|
||||
do {
|
||||
if(start_page >= 44) break;
|
||||
|
||||
const uint8_t auth0 = data->page[42].data[0];
|
||||
const uint8_t auth1 = data->page[43].data[0] & 0x01;
|
||||
|
||||
if(auth0 < 0x03 || auth0 >= 0x30 || auth_state == MfUltralightListenerAuthStateSuccess) {
|
||||
access_success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if((auth0 <= start_page) && ((auth1 == 0) || (auth1 == 1 || is_write_op))) { //-V560
|
||||
break;
|
||||
}
|
||||
|
||||
access_success = true;
|
||||
} while(false);
|
||||
|
||||
return access_success;
|
||||
}
|
||||
@@ -13,6 +13,11 @@ typedef enum {
|
||||
MfUltralightListenerAuthStateSuccess,
|
||||
} MfUltralightListenerAuthState;
|
||||
|
||||
typedef enum {
|
||||
MfUltralightListenerAccessTypeRead,
|
||||
MfUltralightListenerAccessTypeWrite,
|
||||
} MfUltralightListenerAccessType;
|
||||
|
||||
typedef enum {
|
||||
MfUltralightCommandNotFound,
|
||||
MfUltralightCommandProcessed,
|
||||
@@ -63,6 +68,9 @@ struct MfUltralightListener {
|
||||
bool single_counter_increased;
|
||||
MfUltralightMirrorMode mirror;
|
||||
MfUltralightListenerCompositeCommandContext composite_cmd;
|
||||
mbedtls_des3_context des_context;
|
||||
uint8_t rndB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE];
|
||||
uint8_t encB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE];
|
||||
void* context;
|
||||
};
|
||||
|
||||
@@ -118,6 +126,17 @@ bool mf_ultralight_auth_limit_check_and_update(MfUltralightListener* instance, b
|
||||
bool mf_ultralight_auth_check_password(
|
||||
const MfUltralightAuthPassword* config_pass,
|
||||
const MfUltralightAuthPassword* auth_pass);
|
||||
|
||||
bool mf_ultralight_common_check_access(
|
||||
const MfUltralightListener* instance,
|
||||
const uint16_t start_page,
|
||||
const MfUltralightListenerAccessType access_type);
|
||||
|
||||
bool mf_ultralight_c_check_access(
|
||||
const MfUltralightData* data,
|
||||
const uint16_t start_page,
|
||||
const MfUltralightListenerAccessType access_type,
|
||||
const MfUltralightListenerAuthState auth_state);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "MfUltralightPoller"
|
||||
|
||||
@@ -180,7 +181,7 @@ MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_po
|
||||
instance->general_event.protocol = NfcProtocolMfUltralight;
|
||||
instance->general_event.event_data = &instance->mfu_event;
|
||||
instance->general_event.instance = instance;
|
||||
|
||||
mbedtls_des3_init(&instance->des_context);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -193,6 +194,7 @@ void mf_ultralight_poller_free(MfUltralightPoller* instance) {
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
bit_buffer_free(instance->rx_buffer);
|
||||
mf_ultralight_free(instance->data);
|
||||
mbedtls_des3_free(&instance->des_context);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
@@ -258,7 +260,7 @@ static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller*
|
||||
}
|
||||
|
||||
static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPoller* instance) {
|
||||
instance->error = mf_ultralight_poller_authenticate(instance);
|
||||
instance->error = mf_ultralight_poller_authentication_test(instance);
|
||||
if(instance->error == MfUltralightErrorNone) {
|
||||
FURI_LOG_D(TAG, "Ultralight C detected");
|
||||
instance->data->type = MfUltralightTypeMfulC;
|
||||
@@ -315,6 +317,10 @@ static NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Skip reading signature");
|
||||
if(mf_ultralight_support_feature(
|
||||
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
||||
next_state = MfUltralightPollerStateAuthMfulC;
|
||||
}
|
||||
}
|
||||
instance->state = next_state;
|
||||
|
||||
@@ -436,6 +442,50 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) {
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
FURI_LOG_D(TAG, "MfulC auth");
|
||||
if(mf_ultralight_support_feature(
|
||||
instance->feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
||||
instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest;
|
||||
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
if(!instance->mfu_event.data->auth_context.skip_auth) {
|
||||
FURI_LOG_D(TAG, "Trying to authenticate with 3des key");
|
||||
instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key;
|
||||
do {
|
||||
uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE];
|
||||
uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||
furi_hal_random_fill_buf(RndA, sizeof(RndA));
|
||||
instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output);
|
||||
if(instance->error != MfUltralightErrorNone) break;
|
||||
|
||||
uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||
const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||
instance->error = mf_ultralight_poller_authenticate_end(
|
||||
instance, RndB, output, decoded_shifted_RndA);
|
||||
if(instance->error != MfUltralightErrorNone) break;
|
||||
|
||||
mf_ultralight_3des_shift_data(RndA);
|
||||
instance->auth_context.auth_success =
|
||||
(memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0);
|
||||
|
||||
if(instance->auth_context.auth_success) {
|
||||
FURI_LOG_D(TAG, "Auth success");
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) {
|
||||
FURI_LOG_D(TAG, "Auth failed");
|
||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||
}
|
||||
}
|
||||
}
|
||||
instance->state = MfUltralightPollerStateReadPages;
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) {
|
||||
MfUltralightPageReadCommandData data = {};
|
||||
uint16_t start_page = instance->pages_read;
|
||||
@@ -455,8 +505,9 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
||||
instance->error = mf_ultralight_poller_read_page(instance, start_page, &data);
|
||||
}
|
||||
|
||||
const uint8_t read_cnt = instance->data->type == MfUltralightTypeMfulC ? 1 : 4;
|
||||
if(instance->error == MfUltralightErrorNone) {
|
||||
for(size_t i = 0; i < 4; i++) {
|
||||
for(size_t i = 0; i < read_cnt; i++) {
|
||||
if(start_page + i < instance->pages_total) {
|
||||
FURI_LOG_D(TAG, "Read page %d success", start_page + i);
|
||||
instance->data->page[start_page + i] = data.page[i];
|
||||
@@ -468,11 +519,16 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
||||
instance->state = MfUltralightPollerStateReadCounters;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read);
|
||||
if(instance->pages_read) {
|
||||
instance->state = MfUltralightPollerStateReadCounters;
|
||||
if(instance->data->type == MfUltralightTypeMfulC &&
|
||||
!mf_ultralight_3des_key_valid(instance->data)) {
|
||||
instance->state = MfUltralightPollerStateCheckMfulCAuthStatus;
|
||||
} else {
|
||||
instance->state = MfUltralightPollerStateReadFailed;
|
||||
FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read);
|
||||
if(instance->pages_read) {
|
||||
instance->state = MfUltralightPollerStateReadCounters;
|
||||
} else {
|
||||
instance->state = MfUltralightPollerStateReadFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,6 +580,31 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand
|
||||
mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) {
|
||||
instance->state = MfUltralightPollerStateReadSuccess;
|
||||
|
||||
do {
|
||||
if(!mf_ultralight_support_feature(
|
||||
instance->feature_set, MfUltralightFeatureSupportAuthenticate))
|
||||
break;
|
||||
|
||||
if(!instance->auth_context.auth_success) {
|
||||
FURI_LOG_D(TAG, "Skip 3des key populating");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(
|
||||
&instance->data->page[44],
|
||||
instance->auth_context.tdes_key.data,
|
||||
MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);
|
||||
instance->data->pages_read = instance->pages_total;
|
||||
instance->pages_read = instance->pages_total;
|
||||
} while(false);
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read Failed");
|
||||
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
|
||||
@@ -663,6 +744,9 @@ static const MfUltralightPollerReadHandler
|
||||
mf_ultralight_poller_handler_read_tearing_flags,
|
||||
[MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth,
|
||||
[MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass,
|
||||
[MfUltralightPollerStateCheckMfulCAuthStatus] =
|
||||
mf_ultralight_poller_handler_check_mfuc_auth_status,
|
||||
[MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c,
|
||||
[MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages,
|
||||
[MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail,
|
||||
[MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success,
|
||||
|
||||
@@ -42,6 +42,7 @@ typedef enum {
|
||||
*/
|
||||
typedef struct {
|
||||
MfUltralightAuthPassword password; /**< Password to be used for authentication. */
|
||||
MfUltralightC3DesAuthKey tdes_key;
|
||||
MfUltralightAuthPack pack; /**< Pack received on successfull authentication. */
|
||||
bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */
|
||||
bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */
|
||||
@@ -85,12 +86,33 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
|
||||
*
|
||||
* Must ONLY be used inside the callback function.
|
||||
*
|
||||
* This function now is used only to identify Mf Ultralight C cards.
|
||||
* This function is used to start authentication process for Ultralight C cards.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] RndA Randomly generated block which is required for authentication process.
|
||||
* @param[out] output Authentication encryption result.
|
||||
* @return MfUltralightErrorNone if card supports authentication command, an error code on otherwise.
|
||||
*/
|
||||
MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance);
|
||||
MfUltralightError mf_ultralight_poller_authenticate_start(
|
||||
MfUltralightPoller* instance,
|
||||
const uint8_t* RndA,
|
||||
uint8_t* output);
|
||||
|
||||
/**
|
||||
* @brief End authentication procedure
|
||||
*
|
||||
* This function is used to end authentication process for Ultralight C cards.
|
||||
*
|
||||
* @param[in, out] instance pointer to the instance to be used in the transaction.
|
||||
* @param[in] RndB Block received from the card (card generates it randomly) which is required for authentication process.
|
||||
* @param[in] request Contains data of RndA + RndB', where RndB' is decoded and shifted RndB received from the card on previous step.
|
||||
* @param[out] response Must return RndA' which an encrypted shifted RndA value received from the card and decrypted by this function.
|
||||
*/
|
||||
MfUltralightError mf_ultralight_poller_authenticate_end(
|
||||
MfUltralightPoller* instance,
|
||||
const uint8_t* RndB,
|
||||
const uint8_t* request,
|
||||
uint8_t* response);
|
||||
|
||||
/**
|
||||
* @brief Read page from card.
|
||||
|
||||
@@ -62,11 +62,17 @@ MfUltralightError mf_ultralight_poller_auth_pwd(
|
||||
return ret;
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance) {
|
||||
static MfUltralightError mf_ultralight_poller_send_authenticate_cmd(
|
||||
MfUltralightPoller* instance,
|
||||
const uint8_t* cmd,
|
||||
const uint8_t length,
|
||||
const bool initial_cmd,
|
||||
uint8_t* response) {
|
||||
furi_check(instance);
|
||||
furi_check(cmd);
|
||||
furi_check(response);
|
||||
|
||||
uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00};
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, cmd, length);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
@@ -80,12 +86,104 @@ MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance
|
||||
ret = mf_ultralight_process_error(error);
|
||||
break;
|
||||
}
|
||||
if((bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_AUTH_RESPONSE_SIZE) &&
|
||||
(bit_buffer_get_byte(instance->rx_buffer, 0) != 0xAF)) {
|
||||
|
||||
const uint8_t expected_response_code = initial_cmd ? 0xAF : 0x00;
|
||||
if((bit_buffer_get_byte(instance->rx_buffer, 0) != expected_response_code) ||
|
||||
(bit_buffer_get_size_bytes(instance->rx_buffer) !=
|
||||
MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE)) {
|
||||
ret = MfUltralightErrorAuth;
|
||||
break;
|
||||
}
|
||||
//Save encrypted PICC random number RndB here if needed
|
||||
|
||||
memcpy(
|
||||
response,
|
||||
bit_buffer_get_data(instance->rx_buffer) + 1,
|
||||
MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_authentication_test(MfUltralightPoller* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00};
|
||||
uint8_t dummy[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE];
|
||||
return mf_ultralight_poller_send_authenticate_cmd(
|
||||
instance, auth_cmd, sizeof(auth_cmd), true, dummy);
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_authenticate_start(
|
||||
MfUltralightPoller* instance,
|
||||
const uint8_t* RndA,
|
||||
uint8_t* output) {
|
||||
furi_check(instance);
|
||||
furi_check(RndA);
|
||||
furi_check(output);
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
do {
|
||||
uint8_t encRndB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0};
|
||||
uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00};
|
||||
ret = mf_ultralight_poller_send_authenticate_cmd(
|
||||
instance, auth_cmd, sizeof(auth_cmd), true, encRndB /* instance->encRndB */);
|
||||
|
||||
if(ret != MfUltralightErrorNone) break;
|
||||
|
||||
uint8_t iv[MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE] = {0};
|
||||
uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET;
|
||||
mf_ultralight_3des_decrypt(
|
||||
&instance->des_context,
|
||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
||||
iv,
|
||||
encRndB,
|
||||
sizeof(encRndB),
|
||||
RndB);
|
||||
mf_ultralight_3des_shift_data(RndB);
|
||||
|
||||
memcpy(output, RndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE);
|
||||
|
||||
mf_ultralight_3des_encrypt(
|
||||
&instance->des_context,
|
||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
||||
encRndB,
|
||||
output,
|
||||
MF_ULTRALIGHT_C_AUTH_DATA_SIZE,
|
||||
output);
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MfUltralightError mf_ultralight_poller_authenticate_end(
|
||||
MfUltralightPoller* instance,
|
||||
const uint8_t* RndB,
|
||||
const uint8_t* request,
|
||||
uint8_t* response) {
|
||||
furi_check(instance);
|
||||
furi_check(RndB);
|
||||
furi_check(request);
|
||||
furi_check(response);
|
||||
|
||||
uint8_t auth_cmd[MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE] = {0xAF}; //-V1009
|
||||
memcpy(&auth_cmd[1], request, MF_ULTRALIGHT_C_AUTH_DATA_SIZE);
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd));
|
||||
|
||||
MfUltralightError ret = MfUltralightErrorNone;
|
||||
do {
|
||||
ret = mf_ultralight_poller_send_authenticate_cmd(
|
||||
instance, auth_cmd, sizeof(auth_cmd), false, response);
|
||||
|
||||
if(ret != MfUltralightErrorNone) break;
|
||||
|
||||
mf_ultralight_3des_decrypt(
|
||||
&instance->des_context,
|
||||
instance->mfu_event.data->auth_context.tdes_key.data,
|
||||
RndB,
|
||||
bit_buffer_get_data(instance->rx_buffer) + 1,
|
||||
MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE,
|
||||
response);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -58,8 +58,10 @@ typedef enum {
|
||||
MfUltralightPollerStateReadCounters,
|
||||
MfUltralightPollerStateReadTearingFlags,
|
||||
MfUltralightPollerStateAuth,
|
||||
MfUltralightPollerStateAuthMfulC,
|
||||
MfUltralightPollerStateReadPages,
|
||||
MfUltralightPollerStateTryDefaultPass,
|
||||
MfUltralightPollerStateCheckMfulCAuthStatus,
|
||||
MfUltralightPollerStateReadFailed,
|
||||
MfUltralightPollerStateReadSuccess,
|
||||
MfUltralightPollerStateRequestWriteData,
|
||||
@@ -87,6 +89,7 @@ struct MfUltralightPoller {
|
||||
uint8_t tearing_flag_total;
|
||||
uint16_t current_page;
|
||||
MfUltralightError error;
|
||||
mbedtls_des3_context des_context;
|
||||
|
||||
NfcGenericEvent general_event;
|
||||
MfUltralightPollerEvent mfu_event;
|
||||
@@ -110,6 +113,8 @@ bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag(
|
||||
uint8_t* tag,
|
||||
uint8_t* pages_left);
|
||||
|
||||
MfUltralightError mf_ultralight_poller_authentication_test(MfUltralightPoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -251,6 +251,12 @@ static NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void
|
||||
command = NfcCommandStop;
|
||||
} else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {
|
||||
mfu_event->data->auth_context.skip_auth = true;
|
||||
if(mf_ultralight_support_feature(
|
||||
mfu_poller->feature_set, MfUltralightFeatureSupportAuthenticate)) {
|
||||
mfu_event->data->auth_context.skip_auth = false;
|
||||
memset(
|
||||
mfu_poller->auth_context.tdes_key.data, 0x00, MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if(command == NfcCommandStop) {
|
||||
|
||||
Submodule lib/stm32wb_copro updated: 64a060d91f...133182d558
@@ -12,8 +12,8 @@ struct BufferStream {
|
||||
FuriStreamBuffer* stream;
|
||||
|
||||
size_t index;
|
||||
Buffer* buffers;
|
||||
size_t max_buffers_count;
|
||||
Buffer buffers[];
|
||||
};
|
||||
|
||||
bool buffer_write(Buffer* buffer, const uint8_t* data, size_t size) {
|
||||
@@ -44,9 +44,8 @@ void buffer_reset(Buffer* buffer) {
|
||||
BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count) {
|
||||
furi_assert(buffer_size > 0);
|
||||
furi_assert(buffers_count > 0);
|
||||
BufferStream* buffer_stream = malloc(sizeof(BufferStream));
|
||||
BufferStream* buffer_stream = malloc(sizeof(BufferStream) + (sizeof(Buffer) * buffers_count));
|
||||
buffer_stream->max_buffers_count = buffers_count;
|
||||
buffer_stream->buffers = malloc(sizeof(Buffer) * buffer_stream->max_buffers_count);
|
||||
for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) {
|
||||
buffer_stream->buffers[i].occupied = false;
|
||||
buffer_stream->buffers[i].size = 0;
|
||||
@@ -66,7 +65,6 @@ void buffer_stream_free(BufferStream* buffer_stream) {
|
||||
free(buffer_stream->buffers[i].data);
|
||||
}
|
||||
furi_stream_buffer_free(buffer_stream->stream);
|
||||
free(buffer_stream->buffers);
|
||||
free(buffer_stream);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
struct ProtocolDict {
|
||||
const ProtocolBase** base;
|
||||
size_t count;
|
||||
void** data;
|
||||
void* data[];
|
||||
};
|
||||
|
||||
ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t count) {
|
||||
furi_check(protocols);
|
||||
|
||||
ProtocolDict* dict = malloc(sizeof(ProtocolDict));
|
||||
ProtocolDict* dict = malloc(sizeof(ProtocolDict) + (sizeof(void*) * count));
|
||||
dict->base = protocols;
|
||||
dict->count = count;
|
||||
dict->data = malloc(sizeof(void*) * dict->count);
|
||||
|
||||
for(size_t i = 0; i < dict->count; i++) {
|
||||
dict->data[i] = dict->base[i]->alloc();
|
||||
@@ -29,7 +28,6 @@ void protocol_dict_free(ProtocolDict* dict) {
|
||||
dict->base[i]->free(dict->data[i]);
|
||||
}
|
||||
|
||||
free(dict->data);
|
||||
free(dict);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user