diff --git a/applications/main/nfc/api/gallagher/gallagher_util.c b/applications/main/nfc/api/gallagher/gallagher_util.c new file mode 100644 index 000000000..caa3650e7 --- /dev/null +++ b/applications/main/nfc/api/gallagher/gallagher_util.c @@ -0,0 +1,59 @@ +/* gallagher_util.c - Utilities for parsing Gallagher cards (New Zealand). + * Author: Nick Mooney (nick@mooney.nz) + * + * Reference: https://github.com/megabug/gallagher-research +*/ + +#include "gallagher_util.h" + +#define GALLAGHER_CREDENTIAL_SECTOR 15 + +/* The Gallagher obfuscation algorithm is a 256-byte substitution table. The below array is generated from + * https://github.com/megabug/gallagher-research/blob/master/formats/cardholder/substitution-table.bin. +*/ +const uint8_t GALLAGHER_DECODE_TABLE[256] = { + 0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0xf, 0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11, + 0x6d, 0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1, + 0x62, 0x1a, 0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3, + 0xae, 0x1f, 0x9b, 0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19, + 0xe, 0xa9, 0xe2, 0x8d, 0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0, + 0xe8, 0xa6, 0x20, 0xab, 0x87, 0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0xc, 0xc3, 0x3c, 0x61, 0xcc, + 0x40, 0x9e, 0x6, 0x52, 0x1b, 0x32, 0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0xd, 0xc2, + 0x88, 0xd1, 0xe0, 0x7, 0x2d, 0x70, 0xc6, 0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b, + 0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, 0x2, 0x8, 0x30, 0xa7, 0x22, 0xc9, 0x65, + 0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, 0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f, + 0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, 0x51, 0xd4, 0x81, 0x0, 0x2e, + 0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, 0x1, 0x48, 0x4, 0xc1, + 0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0xa, 0xac, 0x18, 0x4b, 0xcd, 0x98, 0xb8, 0x37, + 0xa2, 0x83, 0xec, 0x3, 0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, 0x43, 0xe9, + 0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, 0x89, + 0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0xb, 0xf1, 0xea, 0xfd, 0xdb, + 0xbd, 0x9, 0xb5, 0x5b, 0x5, 0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e, + 0x36}; + +// The second block of a Gallagher credential sector is the literal +// "www.cardax.com " (note two padding spaces) +const uint8_t GALLAGHER_CARDAX_ASCII[MF_CLASSIC_BLOCK_SIZE] = + {'w', 'w', 'w', '.', 'c', 'a', 'r', 'd', 'a', 'x', '.', 'c', 'o', 'm', ' ', ' '}; + +/* Precondition: cardholder_data_obfuscated points to at least 8 safe-to-read bytes of memory. +*/ +void gallagher_deobfuscate_and_parse_credential( + GallagherCredential* credential, + const uint8_t* cardholder_data_obfuscated) { + uint8_t cardholder_data_deobfuscated[8]; + for(int i = 0; i < 8; i++) { + cardholder_data_deobfuscated[i] = GALLAGHER_DECODE_TABLE[cardholder_data_obfuscated[i]]; + } + + // Pull out values from the deobfuscated data + credential->region = (cardholder_data_deobfuscated[3] >> 1) & 0x0F; + credential->facility = ((uint16_t)(cardholder_data_deobfuscated[5] & 0x0F) << 12) + + ((uint16_t)cardholder_data_deobfuscated[1] << 4) + + (((uint16_t)cardholder_data_deobfuscated[7] >> 4) & 0x0F); + credential->card = ((uint32_t)cardholder_data_deobfuscated[0] << 16) + + ((uint32_t)(cardholder_data_deobfuscated[4] & 0x1F) << 11) + + ((uint32_t)cardholder_data_deobfuscated[2] << 3) + + (((uint32_t)cardholder_data_deobfuscated[3] >> 5) & 0x07); + credential->issue = cardholder_data_deobfuscated[7] & 0x0F; +} \ No newline at end of file diff --git a/applications/main/nfc/api/gallagher/gallagher_util.h b/applications/main/nfc/api/gallagher/gallagher_util.h new file mode 100644 index 000000000..79e098389 --- /dev/null +++ b/applications/main/nfc/api/gallagher/gallagher_util.h @@ -0,0 +1,33 @@ +/* gallagher_util.h - Utilities for parsing Gallagher cards (New Zealand). + * Author: Nick Mooney (nick@mooney.nz) + * + * Reference: https://github.com/megabug/gallagher-research +*/ + +#pragma once + +#include + +#define GALLAGHER_CREDENTIAL_SECTOR 15 + +#ifdef __cplusplus +extern "C" { +#endif + +extern const uint8_t GALLAGHER_DECODE_TABLE[256]; +extern const uint8_t GALLAGHER_CARDAX_ASCII[MF_CLASSIC_BLOCK_SIZE]; + +typedef struct GallagherCredential { + uint8_t region; + uint8_t issue; + uint16_t facility; + uint32_t card; +} GallagherCredential; + +void gallagher_deobfuscate_and_parse_credential( + GallagherCredential* credential, + const uint8_t* cardholder_data_obfuscated); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/nfc/api/nfc_app_api_interface.h b/applications/main/nfc/api/nfc_app_api_interface.h new file mode 100644 index 000000000..162355982 --- /dev/null +++ b/applications/main/nfc/api/nfc_app_api_interface.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +/* + * Resolver interface with private application's symbols. + * Implementation is contained in app_api_table.c + */ +extern const ElfApiInterface* const nfc_application_api_interface; diff --git a/applications/main/nfc/api/nfc_app_api_table.cpp b/applications/main/nfc/api/nfc_app_api_table.cpp new file mode 100644 index 000000000..ca190665b --- /dev/null +++ b/applications/main/nfc/api/nfc_app_api_table.cpp @@ -0,0 +1,27 @@ +#include +#include + +/* + * This file contains an implementation of a symbol table + * with private app's symbols. It is used by composite API resolver + * to load plugins that use internal application's APIs. + */ +#include "nfc_app_api_table_i.h" + +static_assert(!has_hash_collisions(nfc_app_api_table), "Detected API method hash collision!"); + +constexpr HashtableApiInterface nfc_application_hashtable_api_interface{ + { + .api_version_major = 0, + .api_version_minor = 0, + /* generic resolver using pre-sorted array */ + .resolver_callback = &elf_resolve_from_hashtable, + }, + /* pointers to application's API table boundaries */ + .table_cbegin = nfc_app_api_table.cbegin(), + .table_cend = nfc_app_api_table.cend(), +}; + +/* Casting to generic resolver to use in Composite API resolver */ +extern "C" const ElfApiInterface* const nfc_application_api_interface = + &nfc_application_hashtable_api_interface; diff --git a/applications/main/nfc/api/nfc_app_api_table_i.h b/applications/main/nfc/api/nfc_app_api_table_i.h new file mode 100644 index 000000000..08bff072e --- /dev/null +++ b/applications/main/nfc/api/nfc_app_api_table_i.h @@ -0,0 +1,13 @@ +#include "gallagher/gallagher_util.h" + +/* + * A list of app's private functions and objects to expose for plugins. + * It is used to generate a table of symbols for import resolver to use. + * TBD: automatically generate this table from app's header files + */ +static constexpr auto nfc_app_api_table = sort(create_array_t( + API_METHOD( + gallagher_deobfuscate_and_parse_credential, + void, + (GallagherCredential * credential, const uint8_t* cardholder_data_obfuscated)), + API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*))); diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 3557a2efa..44d45ff20 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -9,7 +9,7 @@ App( order=30, resources="resources", sources=[ - "*.c", + "*.c*", "!plugins", "!nfc_cli.c", ], @@ -29,6 +29,33 @@ App( sources=["plugins/supported_cards/all_in_one.c"], ) +App( + appid="microel_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="microel_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/microel.c"], +) + +App( + appid="mizip_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="mizip_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/mizip.c"], +) + +App( + appid="hi_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="hi_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/hi.c"], +) + App( appid="opal_parser", apptype=FlipperAppType.PLUGIN, @@ -155,6 +182,15 @@ App( sources=["plugins/supported_cards/zolotaya_korona_online.c"], ) +App( + appid="gallagher_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="gallagher_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/gallagher.c"], +) + App( appid="clipper_parser", apptype=FlipperAppType.PLUGIN, @@ -218,24 +254,6 @@ App( sources=["plugins/supported_cards/sonicare.c"], ) -App( - appid="mizip_parser", - apptype=FlipperAppType.PLUGIN, - entry_point="mizip_plugin_ep", - targets=["f7"], - requires=["nfc"], - sources=["plugins/supported_cards/mizip.c"], -) - -App( - appid="microel_parser", - apptype=FlipperAppType.PLUGIN, - entry_point="microel_plugin_ep", - targets=["f7"], - requires=["nfc"], - sources=["plugins/supported_cards/microel.c"], -) - App( appid="nfc_start", targets=["f7"], diff --git a/applications/main/nfc/helpers/nfc_supported_cards.c b/applications/main/nfc/helpers/nfc_supported_cards.c index 6016ae178..f5668b506 100644 --- a/applications/main/nfc/helpers/nfc_supported_cards.c +++ b/applications/main/nfc/helpers/nfc_supported_cards.c @@ -1,9 +1,11 @@ #include "nfc_supported_cards.h" +#include "../api/nfc_app_api_interface.h" #include "../plugins/supported_cards/nfc_supported_card_plugin.h" #include #include +#include #include #include @@ -45,6 +47,7 @@ typedef struct { } NfcSupportedCardsLoadContext; struct NfcSupportedCards { + CompositeApiResolver* api_resolver; NfcSupportedCardsPluginCache_t plugins_cache_arr; NfcSupportedCardsLoadState load_state; NfcSupportedCardsLoadContext* load_context; @@ -52,6 +55,11 @@ struct NfcSupportedCards { NfcSupportedCards* nfc_supported_cards_alloc() { NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); + + instance->api_resolver = composite_api_resolver_alloc(); + composite_api_resolver_add(instance->api_resolver, firmware_api_interface); + composite_api_resolver_add(instance->api_resolver, nfc_application_api_interface); + NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr); return instance; @@ -67,8 +75,9 @@ void nfc_supported_cards_free(NfcSupportedCards* instance) { NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter); furi_string_free(plugin_cache->path); } - NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr); + + composite_api_resolver_free(instance->api_resolver); free(instance); } @@ -100,15 +109,17 @@ static void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* free(instance); } -static const NfcSupportedCardsPlugin* - nfc_supported_cards_get_plugin(NfcSupportedCardsLoadContext* instance, FuriString* path) { +static const NfcSupportedCardsPlugin* nfc_supported_cards_get_plugin( + NfcSupportedCardsLoadContext* instance, + const FuriString* path, + const ElfApiInterface* api_interface) { furi_assert(instance); furi_assert(path); const NfcSupportedCardsPlugin* plugin = NULL; do { if(instance->app) flipper_application_free(instance->app); - instance->app = flipper_application_alloc(instance->storage, firmware_api_interface); + instance->app = flipper_application_alloc(instance->storage, api_interface); if(flipper_application_preload(instance->app, furi_string_get_cstr(path)) != FlipperApplicationPreloadStatusSuccess) break; @@ -129,8 +140,9 @@ static const NfcSupportedCardsPlugin* return plugin; } -static const NfcSupportedCardsPlugin* - nfc_supported_cards_get_next_plugin(NfcSupportedCardsLoadContext* instance) { +static const NfcSupportedCardsPlugin* nfc_supported_cards_get_next_plugin( + NfcSupportedCardsLoadContext* instance, + const ElfApiInterface* api_interface) { const NfcSupportedCardsPlugin* plugin = NULL; do { @@ -145,7 +157,7 @@ static const NfcSupportedCardsPlugin* path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path); - plugin = nfc_supported_cards_get_plugin(instance, instance->file_path); + plugin = nfc_supported_cards_get_plugin(instance, instance->file_path, api_interface); } while(plugin == NULL); //-V654 return plugin; @@ -162,8 +174,10 @@ void nfc_supported_cards_load_cache(NfcSupportedCards* instance) { instance->load_context = nfc_supported_cards_load_context_alloc(); while(true) { + const ElfApiInterface* api_interface = + composite_api_resolver_get(instance->api_resolver); const NfcSupportedCardsPlugin* plugin = - nfc_supported_cards_get_next_plugin(instance->load_context); + nfc_supported_cards_get_next_plugin(instance->load_context, api_interface); if(plugin == NULL) break; //-V547 NfcSupportedCardsPluginCache plugin_cache = {}; //-V779 @@ -216,8 +230,10 @@ bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nf if(plugin_cache->protocol != protocol) continue; if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue; - const NfcSupportedCardsPlugin* plugin = - nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path); + const ElfApiInterface* api_interface = + composite_api_resolver_get(instance->api_resolver); + const NfcSupportedCardsPlugin* plugin = nfc_supported_cards_get_plugin( + instance->load_context, plugin_cache->path, api_interface); if(plugin == NULL) continue; if(plugin->verify) { @@ -262,8 +278,10 @@ bool nfc_supported_cards_parse( if(plugin_cache->protocol != protocol) continue; if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue; - const NfcSupportedCardsPlugin* plugin = - nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path); + const ElfApiInterface* api_interface = + composite_api_resolver_get(instance->api_resolver); + const NfcSupportedCardsPlugin* plugin = nfc_supported_cards_get_plugin( + instance->load_context, plugin_cache->path, api_interface); if(plugin == NULL) continue; if(plugin->parse) { diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 148601191..6abaa48cd 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -199,6 +199,9 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag } else if(event.event == SubmenuIndexDictAttack) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); consumed = true; + } else if(event.event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = 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 f0a46dd87..eb6911df7 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 @@ -266,16 +266,21 @@ static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) { static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexUnlock) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); - return true; + consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); - return true; + consumed = true; + } else if(event.event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = true; } } - return false; + return consumed; } const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index aa2ed5e9b..03b131a67 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -233,6 +233,15 @@ static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { nfc_protocol_support_common_submenu_callback, instance); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneGenerateInfo)) { + submenu_add_item( + submenu, + "Change UID", + SubmenuIndexCommonEdit, + nfc_protocol_support_common_submenu_callback, + instance); + } + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { submenu_add_item( submenu, diff --git a/applications/main/nfc/helpers/slix_unlock.c b/applications/main/nfc/helpers/slix_unlock.c new file mode 100644 index 000000000..931ec1790 --- /dev/null +++ b/applications/main/nfc/helpers/slix_unlock.c @@ -0,0 +1,64 @@ +#include "slix_unlock.h" + +#include + +#define SLIX_UNLOCK_PASSWORD_NUM_MAX (2) + +struct SlixUnlock { + SlixUnlockMethod method; + SlixPassword password_arr[SLIX_UNLOCK_PASSWORD_NUM_MAX]; + size_t password_arr_len; + size_t password_idx; +}; + +static const SlixPassword tonie_box_pass_arr[] = {0x5B6EFD7F, 0x0F0F0F0F}; + +SlixUnlock* slix_unlock_alloc() { + SlixUnlock* instance = malloc(sizeof(SlixUnlock)); + + return instance; +} + +void slix_unlock_free(SlixUnlock* instance) { + furi_assert(instance); + + free(instance); +} + +void slix_unlock_reset(SlixUnlock* instance) { + furi_assert(instance); + + memset(instance, 0, sizeof(SlixUnlock)); +} + +void slix_unlock_set_method(SlixUnlock* instance, SlixUnlockMethod method) { + furi_assert(instance); + + instance->method = method; + if(method == SlixUnlockMethodTonieBox) { + instance->password_arr_len = COUNT_OF(tonie_box_pass_arr); + memcpy(instance->password_arr, tonie_box_pass_arr, sizeof(tonie_box_pass_arr)); + } +} + +void slix_unlock_set_password(SlixUnlock* instance, SlixPassword password) { + furi_assert(instance); + furi_assert(instance->method == SlixUnlockMethodManual); + + instance->password_arr[0] = password; + instance->password_arr_len = 1; +} + +bool slix_unlock_get_next_password(SlixUnlock* instance, SlixPassword* password) { + furi_assert(instance); + furi_assert(password); + + bool password_set = false; + if(instance->password_arr_len) { + *password = instance->password_arr[instance->password_idx++]; + instance->password_idx %= instance->password_arr_len; + password_set = true; + } + + return password_set; +} diff --git a/applications/main/nfc/helpers/slix_unlock.h b/applications/main/nfc/helpers/slix_unlock.h new file mode 100644 index 000000000..c378891e4 --- /dev/null +++ b/applications/main/nfc/helpers/slix_unlock.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SlixUnlockMethodManual, + SlixUnlockMethodTonieBox, +} SlixUnlockMethod; + +typedef struct SlixUnlock SlixUnlock; + +SlixUnlock* slix_unlock_alloc(); + +void slix_unlock_free(SlixUnlock* instance); + +void slix_unlock_reset(SlixUnlock* instance); + +void slix_unlock_set_method(SlixUnlock* instance, SlixUnlockMethod method); + +void slix_unlock_set_password(SlixUnlock* instance, SlixPassword password); + +bool slix_unlock_get_next_password(SlixUnlock* instance, SlixPassword* password); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index ded104f3e..528fa68ca 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -52,6 +52,7 @@ NfcApp* nfc_app_alloc() { instance->nfc = nfc_alloc(); instance->mf_ul_auth = mf_ultralight_auth_alloc(); + instance->slix_unlock = slix_unlock_alloc(); instance->mfc_key_cache = mf_classic_key_cache_alloc(); instance->nfc_supported_cards = nfc_supported_cards_alloc(); @@ -142,6 +143,7 @@ void nfc_app_free(NfcApp* instance) { nfc_free(instance->nfc); mf_ultralight_auth_free(instance->mf_ul_auth); + slix_unlock_free(instance->slix_unlock); mf_classic_key_cache_free(instance->mfc_key_cache); nfc_supported_cards_free(instance->nfc_supported_cards); diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h index cdf228752..df11c985f 100644 --- a/applications/main/nfc/nfc_app_i.h +++ b/applications/main/nfc/nfc_app_i.h @@ -33,6 +33,7 @@ #include "helpers/nfc_emv_parser.h" #include "helpers/mf_classic_key_cache.h" #include "helpers/nfc_supported_cards.h" +#include "helpers/slix_unlock.h" #include #include @@ -130,6 +131,7 @@ struct NfcApp { NfcListener* listener; MfUltralightAuth* mf_ul_auth; + SlixUnlock* slix_unlock; NfcMfClassicDictAttackContext nfc_dict_context; Mfkey32Logger* mfkey32_logger; MfUserDict* mf_user_dict; diff --git a/applications/main/nfc/plugins/supported_cards/gallagher.c b/applications/main/nfc/plugins/supported_cards/gallagher.c new file mode 100644 index 000000000..8b6a58987 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/gallagher.c @@ -0,0 +1,87 @@ +/* gallagher.c - NFC supported cards plugin for Gallagher access control cards (New Zealand). + * Author: Nick Mooney (nick@mooney.nz) + * + * Reference: https://github.com/megabug/gallagher-research +*/ + +#include "nfc_supported_card_plugin.h" +#include "../../api/gallagher/gallagher_util.h" + +#include +#include +#include +#include + +static bool gallagher_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + if(!(data->type == MfClassicType1k || data->type == MfClassicType4k)) { + return false; + } + + // It's possible for a single tag to contain multiple credentials, + // but this is currently unimplementecd. + const uint8_t credential_sector_start_block_number = + mf_classic_get_first_block_num_of_sector(GALLAGHER_CREDENTIAL_SECTOR); + + // Test 1: The first 8 bytes and the second 8 bytes should be bitwise inverses. + const uint8_t* credential_block_start_ptr = + &data->block[credential_sector_start_block_number].data[0]; + uint64_t cardholder_credential = nfc_util_bytes2num(credential_block_start_ptr, 8); + uint64_t cardholder_credential_inverse = nfc_util_bytes2num(credential_block_start_ptr + 8, 8); + // Due to endianness, this is testing the bytes in the wrong order, + // but the result still should be correct. + if(cardholder_credential != ~cardholder_credential_inverse) { + return false; + } + + // Test 2: The contents of the second block should be equal to the GALLAGHER_CARDAX_ASCII constant. + const uint8_t* cardax_block_start_ptr = + &data->block[credential_sector_start_block_number + 1].data[0]; + if(memcmp(cardax_block_start_ptr, GALLAGHER_CARDAX_ASCII, MF_CLASSIC_BLOCK_SIZE) != 0) { + return false; + } + + // Deobfuscate the credential data + GallagherCredential credential; + gallagher_deobfuscate_and_parse_credential(&credential, credential_block_start_ptr); + + char display_region = 'A'; + // Per https://github.com/megabug/gallagher-research/blob/master/formats/cardholder/cardholder.md, + // regions are generally A-P. + if(credential.region < 16) { + display_region = display_region + (char)credential.region; + } else { + display_region = '?'; + } + + furi_string_cat_printf( + parsed_data, + "\e#Gallagher NZ\nFacility %c%u\nCard %lu (IL %u)", + display_region, + credential.facility, + credential.card, + credential.issue); + return true; +} + +static const NfcSupportedCardsPlugin gallagher_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, + .read = NULL, + .parse = gallagher_parse, +}; + +static const FlipperAppPluginDescriptor gallagher_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &gallagher_plugin, +}; + +/* Plugin entry point */ +const FlipperAppPluginDescriptor* gallagher_plugin_ep() { + return &gallagher_plugin_descriptor; +} \ No newline at end of file diff --git a/applications/main/nfc/plugins/supported_cards/hi.c b/applications/main/nfc/plugins/supported_cards/hi.c new file mode 100644 index 000000000..1d390196c --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/hi.c @@ -0,0 +1,226 @@ +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#define TAG "HI!" +#define KEY_LENGTH 6 +#define HI_KEY_TO_GEN 5 +#define UID_LENGTH 7 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + MfClassicKeyPair* keys; + uint32_t verify_sector; +} HiCardConfig; + +static MfClassicKeyPair hi_1k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0x30871CF60CF1}, // 000 + {.a = 0x000000000000, .b = 0x000000000000}, // 001 + {.a = 0x000000000000, .b = 0x000000000000}, // 002 + {.a = 0x000000000000, .b = 0x000000000000}, // 003 + {.a = 0x000000000000, .b = 0x000000000000}, // 004 + {.a = 0x42FFE4C76209, .b = 0x7B30CFD04CBD}, // 005 + {.a = 0x01ED8145BDF8, .b = 0x92257F472FCE}, // 006 + {.a = 0x7583A07D21A6, .b = 0x51CA6EA8EE26}, // 007 + {.a = 0x1E10BF5D6A1D, .b = 0x87B9B9BFABA6}, // 008 + {.a = 0xF9DB1B2B21BA, .b = 0x80A781F4134C}, // 009 + {.a = 0x7F5283FACB72, .b = 0x73250009D75A}, // 010 + {.a = 0xE48E86A03078, .b = 0xCFFBBF08A254}, // 011 + {.a = 0x39AB26301F60, .b = 0xC71A6E532C83}, // 012 + {.a = 0xAD656C6C639F, .b = 0xFD9819CBD20A}, // 013 + {.a = 0xF0E15160DB3E, .b = 0x3F622D515ADD}, // 014 + {.a = 0x03F44E033C42, .b = 0x61E897875F46}, // 015 +}; + +//KDF +void hi_generate_key(uint8_t* uid, uint8_t keyA[5][KEY_LENGTH], uint8_t keyB[5][KEY_LENGTH]) { + // Static XOR table for key generation + static const uint8_t xor_table_keyB[4][6] = { + {0x1F, 0xC4, 0x4D, 0x94, 0x6A, 0x31}, + {0x12, 0xC1, 0x5C, 0x70, 0xDF, 0x31}, + {0x56, 0xF0, 0x13, 0x1B, 0x63, 0xF2}, + {0x4E, 0xFA, 0xC2, 0xF8, 0xC9, 0xCC}}; + + static const uint8_t xor_table_keyA[4][6] = { + {0xB6, 0xE6, 0xAE, 0x72, 0x91, 0x0D}, + {0x6D, 0x38, 0x50, 0xFB, 0x42, 0x89}, + {0x1E, 0x5F, 0xC7, 0xED, 0xAA, 0x02}, + {0x7E, 0xB9, 0xCA, 0xF1, 0x9C, 0x59}}; + + // Permutation table for rearranging elements in uid + static const uint8_t xorOrderA[6] = {0, 1, 2, 3, 0, 2}; + static const uint8_t xorOrderB[6] = {1, 3, 3, 2, 1, 0}; + + // Generate key based on uid and XOR table + for(uint8_t j = 1; j < 5; j++) { + for(uint8_t i = 0; i < 6; i++) { + keyA[j][i] = uid[xorOrderA[i]] ^ xor_table_keyA[j - 1][i]; + keyB[j][i] = uid[xorOrderB[i]] ^ xor_table_keyB[j - 1][i]; + } + } +} + +static bool hi_get_card_config(HiCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->verify_sector = 0; + config->keys = hi_1k_keys; + } else { + success = false; + } + + return success; +} + +static bool hi_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + HiCardConfig cfg = {}; + if(!hi_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.verify_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.verify_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.verify_sector].b, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D( + TAG, "Failed to read block %u: %d, this is not a HI card", block_num, error); + break; + } + FURI_LOG_D(TAG, "Found a HI Card"); + verified = true; + } while(false); + + return verified; +} + +static bool hi_verify(Nfc* nfc) { + return hi_verify_type(nfc, MfClassicType1k); +} + +static bool hi_read(Nfc* nfc, NfcDevice* device) { + FURI_LOG_D(TAG, "Entering HI KDF"); + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicType1k; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + HiCardConfig cfg = {}; + if(!hi_get_card_config(&cfg, data->type)) break; + + uint8_t uid[UID_LENGTH]; + memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + uint8_t keyA[HI_KEY_TO_GEN][KEY_LENGTH]; + uint8_t keyB[HI_KEY_TO_GEN][KEY_LENGTH]; + hi_generate_key(uid, keyA, keyB); + + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + if(cfg.keys[i].a == 0x000000000000 && cfg.keys[i].b == 0x000000000000) { + cfg.keys[i].a = nfc_util_bytes2num(keyA[i], KEY_LENGTH); + cfg.keys[i].b = nfc_util_bytes2num(keyB[i], KEY_LENGTH); + } + } + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error == MfClassicErrorNotPresent) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = (error == MfClassicErrorNone); + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool hi_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + HiCardConfig cfg = {}; + if(!hi_get_card_config(&cfg, data->type)) break; + + // Verify key + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b.data, 6); + if(key != cfg.keys[cfg.verify_sector].b) return false; + + //Get UID + uint8_t uid[UID_LENGTH]; + memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + //parse data + furi_string_cat_printf(parsed_data, "\e#HI! Card\n"); + furi_string_cat_printf(parsed_data, "UID:"); + for(size_t i = 0; i < UID_LENGTH; i++) { + furi_string_cat_printf(parsed_data, " %02X", uid[i]); + } + furi_string_cat_printf(parsed_data, "\n"); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin hi_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = hi_verify, + .read = hi_read, + .parse = hi_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor hi_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &hi_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* hi_plugin_ep() { + return &hi_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/microel.c b/applications/main/nfc/plugins/supported_cards/microel.c index 7259ce77d..c62d4816f 100644 --- a/applications/main/nfc/plugins/supported_cards/microel.c +++ b/applications/main/nfc/plugins/supported_cards/microel.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "Microel" #define KEY_LENGTH 6 @@ -34,38 +35,47 @@ static MfClassicKeyPair microel_1k_keys[] = { const uint8_t verify_sector = 1; -void calcolaSommaHex(const uint8_t* uid, size_t uidSize, uint8_t sommaHex[]) { +void calculateSumHex(const uint8_t* uid, size_t uidSize, uint8_t sumHex[]) { const uint8_t xorKey[] = {0x01, 0x92, 0xA7, 0x75, 0x2B, 0xF9}; - int somma = 0; + int sum = 0; for(size_t i = 0; i < uidSize; i++) { - somma += uid[i]; + sum += uid[i]; } - int sommaDueNumeri = somma % 256; + int sumTwoDigits = sum % 256; + + if(sumTwoDigits % 2 == 1) { + sumTwoDigits += 2; + } for(size_t i = 0; i < sizeof(xorKey); i++) { - sommaHex[i] = sommaDueNumeri ^ xorKey[i]; + sumHex[i] = sumTwoDigits ^ xorKey[i]; } } void generateKeyA(const uint8_t* uid, uint8_t uidSize, uint8_t keyA[]) { - uint8_t sommaHex[6]; - calcolaSommaHex(uid, uidSize, sommaHex); - uint8_t primoCarattere = (sommaHex[0] >> 4) & 0xF; + uint8_t sumHex[6]; + calculateSumHex(uid, uidSize, sumHex); + uint8_t firstCharacter = (sumHex[0] >> 4) & 0xF; - if(primoCarattere == 0x2 || primoCarattere == 0x3 || primoCarattere == 0xA || - primoCarattere == 0xB) { + if(firstCharacter == 0x2 || firstCharacter == 0x3 || firstCharacter == 0xA || + firstCharacter == 0xB) { // XOR WITH 0x40 - for(size_t i = 0; i < sizeof(sommaHex); i++) { - keyA[i] = 0x40 ^ sommaHex[i]; + for(size_t i = 0; i < sizeof(sumHex); i++) { + keyA[i] = 0x40 ^ sumHex[i]; } } else if( - primoCarattere == 0x6 || primoCarattere == 0x7 || primoCarattere == 0xE || - primoCarattere == 0xF) { + firstCharacter == 0x6 || firstCharacter == 0x7 || firstCharacter == 0xE || + firstCharacter == 0xF) { // XOR WITH 0xC0 - for(size_t i = 0; i < sizeof(sommaHex); i++) { - keyA[i] = 0xC0 ^ sommaHex[i]; + for(size_t i = 0; i < sizeof(sumHex); i++) { + keyA[i] = 0xC0 ^ sumHex[i]; + } + } else { + //Key a is the same as sumHex + for(size_t i = 0; i < sizeof(sumHex); i++) { + keyA[i] = sumHex[i]; } } } @@ -76,51 +86,6 @@ void generateKeyB(uint8_t keyA[], size_t keyASize, uint8_t keyB[]) { } } -/*static bool microel_verify(Nfc* nfc) { - furi_assert(nfc); - - bool verified = false; - - do { - uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); - FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); - - uint8_t uid[UID_LENGTH] = {0xd4, 0x23, 0xb7, 0x34}; - - size_t uid_len; - const uint8_t* uidn = mf_classic_get_uid(data, &uid_len); - FURI_LOG_D(TAG, "Microel provadiocane: UID identified: %02X%02X%02X%02X", uidn[0], uidn[1], uidn[2], uidn[3]); - if(uid_len != UID_LENGTH) break; - size_t uid_len = 0; - const uint8_t* originalUid = mf_classic_get_uid(data, &uid_len); - uint8_t uid[UID_LENGTH]; // Sostituisci UID_LENGTH con la lunghezza effettiva dell'UID - memcpy(uid, originalUid, UID_LENGTH); - FURI_LOG_D(TAG, "UID: %02X %02X %02X %02X", uid[0],uid[1],uid[2],uid[3]); - FURI_LOG_D(TAG, "UID GET: %02X %02X %02X %02X", originalUid[0],originalUid[1],originalUid[2],originalUid[3]); - //memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); - - // Generate key from uid - uint8_t keyA[KEY_LENGTH]; - generateKeyA(uid, UID_LENGTH, keyA); - - MfClassicKey key = {}; - memcpy(key.data, keyA, KEY_LENGTH); - - MfClassicAuthContext auth_ctx = {}; - MfClassicError error = - mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); - if(error != MfClassicErrorNone) { - verified = false; - FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); - break; - } - - verified = true; - } while(false); - - return verified; -}*/ - static bool microel_read(Nfc* nfc, NfcDevice* device) { FURI_LOG_D(TAG, "Entering Microel KDF"); @@ -137,16 +102,30 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) { MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); if(error != MfClassicErrorNone) break; + //Get UID and check if it is 4 bytes size_t uid_len; const uint8_t* uid = mf_classic_get_uid(data, &uid_len); FURI_LOG_D(TAG, "UID identified: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]); if(uid_len != UID_LENGTH) break; + // Generate keys uint8_t keyA[KEY_LENGTH]; uint8_t keyB[KEY_LENGTH]; generateKeyA(uid, UID_LENGTH, keyA); generateKeyB(keyA, KEY_LENGTH, keyB); + // Check key 0a to verify if it is a microel card + MfClassicKey key = {0}; + nfc_util_num2bytes(nfc_util_bytes2num(keyA, KEY_LENGTH), COUNT_OF(key.data), key.data); + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(0); // This is 0 + MfClassicAuthContext auth_context; + error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + break; + } + + // Save keys generated to stucture for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { if(microel_1k_keys[i].a == 0x000000000000) { microel_1k_keys[i].a = nfc_util_bytes2num(keyA, KEY_LENGTH); @@ -155,7 +134,6 @@ static bool microel_read(Nfc* nfc, NfcDevice* device) { microel_1k_keys[i].b = nfc_util_bytes2num(keyB, KEY_LENGTH); } } - MfClassicDeviceKeys keys = {}; for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { nfc_util_num2bytes(microel_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); @@ -232,7 +210,7 @@ static bool microel_parse(const NfcDevice* device, FuriString* parsed_data) { static const NfcSupportedCardsPlugin microel_plugin = { .protocol = NfcProtocolMfClassic, .verify = - NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 + NULL, // the verification I need is based on verifying the keys generated via uid and try to authenticate not like on mizip that there is default b0 but added verify in read function .read = microel_read, .parse = microel_parse, }; diff --git a/applications/main/nfc/plugins/supported_cards/mizip.c b/applications/main/nfc/plugins/supported_cards/mizip.c index f95555ff4..1cdc6d5d5 100644 --- a/applications/main/nfc/plugins/supported_cards/mizip.c +++ b/applications/main/nfc/plugins/supported_cards/mizip.c @@ -1,205 +1,257 @@ -#include "nfc_supported_card_plugin.h" -#include -#include -#include -#include - -#define TAG "MiZIP" -#define KEY_LENGTH 6 -#define UID_LENGTH 4 - -typedef struct { - uint64_t a; - uint64_t b; -} MfClassicKeyPair; - -static MfClassicKeyPair mizip_1k_keys[] = { - {.a = 0xa0a1a2a3a4a5, .b = 0xb4c132439eef}, // 000 - {.a = 0x000000000000, .b = 0x000000000000}, // 001 - {.a = 0x000000000000, .b = 0x000000000000}, // 002 - {.a = 0x000000000000, .b = 0x000000000000}, // 003 - {.a = 0x000000000000, .b = 0x000000000000}, // 004 -}; - -const uint8_t verify_sector = 0; - -//KDF -void mizip_generate_key(uint8_t* uid, uint8_t keyA[4][KEY_LENGTH], uint8_t keyB[4][KEY_LENGTH]) { - // Static XOR table for key generation - static const uint8_t xor_table_keyA[4][6] = { - {0x09, 0x12, 0x5A, 0x25, 0x89, 0xE5}, - {0xAB, 0x75, 0xC9, 0x37, 0x92, 0x2F}, - {0xE2, 0x72, 0x41, 0xAF, 0x2C, 0x09}, - {0x31, 0x7A, 0xB7, 0x2F, 0x44, 0x90}}; - - static const uint8_t xor_table_keyB[4][6] = { - {0xF1, 0x2C, 0x84, 0x53, 0xD8, 0x21}, - {0x73, 0xE7, 0x99, 0xFE, 0x32, 0x41}, - {0xAA, 0x4D, 0x13, 0x76, 0x56, 0xAE}, - {0xB0, 0x13, 0x27, 0x27, 0x2D, 0xFD}}; - - // Permutation table for rearranging elements in uid - static const uint8_t xorOrderA[6] = {0, 1, 2, 3, 0, 1}; - static const uint8_t xorOrderB[6] = {2, 3, 0, 1, 2, 3}; - - // Generate key based on uid and XOR table - for(uint8_t j = 1; j < 5; j++) { - for(uint8_t i = 0; i < 6; i++) { - keyA[j][i] = uid[xorOrderA[i]] ^ xor_table_keyA[j - 1][i]; - keyB[j][i] = uid[xorOrderB[i]] ^ xor_table_keyB[j - 1][i]; - } - } -} - -//Fix get uid -static bool mizip_verify(Nfc* nfc) { - bool verified = false; - - do { - const uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); - FURI_LOG_D(TAG, "Verifying sector %i", verify_sector); - - MfClassicKey key = {0}; - nfc_util_num2bytes(mizip_1k_keys[verify_sector].b, COUNT_OF(key.data), key.data); - - MfClassicAuthContext auth_context; - MfClassicError error = - mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context); - if(error != MfClassicErrorNone) { - FURI_LOG_D( - TAG, "Failed to read block %u: %d, this is not a MiZIP card", block_num, error); - break; - } - FURI_LOG_D(TAG, "Found a MiZIP Card"); - verified = true; - } while(false); - - return verified; -} - -static bool mizip_read(Nfc* nfc, NfcDevice* device) { - FURI_LOG_D(TAG, "Entering MiZIP KDF"); - furi_assert(nfc); - furi_assert(device); - - bool is_read = false; - - MfClassicData* data = mf_classic_alloc(); - nfc_device_copy_data(device, NfcProtocolMfClassic, data); - - do { - MfClassicType type = MfClassicTypeMini; - MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); - if(error != MfClassicErrorNone) break; - - //temp fix but fix mf_classic_poller_sync_detect_type because view type mfclassic1k and not verify mfmini - data->type = MfClassicTypeMini; - - uint8_t uid[UID_LENGTH]; - memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); - - uint8_t keyA[4][KEY_LENGTH]; - uint8_t keyB[4][KEY_LENGTH]; - mizip_generate_key(uid, keyA, keyB); - - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { - if(mizip_1k_keys[i].a == 0x000000000000 && mizip_1k_keys[i].b == 0x000000000000) { - mizip_1k_keys[i].a = nfc_util_bytes2num(keyA[i], KEY_LENGTH); - mizip_1k_keys[i].b = nfc_util_bytes2num(keyB[i], KEY_LENGTH); - } - } - - MfClassicDeviceKeys keys = {}; - for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { - nfc_util_num2bytes(mizip_1k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); - FURI_BIT_SET(keys.key_a_mask, i); - nfc_util_num2bytes(mizip_1k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); - FURI_BIT_SET(keys.key_b_mask, i); - } - - error = mf_classic_poller_sync_read(nfc, &keys, data); - if(error == MfClassicErrorNotPresent) { - FURI_LOG_W(TAG, "Failed to read data"); - break; - } - - nfc_device_set_data(device, NfcProtocolMfClassic, data); - - is_read = (error == MfClassicErrorNone); - } while(false); - - mf_classic_free(data); - - return is_read; -} - -static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) { - furi_assert(device); - furi_assert(parsed_data); - - const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); - - bool parsed = false; - - do { - // Verify key - MfClassicSectorTrailer* sec_tr = - mf_classic_get_sector_trailer_by_sector(data, verify_sector); - uint64_t key = nfc_util_bytes2num(sec_tr->key_b.data, 6); - if(key != mizip_1k_keys[verify_sector].b) break; - - //Get UID - uint8_t uid[UID_LENGTH]; - memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); - - //Get credit - uint8_t credit_pointer = 0x08; - uint8_t previus_credit_pointer = 0x09; - if(data->block[10].data[0] == 0x55) { - credit_pointer = 0x09; - previus_credit_pointer = 0x08; - } - uint16_t balance = (data->block[credit_pointer].data[2] << 8) | - (data->block[credit_pointer].data[1]); - uint16_t previus_balance = (data->block[previus_credit_pointer].data[2] << 8) | - (data->block[previus_credit_pointer].data[1]); - - //parse data - furi_string_cat_printf(parsed_data, "\e#MiZIP Card\n"); - furi_string_cat_printf(parsed_data, "UID:"); - for(size_t i = 0; i < UID_LENGTH; i++) { - furi_string_cat_printf(parsed_data, " %02X", uid[i]); - } - furi_string_cat_printf( - parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); - furi_string_cat_printf( - parsed_data, - "Previus Credit: %d.%02d E \n", - previus_balance / 100, - previus_balance % 100); - - parsed = true; - } while(false); - - return parsed; -} - -/* Actual implementation of app<>plugin interface */ -static const NfcSupportedCardsPlugin mizip_plugin = { - .protocol = NfcProtocolMfClassic, - .verify = mizip_verify, - .read = mizip_read, - .parse = mizip_parse, -}; - -/* Plugin descriptor to comply with basic plugin specification */ -static const FlipperAppPluginDescriptor mizip_plugin_descriptor = { - .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, - .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, - .entry_point = &mizip_plugin, -}; - -/* Plugin entry point - must return a pointer to const descriptor */ -const FlipperAppPluginDescriptor* mizip_plugin_ep() { - return &mizip_plugin_descriptor; -} +#include "nfc_supported_card_plugin.h" +#include +#include +#include +#include +#include + +#define TAG "MiZIP" +#define KEY_LENGTH 6 +#define MIZIP_KEY_TO_GEN 5 +#define UID_LENGTH 4 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + MfClassicKeyPair* keys; + uint32_t verify_sector; +} MizipCardConfig; + +static MfClassicKeyPair mizip_1k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xb4c132439eef}, // 000 + {.a = 0x000000000000, .b = 0x000000000000}, // 001 + {.a = 0x000000000000, .b = 0x000000000000}, // 002 + {.a = 0x000000000000, .b = 0x000000000000}, // 003 + {.a = 0x000000000000, .b = 0x000000000000}, // 004 + {.a = 0x0222179AB995, .b = 0x13321774F9B5}, // 005 + {.a = 0xB25CBD76A7B4, .b = 0x7571359B4274}, // 006 + {.a = 0xDA857B4907CC, .b = 0xD26B856175F7}, // 007 + {.a = 0x16D85830C443, .b = 0x8F790871A21E}, // 008 + {.a = 0x88BD5098FC82, .b = 0xFCD0D77745E4}, // 009 + {.a = 0x983349449D78, .b = 0xEA2631FBDEDD}, // 010 + {.a = 0xC599F962F3D9, .b = 0x949B70C14845}, // 011 + {.a = 0x72E668846BE8, .b = 0x45490B5AD707}, // 012 + {.a = 0xBCA105E5685E, .b = 0x248DAF9D674D}, // 013 + {.a = 0x4F6FE072D1FD, .b = 0x4250A05575FA}, // 014 + {.a = 0x56438ABE8152, .b = 0x59A45912B311}, // 015 +}; + +static MfClassicKeyPair mizip_mini_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xb4c132439eef}, // 000 + {.a = 0x000000000000, .b = 0x000000000000}, // 001 + {.a = 0x000000000000, .b = 0x000000000000}, // 002 + {.a = 0x000000000000, .b = 0x000000000000}, // 003 + {.a = 0x000000000000, .b = 0x000000000000}, // 004 +}; + +//KDF +void mizip_generate_key(uint8_t* uid, uint8_t keyA[5][KEY_LENGTH], uint8_t keyB[5][KEY_LENGTH]) { + // Static XOR table for key generation + static const uint8_t xor_table_keyA[4][6] = { + {0x09, 0x12, 0x5A, 0x25, 0x89, 0xE5}, + {0xAB, 0x75, 0xC9, 0x37, 0x92, 0x2F}, + {0xE2, 0x72, 0x41, 0xAF, 0x2C, 0x09}, + {0x31, 0x7A, 0xB7, 0x2F, 0x44, 0x90}}; + + static const uint8_t xor_table_keyB[4][6] = { + {0xF1, 0x2C, 0x84, 0x53, 0xD8, 0x21}, + {0x73, 0xE7, 0x99, 0xFE, 0x32, 0x41}, + {0xAA, 0x4D, 0x13, 0x76, 0x56, 0xAE}, + {0xB0, 0x13, 0x27, 0x27, 0x2D, 0xFD}}; + + // Permutation table for rearranging elements in uid + static const uint8_t xorOrderA[6] = {0, 1, 2, 3, 0, 1}; + static const uint8_t xorOrderB[6] = {2, 3, 0, 1, 2, 3}; + + // Generate key based on uid and XOR table + for(uint8_t j = 1; j < 5; j++) { + for(uint8_t i = 0; i < 6; i++) { + keyA[j][i] = uid[xorOrderA[i]] ^ xor_table_keyA[j - 1][i]; + keyB[j][i] = uid[xorOrderB[i]] ^ xor_table_keyB[j - 1][i]; + } + } +} + +static bool mizip_get_card_config(MizipCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->verify_sector = 0; + config->keys = mizip_1k_keys; + } else if(type == MfClassicTypeMini) { + config->verify_sector = 0; + config->keys = mizip_mini_keys; + } else { + success = false; + } + + return success; +} + +static bool mizip_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + MizipCardConfig cfg = {}; + if(!mizip_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.verify_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.verify_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.verify_sector].b, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeB, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D( + TAG, "Failed to read block %u: %d, this is not a MiZIP card", block_num, error); + break; + } + FURI_LOG_D(TAG, "Found a MiZIP Card"); + verified = true; + } while(false); + + return verified; +} + +static bool mizip_verify(Nfc* nfc) { + return mizip_verify_type(nfc, MfClassicType1k) || mizip_verify_type(nfc, MfClassicTypeMini); +} + +static bool mizip_read(Nfc* nfc, NfcDevice* device) { + FURI_LOG_D(TAG, "Entering MiZIP KDF"); + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + //temp fix but fix mf_classic_poller_sync_detect_type because view type mfclassic1k and not verify mfmini + data->type = MfClassicTypeMini; + MizipCardConfig cfg = {}; + if(!mizip_get_card_config(&cfg, data->type)) break; + + uint8_t uid[UID_LENGTH]; + memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + uint8_t keyA[MIZIP_KEY_TO_GEN][KEY_LENGTH]; + uint8_t keyB[MIZIP_KEY_TO_GEN][KEY_LENGTH]; + mizip_generate_key(uid, keyA, keyB); + + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + if(cfg.keys[i].a == 0x000000000000 && cfg.keys[i].b == 0x000000000000) { + cfg.keys[i].a = nfc_util_bytes2num(keyA[i], KEY_LENGTH); + cfg.keys[i].b = nfc_util_bytes2num(keyB[i], KEY_LENGTH); + } + } + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error == MfClassicErrorNotPresent) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = (error == MfClassicErrorNone); + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool mizip_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + MizipCardConfig cfg = {}; + if(!mizip_get_card_config(&cfg, data->type)) break; + + // Verify key + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.verify_sector); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b.data, 6); + if(key != cfg.keys[cfg.verify_sector].b) break; + + //Get UID + uint8_t uid[UID_LENGTH]; + memcpy(uid, data->iso14443_3a_data->uid, UID_LENGTH); + + //Get credit + uint8_t credit_pointer = 0x08; + uint8_t previus_credit_pointer = 0x09; + if(data->block[10].data[0] == 0x55) { + credit_pointer = 0x09; + previus_credit_pointer = 0x08; + } + uint16_t balance = (data->block[credit_pointer].data[2] << 8) | + (data->block[credit_pointer].data[1]); + uint16_t previus_balance = (data->block[previus_credit_pointer].data[2] << 8) | + (data->block[previus_credit_pointer].data[1]); + + //parse data + furi_string_cat_printf(parsed_data, "\e#MiZIP Card\n"); + furi_string_cat_printf(parsed_data, "UID:"); + for(size_t i = 0; i < UID_LENGTH; i++) { + furi_string_cat_printf(parsed_data, " %02X", uid[i]); + } + furi_string_cat_printf( + parsed_data, "\nCurrent Credit: %d.%02d E \n", balance / 100, balance % 100); + furi_string_cat_printf( + parsed_data, + "Previus Credit: %d.%02d E \n", + previus_balance / 100, + previus_balance % 100); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin mizip_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = mizip_verify, + .read = mizip_read, + .parse = mizip_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor mizip_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &mizip_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* mizip_plugin_ep() { + return &mizip_plugin_descriptor; +} diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index ad500b717..94d845fa3 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -61,4 +61,9 @@ ADD_SCENE(nfc, set_sak, SetSak) ADD_SCENE(nfc, set_atqa, SetAtqa) ADD_SCENE(nfc, set_uid, SetUid) +ADD_SCENE(nfc, slix_unlock_menu, SlixUnlockMenu) +ADD_SCENE(nfc, slix_key_input, SlixKeyInput) +ADD_SCENE(nfc, slix_unlock, SlixUnlock) +ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess) + ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index d14f80b62..2943c0c55 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexSlixUnlock, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -34,6 +35,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, instance); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexSlixUnlock, + nfc_scene_extra_actions_submenu_callback, + instance); submenu_set_selected_item( submenu, scene_manager_get_scene_state(instance->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); @@ -54,6 +61,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexReadCardType) { scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); consumed = true; + } else if(event.event == SubmenuIndexSlixUnlock) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlockMenu); + consumed = true; } scene_manager_set_scene_state(instance->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index 622996094..b6ad8144d 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -28,7 +28,11 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSlixUnlock)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSlixUnlock); + } else if(scene_manager_has_previous_scene( + nfc->scene_manager, NfcSceneMfClassicDictAttack)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicDictAttack); } else if(scene_manager_has_previous_scene( diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 7376ce0bc..cce7ca525 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -67,6 +67,10 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); consumed = true; } + } else if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneReadMenu)) { + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneReadMenu); + consumed = true; } else { if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) mfclassic_sync_uid(instance->nfc_device); diff --git a/applications/main/nfc/scenes/nfc_scene_slix_key_input.c b/applications/main/nfc/scenes/nfc_scene_slix_key_input.c new file mode 100644 index 000000000..1724eea8d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_slix_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_app_i.h" + +#include + +void nfc_scene_slix_key_input_byte_input_callback(void* context) { + NfcApp* instance = context; + + SlixPassword password = nfc_util_bytes2num(instance->byte_input_store, sizeof(SlixPassword)); + slix_unlock_set_password(instance->slix_unlock, password); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_slix_key_input_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + ByteInput* byte_input = instance->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_slix_key_input_byte_input_callback, + NULL, + instance, + instance->byte_input_store, + sizeof(SlixPassword)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_slix_key_input_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlock); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_slix_key_input_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_slix_unlock.c b/applications/main/nfc/scenes/nfc_scene_slix_unlock.c new file mode 100644 index 000000000..b01876e06 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_slix_unlock.c @@ -0,0 +1,70 @@ +#include "../nfc_app_i.h" + +#include + +NfcCommand nfc_scene_slix_unlock_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolSlix); + + NfcCommand command = NfcCommandContinue; + + NfcApp* instance = context; + SlixPollerEvent* slix_event = event.event_data; + if(slix_event->type == SlixPollerEventTypePrivacyUnlockRequest) { + SlixPassword pwd = 0; + bool get_password_success = slix_unlock_get_next_password(instance->slix_unlock, &pwd); + slix_event->data->privacy_password.password = pwd; + slix_event->data->privacy_password.password_set = get_password_success; + } else if(slix_event->type == SlixPollerEventTypeError) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + } else if(slix_event->type == SlixPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + + return command; +} + +void nfc_scene_slix_unlock_on_enter(void* context) { + NfcApp* instance = context; + + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + popup_set_header(instance->popup, "Unlocking", 97, 15, AlignCenter, AlignTop); + popup_set_text( + instance->popup, "Apply card to\nFlipper's back", 97, 27, AlignCenter, AlignTop); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolSlix); + nfc_poller_start(instance->poller, nfc_scene_slix_unlock_worker_callback, instance); +} + +bool nfc_scene_slix_unlock_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + UNUSED(instance); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventPollerFailure) { + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlockSuccess); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSlixUnlockMenu); + } + + return consumed; +} + +void nfc_scene_slix_unlock_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_slix_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_slix_unlock_menu.c new file mode 100644 index 000000000..78eb9884e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_slix_unlock_menu.c @@ -0,0 +1,60 @@ +#include "../nfc_app_i.h" + +enum SubmenuIndex { + SubmenuIndexSlixUnlockMenuManual, + SubmenuIndexSlixUnlockMenuTonieBox, +}; + +void nfc_scene_slix_unlock_menu_submenu_callback(void* context, uint32_t index) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_scene_slix_unlock_menu_on_enter(void* context) { + NfcApp* instance = context; + Submenu* submenu = instance->submenu; + + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneSlixUnlockMenu); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexSlixUnlockMenuManual, + nfc_scene_slix_unlock_menu_submenu_callback, + instance); + submenu_add_item( + submenu, + "Auth As TommyBox", + SubmenuIndexSlixUnlockMenuTonieBox, + nfc_scene_slix_unlock_menu_submenu_callback, + instance); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_slix_unlock_menu_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSlixUnlockMenuManual) { + slix_unlock_set_method(instance->slix_unlock, SlixUnlockMethodManual); + scene_manager_next_scene(instance->scene_manager, NfcSceneSlixKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexSlixUnlockMenuTonieBox) { + slix_unlock_set_method(instance->slix_unlock, SlixUnlockMethodTonieBox); + scene_manager_next_scene(instance->scene_manager, NfcSceneSlixUnlock); + consumed = true; + } + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneSlixUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_slix_unlock_menu_on_exit(void* context) { + NfcApp* instance = context; + + submenu_reset(instance->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_slix_unlock_success.c b/applications/main/nfc/scenes/nfc_scene_slix_unlock_success.c new file mode 100644 index 000000000..f25eabab2 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_slix_unlock_success.c @@ -0,0 +1,71 @@ +#include "../nfc_app_i.h" + +static void nfc_scene_slix_unlock_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_slix_unlock_success_on_enter(void* context) { + NfcApp* instance = context; + + Widget* widget = instance->widget; + widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "SLIX Unlocked!"); + + FuriString* temp_str = furi_string_alloc_set_str("UID:"); + size_t uid_len = 0; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", uid[i]); + } + furi_string_cat_printf(temp_str, "\nPrivacy Mode: Disabled"); + widget_add_string_multiline_element( + widget, 0, 12, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_slix_unlock_success_widget_callback, + instance); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "More", + nfc_scene_slix_unlock_success_widget_callback, + instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_slix_unlock_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(instance->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +void nfc_scene_slix_unlock_success_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index e475c87f1..1f4f94e44 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -8,7 +8,6 @@ ADD_SCENE(subghz, saved, Saved) ADD_SCENE(subghz, transmitter, Transmitter) ADD_SCENE(subghz, show_error, ShowError) ADD_SCENE(subghz, show_error_sub, ShowErrorSub) -ADD_SCENE(subghz, show_only_rx, ShowOnlyRx) ADD_SCENE(subghz, saved_menu, SavedMenu) ADD_SCENE(subghz, delete, Delete) ADD_SCENE(subghz, delete_success, DeleteSuccess) diff --git a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c deleted file mode 100644 index ffdc1163a..000000000 --- a/applications/main/subghz/scenes/subghz_scene_show_only_rx.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../subghz_i.h" -#include "../helpers/subghz_custom_event.h" - -void subghz_scene_show_only_rx_popup_callback(void* context) { - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneShowOnlyRX); -} - -void subghz_scene_show_only_rx_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - Popup* popup = subghz->popup; - - const char* header_text = "Transmission is blocked"; - const char* message_text = "This frequency is\noutside of default\nrange"; - - popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop); - popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop); - popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); - - popup_set_timeout(popup, 1500); - popup_set_context(popup, subghz); - popup_set_callback(popup, subghz_scene_show_only_rx_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); -} - -bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneShowOnlyRX) { - scene_manager_previous_scene(subghz->scene_manager); - return true; - } - } - return false; -} - -void subghz_scene_show_only_rx_on_exit(void* context) { - SubGhz* subghz = context; - Popup* popup = subghz->popup; - - popup_reset(popup); -} diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 0482d78dc..6a98c64c7 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -50,8 +50,8 @@ void subghz_dialog_message_freq_error(SubGhz* subghz, bool only_rx) { message_text = "Frequency\nis outside of\ndefault range.\nCheck docs."; } - dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop); - dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop); + dialog_message_set_header(message, header_text, 63, 0, AlignCenter, AlignTop); + dialog_message_set_text(message, message_text, 1, 13, AlignLeft, AlignTop); dialog_message_set_icon(message, &I_WarningDolphinFlip_45x42, 83, 22); diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index e44c5d763..1fe979e0c 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -6,6 +6,8 @@ #define TAG "CliSrv" +#define CLI_INPUT_LEN_LIMIT 256 + Cli* cli_alloc() { Cli* cli = malloc(sizeof(Cli)); @@ -348,7 +350,9 @@ void cli_process_input(Cli* cli) { cli_handle_backspace(cli); } else if(in_chr == CliSymbolAsciiCR) { cli_handle_enter(cli); - } else if(in_chr >= 0x20 && in_chr < 0x7F) { //-V560 + } else if( + (in_chr >= 0x20 && in_chr < 0x7F) && //-V560 + (furi_string_size(cli->line) < CLI_INPUT_LEN_LIMIT)) { if(cli->cursor_position == furi_string_size(cli->line)) { furi_string_push_back(cli->line, in_chr); cli_putc(cli, in_chr); diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index 3a2217f25..5b834b48d 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -60,7 +60,7 @@ static void expansion_detect_callback(void* context) { ExpansionMessage message = { .type = ExpansionMessageTypeModuleConnected, - .api_lock = NULL, // Not locking the API here + .api_lock = NULL, // Not locking the API here to avoid a deadlock }; // Not waiting for available queue space, discarding message if there is none @@ -74,7 +74,7 @@ static void expansion_worker_callback(void* context) { ExpansionMessage message = { .type = ExpansionMessageTypeModuleDisconnected, - .api_lock = NULL, // Not locking the API here + .api_lock = NULL, // Not locking the API here to avoid a deadlock }; const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever); diff --git a/applications/services/expansion/expansion_worker.c b/applications/services/expansion/expansion_worker.c index c2219a40d..fd92063d2 100644 --- a/applications/services/expansion/expansion_worker.c +++ b/applications/services/expansion/expansion_worker.c @@ -317,18 +317,6 @@ static inline void expansion_worker_state_machine(ExpansionWorker* instance) { } } -static void expansion_worker_pending_callback(void* context, uint32_t arg) { - furi_assert(context); - UNUSED(arg); - - ExpansionWorker* instance = context; - furi_thread_join(instance->thread); - - if(instance->callback != NULL) { - instance->callback(instance->cb_context); - } -} - static int32_t expansion_worker(void* context) { furi_assert(context); ExpansionWorker* instance = context; @@ -361,9 +349,9 @@ static int32_t expansion_worker(void* context) { furi_hal_serial_control_release(instance->serial_handle); furi_hal_power_insomnia_exit(); - // Do not invoke pending callback on user-requested exit - if(instance->exit_reason != ExpansionWorkerExitReasonUser) { - furi_timer_pending_callback(expansion_worker_pending_callback, instance, 0); + // Do not invoke worker callback on user-requested exit + if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) { + instance->callback(instance->cb_context); } return 0; @@ -385,6 +373,7 @@ ExpansionWorker* expansion_worker_alloc(FuriHalSerialId serial_id) { void expansion_worker_free(ExpansionWorker* instance) { furi_stream_buffer_free(instance->rx_buf); + furi_thread_join(instance->thread); furi_thread_free(instance->thread); free(instance); } diff --git a/applications/system/hid_app/assets/Alt_17x10.png b/applications/system/hid_app/assets/Alt_17x10.png new file mode 100644 index 000000000..93041d250 Binary files /dev/null and b/applications/system/hid_app/assets/Alt_17x10.png differ diff --git a/applications/system/hid_app/assets/Alt_pressed_17x10.png b/applications/system/hid_app/assets/Alt_pressed_17x10.png new file mode 100644 index 000000000..e7421c8c0 Binary files /dev/null and b/applications/system/hid_app/assets/Alt_pressed_17x10.png differ diff --git a/applications/system/hid_app/assets/Backspace_9x7.png b/applications/system/hid_app/assets/Backspace_9x7.png new file mode 100644 index 000000000..e098cf676 Binary files /dev/null and b/applications/system/hid_app/assets/Backspace_9x7.png differ diff --git a/applications/system/hid_app/assets/Cmd_17x10.png b/applications/system/hid_app/assets/Cmd_17x10.png new file mode 100644 index 000000000..26ca3395c Binary files /dev/null and b/applications/system/hid_app/assets/Cmd_17x10.png differ diff --git a/applications/system/hid_app/assets/Cmd_pressed_17x10.png b/applications/system/hid_app/assets/Cmd_pressed_17x10.png new file mode 100644 index 000000000..274c3e070 Binary files /dev/null and b/applications/system/hid_app/assets/Cmd_pressed_17x10.png differ diff --git a/applications/system/hid_app/assets/Ctrl_17x10.png b/applications/system/hid_app/assets/Ctrl_17x10.png new file mode 100644 index 000000000..0eda72160 Binary files /dev/null and b/applications/system/hid_app/assets/Ctrl_17x10.png differ diff --git a/applications/system/hid_app/assets/Ctrl_pressed_17x10.png b/applications/system/hid_app/assets/Ctrl_pressed_17x10.png new file mode 100644 index 000000000..978a1090c Binary files /dev/null and b/applications/system/hid_app/assets/Ctrl_pressed_17x10.png differ diff --git a/applications/system/hid_app/assets/Del_17x10.png b/applications/system/hid_app/assets/Del_17x10.png new file mode 100644 index 000000000..13d736983 Binary files /dev/null and b/applications/system/hid_app/assets/Del_17x10.png differ diff --git a/applications/system/hid_app/assets/Esc_17x10.png b/applications/system/hid_app/assets/Esc_17x10.png new file mode 100644 index 000000000..6a011e97a Binary files /dev/null and b/applications/system/hid_app/assets/Esc_17x10.png differ diff --git a/applications/system/hid_app/assets/KB_key_Alt_17x10.png b/applications/system/hid_app/assets/KB_key_Alt_17x10.png deleted file mode 100644 index 26c4f7315..000000000 Binary files a/applications/system/hid_app/assets/KB_key_Alt_17x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/KB_key_Cmd_17x10.png b/applications/system/hid_app/assets/KB_key_Cmd_17x10.png deleted file mode 100644 index 8d87ffde6..000000000 Binary files a/applications/system/hid_app/assets/KB_key_Cmd_17x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/KB_key_Ctl_17x10.png b/applications/system/hid_app/assets/KB_key_Ctl_17x10.png deleted file mode 100644 index 01f3157c9..000000000 Binary files a/applications/system/hid_app/assets/KB_key_Ctl_17x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/KB_key_Del_17x10.png b/applications/system/hid_app/assets/KB_key_Del_17x10.png deleted file mode 100644 index 62bad18b5..000000000 Binary files a/applications/system/hid_app/assets/KB_key_Del_17x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/KB_key_Esc_17x10.png b/applications/system/hid_app/assets/KB_key_Esc_17x10.png deleted file mode 100644 index 7a9fdda22..000000000 Binary files a/applications/system/hid_app/assets/KB_key_Esc_17x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/KB_key_Tab_17x10.png b/applications/system/hid_app/assets/KB_key_Tab_17x10.png deleted file mode 100644 index d904dd418..000000000 Binary files a/applications/system/hid_app/assets/KB_key_Tab_17x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/Like_pressed_17x17.png b/applications/system/hid_app/assets/Like_pressed_17x17.png deleted file mode 100644 index f5bf276f3..000000000 Binary files a/applications/system/hid_app/assets/Like_pressed_17x17.png and /dev/null differ diff --git a/applications/system/hid_app/assets/Mic_btn_8x10.png b/applications/system/hid_app/assets/Mic_btn_8x10.png deleted file mode 100644 index a08bb35dd..000000000 Binary files a/applications/system/hid_app/assets/Mic_btn_8x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png b/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png deleted file mode 100644 index 929992022..000000000 Binary files a/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png and /dev/null differ diff --git a/applications/system/hid_app/assets/Return_10x7.png b/applications/system/hid_app/assets/Return_10x7.png new file mode 100644 index 000000000..ffa1e9aca Binary files /dev/null and b/applications/system/hid_app/assets/Return_10x7.png differ diff --git a/applications/system/hid_app/assets/Shift_pressed_7x10.png b/applications/system/hid_app/assets/Shift_pressed_7x10.png new file mode 100644 index 000000000..4f6b4b58e Binary files /dev/null and b/applications/system/hid_app/assets/Shift_pressed_7x10.png differ diff --git a/applications/system/hid_app/assets/Tab_17x10.png b/applications/system/hid_app/assets/Tab_17x10.png new file mode 100644 index 000000000..c62d75c0d Binary files /dev/null and b/applications/system/hid_app/assets/Tab_17x10.png differ diff --git a/applications/system/hid_app/views/hid_keyboard.c b/applications/system/hid_app/views/hid_keyboard.c index 1ce0285b2..1ee2c01c9 100644 --- a/applications/system/hid_app/views/hid_keyboard.c +++ b/applications/system/hid_app/views/hid_keyboard.c @@ -20,19 +20,18 @@ typedef struct { uint8_t x; uint8_t y; uint8_t last_key_code; - uint16_t modifier_code; bool ok_pressed; bool back_pressed; bool connected; - char key_string[5]; HidTransport transport; } HidKeyboardModel; typedef struct { uint8_t width; - char* key; + char key; + char shift_key; const Icon* icon; - char* shift_key; + const Icon* icon_toggled; uint8_t value; } HidKeyboardKey; @@ -49,7 +48,7 @@ typedef struct { #define ROW_COUNT 7 #define COLUMN_COUNT 12 -// 0 width items are not drawn, but there value is used +// 0 width items are not drawn, but their value is used const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { { {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, @@ -66,102 +65,107 @@ const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12}, }, { - {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, - {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, - {.width = 1, .icon = NULL, .key = "3", .shift_key = "#", .value = HID_KEYBOARD_3}, - {.width = 1, .icon = NULL, .key = "4", .shift_key = "$", .value = HID_KEYBOARD_4}, - {.width = 1, .icon = NULL, .key = "5", .shift_key = "%", .value = HID_KEYBOARD_5}, - {.width = 1, .icon = NULL, .key = "6", .shift_key = "^", .value = HID_KEYBOARD_6}, - {.width = 1, .icon = NULL, .key = "7", .shift_key = "&", .value = HID_KEYBOARD_7}, - {.width = 1, .icon = NULL, .key = "8", .shift_key = "*", .value = HID_KEYBOARD_8}, - {.width = 1, .icon = NULL, .key = "9", .shift_key = "(", .value = HID_KEYBOARD_9}, - {.width = 1, .icon = NULL, .key = "0", .shift_key = ")", .value = HID_KEYBOARD_0}, - {.width = 2, .icon = &I_Pin_arrow_left_9x7, .value = HID_KEYBOARD_DELETE}, + {.width = 1, .icon = NULL, .key = '1', .shift_key = '!', .value = HID_KEYBOARD_1}, + {.width = 1, .icon = NULL, .key = '2', .shift_key = '@', .value = HID_KEYBOARD_2}, + {.width = 1, .icon = NULL, .key = '3', .shift_key = '#', .value = HID_KEYBOARD_3}, + {.width = 1, .icon = NULL, .key = '4', .shift_key = '$', .value = HID_KEYBOARD_4}, + {.width = 1, .icon = NULL, .key = '5', .shift_key = '%', .value = HID_KEYBOARD_5}, + {.width = 1, .icon = NULL, .key = '6', .shift_key = '^', .value = HID_KEYBOARD_6}, + {.width = 1, .icon = NULL, .key = '7', .shift_key = '&', .value = HID_KEYBOARD_7}, + {.width = 1, .icon = NULL, .key = '8', .shift_key = '*', .value = HID_KEYBOARD_8}, + {.width = 1, .icon = NULL, .key = '9', .shift_key = '(', .value = HID_KEYBOARD_9}, + {.width = 1, .icon = NULL, .key = '0', .shift_key = ')', .value = HID_KEYBOARD_0}, + {.width = 2, .icon = &I_Backspace_9x7, .value = HID_KEYBOARD_DELETE}, {.width = 0, .value = HID_KEYBOARD_DELETE}, }, { - {.width = 1, .icon = NULL, .key = "q", .shift_key = "Q", .value = HID_KEYBOARD_Q}, - {.width = 1, .icon = NULL, .key = "w", .shift_key = "W", .value = HID_KEYBOARD_W}, - {.width = 1, .icon = NULL, .key = "e", .shift_key = "E", .value = HID_KEYBOARD_E}, - {.width = 1, .icon = NULL, .key = "r", .shift_key = "R", .value = HID_KEYBOARD_R}, - {.width = 1, .icon = NULL, .key = "t", .shift_key = "T", .value = HID_KEYBOARD_T}, - {.width = 1, .icon = NULL, .key = "y", .shift_key = "Y", .value = HID_KEYBOARD_Y}, - {.width = 1, .icon = NULL, .key = "u", .shift_key = "U", .value = HID_KEYBOARD_U}, - {.width = 1, .icon = NULL, .key = "i", .shift_key = "I", .value = HID_KEYBOARD_I}, - {.width = 1, .icon = NULL, .key = "o", .shift_key = "O", .value = HID_KEYBOARD_O}, - {.width = 1, .icon = NULL, .key = "p", .shift_key = "P", .value = HID_KEYBOARD_P}, - {.width = 1, .icon = NULL, .key = "[", .shift_key = "{", .value = HID_KEYBOARD_OPEN_BRACKET}, + {.width = 1, .icon = NULL, .key = 'q', .shift_key = 'Q', .value = HID_KEYBOARD_Q}, + {.width = 1, .icon = NULL, .key = 'w', .shift_key = 'W', .value = HID_KEYBOARD_W}, + {.width = 1, .icon = NULL, .key = 'e', .shift_key = 'E', .value = HID_KEYBOARD_E}, + {.width = 1, .icon = NULL, .key = 'r', .shift_key = 'R', .value = HID_KEYBOARD_R}, + {.width = 1, .icon = NULL, .key = 't', .shift_key = 'T', .value = HID_KEYBOARD_T}, + {.width = 1, .icon = NULL, .key = 'y', .shift_key = 'Y', .value = HID_KEYBOARD_Y}, + {.width = 1, .icon = NULL, .key = 'u', .shift_key = 'U', .value = HID_KEYBOARD_U}, + {.width = 1, .icon = NULL, .key = 'i', .shift_key = 'I', .value = HID_KEYBOARD_I}, + {.width = 1, .icon = NULL, .key = 'o', .shift_key = 'O', .value = HID_KEYBOARD_O}, + {.width = 1, .icon = NULL, .key = 'p', .shift_key = 'P', .value = HID_KEYBOARD_P}, + {.width = 1, .icon = NULL, .key = '[', .shift_key = '{', .value = HID_KEYBOARD_OPEN_BRACKET}, {.width = 1, .icon = NULL, - .key = "]", - .shift_key = "}", + .key = ']', + .shift_key = '}', .value = HID_KEYBOARD_CLOSE_BRACKET}, }, { - {.width = 1, .icon = NULL, .key = "a", .shift_key = "A", .value = HID_KEYBOARD_A}, - {.width = 1, .icon = NULL, .key = "s", .shift_key = "S", .value = HID_KEYBOARD_S}, - {.width = 1, .icon = NULL, .key = "d", .shift_key = "D", .value = HID_KEYBOARD_D}, - {.width = 1, .icon = NULL, .key = "f", .shift_key = "F", .value = HID_KEYBOARD_F}, - {.width = 1, .icon = NULL, .key = "g", .shift_key = "G", .value = HID_KEYBOARD_G}, - {.width = 1, .icon = NULL, .key = "h", .shift_key = "H", .value = HID_KEYBOARD_H}, - {.width = 1, .icon = NULL, .key = "j", .shift_key = "J", .value = HID_KEYBOARD_J}, - {.width = 1, .icon = NULL, .key = "k", .shift_key = "K", .value = HID_KEYBOARD_K}, - {.width = 1, .icon = NULL, .key = "l", .shift_key = "L", .value = HID_KEYBOARD_L}, - {.width = 1, .icon = NULL, .key = ";", .shift_key = ":", .value = HID_KEYBOARD_SEMICOLON}, - {.width = 2, .icon = &I_Pin_arrow_right_9x7, .value = HID_KEYBOARD_RETURN}, + {.width = 1, .icon = NULL, .key = 'a', .shift_key = 'A', .value = HID_KEYBOARD_A}, + {.width = 1, .icon = NULL, .key = 's', .shift_key = 'S', .value = HID_KEYBOARD_S}, + {.width = 1, .icon = NULL, .key = 'd', .shift_key = 'D', .value = HID_KEYBOARD_D}, + {.width = 1, .icon = NULL, .key = 'f', .shift_key = 'F', .value = HID_KEYBOARD_F}, + {.width = 1, .icon = NULL, .key = 'g', .shift_key = 'G', .value = HID_KEYBOARD_G}, + {.width = 1, .icon = NULL, .key = 'h', .shift_key = 'H', .value = HID_KEYBOARD_H}, + {.width = 1, .icon = NULL, .key = 'j', .shift_key = 'J', .value = HID_KEYBOARD_J}, + {.width = 1, .icon = NULL, .key = 'k', .shift_key = 'K', .value = HID_KEYBOARD_K}, + {.width = 1, .icon = NULL, .key = 'l', .shift_key = 'L', .value = HID_KEYBOARD_L}, + {.width = 1, .icon = NULL, .key = ';', .shift_key = ':', .value = HID_KEYBOARD_SEMICOLON}, + {.width = 2, .icon = &I_Return_10x7, .value = HID_KEYBOARD_RETURN}, {.width = 0, .value = HID_KEYBOARD_RETURN}, }, { - {.width = 1, .icon = NULL, .key = "z", .shift_key = "Z", .value = HID_KEYBOARD_Z}, - {.width = 1, .icon = NULL, .key = "x", .shift_key = "X", .value = HID_KEYBOARD_X}, - {.width = 1, .icon = NULL, .key = "c", .shift_key = "C", .value = HID_KEYBOARD_C}, - {.width = 1, .icon = NULL, .key = "v", .shift_key = "V", .value = HID_KEYBOARD_V}, - {.width = 1, .icon = NULL, .key = "b", .shift_key = "B", .value = HID_KEYBOARD_B}, - {.width = 1, .icon = NULL, .key = "n", .shift_key = "N", .value = HID_KEYBOARD_N}, - {.width = 1, .icon = NULL, .key = "m", .shift_key = "M", .value = HID_KEYBOARD_M}, - {.width = 1, .icon = NULL, .key = "/", .shift_key = "?", .value = HID_KEYBOARD_SLASH}, - {.width = 1, .icon = NULL, .key = "\\", .shift_key = "|", .value = HID_KEYBOARD_BACKSLASH}, - {.width = 1, .icon = NULL, .key = "`", .shift_key = "~", .value = HID_KEYBOARD_GRAVE_ACCENT}, + {.width = 1, .icon = NULL, .key = 'z', .shift_key = 'Z', .value = HID_KEYBOARD_Z}, + {.width = 1, .icon = NULL, .key = 'x', .shift_key = 'X', .value = HID_KEYBOARD_X}, + {.width = 1, .icon = NULL, .key = 'c', .shift_key = 'C', .value = HID_KEYBOARD_C}, + {.width = 1, .icon = NULL, .key = 'v', .shift_key = 'V', .value = HID_KEYBOARD_V}, + {.width = 1, .icon = NULL, .key = 'b', .shift_key = 'B', .value = HID_KEYBOARD_B}, + {.width = 1, .icon = NULL, .key = 'n', .shift_key = 'N', .value = HID_KEYBOARD_N}, + {.width = 1, .icon = NULL, .key = 'm', .shift_key = 'M', .value = HID_KEYBOARD_M}, + {.width = 1, .icon = NULL, .key = '/', .shift_key = '?', .value = HID_KEYBOARD_SLASH}, + {.width = 1, .icon = NULL, .key = '\\', .shift_key = '|', .value = HID_KEYBOARD_BACKSLASH}, + {.width = 1, .icon = NULL, .key = '`', .shift_key = '~', .value = HID_KEYBOARD_GRAVE_ACCENT}, {.width = 1, .icon = &I_ButtonUp_7x4, .value = HID_KEYBOARD_UP_ARROW}, - {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, + {.width = 1, .icon = NULL, .key = '-', .shift_key = '_', .value = HID_KEYBOARD_MINUS}, }, { - {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, - {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA}, - {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, - {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, + {.width = 1, + .icon = &I_Pin_arrow_up_7x9, + .icon_toggled = &I_Shift_pressed_7x10, + .value = HID_KEYBOARD_L_SHIFT}, + {.width = 1, .icon = NULL, .key = ',', .shift_key = '<', .value = HID_KEYBOARD_COMMA}, + {.width = 1, .icon = NULL, .key = '.', .shift_key = '>', .value = HID_KEYBOARD_DOT}, + {.width = 4, .icon = NULL, .key = ' ', .value = HID_KEYBOARD_SPACEBAR}, {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 1, .icon = NULL, .key = "'", .shift_key = "\"", .value = HID_KEYBOARD_APOSTROPHE}, - {.width = 1, .icon = NULL, .key = "=", .shift_key = "+", .value = HID_KEYBOARD_EQUAL_SIGN}, + {.width = 1, .icon = NULL, .key = '\'', .shift_key = '\"', .value = HID_KEYBOARD_APOSTROPHE}, + {.width = 1, .icon = NULL, .key = '=', .shift_key = '+', .value = HID_KEYBOARD_EQUAL_SIGN}, {.width = 1, .icon = &I_ButtonLeft_4x7, .value = HID_KEYBOARD_LEFT_ARROW}, {.width = 1, .icon = &I_ButtonDown_7x4, .value = HID_KEYBOARD_DOWN_ARROW}, {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, }, { - {.width = 2, .icon = &I_KB_key_Ctl_17x10, .value = HID_KEYBOARD_L_CTRL}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, - {.width = 2, .icon = &I_KB_key_Alt_17x10, .value = HID_KEYBOARD_L_ALT}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, - {.width = 2, .icon = &I_KB_key_Cmd_17x10, .value = HID_KEYBOARD_L_GUI}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, - {.width = 2, .icon = &I_KB_key_Tab_17x10, .value = HID_KEYBOARD_TAB}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, - {.width = 2, .icon = &I_KB_key_Esc_17x10, .value = HID_KEYBOARD_ESCAPE}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE}, - {.width = 2, .icon = &I_KB_key_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD}, + {.width = 2, + .icon = &I_Ctrl_17x10, + .icon_toggled = &I_Ctrl_pressed_17x10, + .value = HID_KEYBOARD_L_CTRL}, + {.width = 0, .value = HID_KEYBOARD_L_CTRL}, + {.width = 2, + .icon = &I_Alt_17x10, + .icon_toggled = &I_Alt_pressed_17x10, + .value = HID_KEYBOARD_L_ALT}, + {.width = 0, .value = HID_KEYBOARD_L_ALT}, + {.width = 2, + .icon = &I_Cmd_17x10, + .icon_toggled = &I_Cmd_pressed_17x10, + .value = HID_KEYBOARD_L_GUI}, + {.width = 0, .value = HID_KEYBOARD_L_GUI}, + {.width = 2, .icon = &I_Tab_17x10, .value = HID_KEYBOARD_TAB}, + {.width = 0, .value = HID_KEYBOARD_TAB}, + {.width = 2, .icon = &I_Esc_17x10, .value = HID_KEYBOARD_ESCAPE}, + {.width = 0, .value = HID_KEYBOARD_ESCAPE}, + {.width = 2, .icon = &I_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD}, + {.width = 0, .value = HID_KEYBOARD_DELETE_FORWARD}, }, }; -static void hid_keyboard_to_upper(char* str) { - while(*str) { - *str = toupper((unsigned char)*str); - str++; - } -} - static void hid_keyboard_draw_key( Canvas* canvas, HidKeyboardModel* model, @@ -192,28 +196,32 @@ static void hid_keyboard_draw_key( KEY_HEIGHT); } if(key.icon != NULL) { + const Icon* key_icon = key.icon; + if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || + (model->alt && key.value == HID_KEYBOARD_L_ALT) || + (model->shift && key.value == HID_KEYBOARD_L_SHIFT) || + (model->gui && key.value == HID_KEYBOARD_L_GUI)) { + if(key.icon_toggled) { + key_icon = key.icon_toggled; + } + } // Draw the icon centered on the button canvas_draw_icon( canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2, - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key.icon->height / 2, - key.icon); + MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key_icon->width / 2, + MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key_icon->height / 2, + key_icon); } else { + char key_str[2] = "\0\0"; // If shift is toggled use the shift key when available - strcpy(model->key_string, (model->shift && key.shift_key != 0) ? key.shift_key : key.key); - // Upper case if ctrl or alt was toggled true - if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || - (model->alt && key.value == HID_KEYBOARD_L_ALT) || - (model->gui && key.value == HID_KEYBOARD_L_GUI)) { - hid_keyboard_to_upper(model->key_string); - } + key_str[0] = (model->shift && key.shift_key != 0) ? key.shift_key : key.key; canvas_draw_str_aligned( canvas, MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1, MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2, AlignCenter, AlignCenter, - model->key_string); + key_str); } } @@ -244,6 +252,8 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { initY = model->y - 4; } + elements_scrollbar(canvas, initY, 3); + for(uint8_t y = initY; y < ROW_COUNT; y++) { const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; uint8_t x = 0; @@ -286,6 +296,14 @@ static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoin 0); // Skip zero width keys, pretend they are one key } +static void hid_keyboard_modifier_set(Hid* hid, uint16_t keycode, bool is_pressed) { + if(is_pressed) { + hid_hal_keyboard_press(hid, keycode); + } else { + hid_hal_keyboard_release(hid, keycode); + } +} + static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { with_view_model( hid_keyboard->view, @@ -300,35 +318,25 @@ static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { // Toggle the modifier key when clicked, and click the key if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { model->shift = !model->shift; - if(model->shift) - model->modifier_code |= KEY_MOD_LEFT_SHIFT; - else - model->modifier_code &= ~KEY_MOD_LEFT_SHIFT; + hid_keyboard_modifier_set( + hid_keyboard->hid, KEY_MOD_LEFT_SHIFT, model->shift); } else if(model->last_key_code == HID_KEYBOARD_L_ALT) { model->alt = !model->alt; - if(model->alt) - model->modifier_code |= KEY_MOD_LEFT_ALT; - else - model->modifier_code &= ~KEY_MOD_LEFT_ALT; + hid_keyboard_modifier_set(hid_keyboard->hid, KEY_MOD_LEFT_ALT, model->alt); } else if(model->last_key_code == HID_KEYBOARD_L_CTRL) { model->ctrl = !model->ctrl; - if(model->ctrl) - model->modifier_code |= KEY_MOD_LEFT_CTRL; - else - model->modifier_code &= ~KEY_MOD_LEFT_CTRL; + hid_keyboard_modifier_set( + hid_keyboard->hid, KEY_MOD_LEFT_CTRL, model->ctrl); } else if(model->last_key_code == HID_KEYBOARD_L_GUI) { model->gui = !model->gui; - if(model->gui) - model->modifier_code |= KEY_MOD_LEFT_GUI; - else - model->modifier_code &= ~KEY_MOD_LEFT_GUI; + hid_keyboard_modifier_set(hid_keyboard->hid, KEY_MOD_LEFT_GUI, model->gui); + } else { + hid_hal_keyboard_press(hid_keyboard->hid, model->last_key_code); } - hid_hal_keyboard_press( - hid_keyboard->hid, model->modifier_code | model->last_key_code); + } else if(event->type == InputTypeRelease) { // Release happens after short and long presses - hid_hal_keyboard_release( - hid_keyboard->hid, model->modifier_code | model->last_key_code); + hid_hal_keyboard_release(hid_keyboard->hid, model->last_key_code); model->ok_pressed = false; } } else if(event->key == InputKeyBack) { @@ -364,6 +372,16 @@ static bool hid_keyboard_input_callback(InputEvent* event, void* context) { if(event->type == InputTypeLong && event->key == InputKeyBack) { hid_hal_keyboard_release_all(hid_keyboard->hid); + with_view_model( + hid_keyboard->view, + HidKeyboardModel * model, + { + model->shift = false; + model->alt = false; + model->ctrl = false; + model->gui = false; + }, + true); } else { hid_keyboard_process(hid_keyboard, event); consumed = true; diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp index ef22ee9ad..11f2d7ecf 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.cpp +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -21,7 +21,7 @@ bool elf_resolve_from_hashtable( auto find_res = std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) { - FURI_LOG_W( + FURI_LOG_T( TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin); result = false; } else { diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c index 21f062605..0ecccc3ac 100644 --- a/lib/nfc/helpers/nfc_data_generator.c +++ b/lib/nfc/helpers/nfc_data_generator.c @@ -35,26 +35,16 @@ static void nfc_generate_mf_ul_uid(uint8_t* uid) { } static void nfc_generate_mf_ul_common(MfUltralightData* mfu_data) { + uint8_t uid[7]; mfu_data->iso14443_3a_data->uid_len = 7; - nfc_generate_mf_ul_uid(mfu_data->iso14443_3a_data->uid); + nfc_generate_mf_ul_uid(uid); + mf_ultralight_set_uid(mfu_data, uid, 7); + mfu_data->iso14443_3a_data->atqa[0] = 0x44; mfu_data->iso14443_3a_data->atqa[1] = 0x00; mfu_data->iso14443_3a_data->sak = 0x00; } -static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) { - *bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; - *bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; -} - -static void nfc_generate_mf_ul_copy_uid_with_bcc(MfUltralightData* mfu_data) { - memcpy(mfu_data->page[0].data, mfu_data->iso14443_3a_data->uid, 3); - memcpy(mfu_data->page[1].data, &mfu_data->iso14443_3a_data->uid[3], 4); - - nfc_generate_calc_bcc( - mfu_data->iso14443_3a_data->uid, &mfu_data->page[0].data[3], &mfu_data->page[2].data[0]); -} - static void nfc_generate_mf_ul_orig(NfcDevice* nfc_device) { MfUltralightData* mfu_data = mf_ultralight_alloc(); nfc_generate_mf_ul_common(mfu_data); @@ -62,7 +52,6 @@ static void nfc_generate_mf_ul_orig(NfcDevice* nfc_device) { mfu_data->type = MfUltralightTypeUnknown; mfu_data->pages_total = 16; mfu_data->pages_read = 16; - nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); memset(&mfu_data->page[4], 0xff, sizeof(MfUltralightPage)); nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); @@ -74,7 +63,7 @@ static void nfc_generate_mf_ul_with_config_common(MfUltralightData* mfu_data, ui mfu_data->pages_total = num_pages; mfu_data->pages_read = num_pages; - nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + uint16_t config_index = (num_pages - 4); mfu_data->page[config_index].data[0] = 0x04; // STRG_MOD_EN mfu_data->page[config_index].data[3] = 0xff; // AUTH0 @@ -150,7 +139,6 @@ static void nfc_generate_ntag203(NfcDevice* nfc_device) { mfu_data->type = MfUltralightTypeNTAG203; mfu_data->pages_total = 42; mfu_data->pages_read = 42; - nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); mfu_data->page[2].data[1] = 0x48; // Internal byte memcpy(&mfu_data->page[3], default_data_ntag203, sizeof(MfUltralightPage)); //-V1086 @@ -379,14 +367,7 @@ static void nfc_generate_mf_classic_block_0( furi_assert(uid_len == 4 || uid_len == 7); furi_assert(block); - if(uid_len == 4) { - // Calculate BCC - block[uid_len] = 0; - - for(int i = 0; i < uid_len; i++) { - block[uid_len] ^= block[i]; - } - } else { + if(uid_len == 7) { uid_len -= 1; } @@ -402,14 +383,12 @@ static void nfc_generate_mf_classic_block_0( static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfClassicType type) { MfClassicData* mfc_data = mf_classic_alloc(); - nfc_generate_mf_classic_uid(mfc_data->block[0].data, uid_len); - nfc_generate_mf_classic_common(mfc_data, uid_len, type); + uint8_t uid[ISO14443_3A_MAX_UID_SIZE]; - // Set the UID - mfc_data->iso14443_3a_data->uid[0] = NXP_MANUFACTURER_ID; - for(int i = 1; i < uid_len; i++) { - mfc_data->iso14443_3a_data->uid[i] = mfc_data->block[0].data[i]; - } + nfc_generate_mf_classic_uid(uid, uid_len); + mf_classic_set_uid(mfc_data, uid, uid_len); + + nfc_generate_mf_classic_common(mfc_data, uid_len, type); mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]); diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c index ca6f5435e..949390305 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -32,17 +32,7 @@ static Iso15693_3Error iso15693_3_poller_filter_error(Iso15693_3Error error) { } } -static Iso15693_3Error iso15693_3_poller_prepare_trx(Iso15693_3Poller* instance) { - furi_assert(instance); - - if(instance->state == Iso15693_3PollerStateIdle) { - return iso15693_3_poller_activate(instance, NULL); - } - - return Iso15693_3ErrorNone; -} - -static Iso15693_3Error iso15693_3_poller_frame_exchange( +Iso15693_3Error iso15693_3_poller_send_frame( Iso15693_3Poller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, @@ -156,7 +146,7 @@ Iso15693_3Error iso15693_3_poller_inventory(Iso15693_3Poller* instance, uint8_t* Iso15693_3Error ret; do { - ret = iso15693_3_poller_frame_exchange( + ret = iso15693_3_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); if(ret != Iso15693_3ErrorNone) break; @@ -183,7 +173,7 @@ Iso15693_3Error Iso15693_3Error ret; do { - ret = iso15693_3_poller_frame_exchange( + ret = iso15693_3_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); if(ret != Iso15693_3ErrorNone) break; @@ -284,20 +274,3 @@ Iso15693_3Error iso15693_3_poller_get_blocks_security( return ret; } - -Iso15693_3Error iso15693_3_poller_send_frame( - Iso15693_3Poller* instance, - const BitBuffer* tx_buffer, - BitBuffer* rx_buffer, - uint32_t fwt) { - Iso15693_3Error ret; - - do { - ret = iso15693_3_poller_prepare_trx(instance); - if(ret != Iso15693_3ErrorNone) break; - - ret = iso15693_3_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt); - } while(false); - - return ret; -} diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c index e68e8c718..27236df4b 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.c +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -346,7 +346,25 @@ const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) { bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) { furi_assert(data); - return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); + bool uid_valid = iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); + + if(uid_valid) { + uint8_t* block = data->block[0].data; + + // Copy UID to block 0 + memcpy(block, data->iso14443_3a_data->uid, uid_len); + + if(uid_len == 4) { + // Calculate BCC byte + block[uid_len] = 0; + + for(size_t i = 0; i < uid_len; i++) { + block[uid_len] ^= block[i]; + } + } + } + + return uid_valid; } Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) { diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c index fd845f3c0..3cffa56ff 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c @@ -482,7 +482,19 @@ const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_l bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len) { furi_assert(data); - return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); + bool uid_valid = iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); + + if(uid_valid) { + // Copy UID across first 2 pages + memcpy(data->page[0].data, data->iso14443_3a_data->uid, 3); + memcpy(data->page[1].data, &data->iso14443_3a_data->uid[3], 4); + + // Calculate BCC bytes + data->page[0].data[3] = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; + data->page[2].data[0] = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; + } + + return uid_valid; } Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data) { diff --git a/lib/nfc/protocols/slix/slix.c b/lib/nfc/protocols/slix/slix.c index 12dc6750d..b575baa72 100644 --- a/lib/nfc/protocols/slix/slix.c +++ b/lib/nfc/protocols/slix/slix.c @@ -338,7 +338,7 @@ const Iso15693_3Data* slix_get_base_data(const SlixData* data) { } SlixType slix_get_type(const SlixData* data) { - SlixType type = SlixTypeCount; + SlixType type = SlixTypeUnknown; do { if(iso15693_3_get_manufacturer_id(data->iso15693_3_data) != SLIX_NXP_MANUFACTURER_CODE) diff --git a/lib/nfc/protocols/slix/slix.h b/lib/nfc/protocols/slix/slix.h index f6c1453c5..26341072b 100644 --- a/lib/nfc/protocols/slix/slix.h +++ b/lib/nfc/protocols/slix/slix.h @@ -37,6 +37,7 @@ extern "C" { #define SLIX_TYPE_FEATURE_EAS (1U << 4) #define SLIX_TYPE_FEATURE_SIGNATURE (1U << 5) #define SLIX_TYPE_FEATURE_PROTECTION (1U << 6) +#define SLIX_TYPE_FEATURE_NFC_SYSTEM_INFO (1U << 7) typedef uint32_t SlixTypeFeatures; @@ -56,7 +57,9 @@ typedef enum { SlixTypeSlixS, SlixTypeSlixL, SlixTypeSlix2, + SlixTypeCount, + SlixTypeUnknown, } SlixType; typedef enum { @@ -71,6 +74,7 @@ typedef enum { typedef uint32_t SlixPassword; typedef uint8_t SlixSignature[SLIX_SIGNATURE_SIZE]; typedef bool SlixPrivacy; +typedef uint16_t SlixRandomNumber; typedef struct { uint8_t pointer; diff --git a/lib/nfc/protocols/slix/slix_i.c b/lib/nfc/protocols/slix/slix_i.c index 97d66484c..26ac80be9 100644 --- a/lib/nfc/protocols/slix/slix_i.c +++ b/lib/nfc/protocols/slix/slix_i.c @@ -99,6 +99,33 @@ SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer return error; } +SlixError slix_get_random_number_response_parse(SlixRandomNumber* data, const BitBuffer* buf) { + SlixError error = SlixErrorNone; + + do { + if(slix_error_response_parse(&error, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t random_number[2]; + } SlixGetRandomNumberResponseLayout; + + const size_t size_received = bit_buffer_get_size_bytes(buf); + const size_t size_required = sizeof(SlixGetRandomNumberResponseLayout); + + if(size_received != size_required) { + error = SlixErrorFormat; + break; + } + + const SlixGetRandomNumberResponseLayout* response = + (const SlixGetRandomNumberResponseLayout*)bit_buffer_get_data(buf); + *data = (response->random_number[1] << 8) | response->random_number[0]; + } while(false); + + return error; +} + void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password) { furi_assert(data); furi_assert(password_type < SlixPasswordTypeCount); diff --git a/lib/nfc/protocols/slix/slix_i.h b/lib/nfc/protocols/slix/slix_i.h index b5e445f31..4a15b50ff 100644 --- a/lib/nfc/protocols/slix/slix_i.h +++ b/lib/nfc/protocols/slix/slix_i.h @@ -48,7 +48,7 @@ extern "C" { #define SLIX_TYPE_FEATURES_SLIX2 \ (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \ SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS | SLIX_TYPE_FEATURE_SIGNATURE | \ - SLIX_TYPE_FEATURE_PROTECTION) + SLIX_TYPE_FEATURE_PROTECTION | SLIX_TYPE_FEATURE_NFC_SYSTEM_INFO) #define SLIX2_FEATURE_FLAGS \ (SLIX_FEATURE_FLAG_UM_PP | SLIX_FEATURE_FLAG_COUNTER | SLIX_FEATURE_FLAG_EAS_ID | \ @@ -74,6 +74,8 @@ SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffe SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf); +SlixError slix_get_random_number_response_parse(SlixRandomNumber* data, const BitBuffer* buf); + // Setters void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password); diff --git a/lib/nfc/protocols/slix/slix_listener.c b/lib/nfc/protocols/slix/slix_listener.c index 204be5ab9..6ff390380 100644 --- a/lib/nfc/protocols/slix/slix_listener.c +++ b/lib/nfc/protocols/slix/slix_listener.c @@ -63,7 +63,7 @@ static NfcCommand slix_listener_run(NfcGenericEvent event, void* context) { if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { const SlixError error = slix_listener_process_request(instance, rx_buffer); if(error == SlixErrorWrongPassword) { - command = NfcCommandStop; + command = NfcCommandSleep; } } diff --git a/lib/nfc/protocols/slix/slix_listener_i.c b/lib/nfc/protocols/slix/slix_listener_i.c index dfcb6c880..15ab2cd3c 100644 --- a/lib/nfc/protocols/slix/slix_listener_i.c +++ b/lib/nfc/protocols/slix/slix_listener_i.c @@ -31,7 +31,7 @@ static SlixPasswordType slix_listener_get_password_type_by_id(uint8_t id) { static SlixPassword slix_listener_unxor_password(const SlixPassword password_xored, uint16_t random) { - return password_xored ^ ((SlixPassword)random << 16 | random); + return REVERSE_BYTES_U32(password_xored ^ ((SlixPassword)random << 16 | random)); } static SlixError slix_listener_set_password( diff --git a/lib/nfc/protocols/slix/slix_poller.c b/lib/nfc/protocols/slix/slix_poller.c index 46a171194..d9d38d102 100644 --- a/lib/nfc/protocols/slix/slix_poller.c +++ b/lib/nfc/protocols/slix/slix_poller.c @@ -44,33 +44,83 @@ static void slix_poller_free(SlixPoller* instance) { static NfcCommand slix_poller_handler_idle(SlixPoller* instance) { iso15693_3_copy( instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller)); - - instance->poller_state = SlixPollerStateGetNxpSysInfo; + instance->type = slix_get_type(instance->data); + if(instance->type >= SlixTypeCount) { + instance->error = SlixErrorNotSupported; + instance->poller_state = SlixPollerStateError; + } else { + instance->poller_state = SlixPollerStateGetNxpSysInfo; + } return NfcCommandContinue; } static NfcCommand slix_poller_handler_get_nfc_system_info(SlixPoller* instance) { - instance->error = slix_poller_get_nxp_system_info(instance, &instance->data->system_info); - if(instance->error == SlixErrorNone) { - instance->poller_state = SlixPollerStateReadSignature; + if(slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_NFC_SYSTEM_INFO)) { + instance->error = slix_poller_get_nxp_system_info(instance, &instance->data->system_info); + if(instance->error == SlixErrorNone) { + instance->poller_state = SlixPollerStateReadSignature; + } else { + instance->poller_state = SlixPollerStateError; + } } else { - instance->poller_state = SlixPollerStateError; + instance->poller_state = SlixPollerStateReadSignature; } return NfcCommandContinue; } static NfcCommand slix_poller_handler_read_signature(SlixPoller* instance) { - instance->error = slix_poller_read_signature(instance, &instance->data->signature); - if(instance->error == SlixErrorNone) { - instance->poller_state = SlixPollerStateReady; + if(slix_type_has_features(instance->type, SLIX_TYPE_FEATURE_SIGNATURE)) { + instance->error = slix_poller_read_signature(instance, &instance->data->signature); + if(instance->error == SlixErrorNone) { + instance->poller_state = SlixPollerStateReady; + } else { + instance->poller_state = SlixPollerStateError; + } } else { - instance->poller_state = SlixPollerStateError; + instance->poller_state = SlixPollerStateReady; } return NfcCommandContinue; } +static NfcCommand slix_poller_handler_privacy_unlock(SlixPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->poller_state = SlixPollerStateError; + + instance->slix_event.type = SlixPollerEventTypePrivacyUnlockRequest; + command = instance->callback(instance->general_event, instance->context); + + bool slix_unlocked = false; + do { + if(!instance->slix_event_data.privacy_password.password_set) break; + SlixPassword pwd = instance->slix_event_data.privacy_password.password; + FURI_LOG_I(TAG, "Trying to disable privacy mode with password: %08lX", pwd); + + instance->error = slix_poller_get_random_number(instance, &instance->random_number); + if(instance->error != SlixErrorNone) break; + + instance->error = slix_poller_set_password(instance, SlixPasswordTypePrivacy, pwd); + if(instance->error != SlixErrorNone) { + command = NfcCommandReset; + break; + } + + FURI_LOG_I(TAG, "Privacy mode disabled"); + instance->data->passwords[SlixPasswordTypePrivacy] = pwd; + instance->poller_state = SlixPollerStateIdle; + slix_unlocked = true; + } while(false); + + if(!slix_unlocked) { + instance->error = SlixErrorTimeout; + instance->poller_state = SlixPollerStateError; + furi_delay_ms(100); + } + + return command; +} + static NfcCommand slix_poller_handler_error(SlixPoller* instance) { instance->slix_event_data.error = instance->error; instance->slix_event.type = SlixPollerEventTypeError; @@ -90,6 +140,7 @@ static const SlixPollerStateHandler slix_poller_state_handler[SlixPollerStateNum [SlixPollerStateError] = slix_poller_handler_error, [SlixPollerStateGetNxpSysInfo] = slix_poller_handler_get_nfc_system_info, [SlixPollerStateReadSignature] = slix_poller_handler_read_signature, + [SlixPollerStatePrivacyUnlock] = slix_poller_handler_privacy_unlock, [SlixPollerStateReady] = slix_poller_handler_ready, }; @@ -117,8 +168,8 @@ static NfcCommand slix_poller_run(NfcGenericEvent event, void* context) { if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { command = slix_poller_state_handler[instance->poller_state](instance); } else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) { - instance->slix_event.type = SlixPollerEventTypeError; - command = instance->callback(instance->general_event, instance->context); + instance->poller_state = SlixPollerStatePrivacyUnlock; + command = slix_poller_state_handler[instance->poller_state](instance); } return command; @@ -138,11 +189,7 @@ static bool slix_poller_detect(NfcGenericEvent event, void* context) { bool protocol_detected = false; if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { - if(slix_get_type(instance->data) < SlixTypeCount) { - SlixSystemInfo system_info = {}; - SlixError error = slix_poller_get_nxp_system_info(instance, &system_info); - protocol_detected = (error == SlixErrorNone); - } + protocol_detected = (slix_get_type(instance->data) < SlixTypeCount); } return protocol_detected; diff --git a/lib/nfc/protocols/slix/slix_poller.h b/lib/nfc/protocols/slix/slix_poller.h index 62d60be5f..4ea7f880d 100644 --- a/lib/nfc/protocols/slix/slix_poller.h +++ b/lib/nfc/protocols/slix/slix_poller.h @@ -18,14 +18,24 @@ typedef struct SlixPoller SlixPoller; */ typedef enum { SlixPollerEventTypeError, /**< An error occured while reading card. */ + SlixPollerEventTypePrivacyUnlockRequest, /**< Poller requests password to disable privacy mode. */ SlixPollerEventTypeReady, /**< The card was successfully read by the poller. */ } SlixPollerEventType; +/** + * @brief Slix poller privacy unlock context data. + */ +typedef struct { + SlixPassword password; /**< Privacy password. */ + bool password_set; /**< Filed to indicate that password was set or not. */ +} SlixPollerEventDataPrivacyUnlockContext; + /** * @brief Slixs poller event data. */ typedef union { SlixError error; /**< Error code indicating card reaing fail reason. */ + SlixPollerEventDataPrivacyUnlockContext privacy_password; /**< Privacy unlock event context. */ } SlixPollerEventData; /** @@ -80,6 +90,30 @@ SlixError slix_poller_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* */ SlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data); +/** + * @brief Get random number from card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the SlixRandomNumber structure to be filled. + * @return SlixErrorNone on success, an error code on failure. + */ +SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber* data); + +/** + * @brief Set password to card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] type SlixPasswordType instance. + * @param[out] password SlixPassword instance. + * @return SlixErrorNone on success, an error code on failure. + */ +SlixError + slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/slix/slix_poller_i.c b/lib/nfc/protocols/slix/slix_poller_i.c index 6d7bdf377..5000efceb 100644 --- a/lib/nfc/protocols/slix/slix_poller_i.c +++ b/lib/nfc/protocols/slix/slix_poller_i.c @@ -1,4 +1,5 @@ #include "slix_poller_i.h" +#include #include @@ -6,18 +7,22 @@ #define TAG "SlixPoller" -static void slix_poller_prepare_request(SlixPoller* instance, uint8_t command) { +static void slix_poller_prepare_request(SlixPoller* instance, uint8_t command, bool skip_uid) { bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); - bit_buffer_append_byte( - instance->tx_buffer, - ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI | - ISO15693_3_REQ_FLAG_T4_ADDRESSED); + uint8_t flags = ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI; + if(!skip_uid) { + flags |= ISO15693_3_REQ_FLAG_T4_ADDRESSED; + } + + bit_buffer_append_byte(instance->tx_buffer, flags); bit_buffer_append_byte(instance->tx_buffer, command); bit_buffer_append_byte(instance->tx_buffer, SLIX_NXP_MANUFACTURER_CODE); - iso15693_3_append_uid(instance->data->iso15693_3_data, instance->tx_buffer); + if(!skip_uid) { + iso15693_3_append_uid(instance->data->iso15693_3_data, instance->tx_buffer); + } } SlixError slix_poller_send_frame( @@ -36,7 +41,7 @@ SlixError slix_poller_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* furi_assert(instance); furi_assert(data); - slix_poller_prepare_request(instance, SLIX_CMD_GET_NXP_SYSTEM_INFORMATION); + slix_poller_prepare_request(instance, SLIX_CMD_GET_NXP_SYSTEM_INFORMATION, false); SlixError error = SlixErrorNone; @@ -54,7 +59,7 @@ SlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data) furi_assert(instance); furi_assert(data); - slix_poller_prepare_request(instance, SLIX_CMD_READ_SIGNATURE); + slix_poller_prepare_request(instance, SLIX_CMD_READ_SIGNATURE, false); SlixError error = SlixErrorNone; @@ -67,3 +72,56 @@ SlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data) return error; } + +SlixError slix_poller_get_random_number(SlixPoller* instance, SlixRandomNumber* data) { + furi_assert(instance); + furi_assert(data); + + slix_poller_prepare_request(instance, SLIX_CMD_GET_RANDOM_NUMBER, true); + + SlixError error = SlixErrorNone; + + do { + error = slix_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(error != SlixErrorNone) break; + + error = slix_get_random_number_response_parse(data, instance->rx_buffer); + } while(false); + + return error; +} + +SlixError + slix_poller_set_password(SlixPoller* instance, SlixPasswordType type, SlixPassword password) { + furi_assert(instance); + + bool skip_uid = (type == SlixPasswordTypePrivacy); + slix_poller_prepare_request(instance, SLIX_CMD_SET_PASSWORD, skip_uid); + + uint8_t password_type = (0x01 << type); + bit_buffer_append_byte(instance->tx_buffer, password_type); + + uint8_t rn_l = instance->random_number >> 8; + uint8_t rn_h = instance->random_number; + uint32_t double_rand_num = (rn_h << 24) | (rn_l << 16) | (rn_h << 8) | rn_l; + uint32_t xored_password = double_rand_num ^ password; + uint8_t xored_password_arr[4] = {}; + nfc_util_num2bytes(xored_password, 4, xored_password_arr); + bit_buffer_append_bytes(instance->tx_buffer, xored_password_arr, 4); + + SlixError error = SlixErrorNone; + + do { + error = slix_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, SLIX_POLLER_SET_PASSWORD_FWT); + if(error != SlixErrorNone) break; + + size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer); + if(rx_len != 1) { + error = SlixErrorWrongPassword; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/slix/slix_poller_i.h b/lib/nfc/protocols/slix/slix_poller_i.h index 1fda1a7d2..7a3b543b7 100644 --- a/lib/nfc/protocols/slix/slix_poller_i.h +++ b/lib/nfc/protocols/slix/slix_poller_i.h @@ -4,6 +4,8 @@ #include "slix_poller.h" +#define SLIX_POLLER_SET_PASSWORD_FWT (100000) + #ifdef __cplusplus extern "C" { #endif @@ -12,6 +14,7 @@ typedef enum { SlixPollerStateIdle, SlixPollerStateGetNxpSysInfo, SlixPollerStateReadSignature, + SlixPollerStatePrivacyUnlock, SlixPollerStateReady, SlixPollerStateError, SlixPollerStateNum, @@ -19,9 +22,11 @@ typedef enum { struct SlixPoller { Iso15693_3Poller* iso15693_3_poller; + SlixType type; SlixData* data; SlixPollerState poller_state; SlixError error; + SlixRandomNumber random_number; BitBuffer* tx_buffer; BitBuffer* rx_buffer; diff --git a/targets/f7/furi_hal/furi_hal_serial_control.c b/targets/f7/furi_hal/furi_hal_serial_control.c index 11ef23a6f..d2ab414c2 100644 --- a/targets/f7/furi_hal/furi_hal_serial_control.c +++ b/targets/f7/furi_hal/furi_hal_serial_control.c @@ -120,16 +120,21 @@ static bool furi_hal_serial_control_handler_stop(void* input, void* output) { static bool furi_hal_serial_control_handler_acquire(void* input, void* output) { FuriHalSerialId serial_id = *(FuriHalSerialId*)input; - if(furi_hal_serial_control->handles[serial_id].in_use) { + FuriHalSerialHandle* handle = &furi_hal_serial_control->handles[serial_id]; + + if(handle->in_use) { *(FuriHalSerialHandle**)output = NULL; } else { // Logging if(furi_hal_serial_control->log_config_serial_id == serial_id) { furi_hal_serial_control_log_set_handle(NULL); + // Expansion + } else if(furi_hal_serial_control->expansion_serial == handle) { + furi_hal_serial_control_enable_expansion_irq(handle, false); } // Return handle - furi_hal_serial_control->handles[serial_id].in_use = true; - *(FuriHalSerialHandle**)output = &furi_hal_serial_control->handles[serial_id]; + handle->in_use = true; + *(FuriHalSerialHandle**)output = handle; } return true; @@ -143,9 +148,12 @@ static bool furi_hal_serial_control_handler_release(void* input, void* output) { furi_hal_serial_deinit(handle); handle->in_use = false; - // Return back logging if(furi_hal_serial_control->log_config_serial_id == handle->id) { + // Return back logging furi_hal_serial_control_log_set_handle(handle); + } else if(furi_hal_serial_control->expansion_serial == handle) { + // Re-enable expansion + furi_hal_serial_control_enable_expansion_irq(handle, true); } return true; diff --git a/targets/f7/furi_hal/furi_hal_usb_cdc.c b/targets/f7/furi_hal/furi_hal_usb_cdc.c index e9cb51e20..014c98bad 100644 --- a/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -7,13 +7,13 @@ #include "usb.h" #include "usb_cdc.h" -#define CDC0_RXD_EP 0x01 +#define CDC0_RXD_EP 0x02 #define CDC0_TXD_EP 0x82 -#define CDC0_NTF_EP 0x83 +#define CDC0_NTF_EP 0x81 #define CDC1_RXD_EP 0x04 -#define CDC1_TXD_EP 0x85 -#define CDC1_NTF_EP 0x86 +#define CDC1_TXD_EP 0x84 +#define CDC1_NTF_EP 0x83 #define CDC_NTF_SZ 0x08 @@ -438,7 +438,9 @@ static void cdc_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { struct usb_string_descriptor* dev_prod_desc = malloc(len * 2 + 2); dev_prod_desc->bLength = len * 2 + 2; dev_prod_desc->bDescriptorType = USB_DTYPE_STRING; - for(uint8_t i = 0; i < len; i++) dev_prod_desc->wString[i] = name[i]; + for(uint8_t i = 0; i < len; i++) { + dev_prod_desc->wString[i] = name[i]; + } name = (char*)furi_hal_version_get_name_ptr(); len = (name == NULL) ? (0) : (strlen(name)); @@ -446,7 +448,9 @@ static void cdc_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { dev_serial_desc->bLength = (len + 5) * 2 + 2; dev_serial_desc->bDescriptorType = USB_DTYPE_STRING; memcpy(dev_serial_desc->wString, "f\0l\0i\0p\0_\0", 5 * 2); - for(uint8_t i = 0; i < len; i++) dev_serial_desc->wString[i + 5] = name[i]; + for(uint8_t i = 0; i < len; i++) { + dev_serial_desc->wString[i + 5] = name[i]; + } cdc_if_cur->str_prod_descr = dev_prod_desc; cdc_if_cur->str_serial_descr = dev_serial_desc; @@ -500,18 +504,20 @@ uint8_t furi_hal_cdc_get_ctrl_line_state(uint8_t if_num) { } void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) { - if(if_num == 0) + if(if_num == 0) { usbd_ep_write(usb_dev, CDC0_TXD_EP, buf, len); - else + } else { usbd_ep_write(usb_dev, CDC1_TXD_EP, buf, len); + } } int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) { int32_t len = 0; - if(if_num == 0) + if(if_num == 0) { len = usbd_ep_read(usb_dev, CDC0_RXD_EP, buf, max_len); - else + } else { len = usbd_ep_read(usb_dev, CDC1_RXD_EP, buf, max_len); + } return ((len < 0) ? 0 : len); } @@ -540,14 +546,16 @@ static void cdc_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); UNUSED(event); uint8_t if_num = 0; - if(ep == CDC0_RXD_EP) + if(ep == CDC0_RXD_EP) { if_num = 0; - else + } else { if_num = 1; + } if(callbacks[if_num] != NULL) { - if(callbacks[if_num]->rx_ep_callback != NULL) + if(callbacks[if_num]->rx_ep_callback != NULL) { callbacks[if_num]->rx_ep_callback(cb_ctx[if_num]); + } } } @@ -555,14 +563,16 @@ static void cdc_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); UNUSED(event); uint8_t if_num = 0; - if(ep == CDC0_TXD_EP) + if(ep == CDC0_TXD_EP) { if_num = 0; - else + } else { if_num = 1; + } if(callbacks[if_num] != NULL) { - if(callbacks[if_num]->tx_ep_callback != NULL) + if(callbacks[if_num]->tx_ep_callback != NULL) { callbacks[if_num]->tx_ep_callback(cb_ctx[if_num]); + } } } @@ -642,25 +652,28 @@ static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) && (req->wIndex == 0 || req->wIndex == 2)) { - if(req->wIndex == 0) + if(req->wIndex == 0) { if_num = 0; - else + } else { if_num = 1; + } switch(req->bRequest) { case USB_CDC_SET_CONTROL_LINE_STATE: if(callbacks[if_num] != NULL) { cdc_ctrl_line_state[if_num] = req->wValue; - if(callbacks[if_num]->ctrl_line_callback != NULL) + if(callbacks[if_num]->ctrl_line_callback != NULL) { callbacks[if_num]->ctrl_line_callback( cb_ctx[if_num], cdc_ctrl_line_state[if_num]); + } } return usbd_ack; case USB_CDC_SET_LINE_CODING: memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0])); if(callbacks[if_num] != NULL) { - if(callbacks[if_num]->config_callback != NULL) + if(callbacks[if_num]->config_callback != NULL) { callbacks[if_num]->config_callback(cb_ctx[if_num], &cdc_config[if_num]); + } } return usbd_ack; case USB_CDC_GET_LINE_CODING: