1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 04:41:26 +04:00

[FL-3835] Ultralight C authentication with des key (#3720)

* Update api_symbols.csv
* Ultralight C 3des implementation added
* Access check for Ultralight cards is now splitted into 2 functions one for ULC card and another for common
* Ultralight C authentication command handlers added
* Update api_symbols.csv and api_symbols.csv
* Length added to ultralight encrypt function
* New structure for storing 3des key added
* Reseting of 3des_key added
* des_context init/deinit added to poller
* New poller step for ultralight c auth added
* Added ultralight c des key to application
* Renamed felica unlock scenes to more generic des auth scenes, because they are now used also for ultralight c
* Show different menus for different ultralight card types
* Update api_symbols.csv and api_symbols.csv
* Some macro defines added
* Different amount of pages will be now read for ultralight C and others
* New unit test for ultralight C
* Some comments and macro replacements
* New function added to api
* Now all data read checks mfulC separately
* Adjusted listener to handle missing 3des_key properly
* Now poller populates 3des_key after reading with auth to card data
* Nfc: rename _3des_key to tdes_key
* Bump API Symbols
* Mute PVS Warnings

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
RebornedBrain
2024-07-03 14:38:30 +03:00
committed by GitHub
parent 95658063af
commit 3224401479
22 changed files with 632 additions and 75 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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.

View File

@@ -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;

View File

@@ -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

View File

@@ -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) {