Key verification flow on BaseUI (#7240)

This commit is contained in:
Jonathan Bennett 2025-07-10 09:45:36 -05:00 committed by GitHub
parent a7e516d6f6
commit 0795b21c2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 211 additions and 126 deletions

View File

@ -171,7 +171,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
} }
// Called to trigger a banner with custom message and duration // Called to trigger a banner with custom message and duration
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(int)> bannerCallback) void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback)
{ {
#ifdef USE_EINK #ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
@ -196,7 +196,6 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct
void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits,
std::function<void(uint32_t)> bannerCallback) std::function<void(uint32_t)> bannerCallback)
{ {
LOG_WARN("Show Number Picker");
#ifdef USE_EINK #ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif #endif
@ -1330,7 +1329,7 @@ int Screen::handleInputEvent(const InputEvent *event)
setFastFramerate(); // Draw ASAP setFastFramerate(); // Draw ASAP
#endif #endif
if (NotificationRenderer::isOverlayBannerShowing()) { if (NotificationRenderer::isOverlayBannerShowing()) {
NotificationRenderer::inEvent = event->inputEvent; NotificationRenderer::inEvent = *event;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
setFastFramerate(); // Draw ASAP setFastFramerate(); // Draw ASAP

View File

@ -92,6 +92,7 @@ class Screen
#include "commands.h" #include "commands.h"
#include "concurrency/LockGuard.h" #include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "graphics/draw/MenuHandler.h"
#include "input/InputBroker.h" #include "input/InputBroker.h"
#include "mesh/MeshModule.h" #include "mesh/MeshModule.h"
#include "modules/AdminModule.h" #include "modules/AdminModule.h"
@ -308,9 +309,15 @@ class Screen : public concurrency::OSThread
void showSimpleBanner(const char *message, uint32_t durationMs = 0); void showSimpleBanner(const char *message, uint32_t durationMs = 0);
void showOverlayBanner(BannerOverlayOptions); void showOverlayBanner(BannerOverlayOptions);
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(int)> bannerCallback); void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback);
void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback); void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback);
void requestMenu(graphics::menuHandler::screenMenus menuToShow)
{
graphics::menuHandler::menuQueue = menuToShow;
runNow();
}
void startFirmwareUpdateScreen() void startFirmwareUpdateScreen()
{ {
ScreenCmd cmd; ScreenCmd cmd;

View File

@ -13,6 +13,7 @@
#include "main.h" #include "main.h"
#include "modules/AdminModule.h" #include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h" #include "modules/CannedMessageModule.h"
#include "modules/KeyVerificationModule.h"
extern uint16_t TFT_MESH; extern uint16_t TFT_MESH;
@ -237,27 +238,25 @@ void menuHandler::clockMenu()
void menuHandler::messageResponseMenu() void menuHandler::messageResponseMenu()
{ {
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
int options = 3;
static const char **optionsArrayPtr;
int options;
enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3 };
if (kb_found) { if (kb_found) {
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"}; optionsArray[options] = "Reply via Freetext";
optionsArrayPtr = optionsArray; optionsEnumArray[options++] = Freetext;
options = 4;
} else {
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"};
optionsArrayPtr = optionsArray;
options = 3;
} }
#ifdef HAS_I2S #ifdef HAS_I2S
static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"}; optionsArray[options] = "Read Aloud";
optionsArrayPtr = optionsArray; optionsEnumArray[options++] = Aloud;
options = 5;
#endif #endif
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action"; bannerOptions.message = "Message Action";
bannerOptions.optionsArrayPtr = optionsArrayPtr; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options; bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Dismiss) { if (selected == Dismiss) {
@ -276,7 +275,7 @@ void menuHandler::messageResponseMenu()
} }
} }
#ifdef HAS_I2S #ifdef HAS_I2S
else if (selected == 4) { else if (selected == Aloud) {
const meshtastic_MeshPacket &mp = devicestate.rx_text_message; const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes); const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes);
@ -289,10 +288,10 @@ void menuHandler::messageResponseMenu()
void menuHandler::homeBaseMenu() void menuHandler::homeBaseMenu()
{ {
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep }; enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd };
static const char *optionsArray[6] = {"Back"}; static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[6] = {Back}; static int optionsEnumArray[enumEnd] = {Back};
int options = 1; int options = 1;
#ifdef PIN_EINK_EN #ifdef PIN_EINK_EN
@ -354,9 +353,9 @@ void menuHandler::systemBaseMenu()
hasSupportBrightness = true; hasSupportBrightness = true;
#endif #endif
enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test }; enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test, enumEnd };
static const char *optionsArray[7] = {"Back"}; static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[7] = {Back}; static int optionsEnumArray[enumEnd] = {Back};
int options = 1; int options = 1;
optionsArray[options] = "Reboot"; optionsArray[options] = "Reboot";
@ -419,21 +418,22 @@ void menuHandler::systemBaseMenu()
void menuHandler::favoriteBaseMenu() void menuHandler::favoriteBaseMenu()
{ {
int options; enum optionsNumbers { Back, Preset, Freetext, Remove, enumEnd };
static const char **optionsArrayPtr; static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
static int optionsEnumArray[enumEnd] = {Back, Preset};
int options = 2;
if (kb_found) { if (kb_found) {
static const char *optionsArray[] = {"Back", "New Preset Msg", "New Freetext Msg", "Remove Favorite"}; optionsArray[options] = "New Freetext Msg";
optionsArrayPtr = optionsArray; optionsEnumArray[options++] = Freetext;
options = 4;
} else {
static const char *optionsArray[] = {"Back", "New Preset Msg", "Remove Favorite"};
optionsArrayPtr = optionsArray;
options = 3;
} }
optionsArray[options] = "Remove Favorite";
optionsEnumArray[options++] = Remove;
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Favorites Action"; bannerOptions.message = "Favorites Action";
bannerOptions.optionsArrayPtr = optionsArrayPtr; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options; bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) { if (selected == 1) {
@ -450,34 +450,29 @@ void menuHandler::favoriteBaseMenu()
void menuHandler::positionBaseMenu() void menuHandler::positionBaseMenu()
{ {
int options; enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd };
static const char **optionsArrayPtr;
static const char *optionsArray[] = {"Back", "GPS Toggle", "Compass"}; static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"};
static const char *optionsArrayCalibrate[] = {"Back", "GPS Toggle", "Compass", "Compass Calibrate"}; static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu};
int options = 3;
if (accelerometerThread) { if (accelerometerThread) {
optionsArrayPtr = optionsArrayCalibrate; optionsArray[options] = "Compass Calibrate";
options = 4; optionsEnumArray[options++] = CompassCalibrate;
} else {
optionsArrayPtr = optionsArray;
options = 3;
} }
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Position Action"; bannerOptions.message = "Position Action";
bannerOptions.optionsArrayPtr = optionsArrayPtr; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options; bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) { if (selected == GPSToggle) {
#if MESHTASTIC_EXCLUDE_GPS
menuQueue = menu_none;
#else
menuQueue = gps_toggle_menu; menuQueue = gps_toggle_menu;
screen->runNow(); screen->runNow();
#endif } else if (selected == CompassMenu) {
} else if (selected == 2) {
menuQueue = compass_point_north_menu; menuQueue = compass_point_north_menu;
screen->runNow(); screen->runNow();
} else if (selected == 3) { } else if (selected == CompassCalibrate) {
accelerometerThread->calibrate(30); accelerometerThread->calibrate(30);
} }
}; };
@ -486,16 +481,20 @@ void menuHandler::positionBaseMenu()
void menuHandler::nodeListMenu() void menuHandler::nodeListMenu()
{ {
static const char *optionsArray[] = {"Back", "Add Favorite", "Reset NodeDB"}; enum optionsNumbers { Back, Favorite, Verify, Reset };
static const char *optionsArray[] = {"Back", "Add Favorite", "Key Verification", "Reset NodeDB"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Node Action"; bannerOptions.message = "Node Action";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3; bannerOptions.optionsCount = 4;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) { if (selected == Favorite) {
menuQueue = add_favorite; menuQueue = add_favorite;
screen->runNow(); screen->runNow();
} else if (selected == 2) { } else if (selected == Verify) {
menuQueue = key_verification_init;
screen->runNow();
} else if (selected == Reset) {
menuQueue = reset_node_db_menu; menuQueue = reset_node_db_menu;
screen->runNow(); screen->runNow();
} }
@ -523,6 +522,7 @@ void menuHandler::resetNodeDBMenu()
void menuHandler::compassNorthMenu() void menuHandler::compassNorthMenu()
{ {
enum optionsNumbers { Back, Dynamic, Fixed, Freeze };
static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"}; static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "North Directions?"; bannerOptions.message = "North Directions?";
@ -530,28 +530,28 @@ void menuHandler::compassNorthMenu()
bannerOptions.optionsCount = 4; bannerOptions.optionsCount = 4;
bannerOptions.InitialSelected = uiconfig.compass_mode + 1; bannerOptions.InitialSelected = uiconfig.compass_mode + 1;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) { if (selected == Dynamic) {
if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) { if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) {
uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC; uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig); &uiconfig);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
} }
} else if (selected == 2) { } else if (selected == Fixed) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) { if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) {
uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING; uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig); &uiconfig);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
} }
} else if (selected == 3) { } else if (selected == Freeze) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) { if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING; uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig); &uiconfig);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
} }
} else if (selected == 0) { } else if (selected == Back) {
menuQueue = position_base_menu; menuQueue = position_base_menu;
screen->runNow(); screen->runNow();
} }
@ -562,6 +562,7 @@ void menuHandler::compassNorthMenu()
#if !MESHTASTIC_EXCLUDE_GPS #if !MESHTASTIC_EXCLUDE_GPS
void menuHandler::GPSToggleMenu() void menuHandler::GPSToggleMenu()
{ {
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"}; static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle GPS"; bannerOptions.message = "Toggle GPS";
@ -796,7 +797,7 @@ void menuHandler::rebootMenu()
void menuHandler::addFavoriteMenu() void menuHandler::addFavoriteMenu()
{ {
screen->showNodePicker("Node To Favorite", 30000, [](int nodenum) -> void { screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
LOG_WARN("Nodenum: %u", nodenum); LOG_WARN("Nodenum: %u", nodenum);
nodeDB->set_favorite(true, nodenum); nodeDB->set_favorite(true, nodenum);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
@ -887,6 +888,37 @@ void menuHandler::wifiToggleMenu()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::keyVerificationInitMenu()
{
screen->showNodePicker("Node to Verify", 30000,
[](uint32_t selected) -> void { keyVerificationModule->sendInitialRequest(selected); });
}
void menuHandler::keyVerificationFinalPrompt()
{
char message[40] = {0};
memset(message, 0, sizeof(message));
sprintf(message, "Verification: \n");
keyVerificationModule->generateVerificationCode(message + 15); // send the toPhone packet
if (screen) {
static const char *optionsArray[] = {"Reject", "Accept"};
graphics::BannerOverlayOptions options;
options.message = message;
options.durationMs = 30000;
options.optionsArrayPtr = optionsArray;
options.optionsCount = 2;
options.notificationType = graphics::notificationTypeEnum::selection_picker;
options.bannerCallback = [=](int selected) {
if (selected == 1) {
auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode());
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
}
};
screen->showOverlayBanner(options);
}
}
void menuHandler::handleMenuSwitch(OLEDDisplay *display) void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{ {
if (menuQueue != menu_none) if (menuQueue != menu_none)
@ -953,9 +985,18 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case wifi_toggle_menu: case wifi_toggle_menu:
wifiToggleMenu(); wifiToggleMenu();
break; break;
case key_verification_init:
keyVerificationInitMenu();
break;
case key_verification_final_prompt:
keyVerificationFinalPrompt();
break;
case bluetooth_toggle_menu: case bluetooth_toggle_menu:
BluetoothToggleMenu(); BluetoothToggleMenu();
break; break;
case throttle_message:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break;
} }
menuQueue = menu_none; menuQueue = menu_none;
} }

View File

@ -1,3 +1,5 @@
#pragma once
#if HAS_SCREEN
#include "configuration.h" #include "configuration.h"
namespace graphics namespace graphics
{ {
@ -26,7 +28,10 @@ class menuHandler
test_menu, test_menu,
number_test, number_test,
wifi_toggle_menu, wifi_toggle_menu,
bluetooth_toggle_menu key_verification_init,
key_verification_final_prompt,
bluetooth_toggle_menu,
throttle_message
}; };
static screenMenus menuQueue; static screenMenus menuQueue;
@ -56,7 +61,10 @@ class menuHandler
static void numberTest(); static void numberTest();
static void wifiBaseMenu(); static void wifiBaseMenu();
static void wifiToggleMenu(); static void wifiToggleMenu();
static void keyVerificationInitMenu();
static void keyVerificationFinalPrompt();
static void BluetoothToggleMenu(); static void BluetoothToggleMenu();
}; };
} // namespace graphics } // namespace graphics
#endif

View File

@ -26,7 +26,7 @@ extern bool hasUnreadMessage;
namespace graphics namespace graphics
{ {
char NotificationRenderer::inEvent = INPUT_BROKER_NONE; InputEvent NotificationRenderer::inEvent;
int8_t NotificationRenderer::curSelected = 0; int8_t NotificationRenderer::curSelected = 0;
char NotificationRenderer::alertBannerMessage[256] = {0}; char NotificationRenderer::alertBannerMessage[256] = {0};
uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever
@ -72,11 +72,25 @@ void NotificationRenderer::resetBanner()
{ {
alertBannerMessage[0] = '\0'; alertBannerMessage[0] = '\0';
current_notification_type = notificationTypeEnum::none; current_notification_type = notificationTypeEnum::none;
inEvent.inputEvent = INPUT_BROKER_NONE;
inEvent.kbchar = 0;
curSelected = 0;
alertBannerOptions = 0; // last x lines are seelctable options
optionsArrayPtr = nullptr;
optionsEnumPtr = nullptr;
alertBannerCallback = NULL;
pauseBanner = false;
numDigits = 0;
currentNumber = 0;
nodeDB->pause_sort(false); nodeDB->pause_sort(false);
} }
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state) void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
{ {
if (!isOverlayBannerShowing() && alertBannerMessage[0] != '\0')
resetBanner();
if (!isOverlayBannerShowing() || pauseBanner) if (!isOverlayBannerShowing() || pauseBanner)
return; return;
switch (current_notification_type) { switch (current_notification_type) {
@ -115,31 +129,40 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
// modulo to extract // modulo to extract
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1)); uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
// Handle input // Handle input
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
if (this_digit == 9) { if (this_digit == 9) {
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1)); currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
} else { } else {
currentNumber += (pow_of_10(numDigits - curSelected - 1)); currentNumber += (pow_of_10(numDigits - curSelected - 1));
} }
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) { } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
if (this_digit == 0) { if (this_digit == 0) {
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1)); currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
} else { } else {
currentNumber -= (pow_of_10(numDigits - curSelected - 1)); currentNumber -= (pow_of_10(numDigits - curSelected - 1));
} }
} else if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_RIGHT) { } else if (inEvent.inputEvent == INPUT_BROKER_ANYKEY) {
if (inEvent.kbchar > 47 && inEvent.kbchar < 58) { // have a digit
currentNumber -= this_digit * (pow_of_10(numDigits - curSelected - 1));
currentNumber += (inEvent.kbchar - 48) * (pow_of_10(numDigits - curSelected - 1));
curSelected++; curSelected++;
} else if (inEvent == INPUT_BROKER_LEFT) { }
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_RIGHT) {
curSelected++;
} else if (inEvent.inputEvent == INPUT_BROKER_LEFT) {
curSelected--; curSelected--;
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) { } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
alertBannerUntil != 0) {
resetBanner(); resetBanner();
return;
} }
if (curSelected == numDigits) { if (curSelected == numDigits) {
resetBanner();
alertBannerCallback(currentNumber); alertBannerCallback(currentNumber);
resetBanner();
return;
} }
inEvent = INPUT_BROKER_NONE; inEvent.inputEvent = INPUT_BROKER_NONE;
if (alertBannerMessage[0] == '\0') if (alertBannerMessage[0] == '\0')
return; return;
@ -193,16 +216,18 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
} }
// Handle input // Handle input
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
curSelected--; curSelected--;
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) { } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
curSelected++; curSelected++;
} else if (inEvent == INPUT_BROKER_SELECT) { } else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
resetBanner();
alertBannerCallback(selectedNodenum); alertBannerCallback(selectedNodenum);
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) {
resetBanner(); resetBanner();
return;
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
alertBannerUntil != 0) {
resetBanner();
return;
} }
if (curSelected == -1) if (curSelected == -1)
@ -210,7 +235,7 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
if (curSelected == alertBannerOptions) if (curSelected == alertBannerOptions)
curSelected = 0; curSelected = 0;
inEvent = INPUT_BROKER_NONE; inEvent.inputEvent = INPUT_BROKER_NONE;
if (alertBannerMessage[0] == '\0') if (alertBannerMessage[0] == '\0')
return; return;
@ -308,11 +333,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
// Handle input // Handle input
if (alertBannerOptions > 0) { if (alertBannerOptions > 0) {
if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
curSelected--; curSelected--;
} else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) { } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
curSelected++; curSelected++;
} else if (inEvent == INPUT_BROKER_SELECT) { } else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
if (optionsEnumPtr != nullptr) { if (optionsEnumPtr != nullptr) {
alertBannerCallback(optionsEnumPtr[curSelected]); alertBannerCallback(optionsEnumPtr[curSelected]);
optionsEnumPtr = nullptr; optionsEnumPtr = nullptr;
@ -320,8 +345,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
alertBannerCallback(curSelected); alertBannerCallback(curSelected);
} }
resetBanner(); resetBanner();
} else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) { return;
} else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
alertBannerUntil != 0) {
resetBanner(); resetBanner();
return;
} }
if (curSelected == -1) if (curSelected == -1)
@ -329,12 +357,14 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
if (curSelected == alertBannerOptions) if (curSelected == alertBannerOptions)
curSelected = 0; curSelected = 0;
} else { } else {
if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_ALT_LONG || inEvent == INPUT_BROKER_CANCEL) { if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_ALT_LONG ||
inEvent.inputEvent == INPUT_BROKER_CANCEL) {
resetBanner(); resetBanner();
return;
} }
} }
inEvent = INPUT_BROKER_NONE; inEvent.inputEvent = INPUT_BROKER_NONE;
if (alertBannerMessage[0] == '\0') if (alertBannerMessage[0] == '\0')
return; return;

View File

@ -11,7 +11,8 @@ namespace graphics
class NotificationRenderer class NotificationRenderer
{ {
public: public:
static char inEvent; static InputEvent inEvent;
static char inKeypress;
static int8_t curSelected; static int8_t curSelected;
static char alertBannerMessage[256]; static char alertBannerMessage[256];
static uint32_t alertBannerUntil; // 0 is a special case meaning forever static uint32_t alertBannerUntil; // 0 is a special case meaning forever

View File

@ -13,8 +13,9 @@ template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi
#if HAS_SCREEN #if HAS_SCREEN
#define IF_SCREEN(X) \ #define IF_SCREEN(X) \
if (screen) \ if (screen) { \
X; X; \
}
#else #else
#define IF_SCREEN(...) #define IF_SCREEN(...)
#endif #endif

View File

@ -716,7 +716,7 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
} }
// Backspace // Backspace
if (event->inputEvent == INPUT_BROKER_BACK) { if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
payload = 0x08; payload = 0x08;
lastTouchMillis = millis(); lastTouchMillis = millis();
runOnce(); runOnce();
@ -739,7 +739,8 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
} }
// Cancel (dismiss freetext screen) // Cancel (dismiss freetext screen)
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG) { if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG ||
(event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() == 0)) {
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
freetext = ""; freetext = "";
cursor = 0; cursor = 0;
@ -989,6 +990,7 @@ int32_t CannedMessageModule::runOnce()
} }
this->cursor--; this->cursor--;
} }
} else {
} }
break; break;
case INPUT_BROKER_MSG_TAB: // Tab key: handled by input handler case INPUT_BROKER_MSG_TAB: // Tab key: handled by input handler

View File

@ -2,6 +2,7 @@
#include "KeyVerificationModule.h" #include "KeyVerificationModule.h"
#include "MeshService.h" #include "MeshService.h"
#include "RTC.h" #include "RTC.h"
#include "graphics/draw/MenuHandler.h"
#include "main.h" #include "main.h"
#include "modules/AdminModule.h" #include "modules/AdminModule.h"
#include <SHA256.h> #include <SHA256.h>
@ -48,18 +49,22 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons
bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_KeyVerification *r) bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_KeyVerification *r)
{ {
updateState(); updateState();
if (mp.pki_encrypted == false) if (mp.pki_encrypted == false) {
return false; return false;
if (mp.from != currentRemoteNode) // because the inital connection request is handled in allocReply() }
if (mp.from != currentRemoteNode) { // because the inital connection request is handled in allocReply()
return false; return false;
}
if (currentState == KEY_VERIFICATION_IDLE) { if (currentState == KEY_VERIFICATION_IDLE) {
return false; // if we're idle, the only acceptable message is an init, which should be handled by allocReply() return false; // if we're idle, the only acceptable message is an init, which should be handled by allocReply()
}
} else if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 && if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
r->hash1.size == 0) { r->hash1.size == 0) {
memcpy(hash2, r->hash2.bytes, 32); memcpy(hash2, r->hash2.bytes, 32);
if (screen) IF_SCREEN(screen->showNumberPicker("Enter Security Number", 60000, 6, [](int number_picked) -> void {
screen->showSimpleBanner("Enter Security Number", 30000); keyVerificationModule->processSecurityNumber(number_picked);
});)
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING; cn->level = meshtastic_LogRecord_Level_WARNING;
@ -79,23 +84,19 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
memset(message, 0, sizeof(message)); memset(message, 0, sizeof(message));
sprintf(message, "Verification: \n"); sprintf(message, "Verification: \n");
generateVerificationCode(message + 15); generateVerificationCode(message + 15);
static const char *optionsArray[] = {"ACCEPT", "REJECT"}; static const char *optionsArray[] = {"Reject", "Accept"};
LOG_INFO("Hash1 matches!"); LOG_INFO("Hash1 matches!");
if (screen) { IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000;
graphics::BannerOverlayOptions options; options.optionsArrayPtr = optionsArray; options.optionsCount = 2;
options.message = message;
options.durationMs = 30000;
options.optionsArrayPtr = optionsArray;
options.optionsCount = 2;
options.notificationType = graphics::notificationTypeEnum::selection_picker; options.notificationType = graphics::notificationTypeEnum::selection_picker;
options.bannerCallback = [=](int selected) { options.bannerCallback =
if (selected == 0) { [=](int selected) {
if (selected == 1) {
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
} }
}; };
screen->showOverlayBanner(options); screen->showOverlayBanner(options);)
}
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING; cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message); sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message);
@ -120,6 +121,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode)
// generate nonce // generate nonce
updateState(); updateState();
if (currentState != KEY_VERIFICATION_IDLE) { if (currentState != KEY_VERIFICATION_IDLE) {
graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;
return false; return false;
} }
currentNonce = random(); currentNonce = random();
@ -190,11 +192,8 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply()
responsePacket = allocDataProtobuf(response); responsePacket = allocDataProtobuf(response);
responsePacket->pki_encrypted = true; responsePacket->pki_encrypted = true;
if (screen) { IF_SCREEN(snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000); screen->showSimpleBanner(message, 30000); LOG_WARN("%s", message);)
screen->showSimpleBanner(message, 30000);
LOG_WARN("%s", message);
}
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING; cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Incoming Key Verification.\nSecurity Number\n%03u %03u", currentSecurityNumber / 1000, sprintf(cn->message, "Incoming Key Verification.\nSecurity Number\n%03u %03u", currentSecurityNumber / 1000,
@ -258,12 +257,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
p->priority = meshtastic_MeshPacket_Priority_HIGH; p->priority = meshtastic_MeshPacket_Priority_HIGH;
service->sendToMesh(p, RX_SRC_LOCAL, true); service->sendToMesh(p, RX_SRC_LOCAL, true);
currentState = KEY_VERIFICATION_SENDER_AWAITING_USER; currentState = KEY_VERIFICATION_SENDER_AWAITING_USER;
memset(message, 0, sizeof(message)); IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);)
sprintf(message, "Verification: \n");
generateVerificationCode(message + 15); // send the toPhone packet
if (screen) {
screen->showSimpleBanner(message, 30000);
}
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING; cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message); sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);
@ -282,7 +276,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
void KeyVerificationModule::updateState() void KeyVerificationModule::updateState()
{ {
if (currentState != KEY_VERIFICATION_IDLE) { if (currentState != KEY_VERIFICATION_IDLE) {
// check for the 30 second timeout // check for the 60 second timeout
if (currentNonceTimestamp < getTime() - 60) { if (currentNonceTimestamp < getTime() - 60) {
resetToIdle(); resetToIdle();
} else { } else {

View File

@ -27,6 +27,8 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
}*/ }*/
virtual bool wantUIFrame() { return false; }; virtual bool wantUIFrame() { return false; };
bool sendInitialRequest(NodeNum remoteNode); bool sendInitialRequest(NodeNum remoteNode);
void generateVerificationCode(char *); // fills char with the user readable verification code
uint32_t getCurrentRemoteNode() { return currentRemoteNode; }
protected: protected:
/* Called to handle a particular incoming message /* Called to handle a particular incoming message
@ -58,7 +60,6 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
void processSecurityNumber(uint32_t); void processSecurityNumber(uint32_t);
void updateState(); // check the timeouts and maybe reset the state to idle void updateState(); // check the timeouts and maybe reset the state to idle
void resetToIdle(); // Zero out module state void resetToIdle(); // Zero out module state
void generateVerificationCode(char *); // fills char with the user readable verification code
}; };
extern KeyVerificationModule *keyVerificationModule; extern KeyVerificationModule *keyVerificationModule;

View File

@ -68,6 +68,7 @@
#define TB_LEFT 1 #define TB_LEFT 1
#define TB_RIGHT 2 #define TB_RIGHT 2
#define TB_PRESS 0 // BUTTON_PIN #define TB_PRESS 0 // BUTTON_PIN
#define TB_DIRECTION FALLING
// microphone // microphone
#define ES7210_SCK 47 #define ES7210_SCK 47