From f9c0db84e7fe48986d1ce6df2b8d757f560686a8 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:20:45 -0700 Subject: [PATCH 1/2] feat(tft): configure packet authenticity policy --- cmake/protobuf.cmake | 5 +- generated/ui_240x320/ui_tabview_settings.c | 74 ++++++++- generated/ui_320x240/ui_tabview_settings.c | 74 ++++++++- include/graphics/common/MeshtasticView.h | 2 +- include/graphics/view/TFT/PacketAuthPolicy.h | 40 +++++ include/graphics/view/TFT/PacketAuthUI.h | 21 +++ include/graphics/view/TFT/TFTView_320x240.h | 15 +- source/graphics/TFT/TFTView_320x240.cpp | 156 ++++++++++++++++++- source/graphics/common/MeshtasticView.cpp | 2 +- source/graphics/common/ViewController.cpp | 2 +- tests/test_PacketAuthPolicy.cpp | 23 +++ 11 files changed, 402 insertions(+), 12 deletions(-) create mode 100644 include/graphics/view/TFT/PacketAuthPolicy.h create mode 100644 include/graphics/view/TFT/PacketAuthUI.h create mode 100644 tests/test_PacketAuthPolicy.cpp diff --git a/cmake/protobuf.cmake b/cmake/protobuf.cmake index 86237314..ce7a6ebf 100644 --- a/cmake/protobuf.cmake +++ b/cmake/protobuf.cmake @@ -1,7 +1,8 @@ message(STATUS "Fetching protobufs ...") FetchContent_Declare( Protobuf - GIT_REPOSITORY "https://github.com/meshtastic/protobufs" - GIT_TAG v2.7.8 + GIT_REPOSITORY "https://github.com/RCGV1/protobufs-fork" + # Packet authenticity policy and XEdDSA capability from protobufs#983. + GIT_TAG 19ab2d2711050cb462f0f57507f75b47bfa6832c ) FetchContent_MakeAvailable(Protobuf) diff --git a/generated/ui_240x320/ui_tabview_settings.c b/generated/ui_240x320/ui_tabview_settings.c index b1830ca4..c9426fbc 100644 --- a/generated/ui_240x320/ui_tabview_settings.c +++ b/generated/ui_240x320/ui_tabview_settings.c @@ -1,4 +1,17 @@ #include "ui.h" +#include "graphics/view/TFT/PacketAuthUI.h" +#include "lv_i18n.h" +#include "styles.h" + +lv_obj_t *ui_PacketAuthButton; +lv_obj_t *ui_PacketAuthLabel; +lv_obj_t *ui_PacketAuthPanel; +lv_obj_t *ui_PacketAuthDropdown; +lv_obj_t *ui_PacketAuthDescription; +lv_obj_t *ui_PacketAuthWarning; +lv_obj_t *ui_PacketAuthOkButton; +lv_obj_t *ui_PacketAuthOkLabel; +lv_obj_t *ui_PacketAuthCancelButton; lv_obj_t * ui_AdvancedSettingsPanel; lv_obj_t * ui_SettingsTabView; @@ -747,4 +760,63 @@ void create_tabview_settings() lv_obj_set_align(ui_RemoteHardwareLabel, LV_ALIGN_LEFT_MID); lv_label_set_long_mode(ui_RemoteHardwareLabel, LV_LABEL_LONG_DOT); lv_label_set_text(ui_RemoteHardwareLabel, "Remote Hardware"); -} \ No newline at end of file + + ui_PacketAuthButton = lv_btn_create(objects.tab_page_basic_settings); + lv_obj_set_size(ui_PacketAuthButton, lv_pct(95), 30); + lv_obj_add_flag(ui_PacketAuthButton, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_SCROLL_ON_FOCUS); + add_style_settings_button_style(ui_PacketAuthButton); + lv_obj_set_style_shadow_width(ui_PacketAuthButton, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + ui_PacketAuthLabel = lv_label_create(ui_PacketAuthButton); + lv_obj_set_width(ui_PacketAuthLabel, lv_pct(100)); + lv_label_set_long_mode(ui_PacketAuthLabel, LV_LABEL_LONG_DOT); + lv_label_set_text(ui_PacketAuthLabel, _("Packet authenticity: Balanced")); + lv_obj_set_style_align(ui_PacketAuthLabel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + + ui_PacketAuthPanel = lv_obj_create(objects.main_screen); + lv_obj_set_size(ui_PacketAuthPanel, lv_pct(90), 190); + lv_obj_set_style_align(ui_PacketAuthPanel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_SCROLLABLE); + add_style_settings_panel_style(ui_PacketAuthPanel); + lv_obj_set_style_border_width(ui_PacketAuthPanel, 2, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_color(ui_PacketAuthPanel, lv_color_hex(0x216ad8), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(ui_PacketAuthPanel, 250, LV_PART_MAIN | LV_STATE_DEFAULT); + + lv_obj_t *title = lv_label_create(ui_PacketAuthPanel); + lv_label_set_text(title, _("Packet authenticity")); + lv_obj_set_style_align(title, LV_ALIGN_TOP_MID, LV_PART_MAIN | LV_STATE_DEFAULT); + + ui_PacketAuthDropdown = lv_dropdown_create(ui_PacketAuthPanel); + lv_dropdown_set_options(ui_PacketAuthDropdown, _("Balanced\nCompatible\nStrict")); + lv_obj_set_pos(ui_PacketAuthDropdown, 0, 25); + lv_obj_set_size(ui_PacketAuthDropdown, lv_pct(100), 30); + + ui_PacketAuthDescription = lv_label_create(ui_PacketAuthPanel); + lv_obj_set_pos(ui_PacketAuthDescription, 0, 62); + lv_obj_set_width(ui_PacketAuthDescription, lv_pct(100)); + lv_label_set_long_mode(ui_PacketAuthDescription, LV_LABEL_LONG_WRAP); + lv_label_set_text(ui_PacketAuthDescription, _("Prefer signed packets (recommended).")); + + ui_PacketAuthWarning = lv_label_create(ui_PacketAuthPanel); + lv_obj_set_pos(ui_PacketAuthWarning, 0, 98); + lv_obj_set_width(ui_PacketAuthWarning, lv_pct(100)); + lv_label_set_long_mode(ui_PacketAuthWarning, LV_LABEL_LONG_WRAP); + lv_label_set_text(ui_PacketAuthWarning, + _("Strict may hide older, ham, no-key, and oversized unsigned broadcasts. Direct PKI messages are unaffected.")); + lv_obj_set_style_text_color(ui_PacketAuthWarning, lv_color_hex(0xeb533f), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); + + ui_PacketAuthOkButton = lv_btn_create(ui_PacketAuthPanel); + lv_obj_set_size(ui_PacketAuthOkButton, lv_pct(47), 30); + lv_obj_set_style_align(ui_PacketAuthOkButton, LV_ALIGN_BOTTOM_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); + ui_PacketAuthOkLabel = lv_label_create(ui_PacketAuthOkButton); + lv_label_set_text(ui_PacketAuthOkLabel, _("Save")); + lv_obj_center(ui_PacketAuthOkLabel); + + ui_PacketAuthCancelButton = lv_btn_create(ui_PacketAuthPanel); + lv_obj_set_size(ui_PacketAuthCancelButton, lv_pct(47), 30); + lv_obj_set_style_align(ui_PacketAuthCancelButton, LV_ALIGN_BOTTOM_RIGHT, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_t *cancelLabel = lv_label_create(ui_PacketAuthCancelButton); + lv_label_set_text(cancelLabel, _("Cancel")); + lv_obj_center(cancelLabel); +} diff --git a/generated/ui_320x240/ui_tabview_settings.c b/generated/ui_320x240/ui_tabview_settings.c index b1830ca4..c9426fbc 100644 --- a/generated/ui_320x240/ui_tabview_settings.c +++ b/generated/ui_320x240/ui_tabview_settings.c @@ -1,4 +1,17 @@ #include "ui.h" +#include "graphics/view/TFT/PacketAuthUI.h" +#include "lv_i18n.h" +#include "styles.h" + +lv_obj_t *ui_PacketAuthButton; +lv_obj_t *ui_PacketAuthLabel; +lv_obj_t *ui_PacketAuthPanel; +lv_obj_t *ui_PacketAuthDropdown; +lv_obj_t *ui_PacketAuthDescription; +lv_obj_t *ui_PacketAuthWarning; +lv_obj_t *ui_PacketAuthOkButton; +lv_obj_t *ui_PacketAuthOkLabel; +lv_obj_t *ui_PacketAuthCancelButton; lv_obj_t * ui_AdvancedSettingsPanel; lv_obj_t * ui_SettingsTabView; @@ -747,4 +760,63 @@ void create_tabview_settings() lv_obj_set_align(ui_RemoteHardwareLabel, LV_ALIGN_LEFT_MID); lv_label_set_long_mode(ui_RemoteHardwareLabel, LV_LABEL_LONG_DOT); lv_label_set_text(ui_RemoteHardwareLabel, "Remote Hardware"); -} \ No newline at end of file + + ui_PacketAuthButton = lv_btn_create(objects.tab_page_basic_settings); + lv_obj_set_size(ui_PacketAuthButton, lv_pct(95), 30); + lv_obj_add_flag(ui_PacketAuthButton, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_SCROLL_ON_FOCUS); + add_style_settings_button_style(ui_PacketAuthButton); + lv_obj_set_style_shadow_width(ui_PacketAuthButton, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + ui_PacketAuthLabel = lv_label_create(ui_PacketAuthButton); + lv_obj_set_width(ui_PacketAuthLabel, lv_pct(100)); + lv_label_set_long_mode(ui_PacketAuthLabel, LV_LABEL_LONG_DOT); + lv_label_set_text(ui_PacketAuthLabel, _("Packet authenticity: Balanced")); + lv_obj_set_style_align(ui_PacketAuthLabel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + + ui_PacketAuthPanel = lv_obj_create(objects.main_screen); + lv_obj_set_size(ui_PacketAuthPanel, lv_pct(90), 190); + lv_obj_set_style_align(ui_PacketAuthPanel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_SCROLLABLE); + add_style_settings_panel_style(ui_PacketAuthPanel); + lv_obj_set_style_border_width(ui_PacketAuthPanel, 2, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_border_color(ui_PacketAuthPanel, lv_color_hex(0x216ad8), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_opa(ui_PacketAuthPanel, 250, LV_PART_MAIN | LV_STATE_DEFAULT); + + lv_obj_t *title = lv_label_create(ui_PacketAuthPanel); + lv_label_set_text(title, _("Packet authenticity")); + lv_obj_set_style_align(title, LV_ALIGN_TOP_MID, LV_PART_MAIN | LV_STATE_DEFAULT); + + ui_PacketAuthDropdown = lv_dropdown_create(ui_PacketAuthPanel); + lv_dropdown_set_options(ui_PacketAuthDropdown, _("Balanced\nCompatible\nStrict")); + lv_obj_set_pos(ui_PacketAuthDropdown, 0, 25); + lv_obj_set_size(ui_PacketAuthDropdown, lv_pct(100), 30); + + ui_PacketAuthDescription = lv_label_create(ui_PacketAuthPanel); + lv_obj_set_pos(ui_PacketAuthDescription, 0, 62); + lv_obj_set_width(ui_PacketAuthDescription, lv_pct(100)); + lv_label_set_long_mode(ui_PacketAuthDescription, LV_LABEL_LONG_WRAP); + lv_label_set_text(ui_PacketAuthDescription, _("Prefer signed packets (recommended).")); + + ui_PacketAuthWarning = lv_label_create(ui_PacketAuthPanel); + lv_obj_set_pos(ui_PacketAuthWarning, 0, 98); + lv_obj_set_width(ui_PacketAuthWarning, lv_pct(100)); + lv_label_set_long_mode(ui_PacketAuthWarning, LV_LABEL_LONG_WRAP); + lv_label_set_text(ui_PacketAuthWarning, + _("Strict may hide older, ham, no-key, and oversized unsigned broadcasts. Direct PKI messages are unaffected.")); + lv_obj_set_style_text_color(ui_PacketAuthWarning, lv_color_hex(0xeb533f), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); + + ui_PacketAuthOkButton = lv_btn_create(ui_PacketAuthPanel); + lv_obj_set_size(ui_PacketAuthOkButton, lv_pct(47), 30); + lv_obj_set_style_align(ui_PacketAuthOkButton, LV_ALIGN_BOTTOM_LEFT, LV_PART_MAIN | LV_STATE_DEFAULT); + ui_PacketAuthOkLabel = lv_label_create(ui_PacketAuthOkButton); + lv_label_set_text(ui_PacketAuthOkLabel, _("Save")); + lv_obj_center(ui_PacketAuthOkLabel); + + ui_PacketAuthCancelButton = lv_btn_create(ui_PacketAuthPanel); + lv_obj_set_size(ui_PacketAuthCancelButton, lv_pct(47), 30); + lv_obj_set_style_align(ui_PacketAuthCancelButton, LV_ALIGN_BOTTOM_RIGHT, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_t *cancelLabel = lv_label_create(ui_PacketAuthCancelButton); + lv_label_set_text(cancelLabel, _("Cancel")); + lv_obj_center(cancelLabel); +} diff --git a/include/graphics/common/MeshtasticView.h b/include/graphics/common/MeshtasticView.h index 1cabd974..7560a070 100644 --- a/include/graphics/common/MeshtasticView.h +++ b/include/graphics/common/MeshtasticView.h @@ -76,7 +76,7 @@ class MeshtasticView : public DeviceGUI virtual bool setupUIConfig(const meshtastic_DeviceUIConfig &uiconfig) { return true; } virtual void setMyInfo(uint32_t nodeNum); virtual void setDeviceMetaData(int hw_model, const char *version, bool has_bluetooth, bool has_wifi, bool has_eth, - bool can_shutdown); + bool can_shutdown, bool has_xeddsa); virtual void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, eRole role, bool hasKey, bool viaMqtt); virtual void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg); virtual void addNode(uint32_t nodeNum, uint8_t channel, const char *userShort, const char *userLong, uint32_t lastHeard, diff --git a/include/graphics/view/TFT/PacketAuthPolicy.h b/include/graphics/view/TFT/PacketAuthPolicy.h new file mode 100644 index 00000000..59709c66 --- /dev/null +++ b/include/graphics/view/TFT/PacketAuthPolicy.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace PacketAuthPolicy +{ +enum class Selection : uint8_t { + Balanced = 0, + Compatible = 1, + Strict = 2, +}; + +constexpr Selection fromWireValue(uint32_t value) +{ + return value <= static_cast(Selection::Strict) ? static_cast(value) : Selection::Balanced; +} + +constexpr uint32_t toWireValue(Selection selection) +{ + return static_cast(selection); +} + +constexpr bool requiresStrictConfirmation(Selection current, Selection requested) +{ + return requested == Selection::Strict && current != Selection::Strict; +} + +constexpr const char *label(Selection selection) +{ + switch (selection) { + case Selection::Compatible: + return "Compatible"; + case Selection::Strict: + return "Strict"; + case Selection::Balanced: + default: + return "Balanced"; + } +} +} // namespace PacketAuthPolicy diff --git a/include/graphics/view/TFT/PacketAuthUI.h b/include/graphics/view/TFT/PacketAuthUI.h new file mode 100644 index 00000000..31aae5c1 --- /dev/null +++ b/include/graphics/view/TFT/PacketAuthUI.h @@ -0,0 +1,21 @@ +#pragma once + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern lv_obj_t *ui_PacketAuthButton; +extern lv_obj_t *ui_PacketAuthLabel; +extern lv_obj_t *ui_PacketAuthPanel; +extern lv_obj_t *ui_PacketAuthDropdown; +extern lv_obj_t *ui_PacketAuthDescription; +extern lv_obj_t *ui_PacketAuthWarning; +extern lv_obj_t *ui_PacketAuthOkButton; +extern lv_obj_t *ui_PacketAuthOkLabel; +extern lv_obj_t *ui_PacketAuthCancelButton; + +#ifdef __cplusplus +} +#endif diff --git a/include/graphics/view/TFT/TFTView_320x240.h b/include/graphics/view/TFT/TFTView_320x240.h index c2787374..b4cafc8c 100644 --- a/include/graphics/view/TFT/TFTView_320x240.h +++ b/include/graphics/view/TFT/TFTView_320x240.h @@ -21,8 +21,8 @@ class TFTView_320x240 : public MeshtasticView // methods to update view void setMyInfo(uint32_t nodeNum) override; - void setDeviceMetaData(int hw_model, const char *version, bool has_bluetooth, bool has_wifi, bool has_eth, - bool can_shutdown) override; + void setDeviceMetaData(int hw_model, const char *version, bool has_bluetooth, bool has_wifi, bool has_eth, bool can_shutdown, + bool has_xeddsa) override; void addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg) override; void addNode(uint32_t nodeNum, uint8_t channel, const char *userShort, const char *userLong, uint32_t lastHeard, eRole role, bool hasKey, bool unmessagable) override; @@ -98,6 +98,7 @@ class TFTView_320x240 : public MeshtasticView eLanguage, eScreenTimeout, eScreenLock, + ePacketAuthPolicy, eScreenBrightness, eTheme, eInputControl, @@ -255,6 +256,8 @@ class TFTView_320x240 : public MeshtasticView void clearChatHistory(void); void showLoRaFrequency(const meshtastic_Config_LoRaConfig &cfg); void setBellText(bool banner, bool sound); + void updatePacketAuthAvailability(void); + void closePacketAuthPanel(void); void setChannelName(const meshtastic_Channel &ch); uint32_t timestamp(char *buf, uint32_t time, bool update); void updateLocationMap(uint32_t objects); @@ -322,6 +325,10 @@ class TFTView_320x240 : public MeshtasticView static void ui_event_calibration_button(lv_event_t *e); static void ui_event_timeout_button(lv_event_t *e); static void ui_event_screen_lock_button(lv_event_t *e); + static void ui_event_packet_auth_button(lv_event_t *e); + static void ui_event_packet_auth_dropdown(lv_event_t *e); + static void ui_event_packet_auth_ok(lv_event_t *e); + static void ui_event_packet_auth_cancel(lv_event_t *e); static void ui_event_input_button(lv_event_t *e); static void ui_event_alert_button(lv_event_t *e); static void ui_event_backup_button(lv_event_t *e); @@ -421,6 +428,8 @@ class TFTView_320x240 : public MeshtasticView static uint32_t pinKeys; // number of keys pressed (lock screen) static bool screenLocked; // screen lock active static bool screenUnlockRequest; // screen unlock request (via button) + bool hasXeddsa = false; // connected firmware can verify packet signatures + bool strictConfirmationPending = false; // Strict requires a second, explicit confirmation uint32_t selectedHops; // remember selected choice bool chooseNodeSignalScanner; // chose a target node for signal scanner bool chooseNodeTraceRoute; // chose a target node for trace route @@ -444,4 +453,4 @@ class TFTView_320x240 : public MeshtasticView }; meshtastic_DeviceProfile_full db{}; // full copy of the node's configuration db (except nodeinfos) plus ui data -}; \ No newline at end of file +}; diff --git a/source/graphics/TFT/TFTView_320x240.cpp b/source/graphics/TFT/TFTView_320x240.cpp index 72f662e8..f1ee65a4 100644 --- a/source/graphics/TFT/TFTView_320x240.cpp +++ b/source/graphics/TFT/TFTView_320x240.cpp @@ -11,6 +11,8 @@ #include "graphics/map/MapPanel.h" #include "graphics/map/TileProvider.h" #include "graphics/map/URLService.h" +#include "graphics/view/TFT/PacketAuthPolicy.h" +#include "graphics/view/TFT/PacketAuthUI.h" #include "graphics/view/TFT/Themes.h" #include "images.h" #include "input/InputDriver.h" @@ -84,6 +86,29 @@ constexpr lv_color_t colorMidGray = LV_COLOR_HEX(0x808080); constexpr lv_color_t colorDarkGray = LV_COLOR_HEX(0x303030); constexpr lv_color_t colorMesh = LV_COLOR_HEX(0x67ea94); +namespace +{ +const char *packetAuthLabel(PacketAuthPolicy::Selection policy) +{ + switch (policy) { + case PacketAuthPolicy::Selection::Compatible: + return _("Compatible"); + case PacketAuthPolicy::Selection::Strict: + return _("Strict"); + case PacketAuthPolicy::Selection::Balanced: + default: + return _("Balanced"); + } +} + +void setPacketAuthButtonLabel(PacketAuthPolicy::Selection policy) +{ + char label[48]; + lv_snprintf(label, sizeof(label), _("Packet authenticity: %s"), packetAuthLabel(policy)); + lv_label_set_text(ui_PacketAuthLabel, label); +} +} // namespace + // children index of nodepanel lv objects (see addNode) enum NodePanelIdx { node_img_idx, @@ -776,6 +801,10 @@ void TFTView_320x240::ui_events_init(void) lv_obj_add_event_cb(objects.basic_settings_channel_button, ui_event_channel_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_timeout_button, ui_event_timeout_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_screen_lock_button, ui_event_screen_lock_button, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_PacketAuthButton, ui_event_packet_auth_button, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_PacketAuthDropdown, ui_event_packet_auth_dropdown, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(ui_PacketAuthOkButton, ui_event_packet_auth_ok, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_PacketAuthCancelButton, ui_event_packet_auth_cancel, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_brightness_button, ui_event_brightness_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_theme_button, ui_event_theme_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_calibration_button, ui_event_calibration_button, LV_EVENT_CLICKED, NULL); @@ -1912,6 +1941,119 @@ void TFTView_320x240::ui_event_screen_lock_button(lv_event_t *e) } } +void TFTView_320x240::ui_event_packet_auth_button(lv_event_t *e) +{ + if (lv_event_get_code(e) != LV_EVENT_CLICKED || THIS->activeSettings != eNone || !THIS->hasXeddsa || + !THIS->db.config.has_security || THIS->state == MeshtasticView::eDisconnected) { + return; + } + + PacketAuthPolicy::Selection policy = PacketAuthPolicy::fromWireValue(THIS->db.config.security.packet_signature_policy); + lv_dropdown_set_selected(ui_PacketAuthDropdown, PacketAuthPolicy::toWireValue(policy)); + THIS->strictConfirmationPending = false; + lv_label_set_text(ui_PacketAuthOkLabel, _("Save")); + lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); + ui_event_packet_auth_dropdown(e); + + lv_obj_clear_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_HIDDEN); + THIS->disablePanel(objects.controller_panel); + THIS->disablePanel(objects.tab_page_basic_settings); + THIS->activeSettings = ePacketAuthPolicy; + lv_group_focus_obj(ui_PacketAuthDropdown); +} + +void TFTView_320x240::ui_event_packet_auth_dropdown(lv_event_t *e) +{ + PacketAuthPolicy::Selection policy = PacketAuthPolicy::fromWireValue(lv_dropdown_get_selected(ui_PacketAuthDropdown)); + THIS->strictConfirmationPending = false; + lv_label_set_text(ui_PacketAuthOkLabel, _("Save")); + + switch (policy) { + case PacketAuthPolicy::Selection::Compatible: + lv_label_set_text(ui_PacketAuthDescription, _("Accept unsigned packets for maximum compatibility.")); + lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); + break; + case PacketAuthPolicy::Selection::Strict: + lv_label_set_text(ui_PacketAuthDescription, _("Require verified signatures for remote mesh packets.")); + lv_obj_clear_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); + break; + case PacketAuthPolicy::Selection::Balanced: + default: + lv_label_set_text(ui_PacketAuthDescription, _("Prefer signed packets (recommended).")); + lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); + break; + } +} + +void TFTView_320x240::ui_event_packet_auth_ok(lv_event_t *e) +{ + if (lv_event_get_code(e) != LV_EVENT_CLICKED || THIS->activeSettings != ePacketAuthPolicy) { + return; + } + + if (!THIS->hasXeddsa || !THIS->db.config.has_security || THIS->state == MeshtasticView::eDisconnected) { + THIS->closePacketAuthPanel(); + return; + } + + PacketAuthPolicy::Selection current = PacketAuthPolicy::fromWireValue(THIS->db.config.security.packet_signature_policy); + PacketAuthPolicy::Selection requested = PacketAuthPolicy::fromWireValue(lv_dropdown_get_selected(ui_PacketAuthDropdown)); + if (PacketAuthPolicy::requiresStrictConfirmation(current, requested) && !THIS->strictConfirmationPending) { + THIS->strictConfirmationPending = true; + lv_label_set_text(ui_PacketAuthOkLabel, _("Confirm Strict")); + lv_group_focus_obj(ui_PacketAuthOkButton); + return; + } + + if (requested != current) { + meshtastic_Config_SecurityConfig security = THIS->db.config.security; + security.packet_signature_policy = + static_cast(PacketAuthPolicy::toWireValue(requested)); + if (!THIS->controller->sendConfig(meshtastic_Config_SecurityConfig{security}, THIS->ownNode)) { + lv_label_set_text(ui_PacketAuthDescription, _("Unable to save while disconnected.")); + return; + } + THIS->db.config.security = security; + setPacketAuthButtonLabel(requested); + } + + THIS->closePacketAuthPanel(); +} + +void TFTView_320x240::ui_event_packet_auth_cancel(lv_event_t *e) +{ + if (lv_event_get_code(e) == LV_EVENT_CLICKED && THIS->activeSettings == ePacketAuthPolicy) { + THIS->closePacketAuthPanel(); + } +} + +void TFTView_320x240::closePacketAuthPanel(void) +{ + lv_obj_add_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_HIDDEN); + strictConfirmationPending = false; + enablePanel(objects.controller_panel); + enablePanel(objects.tab_page_basic_settings); + activeSettings = eNone; + if (hasXeddsa && state != MeshtasticView::eDisconnected) { + lv_group_focus_obj(ui_PacketAuthButton); + } +} + +void TFTView_320x240::updatePacketAuthAvailability(void) +{ + const bool available = hasXeddsa && db.config.has_security && state != MeshtasticView::eDisconnected; + if (available) { + lv_obj_clear_flag(ui_PacketAuthButton, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_state(ui_PacketAuthButton, LV_STATE_DISABLED); + } else { + lv_obj_add_flag(ui_PacketAuthButton, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_state(ui_PacketAuthButton, LV_STATE_DISABLED); + if (activeSettings == ePacketAuthPolicy) { + closePacketAuthPanel(); + } + } +} + void TFTView_320x240::ui_event_input_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); @@ -4769,8 +4911,10 @@ void TFTView_320x240::setMyInfo(uint32_t nodeNum) } void TFTView_320x240::setDeviceMetaData(int hw_model, const char *version, bool has_bluetooth, bool has_wifi, bool has_eth, - bool can_shutdown) + bool can_shutdown, bool has_xeddsa) { + hasXeddsa = has_xeddsa; + updatePacketAuthAvailability(); } void TFTView_320x240::addOrUpdateNode(uint32_t nodeNum, uint8_t channel, uint32_t lastHeard, const meshtastic_User &cfg) @@ -5774,6 +5918,7 @@ void TFTView_320x240::notifyConnected(const char *info) THIS->controller->setConfigRequested(true); } state = MeshtasticView::eRunning; + updatePacketAuthAvailability(); } } @@ -5785,7 +5930,12 @@ void TFTView_320x240::notifyDisconnected(const char *info) if (state == MeshtasticView::eRunning) { messageAlert(_("Disconnected!"), true); } - state = MeshtasticView::eDisconnected; + } + state = MeshtasticView::eDisconnected; + hasXeddsa = false; + db.config.has_security = false; + if (screensInitialised) { + updatePacketAuthAvailability(); } } @@ -6265,6 +6415,8 @@ void TFTView_320x240::updateSecurityConfig(const meshtastic_Config_SecurityConfi { db.config.security = cfg; db.config.has_security = true; + setPacketAuthButtonLabel(PacketAuthPolicy::fromWireValue(cfg.packet_signature_policy)); + updatePacketAuthAvailability(); // display public key in qr code label char buf[64]; diff --git a/source/graphics/common/MeshtasticView.cpp b/source/graphics/common/MeshtasticView.cpp index 92a584c2..cbe097ea 100644 --- a/source/graphics/common/MeshtasticView.cpp +++ b/source/graphics/common/MeshtasticView.cpp @@ -50,7 +50,7 @@ bool MeshtasticView::sleep(int16_t pin) void MeshtasticView::setMyInfo(uint32_t nodeNum) {} void MeshtasticView::setDeviceMetaData(int hw_model, const char *version, bool has_bluetooth, bool has_wifi, bool has_eth, - bool can_shutdown) + bool can_shutdown, bool has_xeddsa) { } diff --git a/source/graphics/common/ViewController.cpp b/source/graphics/common/ViewController.cpp index 451aa191..bdfae0c7 100644 --- a/source/graphics/common/ViewController.cpp +++ b/source/graphics/common/ViewController.cpp @@ -874,7 +874,7 @@ bool ViewController::handleFromRadio(const meshtastic_FromRadio &from) case meshtastic_FromRadio_metadata_tag: { const meshtastic_DeviceMetadata &meta = from.metadata; view->setDeviceMetaData(meta.hw_model, meta.firmware_version, meta.hasBluetooth, meta.hasWifi, meta.hasEthernet, - meta.canShutdown); + meta.canShutdown, meta.has_xeddsa); break; } case meshtastic_FromRadio_config_complete_id_tag: { diff --git a/tests/test_PacketAuthPolicy.cpp b/tests/test_PacketAuthPolicy.cpp new file mode 100644 index 00000000..c67a20ce --- /dev/null +++ b/tests/test_PacketAuthPolicy.cpp @@ -0,0 +1,23 @@ +#include "graphics/view/TFT/PacketAuthPolicy.h" +#include + +using PacketAuthPolicy::Selection; + +TEST_CASE("packet authenticity wire values map to UI selections") +{ + CHECK(PacketAuthPolicy::fromWireValue(0) == Selection::Balanced); + CHECK(PacketAuthPolicy::fromWireValue(1) == Selection::Compatible); + CHECK(PacketAuthPolicy::fromWireValue(2) == Selection::Strict); + CHECK(PacketAuthPolicy::fromWireValue(99) == Selection::Balanced); + CHECK(PacketAuthPolicy::toWireValue(Selection::Balanced) == 0); + CHECK(PacketAuthPolicy::toWireValue(Selection::Compatible) == 1); + CHECK(PacketAuthPolicy::toWireValue(Selection::Strict) == 2); +} + +TEST_CASE("only a transition into Strict requires confirmation") +{ + CHECK(PacketAuthPolicy::requiresStrictConfirmation(Selection::Balanced, Selection::Strict)); + CHECK(PacketAuthPolicy::requiresStrictConfirmation(Selection::Compatible, Selection::Strict)); + CHECK_FALSE(PacketAuthPolicy::requiresStrictConfirmation(Selection::Strict, Selection::Strict)); + CHECK_FALSE(PacketAuthPolicy::requiresStrictConfirmation(Selection::Strict, Selection::Balanced)); +} From 108702882b059da148cb875afab99dcb2c804ac1 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:26:19 -0700 Subject: [PATCH 2/2] fix(tft): prevent packet auth modal overlap --- .../validation/packet-auth/confirm-strict.png | Bin 0 -> 11078 bytes docs/validation/packet-auth/dropdown-open.png | Bin 0 -> 10815 bytes .../validation/packet-auth/modal-balanced.png | Bin 0 -> 10167 bytes generated/ui_240x320/ui_tabview_settings.c | 20 ++++++++++++---- generated/ui_320x240/ui_tabview_settings.c | 20 ++++++++++++---- source/comms/ethernet/EthClient.cpp | 2 +- source/graphics/TFT/TFTView_320x240.cpp | 22 ++++++++++++++++++ 7 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 docs/validation/packet-auth/confirm-strict.png create mode 100644 docs/validation/packet-auth/dropdown-open.png create mode 100644 docs/validation/packet-auth/modal-balanced.png diff --git a/docs/validation/packet-auth/confirm-strict.png b/docs/validation/packet-auth/confirm-strict.png new file mode 100644 index 0000000000000000000000000000000000000000..4551568d95e2e51b6807106be497cd539bb40fa9 GIT binary patch literal 11078 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 NSIbEAgE|!czW|*$E13WQ literal 0 HcmV?d00001 diff --git a/docs/validation/packet-auth/dropdown-open.png b/docs/validation/packet-auth/dropdown-open.png new file mode 100644 index 0000000000000000000000000000000000000000..eb85c6ef3f5d1bf6400a0c943bc40b679a9ad2f8 GIT binary patch literal 10815 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%0Aqw~+vh*D{aGL4EO>7lxQ!>>$g316i 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 literal 0 HcmV?d00001 diff --git a/generated/ui_240x320/ui_tabview_settings.c b/generated/ui_240x320/ui_tabview_settings.c index c9426fbc..fd9541be 100644 --- a/generated/ui_240x320/ui_tabview_settings.c +++ b/generated/ui_240x320/ui_tabview_settings.c @@ -773,14 +773,17 @@ void create_tabview_settings() lv_obj_set_style_align(ui_PacketAuthLabel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); ui_PacketAuthPanel = lv_obj_create(objects.main_screen); - lv_obj_set_size(ui_PacketAuthPanel, lv_pct(90), 190); + // Keep enough vertical room for the expanded selector, wrapped copy, and + // actions. 236px fits the 240px landscape display with a small margin. + lv_obj_set_size(ui_PacketAuthPanel, lv_pct(90), 236); lv_obj_set_style_align(ui_PacketAuthPanel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_add_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_SCROLLABLE); add_style_settings_panel_style(ui_PacketAuthPanel); lv_obj_set_style_border_width(ui_PacketAuthPanel, 2, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_color(ui_PacketAuthPanel, lv_color_hex(0x216ad8), LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_set_style_bg_opa(ui_PacketAuthPanel, 250, LV_PART_MAIN | LV_STATE_DEFAULT); + // This is a modal settings panel; keep the form beneath it from bleeding through its text. + lv_obj_set_style_bg_opa(ui_PacketAuthPanel, LV_OPA_COVER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_t *title = lv_label_create(ui_PacketAuthPanel); lv_label_set_text(title, _("Packet authenticity")); @@ -790,9 +793,17 @@ void create_tabview_settings() lv_dropdown_set_options(ui_PacketAuthDropdown, _("Balanced\nCompatible\nStrict")); lv_obj_set_pos(ui_PacketAuthDropdown, 0, 25); lv_obj_set_size(ui_PacketAuthDropdown, lv_pct(100), 30); + // The default theme adds generous line spacing, causing the list to + // cover the description and action buttons. Compact the list so all + // three choices stay inside the modal's reserved selector area. + lv_obj_t *packetAuthList = lv_dropdown_get_list(ui_PacketAuthDropdown); + lv_obj_set_style_text_line_space(packetAuthList, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(packetAuthList, 2, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(packetAuthList, 2, LV_PART_MAIN | LV_STATE_DEFAULT); ui_PacketAuthDescription = lv_label_create(ui_PacketAuthPanel); - lv_obj_set_pos(ui_PacketAuthDescription, 0, 62); + // Leave the selector's expanded list clear of the explanatory copy. + lv_obj_set_pos(ui_PacketAuthDescription, 0, 112); lv_obj_set_width(ui_PacketAuthDescription, lv_pct(100)); lv_label_set_long_mode(ui_PacketAuthDescription, LV_LABEL_LONG_WRAP); lv_label_set_text(ui_PacketAuthDescription, _("Prefer signed packets (recommended).")); @@ -801,8 +812,7 @@ void create_tabview_settings() lv_obj_set_pos(ui_PacketAuthWarning, 0, 98); lv_obj_set_width(ui_PacketAuthWarning, lv_pct(100)); lv_label_set_long_mode(ui_PacketAuthWarning, LV_LABEL_LONG_WRAP); - lv_label_set_text(ui_PacketAuthWarning, - _("Strict may hide older, ham, no-key, and oversized unsigned broadcasts. Direct PKI messages are unaffected.")); + lv_label_set_text(ui_PacketAuthWarning, _("Unsigned broadcasts filtered.")); lv_obj_set_style_text_color(ui_PacketAuthWarning, lv_color_hex(0xeb533f), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); diff --git a/generated/ui_320x240/ui_tabview_settings.c b/generated/ui_320x240/ui_tabview_settings.c index c9426fbc..fd9541be 100644 --- a/generated/ui_320x240/ui_tabview_settings.c +++ b/generated/ui_320x240/ui_tabview_settings.c @@ -773,14 +773,17 @@ void create_tabview_settings() lv_obj_set_style_align(ui_PacketAuthLabel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); ui_PacketAuthPanel = lv_obj_create(objects.main_screen); - lv_obj_set_size(ui_PacketAuthPanel, lv_pct(90), 190); + // Keep enough vertical room for the expanded selector, wrapped copy, and + // actions. 236px fits the 240px landscape display with a small margin. + lv_obj_set_size(ui_PacketAuthPanel, lv_pct(90), 236); lv_obj_set_style_align(ui_PacketAuthPanel, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_add_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_HIDDEN); lv_obj_clear_flag(ui_PacketAuthPanel, LV_OBJ_FLAG_SCROLLABLE); add_style_settings_panel_style(ui_PacketAuthPanel); lv_obj_set_style_border_width(ui_PacketAuthPanel, 2, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_color(ui_PacketAuthPanel, lv_color_hex(0x216ad8), LV_PART_MAIN | LV_STATE_DEFAULT); - lv_obj_set_style_bg_opa(ui_PacketAuthPanel, 250, LV_PART_MAIN | LV_STATE_DEFAULT); + // This is a modal settings panel; keep the form beneath it from bleeding through its text. + lv_obj_set_style_bg_opa(ui_PacketAuthPanel, LV_OPA_COVER, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_t *title = lv_label_create(ui_PacketAuthPanel); lv_label_set_text(title, _("Packet authenticity")); @@ -790,9 +793,17 @@ void create_tabview_settings() lv_dropdown_set_options(ui_PacketAuthDropdown, _("Balanced\nCompatible\nStrict")); lv_obj_set_pos(ui_PacketAuthDropdown, 0, 25); lv_obj_set_size(ui_PacketAuthDropdown, lv_pct(100), 30); + // The default theme adds generous line spacing, causing the list to + // cover the description and action buttons. Compact the list so all + // three choices stay inside the modal's reserved selector area. + lv_obj_t *packetAuthList = lv_dropdown_get_list(ui_PacketAuthDropdown); + lv_obj_set_style_text_line_space(packetAuthList, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_top(packetAuthList, 2, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_pad_bottom(packetAuthList, 2, LV_PART_MAIN | LV_STATE_DEFAULT); ui_PacketAuthDescription = lv_label_create(ui_PacketAuthPanel); - lv_obj_set_pos(ui_PacketAuthDescription, 0, 62); + // Leave the selector's expanded list clear of the explanatory copy. + lv_obj_set_pos(ui_PacketAuthDescription, 0, 112); lv_obj_set_width(ui_PacketAuthDescription, lv_pct(100)); lv_label_set_long_mode(ui_PacketAuthDescription, LV_LABEL_LONG_WRAP); lv_label_set_text(ui_PacketAuthDescription, _("Prefer signed packets (recommended).")); @@ -801,8 +812,7 @@ void create_tabview_settings() lv_obj_set_pos(ui_PacketAuthWarning, 0, 98); lv_obj_set_width(ui_PacketAuthWarning, lv_pct(100)); lv_label_set_long_mode(ui_PacketAuthWarning, LV_LABEL_LONG_WRAP); - lv_label_set_text(ui_PacketAuthWarning, - _("Strict may hide older, ham, no-key, and oversized unsigned broadcasts. Direct PKI messages are unaffected.")); + lv_label_set_text(ui_PacketAuthWarning, _("Unsigned broadcasts filtered.")); lv_obj_set_style_text_color(ui_PacketAuthWarning, lv_color_hex(0xeb533f), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); diff --git a/source/comms/ethernet/EthClient.cpp b/source/comms/ethernet/EthClient.cpp index 8c272261..c6a9df0b 100644 --- a/source/comms/ethernet/EthClient.cpp +++ b/source/comms/ethernet/EthClient.cpp @@ -64,7 +64,7 @@ bool EthClient::connect(void) { if (clientStatus != eConnected) { setConnectionStatus(eConnecting, "Connecting..."); - if (client->connect(server, SERVER_PORT)) { + if (client->connect(server, serverPort)) { ILOG_INFO("EthClient connected!"); setConnectionStatus(eConnected, "Connected!"); } else { diff --git a/source/graphics/TFT/TFTView_320x240.cpp b/source/graphics/TFT/TFTView_320x240.cpp index f1ee65a4..662e6bdd 100644 --- a/source/graphics/TFT/TFTView_320x240.cpp +++ b/source/graphics/TFT/TFTView_320x240.cpp @@ -107,6 +107,22 @@ void setPacketAuthButtonLabel(PacketAuthPolicy::Selection policy) lv_snprintf(label, sizeof(label), _("Packet authenticity: %s"), packetAuthLabel(policy)); lv_label_set_text(ui_PacketAuthLabel, label); } + +// The packet-auth panel contains copy that can wrap differently across the +// 240x320 and 320x240 layouts. Reflow the warning below the measured +// description instead of relying on fixed coordinates that can collide with +// the action buttons. +void layoutPacketAuthPanel(void) +{ + if (!ui_PacketAuthDescription || !ui_PacketAuthWarning) { + return; + } + + lv_obj_update_layout(ui_PacketAuthDescription); + const lv_coord_t descriptionBottom = lv_obj_get_y(ui_PacketAuthDescription) + lv_obj_get_height(ui_PacketAuthDescription); + lv_obj_set_y(ui_PacketAuthWarning, descriptionBottom + 6); + lv_obj_update_layout(ui_PacketAuthWarning); +} } // namespace // children index of nodepanel lv objects (see addNode) @@ -1983,6 +1999,12 @@ void TFTView_320x240::ui_event_packet_auth_dropdown(lv_event_t *e) lv_obj_add_flag(ui_PacketAuthWarning, LV_OBJ_FLAG_HIDDEN); break; } + + // Strict mode adds a warning line. Pull the description up once the + // selector closes so the warning has a clear gap above the actions; keep + // the lower position while the selector is open so its list has room. + lv_obj_set_y(ui_PacketAuthDescription, policy == PacketAuthPolicy::Selection::Strict ? 76 : 112); + layoutPacketAuthPanel(); } void TFTView_320x240::ui_event_packet_auth_ok(lv_event_t *e)