From 322440147953fc3a12ec5763e485ea21f5881d3f Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:38:30 +0300 Subject: [PATCH 1/5] [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 Co-authored-by: gornekich Co-authored-by: Aleksandr Kutuzov --- .../resources/unit_tests/nfc/Ultralight_C.nfc | 71 ++++++++++ .../debug/unit_tests/tests/nfc/nfc_test.c | 5 + .../main/nfc/helpers/mf_ultralight_auth.c | 1 + .../main/nfc/helpers/mf_ultralight_auth.h | 1 + .../helpers/protocol_support/felica/felica.c | 4 +- .../mf_ultralight/mf_ultralight.c | 9 +- .../nfc_protocol_support_unlock_helper.c | 2 +- .../main/nfc/scenes/nfc_scene_config.h | 4 +- ...input.c => nfc_scene_des_auth_key_input.c} | 27 ++-- ...arn.c => nfc_scene_des_auth_unlock_warn.c} | 17 ++- .../protocols/mf_ultralight/mf_ultralight.c | 74 ++++++++++- .../protocols/mf_ultralight/mf_ultralight.h | 37 +++++- .../mf_ultralight/mf_ultralight_listener.c | 122 +++++++++++++----- .../mf_ultralight/mf_ultralight_listener_i.c | 57 ++++++++ .../mf_ultralight/mf_ultralight_listener_i.h | 19 +++ .../mf_ultralight/mf_ultralight_poller.c | 98 +++++++++++++- .../mf_ultralight/mf_ultralight_poller.h | 26 +++- .../mf_ultralight/mf_ultralight_poller_i.c | 110 +++++++++++++++- .../mf_ultralight/mf_ultralight_poller_i.h | 5 + .../mf_ultralight/mf_ultralight_poller_sync.c | 6 + targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 10 +- 22 files changed, 632 insertions(+), 75 deletions(-) create mode 100644 applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc rename applications/main/nfc/scenes/{nfc_scene_felica_key_input.c => nfc_scene_des_auth_key_input.c} (52%) rename applications/main/nfc/scenes/{nfc_scene_felica_unlock_warn.c => nfc_scene_des_auth_unlock_warn.c} (68%) diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc new file mode 100644 index 000000000..8c125865c --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc @@ -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 diff --git a/applications/debug/unit_tests/tests/nfc/nfc_test.c b/applications/debug/unit_tests/tests/nfc/nfc_test.c index 1e4407076..5f172d0b5 100644 --- a/applications/debug/unit_tests/tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/tests/nfc/nfc_test.c @@ -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); diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.c b/applications/main/nfc/helpers/mf_ultralight_auth.c index d954c1f7e..e97649cb3 100644 --- a/applications/main/nfc/helpers/mf_ultralight_auth.c +++ b/applications/main/nfc/helpers/mf_ultralight_auth.c @@ -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)); } diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.h b/applications/main/nfc/helpers/mf_ultralight_auth.h index 51267b3ea..c9e80765f 100644 --- a/applications/main/nfc/helpers/mf_ultralight_auth.h +++ b/applications/main/nfc/helpers/mf_ultralight_auth.h @@ -17,6 +17,7 @@ typedef enum { typedef struct { MfUltralightAuthType type; MfUltralightAuthPassword password; + MfUltralightC3DesAuthKey tdes_key; MfUltralightAuthPack pack; } MfUltralightAuth; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index ceb180653..561cd4d2e 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -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; } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index b55ba130e..3adf2a1f5 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -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); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c index f1d504d24..598cf7a7e 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c @@ -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; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 3017d16a4..83c8ffeed 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -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) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_key_input.c b/applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c similarity index 52% rename from applications/main/nfc/scenes/nfc_scene_felica_key_input.c rename to applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c index b04f12dae..f3a6ecb32 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c @@ -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) { - nfc->felica_auth->skip_auth = false; - scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockWarn); + NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device); + + if(protocol == NfcProtocolFelica) { + nfc->felica_auth->skip_auth = false; + } 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 diff --git a/applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c similarity index 68% rename from applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c rename to applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c index 15b61dfa5..80c735c9c 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c @@ -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); diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c index 1f3481c17..b10ac40b6 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c @@ -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); +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h index 582e9e0e2..f748f6813 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h @@ -1,6 +1,7 @@ #pragma once #include +#include #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 diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c index 5bef2a354..b5980b388 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c @@ -4,16 +4,12 @@ #include #include +#include #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); } diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c index 64647492d..3a9b893aa 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c @@ -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; } \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h index ba448d087..c43658edd 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h @@ -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 diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 52b445859..0e5ff0011 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -3,6 +3,7 @@ #include #include +#include #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, diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h index 2343be089..e50017324 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h @@ -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. diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c index cc3433f73..141ab6c46 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c @@ -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; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h index 3f8645fe7..1f395ddd2 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h @@ -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 diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c index 157e2bdef..9958dc50d 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c @@ -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) { diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 48c1ab0ab..23e098a91 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -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,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d7782ee91..6cccce1e6 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -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,, @@ -2574,6 +2574,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* @@ -2594,7 +2599,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*" From 2715d9a0e17bc0a71acd7599547033d430a92ff5 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 3 Jul 2024 20:44:17 +0900 Subject: [PATCH 2/5] [FL-3752] Fix iButton/LFRFID Add Manually results being discarded (#3749) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../ibutton/scenes/ibutton_scene_add_value.c | 33 +++++++++++-------- .../lfrfid/scenes/lfrfid_scene_save_data.c | 20 ++++++----- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c index 622ed5ad3..9815e5281 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_add_value.c +++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c @@ -36,21 +36,26 @@ bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == iButtonCustomEventByteEditResult) { - furi_string_printf( - ibutton->file_path, - "%s/%s%s", - IBUTTON_APP_FOLDER, - ibutton->key_name, - IBUTTON_APP_FILENAME_EXTENSION); - - if(ibutton_save_key(ibutton)) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); - + 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 { - const uint32_t possible_scenes[] = { - iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - scene_manager_search_and_switch_to_previous_scene_one_of( - ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); + furi_string_printf( + ibutton->file_path, + "%s/%s%s", + IBUTTON_APP_FOLDER, + ibutton->key_name, + IBUTTON_APP_FILENAME_EXTENSION); + + if(ibutton_save_key(ibutton)) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); + + } else { + const uint32_t possible_scenes[] = { + iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; + 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); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c index 5d5371e91..20c8a10df 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c @@ -32,15 +32,19 @@ 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(!furi_string_empty(app->file_name)) { - lfrfid_delete_key(app); - } - - if(lfrfid_save_key(app)) { - scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess); + if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { + scene_manager_next_scene(scene_manager, LfRfidSceneSaveName); } else { - scene_manager_search_and_switch_to_previous_scene( - scene_manager, LfRfidSceneSavedKeyMenu); + if(!furi_string_empty(app->file_name)) { + lfrfid_delete_key(app); + } + + if(lfrfid_save_key(app)) { + scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneSavedKeyMenu); + } } } } else if(event.type == SceneManagerEventTypeBack) { From 7e0849b44efaa394bcabf0460f6bfac2c5e958e2 Mon Sep 17 00:00:00 2001 From: Silent Date: Wed, 3 Jul 2024 13:56:13 +0200 Subject: [PATCH 3/5] Coalesce some allocations (#3747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * View: Coalesce view model allocations * SceneManager: Coalesce AppScene allocations * BufferStream: Coalesce Buffer allocations * ProtocolDict: Coalesce dict allocations * DigitalSignal: Coalesce buffer allocations Co-authored-by: あく --- applications/services/gui/scene_manager.c | 9 +++------ applications/services/gui/scene_manager_i.h | 2 +- applications/services/gui/view.c | 10 ++-------- applications/services/gui/view_i.h | 2 +- lib/digital_signal/digital_sequence.c | 8 +++----- lib/digital_signal/digital_signal.c | 4 +--- lib/digital_signal/digital_signal_i.h | 2 +- lib/toolbox/buffer_stream.c | 6 ++---- lib/toolbox/protocols/protocol_dict.c | 6 ++---- 9 files changed, 16 insertions(+), 33 deletions(-) diff --git a/applications/services/gui/scene_manager.c b/applications/services/gui/scene_manager.c index 7a0a29137..11acc0796 100644 --- a/applications/services/gui/scene_manager.c +++ b/applications/services/gui/scene_manager.c @@ -2,14 +2,13 @@ #include 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 ScaneManager array for navigation SceneManagerIdStack_init(scene_manager->scene_id_stack); @@ -21,8 +20,6 @@ void scene_manager_free(SceneManager* scene_manager) { // Clear ScaneManager array SceneManagerIdStack_clear(scene_manager->scene_id_stack); - // Clear allocated scenes - free(scene_manager->scene); // Free SceneManager structure free(scene_manager); } diff --git a/applications/services/gui/scene_manager_i.h b/applications/services/gui/scene_manager_i.h index fca798d12..85f7e7bee 100644 --- a/applications/services/gui/scene_manager_i.h +++ b/applications/services/gui/scene_manager_i.h @@ -17,6 +17,6 @@ typedef struct { struct SceneManager { SceneManagerIdStack_t scene_id_stack; const SceneManagerHandlers* scene_handlers; - AppScene* scene; void* context; + AppScene scene[]; }; diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 51d023543..3ac85794a 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -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; } diff --git a/applications/services/gui/view_i.h b/applications/services/gui/view_i.h index 3e895bd94..1cc84c745 100644 --- a/applications/services/gui/view_i.h +++ b/applications/services/gui/view_i.h @@ -9,8 +9,8 @@ #include typedef struct { - void* data; FuriMutex* mutex; + uint8_t data[]; } ViewModelLocking; struct View { diff --git a/lib/digital_signal/digital_sequence.c b/lib/digital_signal/digital_sequence.c index 24dddb77d..3e1643abb 100644 --- a/lib/digital_signal/digital_sequence.c +++ b/lib/digital_signal/digital_sequence.c @@ -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); } diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 585250c26..dd8600a40 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -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); } diff --git a/lib/digital_signal/digital_signal_i.h b/lib/digital_signal/digital_signal_i.h index e473c80c0..cd2f7fa3b 100644 --- a/lib/digital_signal/digital_signal_i.h +++ b/lib/digital_signal/digital_signal_i.h @@ -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. */ }; diff --git a/lib/toolbox/buffer_stream.c b/lib/toolbox/buffer_stream.c index e8cb334d5..dccc97f1c 100644 --- a/lib/toolbox/buffer_stream.c +++ b/lib/toolbox/buffer_stream.c @@ -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); } diff --git a/lib/toolbox/protocols/protocol_dict.c b/lib/toolbox/protocols/protocol_dict.c index 71fa4fe28..34cbcd13a 100644 --- a/lib/toolbox/protocols/protocol_dict.c +++ b/lib/toolbox/protocols/protocol_dict.c @@ -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); } From 6e0115c2e153cae1b442d86c4707fcd09e7bb810 Mon Sep 17 00:00:00 2001 From: Silent Date: Wed, 3 Jul 2024 14:09:37 +0200 Subject: [PATCH 4/5] input_srv: Put input state data on the stack of the service (#3748) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * input_srv: Put input state data on the stack of the service * input_srv: Use post-increment on the global counter Co-authored-by: あく --- applications/services/input/input.c | 115 ++++++++++++++---------- applications/services/input/input_cli.c | 18 ++-- applications/services/input/input_i.h | 48 ---------- 3 files changed, 78 insertions(+), 103 deletions(-) delete mode 100644 applications/services/input/input_i.h diff --git a/applications/services/input/input.c b/applications/services/input/input.c index 216aa39b2..a7610688e 100644 --- a/applications/services/input/input.c +++ b/applications/services/input/input.c @@ -1,11 +1,36 @@ -#include "input_i.h" +#include "input.h" + +#include +#include +#include +#include +#include +#include + +#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); diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index 8aa04d6bd..8e711c895 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -1,4 +1,4 @@ -#include "input_i.h" +#include "input.h" #include #include @@ -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 \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; } diff --git a/applications/services/input/input_i.h b/applications/services/input/input_i.h deleted file mode 100644 index 14d8b0735..000000000 --- a/applications/services/input/input_i.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file input_i.h - * Input: internal API - */ - -#pragma once - -#include "input.h" -#include -#include -#include -#include -#include -#include - -#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); From 8c380ebe9461218018c4e09a9b333727ac586edb Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 3 Jul 2024 21:31:55 +0300 Subject: [PATCH 5/5] copro: bumped to 1.20.0 (#3727) * copro: bumped to 1.19.1 * copro: bumped to 1.20.0 --- fbt_options.py | 2 +- lib/stm32wb_copro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index c4b841058..e30f7fc2d 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -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" diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro index 64a060d91..133182d55 160000 --- a/lib/stm32wb_copro +++ b/lib/stm32wb_copro @@ -1 +1 @@ -Subproject commit 64a060d91f5cbf25d765cf23231876add006bcf4 +Subproject commit 133182d5583e998bb263cd947105be4df9c29cb3