mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 05:14:16 +03:00
Merge remote-tracking branch 'OFW/dev' into dev [ci skip]
This commit is contained in:
commit
842922f018
@ -0,0 +1,71 @@
|
||||
Filetype: Flipper NFC device
|
||||
Version: 4
|
||||
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB
|
||||
Device type: NTAG/Ultralight
|
||||
# UID is common for all formats
|
||||
UID: 04 BA FF CA 4D 5D 80
|
||||
# ISO14443-3A specific data
|
||||
ATQA: 00 44
|
||||
SAK: 00
|
||||
# NTAG/Ultralight specific data
|
||||
Data format version: 2
|
||||
NTAG/Ultralight type: Mifare Ultralight C
|
||||
Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
Mifare version: 00 00 00 00 00 00 00 00
|
||||
Counter 0: 0
|
||||
Tearing 0: 00
|
||||
Counter 1: 0
|
||||
Tearing 1: 00
|
||||
Counter 2: 0
|
||||
Tearing 2: 00
|
||||
Pages total: 48
|
||||
Pages read: 48
|
||||
Page 0: 04 BA FF C9
|
||||
Page 1: CA 4D 5D 80
|
||||
Page 2: 5A 48 00 00
|
||||
Page 3: E1 10 12 00
|
||||
Page 4: 01 03 A0 0C
|
||||
Page 5: 34 03 00 FE
|
||||
Page 6: 00 00 00 00
|
||||
Page 7: 00 00 00 00
|
||||
Page 8: 00 00 00 00
|
||||
Page 9: 00 00 00 00
|
||||
Page 10: 00 00 BE AF
|
||||
Page 11: 00 00 00 00
|
||||
Page 12: 00 00 00 00
|
||||
Page 13: 00 00 00 00
|
||||
Page 14: 00 00 00 00
|
||||
Page 15: 00 00 00 00
|
||||
Page 16: 00 00 00 00
|
||||
Page 17: 00 00 00 00
|
||||
Page 18: 00 00 00 00
|
||||
Page 19: 00 00 00 00
|
||||
Page 20: 00 00 00 00
|
||||
Page 21: 00 00 00 00
|
||||
Page 22: 00 00 00 00
|
||||
Page 23: 00 00 00 00
|
||||
Page 24: 00 00 00 00
|
||||
Page 25: 00 00 00 00
|
||||
Page 26: 00 00 00 00
|
||||
Page 27: 00 00 00 00
|
||||
Page 28: 00 00 00 00
|
||||
Page 29: 00 00 00 00
|
||||
Page 30: 00 00 00 00
|
||||
Page 31: 00 00 00 00
|
||||
Page 32: 00 00 00 00
|
||||
Page 33: 00 00 00 00
|
||||
Page 34: 00 00 00 00
|
||||
Page 35: 00 00 00 00
|
||||
Page 36: 00 00 00 00
|
||||
Page 37: 00 00 00 00
|
||||
Page 38: 00 00 00 00
|
||||
Page 39: 00 00 00 00
|
||||
Page 40: 00 00 00 00
|
||||
Page 41: 00 00 00 00
|
||||
Page 42: 05 00 00 00
|
||||
Page 43: 00 00 00 00
|
||||
Page 44: 00 00 00 00
|
||||
Page 45: 00 00 00 00
|
||||
Page 46: 00 00 00 00
|
||||
Page 47: 00 00 00 00
|
||||
Failed authentication attempts: 0
|
@ -286,6 +286,10 @@ MU_TEST(mf_ultralight_21_reader) {
|
||||
mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_21.nfc"));
|
||||
}
|
||||
|
||||
MU_TEST(mf_ultralight_c_reader) {
|
||||
mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_C.nfc"));
|
||||
}
|
||||
|
||||
MU_TEST(ntag_215_reader) {
|
||||
mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag215.nfc"));
|
||||
}
|
||||
@ -828,6 +832,7 @@ MU_TEST_SUITE(nfc) {
|
||||
MU_RUN_TEST(ntag_215_reader);
|
||||
MU_RUN_TEST(ntag_216_reader);
|
||||
MU_RUN_TEST(ntag_213_locked_reader);
|
||||
MU_RUN_TEST(mf_ultralight_c_reader);
|
||||
|
||||
MU_RUN_TEST(mf_ultralight_write);
|
||||
|
||||
|
@ -36,6 +36,10 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
if(event.event == iButtonCustomEventByteEditResult) {
|
||||
if(scene_manager_has_previous_scene(scene_manager, iButtonSceneAddType)) {
|
||||
ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);
|
||||
scene_manager_next_scene(scene_manager, iButtonSceneSaveName);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
ibutton->file_path,
|
||||
"%s/%s%s",
|
||||
@ -52,6 +56,7 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_search_and_switch_to_previous_scene_one_of(
|
||||
ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes));
|
||||
}
|
||||
}
|
||||
} else if(event.event == iButtonCustomEventByteEditChanged) {
|
||||
ibutton_protocols_apply_edits(ibutton->protocols, ibutton->key);
|
||||
}
|
||||
|
@ -32,6 +32,9 @@ bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) {
|
||||
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
|
||||
protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size);
|
||||
|
||||
if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) {
|
||||
scene_manager_next_scene(scene_manager, LfRfidSceneSaveName);
|
||||
} else {
|
||||
if(!furi_string_empty(app->file_name)) {
|
||||
lfrfid_delete_key(app);
|
||||
}
|
||||
@ -43,6 +46,7 @@ bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager, LfRfidSceneSavedKeyMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 0);
|
||||
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
|
||||
|
@ -20,6 +20,7 @@ void mf_ultralight_auth_reset(MfUltralightAuth* instance) {
|
||||
|
||||
instance->type = MfUltralightAuthTypeNone;
|
||||
memset(&instance->password, 0, sizeof(MfUltralightAuthPassword));
|
||||
memset(&instance->tdes_key, 0, sizeof(MfUltralightC3DesAuthKey));
|
||||
memset(&instance->pack, 0, sizeof(MfUltralightAuthPack));
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ typedef enum {
|
||||
typedef struct {
|
||||
MfUltralightAuthType type;
|
||||
MfUltralightAuthPassword password;
|
||||
MfUltralightC3DesAuthKey tdes_key;
|
||||
MfUltralightAuthPack pack;
|
||||
} MfUltralightAuth;
|
||||
|
||||
|
@ -106,7 +106,7 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn)) {
|
||||
if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDesAuthUnlockWarn)) {
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
@ -163,7 +163,7 @@ static void nfc_scene_read_menu_on_enter_felica(NfcApp* instance) {
|
||||
static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexUnlock) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaKeyInput);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneDesAuthKeyInput);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,7 @@ static NfcCommand
|
||||
}
|
||||
if(!mf_ultralight_event->data->auth_context.skip_auth) {
|
||||
mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password;
|
||||
mf_ultralight_event->data->auth_context.tdes_key = instance->mf_ul_auth->tdes_key;
|
||||
}
|
||||
} else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) {
|
||||
instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack;
|
||||
@ -243,7 +244,13 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexUnlock) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||
const MfUltralightData* data =
|
||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
|
||||
|
||||
uint32_t next_scene = (data->type == MfUltralightTypeMfulC) ?
|
||||
NfcSceneDesAuthKeyInput :
|
||||
NfcSceneMfUltralightUnlockMenu;
|
||||
scene_manager_next_scene(instance->scene_manager, next_scene);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
|
||||
|
@ -22,7 +22,7 @@ void nfc_unlock_helper_setup_from_state(NfcApp* instance) {
|
||||
bool unlocking =
|
||||
scene_manager_has_previous_scene(
|
||||
instance->scene_manager, NfcSceneMfUltralightUnlockWarn) ||
|
||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||
scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDesAuthUnlockWarn);
|
||||
|
||||
uint32_t state = unlocking ? NfcSceneReadMenuStateCardSearch : NfcSceneReadMenuStateCardFound;
|
||||
|
||||
|
@ -33,8 +33,8 @@ ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
|
||||
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
|
||||
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
|
||||
ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)
|
||||
ADD_SCENE(nfc, felica_key_input, FelicaKeyInput)
|
||||
ADD_SCENE(nfc, felica_unlock_warn, FelicaUnlockWarn)
|
||||
ADD_SCENE(nfc, des_auth_key_input, DesAuthKeyInput)
|
||||
ADD_SCENE(nfc, des_auth_unlock_warn, DesAuthUnlockWarn)
|
||||
|
||||
ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)
|
||||
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
||||
|
@ -1,43 +1,54 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_felica_key_input_byte_input_callback(void* context) {
|
||||
void nfc_scene_des_auth_key_input_byte_input_callback(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone);
|
||||
}
|
||||
|
||||
void nfc_scene_felica_key_input_on_enter(void* context) {
|
||||
void nfc_scene_des_auth_key_input_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Setup view
|
||||
NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device);
|
||||
uint8_t* key = (protocol == NfcProtocolFelica) ? nfc->felica_auth->card_key.data :
|
||||
nfc->mf_ul_auth->tdes_key.data;
|
||||
|
||||
ByteInput* byte_input = nfc->byte_input;
|
||||
byte_input_set_header_text(byte_input, "Enter key in hex");
|
||||
byte_input_set_result_callback(
|
||||
byte_input,
|
||||
nfc_scene_felica_key_input_byte_input_callback,
|
||||
nfc_scene_des_auth_key_input_byte_input_callback,
|
||||
NULL,
|
||||
nfc,
|
||||
nfc->felica_auth->card_key.data,
|
||||
key,
|
||||
FELICA_DATA_BLOCK_SIZE);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
bool nfc_scene_des_auth_key_input_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventByteInputDone) {
|
||||
NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device);
|
||||
|
||||
if(protocol == NfcProtocolFelica) {
|
||||
nfc->felica_auth->skip_auth = false;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockWarn);
|
||||
} else {
|
||||
nfc->mf_ul_auth->type = MfUltralightAuthTypeManual;
|
||||
}
|
||||
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDesAuthUnlockWarn);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_key_input_on_exit(void* context) {
|
||||
void nfc_scene_des_auth_key_input_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Clear view
|
@ -1,25 +1,30 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
void nfc_scene_felica_unlock_warn_dialog_callback(DialogExResult result, void* context) {
|
||||
void nfc_scene_des_auth_unlock_warn_dialog_callback(DialogExResult result, void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
|
||||
void nfc_scene_felica_unlock_warn_on_enter(void* context) {
|
||||
void nfc_scene_des_auth_unlock_warn_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
const char* message = "Risky Action!";
|
||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||
dialog_ex_set_context(dialog_ex, nfc);
|
||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_felica_unlock_warn_dialog_callback);
|
||||
dialog_ex_set_result_callback(dialog_ex, nfc_scene_des_auth_unlock_warn_dialog_callback);
|
||||
|
||||
dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop);
|
||||
|
||||
FuriString* str = furi_string_alloc();
|
||||
furi_string_cat_printf(str, "Unlock with key: ");
|
||||
|
||||
NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device);
|
||||
uint8_t* key = (protocol == NfcProtocolFelica) ? nfc->felica_auth->card_key.data :
|
||||
nfc->mf_ul_auth->tdes_key.data;
|
||||
|
||||
for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++)
|
||||
furi_string_cat_printf(str, "%02X ", nfc->felica_auth->card_key.data[i]);
|
||||
furi_string_cat_printf(str, "%02X ", key[i]);
|
||||
furi_string_cat_printf(str, "?");
|
||||
|
||||
nfc_text_store_set(nfc, furi_string_get_cstr(str));
|
||||
@ -33,7 +38,7 @@ void nfc_scene_felica_unlock_warn_on_enter(void* context) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
|
||||
}
|
||||
|
||||
bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent event) {
|
||||
bool nfc_scene_des_auth_unlock_warn_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
UNUSED(event);
|
||||
bool consumed = false;
|
||||
@ -51,7 +56,7 @@ bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent even
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_felica_unlock_warn_on_exit(void* context) {
|
||||
void nfc_scene_des_auth_unlock_warn_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
dialog_ex_reset(nfc->dialog_ex);
|
@ -2,15 +2,14 @@
|
||||
#include <furi.h>
|
||||
|
||||
SceneManager* scene_manager_alloc(const SceneManagerHandlers* app_scene_handlers, void* context) {
|
||||
furi_check(context);
|
||||
furi_check(app_scene_handlers);
|
||||
|
||||
SceneManager* scene_manager = malloc(sizeof(SceneManager));
|
||||
SceneManager* scene_manager =
|
||||
malloc(sizeof(SceneManager) + (sizeof(AppScene) * app_scene_handlers->scene_num));
|
||||
// Set SceneManager context and scene handlers
|
||||
scene_manager->context = context;
|
||||
scene_manager->scene_handlers = app_scene_handlers;
|
||||
// Allocate all scenes
|
||||
scene_manager->scene = malloc(sizeof(AppScene) * app_scene_handlers->scene_num);
|
||||
// Initialize SceneManager array for navigation
|
||||
// Initialize ScaneManager array for navigation
|
||||
SceneManagerIdStack_init(scene_manager->scene_id_stack);
|
||||
|
||||
return scene_manager;
|
||||
@ -21,8 +20,6 @@ void scene_manager_free(SceneManager* scene_manager) {
|
||||
|
||||
// Clear SceneManager array
|
||||
SceneManagerIdStack_clear(scene_manager->scene_id_stack);
|
||||
// Clear allocated scenes
|
||||
free(scene_manager->scene);
|
||||
// Free SceneManager structure
|
||||
free(scene_manager);
|
||||
}
|
||||
|
@ -17,6 +17,6 @@ typedef struct {
|
||||
struct SceneManager {
|
||||
SceneManagerIdStack_t scene_id_stack;
|
||||
const SceneManagerHandlers* scene_handlers;
|
||||
AppScene* scene;
|
||||
void* context;
|
||||
AppScene scene[];
|
||||
};
|
||||
|
@ -76,9 +76,8 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) {
|
||||
if(view->model_type == ViewModelTypeLockFree) {
|
||||
view->model = malloc(size);
|
||||
} else if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = malloc(sizeof(ViewModelLocking));
|
||||
ViewModelLocking* model = malloc(sizeof(ViewModelLocking) + size);
|
||||
model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||
model->data = malloc(size);
|
||||
view->model = model;
|
||||
} else {
|
||||
furi_crash();
|
||||
@ -89,16 +88,11 @@ void view_free_model(View* view) {
|
||||
furi_check(view);
|
||||
if(view->model_type == ViewModelTypeNone) {
|
||||
return;
|
||||
} else if(view->model_type == ViewModelTypeLockFree) {
|
||||
free(view->model);
|
||||
} else if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = view->model;
|
||||
furi_mutex_free(model->mutex);
|
||||
free(model->data);
|
||||
free(model);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
free(view->model);
|
||||
view->model = NULL;
|
||||
view->model_type = ViewModelTypeNone;
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
FuriMutex* mutex;
|
||||
uint8_t data[];
|
||||
} ViewModelLocking;
|
||||
|
||||
struct View {
|
||||
|
@ -1,11 +1,36 @@
|
||||
#include "input_i.h"
|
||||
#include "input.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
|
||||
#define INPUT_PRESS_TICKS 150
|
||||
#define INPUT_LONG_PRESS_COUNTS 2
|
||||
#define INPUT_THREAD_FLAG_ISR 0x00000001
|
||||
|
||||
/** Input pin state */
|
||||
typedef struct {
|
||||
const InputPin* pin;
|
||||
// State
|
||||
volatile bool state;
|
||||
volatile uint8_t debounce;
|
||||
FuriTimer* press_timer;
|
||||
FuriPubSub* event_pubsub;
|
||||
volatile uint8_t press_counter;
|
||||
volatile uint32_t counter;
|
||||
} InputPinState;
|
||||
|
||||
/** Input CLI command handler */
|
||||
void input_cli(Cli* cli, FuriString* args, void* context);
|
||||
|
||||
// #define INPUT_DEBUG
|
||||
|
||||
#define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted))
|
||||
|
||||
static Input* input = NULL;
|
||||
|
||||
void input_press_timer_callback(void* arg) {
|
||||
InputPinState* input_pin = arg;
|
||||
InputEvent event;
|
||||
@ -15,17 +40,17 @@ void input_press_timer_callback(void* arg) {
|
||||
input_pin->press_counter++;
|
||||
if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) {
|
||||
event.type = InputTypeLong;
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
furi_pubsub_publish(input_pin->event_pubsub, &event);
|
||||
} else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) {
|
||||
input_pin->press_counter--;
|
||||
event.type = InputTypeRepeat;
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
furi_pubsub_publish(input_pin->event_pubsub, &event);
|
||||
}
|
||||
}
|
||||
|
||||
void input_isr(void* _ctx) {
|
||||
UNUSED(_ctx);
|
||||
furi_thread_flags_set(input->thread_id, INPUT_THREAD_FLAG_ISR);
|
||||
FuriThreadId thread_id = (FuriThreadId)_ctx;
|
||||
furi_thread_flags_set(thread_id, INPUT_THREAD_FLAG_ISR);
|
||||
}
|
||||
|
||||
const char* input_get_key_name(InputKey key) {
|
||||
@ -56,85 +81,83 @@ const char* input_get_type_name(InputType type) {
|
||||
|
||||
int32_t input_srv(void* p) {
|
||||
UNUSED(p);
|
||||
input = malloc(sizeof(Input));
|
||||
input->thread_id = furi_thread_get_current_id();
|
||||
input->event_pubsub = furi_pubsub_alloc();
|
||||
furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub);
|
||||
|
||||
#if INPUT_DEBUG
|
||||
const FuriThreadId thread_id = furi_thread_get_current_id();
|
||||
FuriPubSub* event_pubsub = furi_pubsub_alloc();
|
||||
uint32_t counter = 1;
|
||||
furi_record_create(RECORD_INPUT_EVENTS, event_pubsub);
|
||||
|
||||
#ifdef INPUT_DEBUG
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull);
|
||||
#endif
|
||||
|
||||
#ifdef SRV_CLI
|
||||
input->cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input);
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub);
|
||||
#endif
|
||||
|
||||
input->pin_states = malloc(input_pins_count * sizeof(InputPinState));
|
||||
InputPinState pin_states[input_pins_count];
|
||||
|
||||
for(size_t i = 0; i < input_pins_count; i++) {
|
||||
furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr, NULL);
|
||||
input->pin_states[i].pin = &input_pins[i];
|
||||
input->pin_states[i].state = GPIO_Read(input->pin_states[i]);
|
||||
input->pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF;
|
||||
input->pin_states[i].press_timer = furi_timer_alloc(
|
||||
input_press_timer_callback, FuriTimerTypePeriodic, &input->pin_states[i]);
|
||||
input->pin_states[i].press_counter = 0;
|
||||
furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr, thread_id);
|
||||
pin_states[i].pin = &input_pins[i];
|
||||
pin_states[i].state = GPIO_Read(pin_states[i]);
|
||||
pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF;
|
||||
pin_states[i].press_timer =
|
||||
furi_timer_alloc(input_press_timer_callback, FuriTimerTypePeriodic, &pin_states[i]);
|
||||
pin_states[i].event_pubsub = event_pubsub;
|
||||
pin_states[i].press_counter = 0;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
bool is_changing = false;
|
||||
for(size_t i = 0; i < input_pins_count; i++) {
|
||||
bool state = GPIO_Read(input->pin_states[i]);
|
||||
bool state = GPIO_Read(pin_states[i]);
|
||||
if(state) {
|
||||
if(input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS)
|
||||
input->pin_states[i].debounce += 1;
|
||||
if(pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) pin_states[i].debounce += 1;
|
||||
} else {
|
||||
if(input->pin_states[i].debounce > 0) input->pin_states[i].debounce -= 1;
|
||||
if(pin_states[i].debounce > 0) pin_states[i].debounce -= 1;
|
||||
}
|
||||
|
||||
if(input->pin_states[i].debounce > 0 &&
|
||||
input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) {
|
||||
if(pin_states[i].debounce > 0 && pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) {
|
||||
is_changing = true;
|
||||
} else if(input->pin_states[i].state != state) {
|
||||
input->pin_states[i].state = state;
|
||||
} else if(pin_states[i].state != state) {
|
||||
pin_states[i].state = state;
|
||||
|
||||
// Common state info
|
||||
InputEvent event;
|
||||
event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE;
|
||||
event.key = input->pin_states[i].pin->key;
|
||||
event.key = pin_states[i].pin->key;
|
||||
|
||||
// Short / Long / Repeat timer routine
|
||||
if(state) {
|
||||
input->counter++;
|
||||
input->pin_states[i].counter = input->counter;
|
||||
event.sequence_counter = input->pin_states[i].counter;
|
||||
furi_timer_start(input->pin_states[i].press_timer, INPUT_PRESS_TICKS);
|
||||
pin_states[i].counter = counter++;
|
||||
event.sequence_counter = pin_states[i].counter;
|
||||
furi_timer_start(pin_states[i].press_timer, INPUT_PRESS_TICKS);
|
||||
} else {
|
||||
event.sequence_counter = input->pin_states[i].counter;
|
||||
furi_timer_stop(input->pin_states[i].press_timer);
|
||||
while(furi_timer_is_running(input->pin_states[i].press_timer))
|
||||
furi_delay_tick(1);
|
||||
if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) {
|
||||
event.sequence_counter = pin_states[i].counter;
|
||||
furi_timer_stop(pin_states[i].press_timer);
|
||||
while(furi_timer_is_running(pin_states[i].press_timer)) furi_delay_tick(1);
|
||||
if(pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) {
|
||||
event.type = InputTypeShort;
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
furi_pubsub_publish(event_pubsub, &event);
|
||||
}
|
||||
input->pin_states[i].press_counter = 0;
|
||||
pin_states[i].press_counter = 0;
|
||||
}
|
||||
|
||||
// Send Press/Release event
|
||||
event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease;
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
event.type = pin_states[i].state ? InputTypePress : InputTypeRelease;
|
||||
furi_pubsub_publish(event_pubsub, &event);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_changing) {
|
||||
#if INPUT_DEBUG
|
||||
#ifdef INPUT_DEBUG
|
||||
furi_hal_gpio_write(&gpio_ext_pa4, 1);
|
||||
#endif
|
||||
furi_delay_tick(1);
|
||||
} else {
|
||||
#if INPUT_DEBUG
|
||||
#ifdef INPUT_DEBUG
|
||||
furi_hal_gpio_write(&gpio_ext_pa4, 0);
|
||||
#endif
|
||||
furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "input_i.h"
|
||||
#include "input.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
@ -19,11 +19,11 @@ static void input_cli_dump_events_callback(const void* value, void* ctx) {
|
||||
furi_message_queue_put(input_queue, value, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void input_cli_dump(Cli* cli, FuriString* args, Input* input) {
|
||||
static void input_cli_dump(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) {
|
||||
UNUSED(args);
|
||||
FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
FuriPubSubSubscription* input_subscription =
|
||||
furi_pubsub_subscribe(input->event_pubsub, input_cli_dump_events_callback, input_queue);
|
||||
furi_pubsub_subscribe(event_pubsub, input_cli_dump_events_callback, input_queue);
|
||||
|
||||
InputEvent input_event;
|
||||
printf("Press CTRL+C to stop\r\n");
|
||||
@ -36,7 +36,7 @@ static void input_cli_dump(Cli* cli, FuriString* args, Input* input) {
|
||||
}
|
||||
}
|
||||
|
||||
furi_pubsub_unsubscribe(input->event_pubsub, input_subscription);
|
||||
furi_pubsub_unsubscribe(event_pubsub, input_subscription);
|
||||
furi_message_queue_free(input_queue);
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ static void input_cli_send_print_usage(void) {
|
||||
printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n");
|
||||
}
|
||||
|
||||
static void input_cli_send(Cli* cli, FuriString* args, Input* input) {
|
||||
static void input_cli_send(Cli* cli, FuriString* args, FuriPubSub* event_pubsub) {
|
||||
UNUSED(cli);
|
||||
InputEvent event;
|
||||
FuriString* key_str;
|
||||
@ -90,7 +90,7 @@ static void input_cli_send(Cli* cli, FuriString* args, Input* input) {
|
||||
} while(false);
|
||||
|
||||
if(parsed) { //-V547
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
furi_pubsub_publish(event_pubsub, &event);
|
||||
} else {
|
||||
input_cli_send_print_usage();
|
||||
}
|
||||
@ -100,7 +100,7 @@ static void input_cli_send(Cli* cli, FuriString* args, Input* input) {
|
||||
void input_cli(Cli* cli, FuriString* args, void* context) {
|
||||
furi_assert(cli);
|
||||
furi_assert(context);
|
||||
Input* input = context;
|
||||
FuriPubSub* event_pubsub = context;
|
||||
FuriString* cmd;
|
||||
cmd = furi_string_alloc();
|
||||
|
||||
@ -110,11 +110,11 @@ void input_cli(Cli* cli, FuriString* args, void* context) {
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "dump") == 0) {
|
||||
input_cli_dump(cli, args, input);
|
||||
input_cli_dump(cli, args, event_pubsub);
|
||||
break;
|
||||
}
|
||||
if(furi_string_cmp_str(cmd, "send") == 0) {
|
||||
input_cli_send(cli, args, input);
|
||||
input_cli_send(cli, args, event_pubsub);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* @file input_i.h
|
||||
* Input: internal API
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "input.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
|
||||
#define INPUT_PRESS_TICKS 150
|
||||
#define INPUT_LONG_PRESS_COUNTS 2
|
||||
#define INPUT_THREAD_FLAG_ISR 0x00000001
|
||||
|
||||
/** Input pin state */
|
||||
typedef struct {
|
||||
const InputPin* pin;
|
||||
// State
|
||||
volatile bool state;
|
||||
volatile uint8_t debounce;
|
||||
FuriTimer* press_timer;
|
||||
volatile uint8_t press_counter;
|
||||
volatile uint32_t counter;
|
||||
} InputPinState;
|
||||
|
||||
/** Input state */
|
||||
typedef struct {
|
||||
FuriThreadId thread_id;
|
||||
FuriPubSub* event_pubsub;
|
||||
InputPinState* pin_states;
|
||||
Cli* cli;
|
||||
volatile uint32_t counter;
|
||||
} Input;
|
||||
|
||||
/** Input press timer callback */
|
||||
void input_press_timer_callback(void* arg);
|
||||
|
||||
/** Input interrupt handler */
|
||||
void input_isr(void* _ctx);
|
||||
|
||||
/** Input CLI command handler */
|
||||
void input_cli(Cli* cli, FuriString* args, void* context);
|
@ -22,7 +22,7 @@ DIST_SUFFIX = "local"
|
||||
COPRO_OB_DATA = "scripts/ob.data"
|
||||
|
||||
# Must match lib/stm32wb_copro version
|
||||
COPRO_CUBE_VERSION = "1.19.0"
|
||||
COPRO_CUBE_VERSION = "1.20.0"
|
||||
|
||||
COPRO_CUBE_DIR = "lib/stm32wb_copro"
|
||||
|
||||
|
@ -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)) {
|
||||
|
||||
if(data->pages_read == data->pages_total) {
|
||||
uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type);
|
||||
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.
|
||||
uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type);
|
||||
if(!mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportPasswordAuth)) {
|
||||
all_read = true;
|
||||
} else {
|
||||
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);
|
||||
bool access_success = true;
|
||||
|
||||
do {
|
||||
if(!mf_ultralight_support_feature(
|
||||
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 = true;
|
||||
break;
|
||||
access_success = mf_ultralight_common_check_access(instance, start_page, access_type);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -577,3 +577,60 @@ bool mf_ultralight_auth_check_password(
|
||||
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];
|
||||
@ -467,6 +518,10 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
||||
if(instance->pages_read == instance->pages_total) {
|
||||
instance->state = MfUltralightPollerStateReadCounters;
|
||||
}
|
||||
} else {
|
||||
if(instance->data->type == MfUltralightTypeMfulC &&
|
||||
!mf_ultralight_3des_key_valid(instance->data)) {
|
||||
instance->state = MfUltralightPollerStateCheckMfulCAuthStatus;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read);
|
||||
if(instance->pages_read) {
|
||||
@ -475,6 +530,7 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in
|
||||
instance->state = MfUltralightPollerStateReadFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
@ -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) {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 64a060d91f5cbf25d765cf23231876add006bcf4
|
||||
Subproject commit 133182d5583e998bb263cd947105be4df9c29cb3
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,67.2,,
|
||||
Version,+,68.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
|
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,67.2,,
|
||||
Version,+,68.0,,
|
||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||
@ -2617,6 +2617,11 @@ Function,+,mf_plus_reset,void,MfPlusData*
|
||||
Function,+,mf_plus_save,_Bool,"const MfPlusData*, FlipperFormat*"
|
||||
Function,+,mf_plus_set_uid,_Bool,"MfPlusData*, const uint8_t*, size_t"
|
||||
Function,+,mf_plus_verify,_Bool,"MfPlusData*, const FuriString*"
|
||||
Function,+,mf_ultralight_3des_decrypt,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*"
|
||||
Function,+,mf_ultralight_3des_encrypt,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*"
|
||||
Function,+,mf_ultralight_3des_get_key,const uint8_t*,const MfUltralightData*
|
||||
Function,+,mf_ultralight_3des_key_valid,_Bool,const MfUltralightData*
|
||||
Function,+,mf_ultralight_3des_shift_data,void,uint8_t*
|
||||
Function,+,mf_ultralight_alloc,MfUltralightData*,
|
||||
Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*"
|
||||
Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData*
|
||||
@ -2637,7 +2642,8 @@ Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltral
|
||||
Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t"
|
||||
Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t"
|
||||
Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*"
|
||||
Function,+,mf_ultralight_poller_authenticate,MfUltralightError,MfUltralightPoller*
|
||||
Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*"
|
||||
Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*"
|
||||
Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*"
|
||||
Function,+,mf_ultralight_poller_read_page,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightPageReadCommandData*"
|
||||
Function,+,mf_ultralight_poller_read_page_from_sector,MfUltralightError,"MfUltralightPoller*, uint8_t, uint8_t, MfUltralightPageReadCommandData*"
|
||||
|
|
Loading…
Reference in New Issue
Block a user