From 6cd1882aaa861ef44882d5e55a6c769d42edaccd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 11 Aug 2024 17:22:01 -0500 Subject: [PATCH 01/38] [create-pull-request] automated change (#4439) Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 4 ++++ src/mesh/generated/meshtastic/telemetry.pb.h | 12 +++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/protobufs b/protobufs index 2fa7d6a4b..071fd931e 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 2fa7d6a4b702fcd58b54b0d1d6e4b3b85164f649 +Subproject commit 071fd931ec6679bb21427c872f9839edea63e351 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 59664b792..1bea952ce 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -180,6 +180,10 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_SENSECAP_INDICATOR = 70, /* Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. */ meshtastic_HardwareModel_TRACKER_T1000_E = 71, + /* RAK3172 STM32WLE5 Module (https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172) */ + meshtastic_HardwareModel_RAK3172 = 72, + /* Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. */ + meshtastic_HardwareModel_WIO_E5 = 73, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index 82cd0a55d..a4acd3f4a 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -63,7 +63,13 @@ typedef enum _meshtastic_TelemetrySensorType { /* DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) */ meshtastic_TelemetrySensorType_DFROBOT_LARK = 24, /* NAU7802 Scale Chip or compatible */ - meshtastic_TelemetrySensorType_NAU7802 = 25 + meshtastic_TelemetrySensorType_NAU7802 = 25, + /* BMP3XX High accuracy temperature and pressure */ + meshtastic_TelemetrySensorType_BMP3XX = 26, + /* ICM-20948 9-Axis digital motion processor */ + meshtastic_TelemetrySensorType_ICM20948 = 27, + /* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */ + meshtastic_TelemetrySensorType_MAX17048 = 28 } meshtastic_TelemetrySensorType; /* Struct definitions */ @@ -197,8 +203,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET -#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_NAU7802 -#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_NAU7802+1)) +#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_MAX17048 +#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_MAX17048+1)) From c74bce93607cd1749d7e8157089b6e1172f1b521 Mon Sep 17 00:00:00 2001 From: Ben Loomis Date: Mon, 12 Aug 2024 04:40:57 -0700 Subject: [PATCH 02/38] Detect UM600 as UC6580 (#4444) --- src/gps/GPS.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 82370937b..0164b554d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1220,6 +1220,14 @@ GnssModel_t GPS::probe(int serialSpeed) return GNSS_MODEL_UC6580; } + clearBuffer(); + _serial_gps->write("$PDTINFO\r\n"); + delay(750); + if (getACK("UM600", 500) == GNSS_RESPONSE_OK) { + LOG_INFO("UM600 detected, using UC6580 Module\n"); + return GNSS_MODEL_UC6580; + } + // Get version information for ATGM336H clearBuffer(); _serial_gps->write("$PCAS06,1*1A\r\n"); @@ -1822,4 +1830,4 @@ void GPS::toggleGpsMode() enable(); } } -#endif // Exclude GPS +#endif // Exclude GPS \ No newline at end of file From f97ae522630f9ae73005807bb6e9bc7e8b8e6ddd Mon Sep 17 00:00:00 2001 From: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Date: Tue, 13 Aug 2024 03:31:45 +0200 Subject: [PATCH 03/38] STM32WL improvements (#4449) * STM32WL: Enable DeviceTelemetry * Add long/short name user preference options * Add new STM32WL-based hardware models --- arch/stm32/stm32.ini | 4 ++-- src/mesh/NodeDB.cpp | 8 ++++++++ src/platform/stm32wl/architecture.h | 12 +++++++++--- userPrefs.h | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index d6d14e2c9..ffbc51680 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -21,7 +21,7 @@ build_flags = -fdata-sections build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink @@ -33,4 +33,4 @@ lib_deps = lib_ignore = https://github.com/mathertel/OneButton@~2.6.1 - Wire + Wire \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 0f6c444ce..666255c74 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -563,8 +563,16 @@ void NodeDB::installDefaultDeviceState() // Set default owner name pickNewNodeNum(); // based on macaddr now +#ifdef CONFIG_OWNER_LONG_NAME_USERPREFS + snprintf(owner.long_name, sizeof(owner.long_name), CONFIG_OWNER_LONG_NAME_USERPREFS); +#else snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]); +#endif +#ifdef CONFIG_OWNER_SHORT_NAME_USERPREFS + snprintf(owner.short_name, sizeof(owner.short_name), CONFIG_OWNER_SHORT_NAME_USERPREFS); +#else snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]); +#endif snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr)); } diff --git a/src/platform/stm32wl/architecture.h b/src/platform/stm32wl/architecture.h index 1c021903a..325a192a4 100644 --- a/src/platform/stm32wl/architecture.h +++ b/src/platform/stm32wl/architecture.h @@ -9,12 +9,18 @@ #ifndef HAS_RADIO #define HAS_RADIO 1 #endif +#ifndef HAS_TELEMETRY +#define HAS_TELEMETRY 1 +#endif // // set HW_VENDOR // - -#ifndef HW_VENDOR +#ifdef _VARIANT_WIOE5_ +#define HW_VENDOR meshtastic_HardwareModel_WIO_E5 +#elif defined(_VARIANT_RAK3172_) +#define HW_VENDOR meshtastic_HardwareModel_RAK3172 +#else #define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW #endif @@ -22,4 +28,4 @@ #define SX126X_CS 1000 #define SX126X_DIO1 1001 #define SX126X_RESET 1003 -#define SX126X_BUSY 1004 +#define SX126X_BUSY 1004 \ No newline at end of file diff --git a/userPrefs.h b/userPrefs.h index b365e8c6f..3ebbefcaf 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -19,6 +19,9 @@ // #define CHANNEL_0_NAME_USERPREFS "DEFCONnect" // #define CHANNEL_0_PRECISION_USERPREFS 13 +// #define CONFIG_OWNER_LONG_NAME_USERPREFS "My Long Name" +// #define CONFIG_OWNER_SHORT_NAME_USERPREFS "MLN" + // #define SPLASH_TITLE_USERPREFS "DEFCONtastic" // #define icon_width 34 // #define icon_height 29 From 6e8300287b8ff056f5987242456334480493d467 Mon Sep 17 00:00:00 2001 From: "Aaron.Lee" <32860565+Heltec-Aaron-Lee@users.noreply.github.com> Date: Tue, 13 Aug 2024 19:30:35 +0800 Subject: [PATCH 04/38] Heltec boards sensor and low power features update (#4418) * Update sensor drive and low power features. * Update ST7789 TFT control logic. * Update Heltec nRF board low power features. * Update the GPS UART port pointer --- src/gps/GPS.cpp | 10 ++++++++++ src/graphics/Screen.cpp | 20 +++++++++++++++++--- src/main.cpp | 19 +++++++++++++++++++ src/platform/esp32/main-esp32.cpp | 5 +++++ src/platform/nrf52/NRF52Bluetooth.cpp | 11 +++++++++-- src/platform/nrf52/main-nrf52.cpp | 6 ++++++ src/sleep.cpp | 7 +++++++ variants/heltec_capsule_sensor_v3/variant.h | 10 ++++++++++ variants/heltec_mesh_node_t114/variant.h | 2 +- 9 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 0164b554d..89af0430a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -810,6 +810,16 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) powerState = newState; LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); +#ifdef HELTEC_MESH_NODE_T114 + if( (oldState==GPS_OFF || oldState==GPS_HARDSLEEP) && (newState!=GPS_OFF && newState!=GPS_HARDSLEEP) ) + { + _serial_gps->begin(serialSpeeds[speedSelect]); + } + else if( (newState==GPS_OFF || newState==GPS_HARDSLEEP) && (oldState!=GPS_OFF && oldState!=GPS_HARDSLEEP) ) + { + _serial_gps->end(); + } +#endif switch (newState) { case GPS_ACTIVE: case GPS_IDLE: diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index ea5ab9788..bc7b3d1e6 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1591,6 +1591,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #ifdef USE_ST7789 + pinMode(VTFT_CTRL, OUTPUT); + digitalWrite(VTFT_CTRL,LOW); + ui->init(); #ifdef ESP_PLATFORM analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT); #else @@ -1609,10 +1612,21 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #endif LOG_INFO("Turning off screen\n"); dispdev->displayOff(); - #ifdef USE_ST7789 - pinMode(VTFT_LEDA, OUTPUT); - digitalWrite(VTFT_LEDA, !TFT_BACKLIGHT_ON); + SPI1.end(); +#if defined(ARCH_ESP32) + pinMode(VTFT_LEDA, ANALOG); + pinMode(VTFT_CTRL, ANALOG); + pinMode(ST7789_RESET,ANALOG); + pinMode(ST7789_RS,ANALOG); + pinMode(ST7789_NSS,ANALOG); +#else + nrf_gpio_cfg_default(VTFT_LEDA); + nrf_gpio_cfg_default(VTFT_CTRL); + nrf_gpio_cfg_default(ST7789_RESET); + nrf_gpio_cfg_default(ST7789_RS); + nrf_gpio_cfg_default(ST7789_NSS); +#endif #endif #ifdef T_WATCH_S3 diff --git a/src/main.cpp b/src/main.cpp index 0a3c1ae0b..c3e779554 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -319,6 +319,14 @@ void setup() digitalWrite(RESET_OLED, 1); #endif +#ifdef SENSOR_POWER_CTRL_PIN + pinMode(SENSOR_POWER_CTRL_PIN,OUTPUT); + digitalWrite(SENSOR_POWER_CTRL_PIN,SENSOR_POWER_ON); +#endif + +#ifdef SENSOR_GPS_CONFLICT + bool sensor_detected=false; +#endif #ifdef PERIPHERAL_WARMUP_MS // Some peripherals may require additional time to stabilize after power is connected // e.g. I2C on Heltec Vision Master @@ -458,6 +466,9 @@ void setup() LOG_INFO("No I2C devices found\n"); } else { LOG_INFO("%i I2C devices found\n", i2cCount); +#ifdef SENSOR_GPS_CONFLICT + sensor_detected=true; +#endif } #ifdef ARCH_ESP32 @@ -703,6 +714,10 @@ void setup() #if !MESHTASTIC_EXCLUDE_GPS // If we're taking on the repeater role, ignore GPS +#ifdef SENSOR_GPS_CONFLICT + if(sensor_detected==false) + { +#endif if (HAS_GPS) { if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { @@ -714,6 +729,10 @@ void setup() } } } +#ifdef SENSOR_GPS_CONFLICT + } +#endif + #endif nodeStatus->observe(&nodeDB->newStatus); diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 19f435908..36bd3fbb3 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -228,6 +228,9 @@ void cpuDeepSleep(uint32_t msecToWake) // FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead // of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN); +#ifdef ESP32S3_WAKE_TYPE + esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE); +#else #if SOC_PM_SUPPORT_EXT_WAKEUP #ifdef CONFIG_IDF_TARGET_ESP32 // ESP_EXT1_WAKEUP_ALL_LOW has been deprecated since esp-idf v5.4 for any other target. @@ -236,6 +239,8 @@ void cpuDeepSleep(uint32_t msecToWake) esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_LOW); #endif #endif + +#endif //#end ESP32S3_WAKE_TYPE #endif // We want RTC peripherals to stay on diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 81a165f2d..b27f9df0c 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -198,8 +198,15 @@ void NRF52Bluetooth::shutdown() { // Shutdown bluetooth for minimum power draw LOG_INFO("Disable NRF52 bluetooth\n"); - if (connectionHandle != 0) { - Bluefruit.disconnect(connectionHandle); + uint8_t connection_num=Bluefruit.connected(); + if(connection_num) + { + for(uint8_t i=0;i 100ms will reset the L76K +//#define PIN_GPS_RESET (32 + 6) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K #define GPS_RESET_MODE LOW #define PIN_GPS_EN (21) #define GPS_EN_ACTIVE HIGH From 62a0321c7d0dc351bb87ea6abda3b1a991b47e20 Mon Sep 17 00:00:00 2001 From: geeksville Date: Tue, 13 Aug 2024 04:45:39 -0700 Subject: [PATCH 05/38] Fixes for #4395: nrf52 flash filesystem reliability (#4406) * bug #4184: fix config file loss due to filesystem write errors * Use SafeFile for atomic file writing (with xor checksum readback) * Write db.proto last because it could be the largest file on the FS (and less critical) * Don't keep a tmp file around while writing db.proto (because too big to fit two files in the filesystem) * generate a new critial fault if we encounter errors writing to flash either CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE or CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE (depending on if the second write attempt worked) * reformat the filesystem if we detect it is corrupted (then rewrite our config files) (only on nrf52 - not sure yet if we should bother on ESP32) * If we have to format the FS, make sure to preserve the oem.proto if it exists * add logLegacy() so old C code in libs can log via our logging * move filesList() to a better location (used only in developer builds) * Reformat with "trunk fmt" to match our coding conventions * for #4395: don't use .exists() to before attempting file open If a LFS filesystem is corrupted, .exists() can fail when a mere .open() attempt would have succeeded. Therefore better to do the .open() in hopes that we can read the file (in case we need to reformat to fix the FS). (Seen and confirmed in stress testing) * for #4395 more fixes, see below for details: * check for LFS assertion failures during file operations (needs customized lfs_util.h to provide suitable hooks) * Remove fsCheck() because checking filesystem by writing to it is very high risk, it makes likelyhood that we will be able to read the config protobufs quite low. * Update the LFS inside of adafruitnrf52 to 1.7.2 (from their old 1.6.1) to get the following fix: https://github.com/littlefs-project/littlefs/commit/97d8d5e96a7781596708664f18f2ea6c3a179330 * use disable_adafruit_usb.py now that we are (temporarily?) using a forked adafruit lib We need to reach inside the adafruit project and turn off USE_TINYUSB, just doing that from platformio.ini is no longer sufficient. Tested on a wio-sdk-wm1110 board (which is the only board that had this problem) --------- Co-authored-by: Ben Meadors --- arch/nrf52/cpp_overrides/lfs_util.h | 208 +++++++++++++++++++++++++ arch/nrf52/nrf52.ini | 6 +- extra_scripts/disable_adafruit_usb.py | 18 ++- monitor/filter_c3_exception_decoder.py | 21 +-- src/DebugConfiguration.cpp | 12 +- src/DebugConfiguration.h | 3 + src/FSCommon.cpp | 100 ++---------- src/FSCommon.h | 8 +- src/SafeFile.cpp | 8 +- src/mesh/NodeDB.cpp | 26 ---- variants/wio-sdk-wm1110/platformio.ini | 30 ++-- 11 files changed, 285 insertions(+), 155 deletions(-) create mode 100644 arch/nrf52/cpp_overrides/lfs_util.h diff --git a/arch/nrf52/cpp_overrides/lfs_util.h b/arch/nrf52/cpp_overrides/lfs_util.h new file mode 100644 index 000000000..bf5a347b1 --- /dev/null +++ b/arch/nrf52/cpp_overrides/lfs_util.h @@ -0,0 +1,208 @@ +/* + * lfs utility functions + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +// MESHTASTIC/@geeksville note: This file is copied from the Adafruit nrf52 arduino lib. And we use a special -include in +// nrf52.ini to load it before EVERY file we do this hack because the default definitions for LFS_ASSERT are quite poor and we +// don't want to fork the adafruit lib (again) and send in a PR that they probably won't merge anyways. This file might break if +// they ever update lfs.util on their side, in which case we'll need to update this file to match their new version. The version +// this is a copy from is almost exactly +// https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/c25d93268a3b9c23e9a1ccfcaf9b208beca624ca/libraries/Adafruit_LittleFS/src/littlefs/lfs_util.h + +#ifndef LFS_UTIL_H +#define LFS_UTIL_H + +// Users can override lfs_util.h with their own configuration by defining +// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). +// +// If LFS_CONFIG is used, none of the default utils will be emitted and must be +// provided by the config file. To start I would suggest copying lfs_util.h and +// modifying as needed. +#ifdef LFS_CONFIG +#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) +#define LFS_STRINGIZE2(x) #x +#include LFS_STRINGIZE(LFS_CONFIG) +#else + +// System includes +#include +#include +#include + +#ifndef LFS_NO_MALLOC +#include +#endif +#ifndef LFS_NO_ASSERT +#include +#endif + +#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Macros, may be replaced by system specific wrappers. Arguments to these +// macros must not have side-effects as the macros can be removed for a smaller +// code footprint + +// Logging functions +#ifndef LFS_NO_DEBUG + +void logLegacy(const char *level, const char *fmt, ...); +#define LFS_DEBUG(fmt, ...) logLegacy("DEBUG", "lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#else +#define LFS_DEBUG(fmt, ...) +#endif + +#ifndef LFS_NO_WARN +#define LFS_WARN(fmt, ...) logLegacy("WARN", "lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#else +#define LFS_WARN(fmt, ...) +#endif + +#ifndef LFS_NO_ERROR +#define LFS_ERROR(fmt, ...) logLegacy("ERROR", "lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__) +#else +#define LFS_ERROR(fmt, ...) +#endif + +// Runtime assertions +#ifndef LFS_NO_ASSERT +#define LFS_ASSERT(test) assert(test) +#else +extern void lfs_assert(const char *reason); +#define LFS_ASSERT(test) \ + if (!(test)) \ + lfs_assert(#test) +#endif + +// Builtin functions, these may be replaced by more efficient +// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more +// expensive basic C implementation for debugging purposes + +// Min/max functions for unsigned 32-bit numbers +static inline uint32_t lfs_max(uint32_t a, uint32_t b) +{ + return (a > b) ? a : b; +} + +static inline uint32_t lfs_min(uint32_t a, uint32_t b) +{ + return (a < b) ? a : b; +} + +// Find the next smallest power of 2 less than or equal to a +static inline uint32_t lfs_npw2(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return 32 - __builtin_clz(a - 1); +#else + uint32_t r = 0; + uint32_t s; + a -= 1; + s = (a > 0xffff) << 4; + a >>= s; + r |= s; + s = (a > 0xff) << 3; + a >>= s; + r |= s; + s = (a > 0xf) << 2; + a >>= s; + r |= s; + s = (a > 0x3) << 1; + a >>= s; + r |= s; + return (r | (a >> 1)) + 1; +#endif +} + +// Count the number of trailing binary zeros in a +// lfs_ctz(0) may be undefined +static inline uint32_t lfs_ctz(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) + return __builtin_ctz(a); +#else + return lfs_npw2((a & -a) + 1) - 1; +#endif +} + +// Count the number of binary ones in a +static inline uint32_t lfs_popc(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return __builtin_popcount(a); +#else + a = a - ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; +#endif +} + +// Find the sequence comparison of a and b, this is the distance +// between a and b ignoring overflow +static inline int lfs_scmp(uint32_t a, uint32_t b) +{ + return (int)(unsigned)(a - b); +} + +// Convert from 32-bit little-endian to native order +static inline uint32_t lfs_fromle32(uint32_t a) +{ +#if !defined(LFS_NO_INTRINSICS) && ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + return a; +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + return __builtin_bswap32(a); +#else + return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) | (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24); +#endif +} + +// Convert to 32-bit little-endian from native order +static inline uint32_t lfs_tole32(uint32_t a) +{ + return lfs_fromle32(a); +} + +// Calculate CRC-32 with polynomial = 0x04c11db7 +void lfs_crc(uint32_t *crc, const void *buffer, size_t size); + +// Allocate memory, only used if buffers are not provided to littlefs +static inline void *lfs_malloc(size_t size) +{ +#ifndef LFS_NO_MALLOC + extern void *pvPortMalloc(size_t xWantedSize); + return pvPortMalloc(size); +#else + (void)size; + return NULL; +#endif +} + +// Deallocate memory, only used if buffers are not provided to littlefs +static inline void lfs_free(void *p) +{ +#ifndef LFS_NO_MALLOC + extern void vPortFree(void *pv); + vPortFree(p); +#else + (void)p; +#endif +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif \ No newline at end of file diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index 1a371e920..f3e7c3036 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -2,9 +2,13 @@ ; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files platform = platformio/nordicnrf52@^10.5.0 extends = arduino_base +platform_packages = + ; our custom Git version until they merge our PR + framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git build_type = debug -build_flags = +build_flags = + -include arch/nrf52/cpp_overrides/lfs_util.h ${arduino_base.build_flags} -DSERIAL_BUFFER_SIZE=1024 -Wno-unused-variable diff --git a/extra_scripts/disable_adafruit_usb.py b/extra_scripts/disable_adafruit_usb.py index fb391715c..596242184 100644 --- a/extra_scripts/disable_adafruit_usb.py +++ b/extra_scripts/disable_adafruit_usb.py @@ -5,16 +5,24 @@ Import("env") # NOTE: This is not currently used, but can serve as an example on how to write extra_scripts -print("Current CLI targets", COMMAND_LINE_TARGETS) -print("Current Build targets", BUILD_TARGETS) -print("CPP defs", env.get("CPPDEFINES")) +# print("Current CLI targets", COMMAND_LINE_TARGETS) +# print("Current Build targets", BUILD_TARGETS) +# print("CPP defs", env.get("CPPDEFINES")) +# print(env.Dump()) # Adafruit.py in the platformio build tree is a bit naive and always enables their USB stack for building. We don't want this. # So come in after that python script has run and disable it. This hack avoids us having to fork that big project and send in a PR # which might not be accepted. -@geeksville -env["CPPDEFINES"].remove("USBCON") -env["CPPDEFINES"].remove("USE_TINYUSB") +lib_builders = env.get("__PIO_LIB_BUILDERS", None) +if lib_builders is not None: + print("Disabling Adafruit USB stack") + for k in lib_builders: + if k.name == "Adafruit TinyUSB Library": + libenv = k.env + # print(f"{k.name }: { libenv.Dump() } ") + # libenv["CPPDEFINES"].remove("USBCON") + libenv["CPPDEFINES"].remove("USE_TINYUSB") # Custom actions when building program/firmware # env.AddPreAction("buildprog", callback...) diff --git a/monitor/filter_c3_exception_decoder.py b/monitor/filter_c3_exception_decoder.py index e59b0be2a..6d7b5370c 100644 --- a/monitor/filter_c3_exception_decoder.py +++ b/monitor/filter_c3_exception_decoder.py @@ -18,10 +18,7 @@ import subprocess import sys from platformio.project.exception import PlatformioException -from platformio.public import ( - DeviceMonitorFilterBase, - load_build_metadata, -) +from platformio.public import DeviceMonitorFilterBase, load_build_metadata # By design, __init__ is called inside miniterm and we can't pass context to it. # pylint: disable=attribute-defined-outside-init @@ -32,7 +29,7 @@ IS_WINDOWS = sys.platform.startswith("win") class Esp32C3ExceptionDecoder(DeviceMonitorFilterBase): NAME = "esp32_c3_exception_decoder" - PCADDR_PATTERN = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE) + PCADDR_PATTERN = re.compile(r"0x4[0-9a-f]{7}", re.IGNORECASE) def __call__(self): self.buffer = "" @@ -75,14 +72,14 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html % self.__class__.__name__ ) return False - + if not os.path.isfile(self.addr2line_path): sys.stderr.write( "%s: disabling, addr2line at %s does not exist\n" % (self.__class__.__name__, self.addr2line_path) ) return False - + return True except PlatformioException as e: sys.stderr.write( @@ -117,7 +114,7 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html trace = self.get_backtrace(m) if len(trace) != "": - text = text[: last] + trace + text[last :] + text = text[:last] + trace + text[last:] last += len(trace) return text @@ -125,14 +122,10 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html def get_backtrace(self, match): trace = "\n" enc = "mbcs" if IS_WINDOWS else "utf-8" - args = [self.addr2line_path, u"-fipC", u"-e", self.firmware_path] + args = [self.addr2line_path, "-fipC", "-e", self.firmware_path] try: addr = match.group() - output = ( - subprocess.check_output(args + [addr]) - .decode(enc) - .strip() - ) + output = subprocess.check_output(args + [addr]).decode(enc).strip() output = output.replace( "\n", "\n " ) # newlines happen with inlined methods diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp index d9ecd9fe3..d58856c4d 100644 --- a/src/DebugConfiguration.cpp +++ b/src/DebugConfiguration.cpp @@ -26,6 +26,16 @@ SOFTWARE.*/ #include "DebugConfiguration.h" +/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic +extern "C" void logLegacy(const char *level, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (console) + console->vprintf(level, fmt, args); + va_end(args); +} + #if HAS_NETWORKING Syslog::Syslog(UDP &client) @@ -169,4 +179,4 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess return true; } -#endif +#endif \ No newline at end of file diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index f5b3fa25a..7987e7fa1 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -62,6 +62,9 @@ #endif #endif +/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic +extern "C" void logLegacy(const char *level, const char *fmt, ...); + #define SYSLOG_NILVALUE "-" #define SYSLOG_CRIT 2 /* critical conditions */ diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp index 3017ec085..f45319c0b 100644 --- a/src/FSCommon.cpp +++ b/src/FSCommon.cpp @@ -48,6 +48,15 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input) } #endif +bool lfs_assert_failed = + false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h) + +extern "C" void lfs_assert(const char *reason) +{ + LOG_ERROR("LFS assert: %s\n", reason); + lfs_assert_failed = true; +} + /** * @brief Copies a file from one location to another. * @@ -199,7 +208,7 @@ std::vector getFiles(const char *dirname, uint8_t levels) * @param levels The number of levels of subdirectories to list. * @param del Whether or not to delete the contents of the directory after listing. */ -void listDir(const char *dirname, uint8_t levels, bool del = false) +void listDir(const char *dirname, uint8_t levels, bool del) { #ifdef FSCom #if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)) @@ -214,7 +223,9 @@ void listDir(const char *dirname, uint8_t levels, bool del = false) } File file = root.openNextFile(); - while (file) { + while ( + file && + file.name()[0]) { // This file.name() check is a workaround for a bug in the Adafruit LittleFS nrf52 glue (see issue 4395) if (file.isDirectory() && !String(file.name()).endsWith(".")) { if (levels) { #ifdef ARCH_ESP32 @@ -313,62 +324,6 @@ void rmDir(const char *dirname) #endif } -bool fsCheck() -{ -#if defined(ARCH_NRF52) - size_t write_size = 0; - size_t read_size = 0; - char buf[32] = {0}; - - Adafruit_LittleFS_Namespace::File file(FSCom); - const char *text = "meshtastic fs test"; - size_t text_length = strlen(text); - const char *filename = "/meshtastic.txt"; - - LOG_DEBUG("Try create file .\n"); - if (file.open(filename, FILE_O_WRITE)) { - write_size = file.write(text); - } else { - LOG_DEBUG("Open file failed .\n"); - goto FORMAT_FS; - } - - if (write_size != text_length) { - LOG_DEBUG("Text bytes do not match .\n"); - file.close(); - goto FORMAT_FS; - } - - file.close(); - - if (!file.open(filename, FILE_O_READ)) { - LOG_DEBUG("Open file failed .\n"); - goto FORMAT_FS; - } - - read_size = file.readBytes(buf, text_length); - if (read_size != text_length) { - LOG_DEBUG("Text bytes do not match .\n"); - file.close(); - goto FORMAT_FS; - } - - if (memcmp(buf, text, text_length) != 0) { - LOG_DEBUG("The written bytes do not match the read bytes .\n"); - file.close(); - goto FORMAT_FS; - } - return true; -FORMAT_FS: - LOG_DEBUG("Format FS ....\n"); - FSCom.format(); - FSCom.begin(); - return false; -#else - return true; -#endif -} - void fsInit() { #ifdef FSCom @@ -378,35 +333,6 @@ void fsInit() } #if defined(ARCH_ESP32) LOG_DEBUG("Filesystem files (%d/%d Bytes):\n", FSCom.usedBytes(), FSCom.totalBytes()); -#elif defined(ARCH_NRF52) - /* - * nRF52840 has a certain chance of automatic formatting failure. - * Try to create a file after initializing the file system. If the creation fails, - * it means that the file system is not working properly. Please format it manually again. - * To check the normality of the file system, you need to disable the LFS_NO_ASSERT assertion. - * Otherwise, the assertion will be entered at the moment of reading or opening, and the FS will not be formatted. - * */ - bool ret = false; - uint8_t retry = 3; - - while (retry--) { - ret = fsCheck(); - if (ret) { - LOG_DEBUG("File system check is OK.\n"); - break; - } - delay(10); - } - - // It may not be possible to reach this step. - // Add a loop here to prevent unpredictable situations from happening. - // Can add a screen to display error status later. - if (!ret) { - while (1) { - LOG_ERROR("The file system is damaged and cannot proceed to the next step.\n"); - delay(1000); - } - } #else LOG_DEBUG("Filesystem files:\n"); #endif diff --git a/src/FSCommon.h b/src/FSCommon.h index 3d485d1b1..254245b29 100644 --- a/src/FSCommon.h +++ b/src/FSCommon.h @@ -51,9 +51,13 @@ using namespace Adafruit_LittleFS_Namespace; #endif void fsInit(); +void fsListFiles(); bool copyFile(const char *from, const char *to); bool renameFile(const char *pathFrom, const char *pathTo); std::vector getFiles(const char *dirname, uint8_t levels); -void listDir(const char *dirname, uint8_t levels, bool del); +void listDir(const char *dirname, uint8_t levels, bool del = false); void rmDir(const char *dirname); -void setupSDCard(); \ No newline at end of file +void setupSDCard(); + +extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our + // modified lfs_util.h) diff --git a/src/SafeFile.cpp b/src/SafeFile.cpp index 161a80870..c17d422bd 100644 --- a/src/SafeFile.cpp +++ b/src/SafeFile.cpp @@ -11,6 +11,9 @@ static File openFile(const char *filename, bool fullAtomic) String filenameTmp = filename; filenameTmp += ".tmp"; + // clear any previous LFS errors + lfs_assert_failed = false; + return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE); } @@ -73,6 +76,9 @@ bool SafeFile::close() /// Read our (closed) tempfile back in and compare the hash bool SafeFile::testReadback() { + bool lfs_failed = lfs_assert_failed; + lfs_assert_failed = false; + String filenameTmp = filename; filenameTmp += ".tmp"; auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ); @@ -93,7 +99,7 @@ bool SafeFile::testReadback() return false; } - return true; + return !lfs_failed; } #endif \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 666255c74..000da335f 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -56,27 +56,6 @@ meshtastic_ChannelFile channelFile; meshtastic_OEMStore oemStore; static bool hasOemStore = false; -// These are not publically exposed - copied from InternalFileSystem.cpp -// #define FLASH_NRF52_PAGE_SIZE 4096 -// #define LFS_FLASH_TOTAL_SIZE (7*FLASH_NRF52_PAGE_SIZE) -// #define LFS_BLOCK_SIZE 128 - -/// List all files in the FS and test write and readback. -/// Useful for filesystem stress testing - normally stripped from build by the linker. -void flashTest() -{ - auto filesManifest = getFiles("/", 5); - - uint32_t totalSize = 0; - for (size_t i = 0; i < filesManifest.size(); i++) { - LOG_INFO("File %s (size %d)\n", filesManifest[i].file_name, filesManifest[i].size_bytes); - totalSize += filesManifest[i].size_bytes; - } - LOG_INFO("%d files (total size %u)\n", filesManifest.size(), totalSize); - // LOG_INFO("Filesystem block size %u, total bytes %u", LFS_FLASH_TOTAL_SIZE, LFS_BLOCK_SIZE); - nodeDB->saveToDisk(); -} - bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field) { if (ostream) { @@ -617,11 +596,6 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t LoadFileResult state = LoadFileResult::OTHER_FAILURE; #ifdef FSCom - if (!FSCom.exists(filename)) { - LOG_INFO("File %s not found\n", filename); - return LoadFileResult::NOT_FOUND; - } - auto f = FSCom.open(filename, FILE_O_READ); if (f) { diff --git a/variants/wio-sdk-wm1110/platformio.ini b/variants/wio-sdk-wm1110/platformio.ini index 077356553..e77455bcf 100644 --- a/variants/wio-sdk-wm1110/platformio.ini +++ b/variants/wio-sdk-wm1110/platformio.ini @@ -3,29 +3,23 @@ extends = nrf52840_base board = wio-sdk-wm1110 +extra_scripts = + bin/platformio-custom.py + extra_scripts/disable_adafruit_usb.py + # Remove adafruit USB serial from the build (it is incompatible with using the ch340 serial chip on this board) build_unflags = ${nrf52840_base:build_unflags} -DUSBCON -DUSE_TINYUSB build_flags = ${nrf52840_base.build_flags} -Ivariants/wio-sdk-wm1110 -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DWIO_WM1110 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DCFG_TUD_CDC=0 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/wio-sdk-wm1110> -lib_deps = - ${nrf52840_base.lib_deps} -debug_tool = jlink -;debug_tool = stlink -;debug_speed = 4000 -; No need to reflash if the binary hasn't changed -debug_load_mode = modified -; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -upload_protocol = nrfutil -;upload_protocol = stlink -; we prefer to stop in setup() because we are an 'ardiuno' app -debug_init_break = tbreak setup -; we need to turn off BLE/soft device if we are debugging otherwise it will watchdog reset us. -debug_extra_cmds = - echo Running .gdbinit script - commands 1 - set useSoftDevice = false - end +;debug_tool = jlink +debug_tool = stlink +; No need to reflash if the binary hasn't changed + +; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) +;upload_protocol = nrfutil +;upload_protocol = stlink \ No newline at end of file From e85a2e827bfe455648c644ac263adc23b4d6bc8c Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 13 Aug 2024 06:49:32 -0500 Subject: [PATCH 06/38] Update protos --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 071fd931e..c9ca0dbe9 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 071fd931ec6679bb21427c872f9839edea63e351 +Subproject commit c9ca0dbe9cc7105399e0486c07e0601f849b94af diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 1bea952ce..955484cb0 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -184,6 +184,9 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_RAK3172 = 72, /* Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. */ meshtastic_HardwareModel_WIO_E5 = 73, + /* RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module + SSD1306 OLED and No GPS */ + meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ From 7740b4bccd8859fa92a5bb6285836722b8fa5318 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 13 Aug 2024 06:52:03 -0500 Subject: [PATCH 07/38] Sweep up some missed trunk formatting --- src/gps/GPS.cpp | 7 ++--- src/graphics/Screen.cpp | 8 +++--- src/main.cpp | 29 ++++++++++----------- src/platform/esp32/main-esp32.cpp | 4 +-- src/platform/nrf52/NRF52Bluetooth.cpp | 12 ++++----- src/sleep.cpp | 6 ++--- variants/heltec_capsule_sensor_v3/variant.h | 2 +- variants/heltec_mesh_node_t114/variant.h | 2 +- 8 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 89af0430a..7d7de8700 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -811,12 +811,9 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); #ifdef HELTEC_MESH_NODE_T114 - if( (oldState==GPS_OFF || oldState==GPS_HARDSLEEP) && (newState!=GPS_OFF && newState!=GPS_HARDSLEEP) ) - { + if ((oldState == GPS_OFF || oldState == GPS_HARDSLEEP) && (newState != GPS_OFF && newState != GPS_HARDSLEEP)) { _serial_gps->begin(serialSpeeds[speedSelect]); - } - else if( (newState==GPS_OFF || newState==GPS_HARDSLEEP) && (oldState!=GPS_OFF && oldState!=GPS_HARDSLEEP) ) - { + } else if ((newState == GPS_OFF || newState == GPS_HARDSLEEP) && (oldState != GPS_OFF && oldState != GPS_HARDSLEEP)) { _serial_gps->end(); } #endif diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index bc7b3d1e6..3a4db6ee0 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1592,7 +1592,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #ifdef USE_ST7789 pinMode(VTFT_CTRL, OUTPUT); - digitalWrite(VTFT_CTRL,LOW); + digitalWrite(VTFT_CTRL, LOW); ui->init(); #ifdef ESP_PLATFORM analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT); @@ -1617,9 +1617,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #if defined(ARCH_ESP32) pinMode(VTFT_LEDA, ANALOG); pinMode(VTFT_CTRL, ANALOG); - pinMode(ST7789_RESET,ANALOG); - pinMode(ST7789_RS,ANALOG); - pinMode(ST7789_NSS,ANALOG); + pinMode(ST7789_RESET, ANALOG); + pinMode(ST7789_RS, ANALOG); + pinMode(ST7789_NSS, ANALOG); #else nrf_gpio_cfg_default(VTFT_LEDA); nrf_gpio_cfg_default(VTFT_CTRL); diff --git a/src/main.cpp b/src/main.cpp index c3e779554..7f45905d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -320,12 +320,12 @@ void setup() #endif #ifdef SENSOR_POWER_CTRL_PIN - pinMode(SENSOR_POWER_CTRL_PIN,OUTPUT); - digitalWrite(SENSOR_POWER_CTRL_PIN,SENSOR_POWER_ON); + pinMode(SENSOR_POWER_CTRL_PIN, OUTPUT); + digitalWrite(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON); #endif #ifdef SENSOR_GPS_CONFLICT - bool sensor_detected=false; + bool sensor_detected = false; #endif #ifdef PERIPHERAL_WARMUP_MS // Some peripherals may require additional time to stabilize after power is connected @@ -467,7 +467,7 @@ void setup() } else { LOG_INFO("%i I2C devices found\n", i2cCount); #ifdef SENSOR_GPS_CONFLICT - sensor_detected=true; + sensor_detected = true; #endif } @@ -715,20 +715,19 @@ void setup() #if !MESHTASTIC_EXCLUDE_GPS // If we're taking on the repeater role, ignore GPS #ifdef SENSOR_GPS_CONFLICT - if(sensor_detected==false) - { + if (sensor_detected == false) { #endif - if (HAS_GPS) { - if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && - config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { - gps = GPS::createGps(); - if (gps) { - gpsStatus->observe(&gps->newStatus); - } else { - LOG_DEBUG("Running without GPS.\n"); + if (HAS_GPS) { + if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && + config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) { + gps = GPS::createGps(); + if (gps) { + gpsStatus->observe(&gps->newStatus); + } else { + LOG_DEBUG("Running without GPS.\n"); + } } } - } #ifdef SENSOR_GPS_CONFLICT } #endif diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 36bd3fbb3..1b997500a 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -229,7 +229,7 @@ void cpuDeepSleep(uint32_t msecToWake) // of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN); #ifdef ESP32S3_WAKE_TYPE - esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE); + esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE); #else #if SOC_PM_SUPPORT_EXT_WAKEUP #ifdef CONFIG_IDF_TARGET_ESP32 @@ -240,7 +240,7 @@ void cpuDeepSleep(uint32_t msecToWake) #endif #endif -#endif //#end ESP32S3_WAKE_TYPE +#endif // #end ESP32S3_WAKE_TYPE #endif // We want RTC peripherals to stay on diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index b27f9df0c..1405ea4f3 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -198,15 +198,13 @@ void NRF52Bluetooth::shutdown() { // Shutdown bluetooth for minimum power draw LOG_INFO("Disable NRF52 bluetooth\n"); - uint8_t connection_num=Bluefruit.connected(); - if(connection_num) - { - for(uint8_t i=0;i 100ms will reset the L76K +// #define PIN_GPS_RESET (32 + 6) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K #define GPS_RESET_MODE LOW #define PIN_GPS_EN (21) #define GPS_EN_ACTIVE HIGH From 464f270b12e31d5ec3545ca9033a38ab2c4d3a0e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 13 Aug 2024 06:56:20 -0500 Subject: [PATCH 08/38] More explicit guards for attempting to set RTC (#4452) * Guard against timesources from the mesh if we have good time * Trunk * Consider phone time in the past 24 hours authoritative as well * Rename * GPS can be null * Declaration * Remove RemoteHardware * Explicitly remove GPS * Exclude GPS earlier for RAK2560 --- arch/stm32/stm32.ini | 3 ++- src/gps/RTC.cpp | 4 ++++ src/gps/RTC.h | 2 ++ src/modules/PositionModule.cpp | 13 ++++++++++++- src/modules/PositionModule.h | 1 + variants/rak2560/platformio.ini | 1 + variants/rak2560/variant.h | 2 +- 7 files changed, 23 insertions(+), 3 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index ffbc51680..050dbf7f0 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -12,6 +12,7 @@ build_flags = -flto -Isrc/platform/stm32wl -g -DMESHTASTIC_MINIMIZE_BUILD + -DMESHTASTIC_EXCLUDE_GPS -DDEBUG_MUTE ; -DVECT_TAB_OFFSET=0x08000000 -DconfigUSE_CMSIS_RTOS_V2=1 @@ -21,7 +22,7 @@ build_flags = -fdata-sections build_src_filter = - ${arduino_base.build_src_filter} - - - - - - - - - - - - - + ${arduino_base.build_src_filter} - - - - - - - - - - - - - - board_upload.offset_address = 0x08000000 upload_protocol = stlink diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 710baca98..070038672 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -6,6 +6,7 @@ #include static RTCQuality currentQuality = RTCQualityNone; +uint32_t lastSetFromPhoneNtpOrGps = 0; RTCQuality getRTCQuality() { @@ -121,6 +122,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) if (shouldSet) { currentQuality = q; lastSetMsec = now; + if (currentQuality >= RTCQualityNTP) { + lastSetFromPhoneNtpOrGps = now; + } // This delta value works on all platforms timeStartMsec = now; diff --git a/src/gps/RTC.h b/src/gps/RTC.h index d31e85433..caa48dc06 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -24,6 +24,8 @@ enum RTCQuality { RTCQuality getRTCQuality(); +extern uint32_t lastSetFromPhoneNtpOrGps; + /// If we haven't yet set our RTC this boot, set it from a GPS derived time bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false); bool perhapsSetRTC(RTCQuality q, struct tm &t); diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 5da918049..1618ba3ed 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -90,7 +90,6 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes // will always be an equivalent or lesser RTCQuality (RTCQualityNTP or RTCQualityNet). force = true; #endif - // Set from phone RTC Quality to RTCQualityNTP since it should be approximately so trySetRtc(p, isLocal, force); } @@ -126,14 +125,26 @@ void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate) { + if (hasQualityTimesource() && !isLocal) { + LOG_DEBUG("Ignoring time from mesh because we have a GPS, RTC, or Phone/NTP time source in the past day\n"); + return; + } struct timeval tv; uint32_t secs = p.time; tv.tv_sec = secs; tv.tv_usec = 0; + perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv, forceUpdate); } +bool PositionModule::hasQualityTimesource() +{ + bool setFromPhoneOrNtpToday = (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); + bool hasGpsOrRtc = (gps && gps->isConnected()) || (rtc_found.address != ScanI2C::ADDRESS_NONE.address); + return hasGpsOrRtc || setFromPhoneOrNtpToday; +} + meshtastic_MeshPacket *PositionModule::allocReply() { if (precision == 0) { diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h index a5277aff6..c4ef66501 100644 --- a/src/modules/PositionModule.h +++ b/src/modules/PositionModule.h @@ -60,6 +60,7 @@ class PositionModule : public ProtobufModule, private concu void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false); uint32_t precision; void sendLostAndFoundText(); + bool hasQualityTimesource(); const uint32_t minimumTimeThreshold = Default::getConfiguredOrDefaultMsScaled(config.position.broadcast_smart_minimum_interval_secs, 30, numOnlineNodes); diff --git a/variants/rak2560/platformio.ini b/variants/rak2560/platformio.ini index 96c1bfa92..93c7acf71 100644 --- a/variants/rak2560/platformio.ini +++ b/variants/rak2560/platformio.ini @@ -6,6 +6,7 @@ board_check = true build_flags = ${nrf52840_base.build_flags} -Ivariants/rak2560 -D RAK_4631 -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DMESHTASTIC_EXCLUDE_GPS=1 -DHAS_RAKPROT=1 ; Define if RAk OneWireSerial is used (disables GPS) build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak2560> + + + lib_deps = diff --git a/variants/rak2560/variant.h b/variants/rak2560/variant.h index 0c1c9aed9..8e5d90553 100644 --- a/variants/rak2560/variant.h +++ b/variants/rak2560/variant.h @@ -227,7 +227,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG // #define GPS_TX_PIN PIN_SERIAL2_TX // #define PIN_GPS_EN PIN_3V3_EN // Disable GPS -#define MESHTASTIC_EXCLUDE_GPS 1 +// #define MESHTASTIC_EXCLUDE_GPS 1 // Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press // RAK12002 RTC Module From 8d1a34a4bf074aa3a5302d2105d128279f9a4fb1 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 07:25:05 -0500 Subject: [PATCH 09/38] Protobufs --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 +- src/mesh/generated/meshtastic/config.pb.cpp | 3 + src/mesh/generated/meshtastic/config.pb.h | 83 +++++++++-- src/mesh/generated/meshtastic/deviceonly.pb.h | 4 +- src/mesh/generated/meshtastic/localonly.pb.h | 14 +- src/mesh/generated/meshtastic/mesh.pb.cpp | 5 +- src/mesh/generated/meshtastic/mesh.pb.h | 110 +++++++++++--- src/mesh/generated/meshtastic/telemetry.pb.h | 136 +++++++++++------- 9 files changed, 273 insertions(+), 96 deletions(-) diff --git a/protobufs b/protobufs index c9ca0dbe9..126293c38 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c9ca0dbe9cc7105399e0486c07e0601f849b94af +Subproject commit 126293c38ff974ca253606c36da48cfab7fe6446 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bef2abf9b..d0e643dff 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,8 +168,6 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; - /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ - int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -180,8 +178,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ - int32_t factory_reset_config; + /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ + int32_t factory_reset; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -256,12 +254,11 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 -#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_config_tag 99 +#define meshtastic_AdminMessage_factory_reset_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -301,12 +298,11 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp index bb82198c0..c6274aed4 100644 --- a/src/mesh/generated/meshtastic/config.pb.cpp +++ b/src/mesh/generated/meshtastic/config.pb.cpp @@ -33,6 +33,9 @@ PB_BIND(meshtastic_Config_LoRaConfig, meshtastic_Config_LoRaConfig, 2) PB_BIND(meshtastic_Config_BluetoothConfig, meshtastic_Config_BluetoothConfig, AUTO) +PB_BIND(meshtastic_Config_SecurityConfig, meshtastic_Config_SecurityConfig, AUTO) + + diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 44a86f4d6..dbb0deb00 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -248,7 +248,8 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0, /* Long Range - Slow */ meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, - /* Very Long Range - Slow */ + /* Very Long Range - Slow + Deprecated in 2.5: Works only with txco and is unusably slow */ meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW = 2, /* Medium Range - Slow */ meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW = 3, @@ -259,7 +260,11 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { /* Short Range - Fast */ meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST = 6, /* Long Range - Moderately Fast */ - meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7 + meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE = 7, + /* Short Range - Turbo + This is the fastest preset and the only one with 500kHz bandwidth. + It is not legal to use in all regions due to this wider bandwidth. */ + meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8 } meshtastic_Config_LoRaConfig_ModemPreset; typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { @@ -276,10 +281,12 @@ typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { typedef struct _meshtastic_Config_DeviceConfig { /* Sets the role of node */ meshtastic_Config_DeviceConfig_Role role; - /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI */ + /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI + Moved to SecurityConfig */ bool serial_enabled; /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). - Set this to true to leave the debug log outputting even when API is active. */ + Set this to true to leave the debug log outputting even when API is active. + Moved to SecurityConfig */ bool debug_log_enabled; /* For boards without a hard wired button, this is the pin number that will be used Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */ @@ -295,7 +302,8 @@ typedef struct _meshtastic_Config_DeviceConfig { /* Treat double tap interrupt on supported accelerometers as a button press if set to true */ bool double_tap_as_button_press; /* If true, device is considered to be "managed" by a mesh administrator - Clients should then limit available configuration and administrative options inside the user interface */ + Clients should then limit available configuration and administrative options inside the user interface + Moved to SecurityConfig */ bool is_managed; /* Disables the triple-press of user button to enable or disable GPS */ bool disable_triple_click; @@ -515,10 +523,37 @@ typedef struct _meshtastic_Config_BluetoothConfig { meshtastic_Config_BluetoothConfig_PairingMode mode; /* Specified PIN for PairingMode.FixedPin */ uint32_t fixed_pin; - /* Enables device (serial style logs) over Bluetooth */ + /* Enables device (serial style logs) over Bluetooth + Moved to SecurityConfig */ bool device_logging_enabled; } meshtastic_Config_BluetoothConfig; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_public_key_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_private_key_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_Config_SecurityConfig_admin_key_t; +typedef struct _meshtastic_Config_SecurityConfig { + /* The public key of the user's device. + Sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_Config_SecurityConfig_public_key_t public_key; + /* The private key of the device. + Used to create a shared key with a remote device. */ + meshtastic_Config_SecurityConfig_private_key_t private_key; + /* The public key authorized to send admin messages to this node. */ + meshtastic_Config_SecurityConfig_admin_key_t admin_key; + /* If true, device is considered to be "managed" by a mesh administrator via admin messages + Device is managed by a mesh administrator. */ + bool is_managed; + /* Serial Console over the Stream API." */ + bool serial_enabled; + /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). + Output live debug logging over serial. */ + bool debug_log_api_enabled; + /* Enables device (serial style logs) over Bluetooth */ + bool bluetooth_logging_enabled; + /* Allow incoming device control over the insecure legacy admin channel. */ + bool admin_channel_enabled; +} meshtastic_Config_SecurityConfig; + typedef struct _meshtastic_Config { pb_size_t which_payload_variant; union { @@ -529,6 +564,7 @@ typedef struct _meshtastic_Config { meshtastic_Config_DisplayConfig display; meshtastic_Config_LoRaConfig lora; meshtastic_Config_BluetoothConfig bluetooth; + meshtastic_Config_SecurityConfig security; } payload_variant; } meshtastic_Config; @@ -583,8 +619,8 @@ extern "C" { #define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1)) #define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST -#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE -#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE+1)) +#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO +#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1)) #define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN #define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN @@ -612,6 +648,7 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_mode_ENUMTYPE meshtastic_Config_BluetoothConfig_PairingMode + /* Initializer values for message structs */ #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} #define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} @@ -622,6 +659,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} +#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} @@ -631,6 +669,7 @@ extern "C" { #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} +#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0} /* Field tags (for use in manual encoding/decoding) */ #define meshtastic_Config_DeviceConfig_role_tag 1 @@ -711,6 +750,14 @@ extern "C" { #define meshtastic_Config_BluetoothConfig_mode_tag 2 #define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3 #define meshtastic_Config_BluetoothConfig_device_logging_enabled_tag 4 +#define meshtastic_Config_SecurityConfig_public_key_tag 1 +#define meshtastic_Config_SecurityConfig_private_key_tag 2 +#define meshtastic_Config_SecurityConfig_admin_key_tag 3 +#define meshtastic_Config_SecurityConfig_is_managed_tag 4 +#define meshtastic_Config_SecurityConfig_serial_enabled_tag 5 +#define meshtastic_Config_SecurityConfig_debug_log_api_enabled_tag 6 +#define meshtastic_Config_SecurityConfig_bluetooth_logging_enabled_tag 7 +#define meshtastic_Config_SecurityConfig_admin_channel_enabled_tag 8 #define meshtastic_Config_device_tag 1 #define meshtastic_Config_position_tag 2 #define meshtastic_Config_power_tag 3 @@ -718,6 +765,7 @@ extern "C" { #define meshtastic_Config_display_tag 5 #define meshtastic_Config_lora_tag 6 #define meshtastic_Config_bluetooth_tag 7 +#define meshtastic_Config_security_tag 8 /* Struct field encoding specification for nanopb */ #define meshtastic_Config_FIELDLIST(X, a) \ @@ -727,7 +775,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,power,payload_variant.power) X(a, STATIC, ONEOF, MESSAGE, (payload_variant,network,payload_variant.network), 4) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.display), 5) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) #define meshtastic_Config_CALLBACK NULL #define meshtastic_Config_DEFAULT NULL #define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -737,6 +786,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bl #define meshtastic_Config_payload_variant_display_MSGTYPE meshtastic_Config_DisplayConfig #define meshtastic_Config_payload_variant_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig +#define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, role, 1) \ @@ -849,6 +899,18 @@ X(a, STATIC, SINGULAR, BOOL, device_logging_enabled, 4) #define meshtastic_Config_BluetoothConfig_CALLBACK NULL #define meshtastic_Config_BluetoothConfig_DEFAULT NULL +#define meshtastic_Config_SecurityConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 1) \ +X(a, STATIC, SINGULAR, BYTES, private_key, 2) \ +X(a, STATIC, SINGULAR, BYTES, admin_key, 3) \ +X(a, STATIC, SINGULAR, BOOL, is_managed, 4) \ +X(a, STATIC, SINGULAR, BOOL, serial_enabled, 5) \ +X(a, STATIC, SINGULAR, BOOL, debug_log_api_enabled, 6) \ +X(a, STATIC, SINGULAR, BOOL, bluetooth_logging_enabled, 7) \ +X(a, STATIC, SINGULAR, BOOL, admin_channel_enabled, 8) +#define meshtastic_Config_SecurityConfig_CALLBACK NULL +#define meshtastic_Config_SecurityConfig_DEFAULT NULL + extern const pb_msgdesc_t meshtastic_Config_msg; extern const pb_msgdesc_t meshtastic_Config_DeviceConfig_msg; extern const pb_msgdesc_t meshtastic_Config_PositionConfig_msg; @@ -858,6 +920,7 @@ extern const pb_msgdesc_t meshtastic_Config_NetworkConfig_IpV4Config_msg; extern const pb_msgdesc_t meshtastic_Config_DisplayConfig_msg; extern const pb_msgdesc_t meshtastic_Config_LoRaConfig_msg; extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; +extern const pb_msgdesc_t meshtastic_Config_SecurityConfig_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_Config_fields &meshtastic_Config_msg @@ -869,6 +932,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; #define meshtastic_Config_DisplayConfig_fields &meshtastic_Config_DisplayConfig_msg #define meshtastic_Config_LoRaConfig_fields &meshtastic_Config_LoRaConfig_msg #define meshtastic_Config_BluetoothConfig_fields &meshtastic_Config_BluetoothConfig_msg +#define meshtastic_Config_SecurityConfig_fields &meshtastic_Config_SecurityConfig_msg /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size @@ -880,6 +944,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg; #define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PowerConfig_size 52 +#define meshtastic_Config_SecurityConfig_size 112 #define meshtastic_Config_size 199 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index eb37f4f95..2c91fe30e 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -306,8 +306,8 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* meshtastic_DeviceState_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 166 -#define meshtastic_OEMStore_size 3388 +#define meshtastic_NodeInfoLite_size 200 +#define meshtastic_OEMStore_size 3502 #define meshtastic_PositionLite_size 28 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index 983f48ad3..c612b24ab 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -38,6 +38,9 @@ typedef struct _meshtastic_LocalConfig { incompatible changes This integer is set at build time and is private to NodeDB.cpp in the device code. */ uint32_t version; + /* The part of the config that is specific to Security settings */ + bool has_security; + meshtastic_Config_SecurityConfig security; } meshtastic_LocalConfig; typedef struct _meshtastic_LocalModuleConfig { @@ -92,9 +95,9 @@ extern "C" { #endif /* Initializer values for message structs */ -#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0} +#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default} #define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default} -#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0} +#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero} #define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero} /* Field tags (for use in manual encoding/decoding) */ @@ -106,6 +109,7 @@ extern "C" { #define meshtastic_LocalConfig_lora_tag 6 #define meshtastic_LocalConfig_bluetooth_tag 7 #define meshtastic_LocalConfig_version_tag 8 +#define meshtastic_LocalConfig_security_tag 9 #define meshtastic_LocalModuleConfig_mqtt_tag 1 #define meshtastic_LocalModuleConfig_serial_tag 2 #define meshtastic_LocalModuleConfig_external_notification_tag 3 @@ -130,7 +134,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, network, 4) \ X(a, STATIC, OPTIONAL, MESSAGE, display, 5) \ X(a, STATIC, OPTIONAL, MESSAGE, lora, 6) \ X(a, STATIC, OPTIONAL, MESSAGE, bluetooth, 7) \ -X(a, STATIC, SINGULAR, UINT32, version, 8) +X(a, STATIC, SINGULAR, UINT32, version, 8) \ +X(a, STATIC, OPTIONAL, MESSAGE, security, 9) #define meshtastic_LocalConfig_CALLBACK NULL #define meshtastic_LocalConfig_DEFAULT NULL #define meshtastic_LocalConfig_device_MSGTYPE meshtastic_Config_DeviceConfig @@ -140,6 +145,7 @@ X(a, STATIC, SINGULAR, UINT32, version, 8) #define meshtastic_LocalConfig_display_MSGTYPE meshtastic_Config_DisplayConfig #define meshtastic_LocalConfig_lora_MSGTYPE meshtastic_Config_LoRaConfig #define meshtastic_LocalConfig_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig +#define meshtastic_LocalConfig_security_MSGTYPE meshtastic_Config_SecurityConfig #define meshtastic_LocalModuleConfig_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, mqtt, 1) \ @@ -181,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size -#define meshtastic_LocalConfig_size 555 +#define meshtastic_LocalConfig_size 669 #define meshtastic_LocalModuleConfig_size 687 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp index 3fa81e131..8c8b9ded7 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.cpp +++ b/src/mesh/generated/meshtastic/mesh.pb.cpp @@ -30,7 +30,7 @@ PB_BIND(meshtastic_MqttClientProxyMessage, meshtastic_MqttClientProxyMessage, 2) PB_BIND(meshtastic_MeshPacket, meshtastic_MeshPacket, 2) -PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO) +PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, 2) PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO) @@ -45,6 +45,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO) PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2) +PB_BIND(meshtastic_ClientNotification, meshtastic_ClientNotification, 2) + + PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO) diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 955484cb0..70423f673 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -374,10 +374,13 @@ typedef enum _meshtastic_LogRecord_Level { typedef struct _meshtastic_Position { /* The new preferred location encoding, multiply by 1e-7 to get degrees in floating point */ + bool has_latitude_i; int32_t latitude_i; /* TODO: REPLACE */ + bool has_longitude_i; int32_t longitude_i; /* In meters above MSL (but see issue #359) */ + bool has_altitude; int32_t altitude; /* This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its time if it is sent over @@ -393,8 +396,10 @@ typedef struct _meshtastic_Position { /* Pos. timestamp milliseconds adjustment (rarely available or required) */ int32_t timestamp_millis_adjust; /* HAE altitude in meters - can be used instead of MSL altitude */ + bool has_altitude_hae; int32_t altitude_hae; /* Geoidal separation in meters */ + bool has_altitude_geoidal_separation; int32_t altitude_geoidal_separation; /* Horizontal, Vertical and Position Dilution of Precision, in 1/100 units - PDOP is sufficient for most cases @@ -416,8 +421,10 @@ typedef struct _meshtastic_Position { - "heading" is where the fuselage points (measured in horizontal plane) - "yaw" indicates a relative rotation about the vertical axis TODO: REMOVE/INTEGRATE */ + bool has_ground_speed; uint32_t ground_speed; /* TODO: REPLACE */ + bool has_ground_track; uint32_t ground_track; /* GPS fix quality (from NMEA GxGGA statement or similar) */ uint32_t fix_quality; @@ -439,6 +446,7 @@ typedef struct _meshtastic_Position { uint32_t precision_bits; } meshtastic_Position; +typedef PB_BYTES_ARRAY_T(32) meshtastic_User_public_key_t; /* Broadcast when a newly powered mesh node wants to find a node num it can use Sent from the phone over bluetooth to set the user id for the owner of this node. Also sent from nodes to each other when a new node signs on (so all clients can have this info) @@ -485,6 +493,9 @@ typedef struct _meshtastic_User { bool is_licensed; /* Indicates that the user's role in the mesh */ meshtastic_Config_DeviceConfig_Role role; + /* The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_User_public_key_t public_key; } meshtastic_User; /* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */ @@ -546,8 +557,10 @@ typedef struct _meshtastic_Waypoint { /* Id of the waypoint */ uint32_t id; /* latitude_i */ + bool has_latitude_i; int32_t latitude_i; /* longitude_i */ + bool has_longitude_i; int32_t longitude_i; /* Time the waypoint is to expire (epoch) */ uint32_t expire; @@ -579,6 +592,7 @@ typedef struct _meshtastic_MqttClientProxyMessage { } meshtastic_MqttClientProxyMessage; typedef PB_BYTES_ARRAY_T(256) meshtastic_MeshPacket_encrypted_t; +typedef PB_BYTES_ARRAY_T(32) meshtastic_MeshPacket_public_key_t; /* A packet envelope sent/received over the mesh only payload_variant is sent in the payload portion of the LORA packet. The other fields are either not sent at all, or sent in the special 16 byte LORA header. */ @@ -649,6 +663,10 @@ typedef struct _meshtastic_MeshPacket { /* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header. When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */ uint8_t hop_start; + /* Records the public key the packet was encrypted with, if applicable. */ + meshtastic_MeshPacket_public_key_t public_key; + /* Indicates whether the packet was en/decrypted using PKI */ + bool pki_encrypted; } meshtastic_MeshPacket; /* The bluetooth to device link: @@ -738,6 +756,22 @@ typedef struct _meshtastic_QueueStatus { uint32_t mesh_packet_id; } meshtastic_QueueStatus; +/* A notification message from the device to the client + To be used for important messages that should to be displayed to the user + in the form of push notifications or validation messages when saving + invalid configuration. */ +typedef struct _meshtastic_ClientNotification { + /* The id of the packet we're notifying in response to */ + bool has_reply_id; + uint32_t reply_id; + /* Seconds since 1970 - or 0 for unknown/unset */ + uint32_t time; + /* The level type of notification */ + meshtastic_LogRecord_Level level; + /* The message body of the notification */ + char message[400]; +} meshtastic_ClientNotification; + /* Individual File info for the device */ typedef struct _meshtastic_FileInfo { /* The fully qualified path of the file */ @@ -852,6 +886,8 @@ typedef struct _meshtastic_FromRadio { meshtastic_MqttClientProxyMessage mqttClientProxyMessage; /* File system manifest messages */ meshtastic_FileInfo fileInfo; + /* Notification message to the client */ + meshtastic_ClientNotification clientNotification; }; } meshtastic_FromRadio; @@ -994,6 +1030,8 @@ extern "C" { +#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level + #define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum @@ -1010,19 +1048,20 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN} +#define meshtastic_Position_init_default {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}} #define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} -#define meshtastic_Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0} +#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0} +#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} #define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} #define meshtastic_MyNodeInfo_init_default {0, 0, 0} #define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_default {0, 0, 0, 0} #define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}} +#define meshtastic_ClientNotification_init_default {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""} #define meshtastic_FileInfo_init_default {"", 0} #define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}} #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} @@ -1034,19 +1073,20 @@ extern "C" { #define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}} #define meshtastic_resend_chunks_init_default {{{NULL}, NULL}} #define meshtastic_ChunkedPayloadResponse_init_default {0, 0, {0}} -#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN} +#define meshtastic_Position_init_zero {false, 0, false, 0, false, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, false, 0, false, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} #define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}} #define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0} -#define meshtastic_Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0} +#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0} #define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0} -#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0} +#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0} #define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} #define meshtastic_MyNodeInfo_init_zero {0, 0, 0} #define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_QueueStatus_init_zero {0, 0, 0, 0} #define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}} +#define meshtastic_ClientNotification_init_zero {false, 0, 0, _meshtastic_LogRecord_Level_MIN, ""} #define meshtastic_FileInfo_init_zero {"", 0} #define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} @@ -1090,6 +1130,7 @@ extern "C" { #define meshtastic_User_hw_model_tag 5 #define meshtastic_User_is_licensed_tag 6 #define meshtastic_User_role_tag 7 +#define meshtastic_User_public_key_tag 8 #define meshtastic_RouteDiscovery_route_tag 1 #define meshtastic_Routing_route_request_tag 1 #define meshtastic_Routing_route_reply_tag 2 @@ -1129,6 +1170,8 @@ extern "C" { #define meshtastic_MeshPacket_delayed_tag 13 #define meshtastic_MeshPacket_via_mqtt_tag 14 #define meshtastic_MeshPacket_hop_start_tag 15 +#define meshtastic_MeshPacket_public_key_tag 16 +#define meshtastic_MeshPacket_pki_encrypted_tag 17 #define meshtastic_NodeInfo_num_tag 1 #define meshtastic_NodeInfo_user_tag 2 #define meshtastic_NodeInfo_position_tag 3 @@ -1150,6 +1193,10 @@ extern "C" { #define meshtastic_QueueStatus_free_tag 2 #define meshtastic_QueueStatus_maxlen_tag 3 #define meshtastic_QueueStatus_mesh_packet_id_tag 4 +#define meshtastic_ClientNotification_reply_id_tag 1 +#define meshtastic_ClientNotification_time_tag 2 +#define meshtastic_ClientNotification_level_tag 3 +#define meshtastic_ClientNotification_message_tag 4 #define meshtastic_FileInfo_file_name_tag 1 #define meshtastic_FileInfo_size_bytes_tag 2 #define meshtastic_Compressed_portnum_tag 1 @@ -1187,6 +1234,7 @@ extern "C" { #define meshtastic_FromRadio_metadata_tag 13 #define meshtastic_FromRadio_mqttClientProxyMessage_tag 14 #define meshtastic_FromRadio_fileInfo_tag 15 +#define meshtastic_FromRadio_clientNotification_tag 16 #define meshtastic_ToRadio_packet_tag 1 #define meshtastic_ToRadio_want_config_id_tag 3 #define meshtastic_ToRadio_disconnect_tag 4 @@ -1207,22 +1255,22 @@ extern "C" { /* Struct field encoding specification for nanopb */ #define meshtastic_Position_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \ -X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \ -X(a, STATIC, SINGULAR, INT32, altitude, 3) \ +X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 1) \ +X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 2) \ +X(a, STATIC, OPTIONAL, INT32, altitude, 3) \ X(a, STATIC, SINGULAR, FIXED32, time, 4) \ X(a, STATIC, SINGULAR, UENUM, location_source, 5) \ X(a, STATIC, SINGULAR, UENUM, altitude_source, 6) \ X(a, STATIC, SINGULAR, FIXED32, timestamp, 7) \ X(a, STATIC, SINGULAR, INT32, timestamp_millis_adjust, 8) \ -X(a, STATIC, SINGULAR, SINT32, altitude_hae, 9) \ -X(a, STATIC, SINGULAR, SINT32, altitude_geoidal_separation, 10) \ +X(a, STATIC, OPTIONAL, SINT32, altitude_hae, 9) \ +X(a, STATIC, OPTIONAL, SINT32, altitude_geoidal_separation, 10) \ X(a, STATIC, SINGULAR, UINT32, PDOP, 11) \ X(a, STATIC, SINGULAR, UINT32, HDOP, 12) \ X(a, STATIC, SINGULAR, UINT32, VDOP, 13) \ X(a, STATIC, SINGULAR, UINT32, gps_accuracy, 14) \ -X(a, STATIC, SINGULAR, UINT32, ground_speed, 15) \ -X(a, STATIC, SINGULAR, UINT32, ground_track, 16) \ +X(a, STATIC, OPTIONAL, UINT32, ground_speed, 15) \ +X(a, STATIC, OPTIONAL, UINT32, ground_track, 16) \ X(a, STATIC, SINGULAR, UINT32, fix_quality, 17) \ X(a, STATIC, SINGULAR, UINT32, fix_type, 18) \ X(a, STATIC, SINGULAR, UINT32, sats_in_view, 19) \ @@ -1240,7 +1288,8 @@ X(a, STATIC, SINGULAR, STRING, short_name, 3) \ X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \ X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \ X(a, STATIC, SINGULAR, BOOL, is_licensed, 6) \ -X(a, STATIC, SINGULAR, UENUM, role, 7) +X(a, STATIC, SINGULAR, UENUM, role, 7) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 8) #define meshtastic_User_CALLBACK NULL #define meshtastic_User_DEFAULT NULL @@ -1272,8 +1321,8 @@ X(a, STATIC, SINGULAR, FIXED32, emoji, 8) #define meshtastic_Waypoint_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, id, 1) \ -X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 2) \ -X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 3) \ +X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \ +X(a, STATIC, OPTIONAL, SFIXED32, longitude_i, 3) \ X(a, STATIC, SINGULAR, UINT32, expire, 4) \ X(a, STATIC, SINGULAR, UINT32, locked_to, 5) \ X(a, STATIC, SINGULAR, STRING, name, 6) \ @@ -1305,7 +1354,9 @@ X(a, STATIC, SINGULAR, UENUM, priority, 11) \ X(a, STATIC, SINGULAR, INT32, rx_rssi, 12) \ X(a, STATIC, SINGULAR, UENUM, delayed, 13) \ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14) \ -X(a, STATIC, SINGULAR, UINT32, hop_start, 15) +X(a, STATIC, SINGULAR, UINT32, hop_start, 15) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 16) \ +X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) #define meshtastic_MeshPacket_CALLBACK NULL #define meshtastic_MeshPacket_DEFAULT NULL #define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data @@ -1365,7 +1416,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 1 X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,clientNotification,clientNotification), 16) #define meshtastic_FromRadio_CALLBACK NULL #define meshtastic_FromRadio_DEFAULT NULL #define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket @@ -1380,6 +1432,15 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15) #define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata #define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage #define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo +#define meshtastic_FromRadio_payload_variant_clientNotification_MSGTYPE meshtastic_ClientNotification + +#define meshtastic_ClientNotification_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, UINT32, reply_id, 1) \ +X(a, STATIC, SINGULAR, FIXED32, time, 2) \ +X(a, STATIC, SINGULAR, UENUM, level, 3) \ +X(a, STATIC, SINGULAR, STRING, message, 4) +#define meshtastic_ClientNotification_CALLBACK NULL +#define meshtastic_ClientNotification_DEFAULT NULL #define meshtastic_FileInfo_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, STRING, file_name, 1) \ @@ -1485,6 +1546,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg; extern const pb_msgdesc_t meshtastic_LogRecord_msg; extern const pb_msgdesc_t meshtastic_QueueStatus_msg; extern const pb_msgdesc_t meshtastic_FromRadio_msg; +extern const pb_msgdesc_t meshtastic_ClientNotification_msg; extern const pb_msgdesc_t meshtastic_FileInfo_msg; extern const pb_msgdesc_t meshtastic_ToRadio_msg; extern const pb_msgdesc_t meshtastic_Compressed_msg; @@ -1511,6 +1573,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg #define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg #define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg +#define meshtastic_ClientNotification_fields &meshtastic_ClientNotification_msg #define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg #define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg #define meshtastic_Compressed_fields &meshtastic_Compressed_msg @@ -1528,6 +1591,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; /* meshtastic_ChunkedPayloadResponse_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_MESH_PB_H_MAX_SIZE meshtastic_FromRadio_size #define meshtastic_ChunkedPayload_size 245 +#define meshtastic_ClientNotification_size 415 #define meshtastic_Compressed_size 243 #define meshtastic_Data_size 270 #define meshtastic_DeviceMetadata_size 46 @@ -1535,19 +1599,19 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg; #define meshtastic_FromRadio_size 510 #define meshtastic_Heartbeat_size 0 #define meshtastic_LogRecord_size 426 -#define meshtastic_MeshPacket_size 326 +#define meshtastic_MeshPacket_size 364 #define meshtastic_MqttClientProxyMessage_size 501 #define meshtastic_MyNodeInfo_size 18 #define meshtastic_NeighborInfo_size 258 #define meshtastic_Neighbor_size 22 -#define meshtastic_NodeInfo_size 283 +#define meshtastic_NodeInfo_size 317 #define meshtastic_NodeRemoteHardwarePin_size 29 #define meshtastic_Position_size 144 #define meshtastic_QueueStatus_size 23 #define meshtastic_RouteDiscovery_size 40 #define meshtastic_Routing_size 42 #define meshtastic_ToRadio_size 504 -#define meshtastic_User_size 79 +#define meshtastic_User_size 113 #define meshtastic_Waypoint_size 165 #ifdef __cplusplus diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h index a4acd3f4a..60681916f 100644 --- a/src/mesh/generated/meshtastic/telemetry.pb.h +++ b/src/mesh/generated/meshtastic/telemetry.pb.h @@ -76,98 +76,138 @@ typedef enum _meshtastic_TelemetrySensorType { /* Key native device metrics such as battery level */ typedef struct _meshtastic_DeviceMetrics { /* 0-100 (>100 means powered) */ + bool has_battery_level; uint32_t battery_level; /* Voltage measured */ + bool has_voltage; float voltage; /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */ + bool has_channel_utilization; float channel_utilization; /* Percent of airtime for transmission used within the last hour. */ + bool has_air_util_tx; float air_util_tx; /* How long the device has been running since the last reboot (in seconds) */ + bool has_uptime_seconds; uint32_t uptime_seconds; } meshtastic_DeviceMetrics; /* Weather station or other environmental metrics */ typedef struct _meshtastic_EnvironmentMetrics { /* Temperature measured */ + bool has_temperature; float temperature; /* Relative humidity percent measured */ + bool has_relative_humidity; float relative_humidity; /* Barometric pressure in hPA measured */ + bool has_barometric_pressure; float barometric_pressure; /* Gas resistance in MOhm measured */ + bool has_gas_resistance; float gas_resistance; /* Voltage measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */ + bool has_voltage; float voltage; /* Current measured (To be depreciated in favor of PowerMetrics in Meshtastic 3.x) */ + bool has_current; float current; /* relative scale IAQ value as measured by Bosch BME680 . value 0-500. Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. */ + bool has_iaq; uint16_t iaq; /* RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm. */ + bool has_distance; float distance; /* VEML7700 high accuracy ambient light(Lux) digital 16-bit resolution sensor. */ + bool has_lux; float lux; /* VEML7700 high accuracy white light(irradiance) not calibrated digital 16-bit resolution sensor. */ + bool has_white_lux; float white_lux; /* Infrared lux */ + bool has_ir_lux; float ir_lux; /* Ultraviolet lux */ + bool has_uv_lux; float uv_lux; /* Wind direction in degrees 0 degrees = North, 90 = East, etc... */ + bool has_wind_direction; uint16_t wind_direction; /* Wind speed in m/s */ + bool has_wind_speed; float wind_speed; /* Weight in KG */ + bool has_weight; float weight; /* Wind gust in m/s */ + bool has_wind_gust; float wind_gust; /* Wind lull in m/s */ + bool has_wind_lull; float wind_lull; } meshtastic_EnvironmentMetrics; /* Power Metrics (voltage / current / etc) */ typedef struct _meshtastic_PowerMetrics { /* Voltage (Ch1) */ + bool has_ch1_voltage; float ch1_voltage; /* Current (Ch1) */ + bool has_ch1_current; float ch1_current; /* Voltage (Ch2) */ + bool has_ch2_voltage; float ch2_voltage; /* Current (Ch2) */ + bool has_ch2_current; float ch2_current; /* Voltage (Ch3) */ + bool has_ch3_voltage; float ch3_voltage; /* Current (Ch3) */ + bool has_ch3_current; float ch3_current; } meshtastic_PowerMetrics; /* Air quality metrics */ typedef struct _meshtastic_AirQualityMetrics { /* Concentration Units Standard PM1.0 */ + bool has_pm10_standard; uint32_t pm10_standard; /* Concentration Units Standard PM2.5 */ + bool has_pm25_standard; uint32_t pm25_standard; /* Concentration Units Standard PM10.0 */ + bool has_pm100_standard; uint32_t pm100_standard; /* Concentration Units Environmental PM1.0 */ + bool has_pm10_environmental; uint32_t pm10_environmental; /* Concentration Units Environmental PM2.5 */ + bool has_pm25_environmental; uint32_t pm25_environmental; /* Concentration Units Environmental PM10.0 */ + bool has_pm100_environmental; uint32_t pm100_environmental; /* 0.3um Particle Count */ + bool has_particles_03um; uint32_t particles_03um; /* 0.5um Particle Count */ + bool has_particles_05um; uint32_t particles_05um; /* 1.0um Particle Count */ + bool has_particles_10um; uint32_t particles_10um; /* 2.5um Particle Count */ + bool has_particles_25um; uint32_t particles_25um; /* 5.0um Particle Count */ + bool has_particles_50um; uint32_t particles_50um; /* 10.0um Particle Count */ + bool has_particles_100um; uint32_t particles_100um; } meshtastic_AirQualityMetrics; @@ -214,16 +254,16 @@ extern "C" { /* Initializer values for message structs */ -#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0} -#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Nau7802Config_init_default {0, 0} -#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0} -#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0} -#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} +#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Nau7802Config_init_zero {0, 0} @@ -278,58 +318,58 @@ extern "C" { /* Struct field encoding specification for nanopb */ #define meshtastic_DeviceMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, battery_level, 1) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 2) \ -X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 3) \ -X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 4) \ -X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 5) +X(a, STATIC, OPTIONAL, UINT32, battery_level, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, voltage, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, channel_utilization, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, air_util_tx, 4) \ +X(a, STATIC, OPTIONAL, UINT32, uptime_seconds, 5) #define meshtastic_DeviceMetrics_CALLBACK NULL #define meshtastic_DeviceMetrics_DEFAULT NULL #define meshtastic_EnvironmentMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \ -X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \ -X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \ -X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \ -X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \ -X(a, STATIC, SINGULAR, FLOAT, current, 6) \ -X(a, STATIC, SINGULAR, UINT32, iaq, 7) \ -X(a, STATIC, SINGULAR, FLOAT, distance, 8) \ -X(a, STATIC, SINGULAR, FLOAT, lux, 9) \ -X(a, STATIC, SINGULAR, FLOAT, white_lux, 10) \ -X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \ -X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \ -X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \ -X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \ -X(a, STATIC, SINGULAR, FLOAT, weight, 15) \ -X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \ -X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17) +X(a, STATIC, OPTIONAL, FLOAT, temperature, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, relative_humidity, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, barometric_pressure, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, gas_resistance, 4) \ +X(a, STATIC, OPTIONAL, FLOAT, voltage, 5) \ +X(a, STATIC, OPTIONAL, FLOAT, current, 6) \ +X(a, STATIC, OPTIONAL, UINT32, iaq, 7) \ +X(a, STATIC, OPTIONAL, FLOAT, distance, 8) \ +X(a, STATIC, OPTIONAL, FLOAT, lux, 9) \ +X(a, STATIC, OPTIONAL, FLOAT, white_lux, 10) \ +X(a, STATIC, OPTIONAL, FLOAT, ir_lux, 11) \ +X(a, STATIC, OPTIONAL, FLOAT, uv_lux, 12) \ +X(a, STATIC, OPTIONAL, UINT32, wind_direction, 13) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \ +X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \ +X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) #define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL #define meshtastic_PowerMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, FLOAT, ch1_voltage, 1) \ -X(a, STATIC, SINGULAR, FLOAT, ch1_current, 2) \ -X(a, STATIC, SINGULAR, FLOAT, ch2_voltage, 3) \ -X(a, STATIC, SINGULAR, FLOAT, ch2_current, 4) \ -X(a, STATIC, SINGULAR, FLOAT, ch3_voltage, 5) \ -X(a, STATIC, SINGULAR, FLOAT, ch3_current, 6) +X(a, STATIC, OPTIONAL, FLOAT, ch1_voltage, 1) \ +X(a, STATIC, OPTIONAL, FLOAT, ch1_current, 2) \ +X(a, STATIC, OPTIONAL, FLOAT, ch2_voltage, 3) \ +X(a, STATIC, OPTIONAL, FLOAT, ch2_current, 4) \ +X(a, STATIC, OPTIONAL, FLOAT, ch3_voltage, 5) \ +X(a, STATIC, OPTIONAL, FLOAT, ch3_current, 6) #define meshtastic_PowerMetrics_CALLBACK NULL #define meshtastic_PowerMetrics_DEFAULT NULL #define meshtastic_AirQualityMetrics_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, pm10_standard, 1) \ -X(a, STATIC, SINGULAR, UINT32, pm25_standard, 2) \ -X(a, STATIC, SINGULAR, UINT32, pm100_standard, 3) \ -X(a, STATIC, SINGULAR, UINT32, pm10_environmental, 4) \ -X(a, STATIC, SINGULAR, UINT32, pm25_environmental, 5) \ -X(a, STATIC, SINGULAR, UINT32, pm100_environmental, 6) \ -X(a, STATIC, SINGULAR, UINT32, particles_03um, 7) \ -X(a, STATIC, SINGULAR, UINT32, particles_05um, 8) \ -X(a, STATIC, SINGULAR, UINT32, particles_10um, 9) \ -X(a, STATIC, SINGULAR, UINT32, particles_25um, 10) \ -X(a, STATIC, SINGULAR, UINT32, particles_50um, 11) \ -X(a, STATIC, SINGULAR, UINT32, particles_100um, 12) +X(a, STATIC, OPTIONAL, UINT32, pm10_standard, 1) \ +X(a, STATIC, OPTIONAL, UINT32, pm25_standard, 2) \ +X(a, STATIC, OPTIONAL, UINT32, pm100_standard, 3) \ +X(a, STATIC, OPTIONAL, UINT32, pm10_environmental, 4) \ +X(a, STATIC, OPTIONAL, UINT32, pm25_environmental, 5) \ +X(a, STATIC, OPTIONAL, UINT32, pm100_environmental, 6) \ +X(a, STATIC, OPTIONAL, UINT32, particles_03um, 7) \ +X(a, STATIC, OPTIONAL, UINT32, particles_05um, 8) \ +X(a, STATIC, OPTIONAL, UINT32, particles_10um, 9) \ +X(a, STATIC, OPTIONAL, UINT32, particles_25um, 10) \ +X(a, STATIC, OPTIONAL, UINT32, particles_50um, 11) \ +X(a, STATIC, OPTIONAL, UINT32, particles_100um, 12) #define meshtastic_AirQualityMetrics_CALLBACK NULL #define meshtastic_AirQualityMetrics_DEFAULT NULL From da53b8152d8a3aa813bd0b9e1cb3093c02819a30 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 07:51:59 -0500 Subject: [PATCH 10/38] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/protobufs b/protobufs index 126293c38..778667d93 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 126293c38ff974ca253606c36da48cfab7fe6446 +Subproject commit 778667d93b9769d51c386c461456bdec4f14f433 diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index d0e643dff..bef2abf9b 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -168,6 +168,8 @@ typedef struct _meshtastic_AdminMessage { bool begin_edit_settings; /* Commits an open transaction for any edits made to config, module config, owner, and channel settings */ bool commit_edit_settings; + /* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */ + int32_t factory_reset_device; /* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */ int32_t reboot_ota_seconds; @@ -178,8 +180,8 @@ typedef struct _meshtastic_AdminMessage { int32_t reboot_seconds; /* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */ int32_t shutdown_seconds; - /* Tell the node to factory reset, all device settings will be returned to factory defaults. */ - int32_t factory_reset; + /* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */ + int32_t factory_reset_config; /* Tell the node to reset the nodedb. */ int32_t nodedb_reset; }; @@ -254,11 +256,12 @@ extern "C" { #define meshtastic_AdminMessage_remove_fixed_position_tag 42 #define meshtastic_AdminMessage_begin_edit_settings_tag 64 #define meshtastic_AdminMessage_commit_edit_settings_tag 65 +#define meshtastic_AdminMessage_factory_reset_device_tag 94 #define meshtastic_AdminMessage_reboot_ota_seconds_tag 95 #define meshtastic_AdminMessage_exit_simulator_tag 96 #define meshtastic_AdminMessage_reboot_seconds_tag 97 #define meshtastic_AdminMessage_shutdown_seconds_tag 98 -#define meshtastic_AdminMessage_factory_reset_tag 99 +#define meshtastic_AdminMessage_factory_reset_config_tag 99 #define meshtastic_AdminMessage_nodedb_reset_tag 100 /* Struct field encoding specification for nanopb */ @@ -298,11 +301,12 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \ X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \ -X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \ +X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) #define meshtastic_AdminMessage_CALLBACK NULL #define meshtastic_AdminMessage_DEFAULT NULL From 95682c9095e1c36e92e846dd8c23639dfb4a6a0b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 08:33:42 -0500 Subject: [PATCH 11/38] Add ClientNotification hello world --- src/mesh/MeshService.cpp | 20 +++++++++++++++++++- src/mesh/MeshService.h | 10 ++++++++++ src/mesh/PhoneAPI.h | 3 +++ src/mesh/Router.cpp | 8 ++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 697644a4f..d05f43b01 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -48,14 +48,18 @@ static MemoryDynamic staticMqttClientProxyMes static MemoryDynamic staticQueueStatusPool; +static MemoryDynamic staticClientNotificationPool; + Allocator &mqttClientProxyMessagePool = staticMqttClientProxyMessagePool; +Allocator &clientNotificationPool = staticClientNotificationPool; + Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE) + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) { lastQueueStatus = {0, 0, 16, 0}; } @@ -324,6 +328,20 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage fromNum++; } +void MeshService::sendClientNotification(meshtastic_ClientNotification *n) +{ + LOG_DEBUG("Sending client notification to phone\n"); + if (toPhoneClientNotificationQueue.numFree() == 0) { + LOG_WARN("ClientNotification queue is full, discarding oldest\n"); + meshtastic_ClientNotification *d = toPhoneClientNotificationQueue.dequeuePtr(0); + if (d) + releaseClientNotificationToPool(d); + } + + assert(toPhoneClientNotificationQueue.enqueue(n, 0)); + fromNum++; +} + meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode() { meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum()); diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 528adb137..ea1c4e345 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -21,6 +21,7 @@ extern Allocator &queueStatusPool; extern Allocator &mqttClientProxyMessagePool; +extern Allocator &clientNotificationPool; /** * Top level app for this service. keeps the mesh, the radio config and the queue of received packets. @@ -44,6 +45,9 @@ class MeshService // keep list of MqttClientProxyMessages to be send to the client for delivery PointerQueue toPhoneMqttProxyQueue; + // keep list of ClientNotifications to be send to the client (phone) + PointerQueue toPhoneClientNotificationQueue; + // This holds the last QueueStatus send meshtastic_QueueStatus lastQueueStatus; @@ -97,6 +101,9 @@ class MeshService // Release MqttClientProxyMessage packet to pool void releaseMqttClientProxyMessageToPool(meshtastic_MqttClientProxyMessage *p) { mqttClientProxyMessagePool.release(p); } + /// Release the next ClientNotification packet to pool. + void releaseClientNotificationToPool(meshtastic_ClientNotification *p) { clientNotificationPool.release(p); } + /** * Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep @@ -134,6 +141,9 @@ class MeshService /// Send an MQTT message to the phone for client proxying void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m); + /// Send a ClientNotification to the phone + void sendClientNotification(meshtastic_ClientNotification *cn); + bool isToPhoneQueueEmpty(); ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id); diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 3c3668300..5feb1c4bf 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -66,6 +66,9 @@ class PhoneAPI // Keep MqttClientProxyMessage packet just as packetForPhone meshtastic_MqttClientProxyMessage *mqttClientProxyMessageForPhone = NULL; + // Keep ClientNotification packet just as packetForPhone + meshtastic_ClientNotification *clientNotification = NULL; + /// We temporarily keep the nodeInfo here between the call to available and getFromRadio meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 79095805d..f59d61ea2 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -2,6 +2,7 @@ #include "Channels.h" #include "CryptoEngine.h" #include "MeshRadio.h" +#include "MeshService.h" #include "NodeDB.h" #include "RTC.h" #include "configuration.h" @@ -209,6 +210,13 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) #ifdef DEBUG_PORT uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle); LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes); + meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); + cn->has_reply_id = true; + cn->reply_id = p->id; + cn->level = meshtastic_LogRecord_Level_WARNING; + cn->time = getValidTime(RTCQualityFromNet); + sprintf(cn->message, "Duty cycle limit exceeded. You can send again in %d minutes.", silentMinutes); + service->sendClientNotification(cn); #endif meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT; if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh From c451db3a3f1ac88668d86834404b89e6225f5abf Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 10 Aug 2024 08:57:37 -0500 Subject: [PATCH 12/38] Get in the trunk! --- src/mesh/MeshService.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index d05f43b01..ac97d51a7 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -59,7 +59,8 @@ Allocator &queueStatusPool = staticQueueStatusPool; #include "Router.h" MeshService::MeshService() - : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) + : toPhoneQueue(MAX_RX_TOPHONE), toPhoneQueueStatusQueue(MAX_RX_TOPHONE), toPhoneMqttProxyQueue(MAX_RX_TOPHONE), + toPhoneClientNotificationQueue(MAX_RX_TOPHONE / 2) { lastQueueStatus = {0, 0, 16, 0}; } From b726792efd1ea7ba34eeb23af22a48eda1a00ec9 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 13:45:41 -0500 Subject: [PATCH 13/38] Re-implement PKI from #1509 (#4379) * Re-implement PKI from #1509 co-authored-by: edinnen * Set the key lengnth to actually make PKI work. * Remove unused variable and initialize keys to null * move printBytes() to meshUtils * Don't reset PKI key son reboot unless needed. * Remove double encryption for PKI messages * Cleanup encrypt logic * Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now. * Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32 * Fix a crash when node is null * Don't send PKI encrypted packets while licensed * use chIndex 8 for PKI * Don't be so clever, that you corrupt incoming packets * Pass on channel 8 for now * Typo * Lock keys once non-zero * We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf. * Lighter approach to retaining known key * Attach the public key to PKI decrypted packets in device memory * Turn PKI back off for STM32 :( * Don't just memcp over a protobuf * Don't PKI encrypt nodeinfo packets * Add a bit more memory logging around nodeDB * Use the proper macro to refer to NODENUM_BROADCAST * Typo fix * Don't PKI encrypt ROUTING (naks and acks) * Adds SecurityConfig protobuf * Add admin messages over PKI * Disable PKI for the WIO-e5 * Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k * Add missed "has_security" * Add the admin_channel_enabled option * STM32 again * add missed configuration.h at the top of files * Add EXCLUDE_TZ and RTC * Enable PKI build on STM32 once again * Attempt 1 at moving PKI to aes-ccm * Fix buffers for encrypt/decrypt * Eliminate unused aes variable * Add debugging lines * Set hash to 0 for PKI * Fix debug lines so they don't print pointers. * logic fix and more debug * Rather important typo * Check for short packets before attempting decrypt * Don't forget to give cryptoEngine the keys! * Use the right scratch buffer * Cleanup * moar cleanups * Minor hardening * Remove some in-progress stuff * Turn PKI back off on STM32 * Return false * 2.5 protos * Sync up protos * Add initial cryptography test vector tests * re-add MINIMUM_SAFE_FREE_HEAP * Housekeeping and comment fixes * Add explanatory comment about weak dh25519 keys --------- Co-authored-by: Ben Meadors --- arch/esp32/esp32.ini | 1 + arch/nrf52/nrf52.ini | 1 + platformio.ini | 1 + src/RedirectablePrint.cpp | 4 +- src/SerialConsole.cpp | 4 +- src/configuration.h | 5 + src/main.cpp | 9 +- src/mesh/CryptoEngine.cpp | 170 ++++++++++++++++++++++++++++++++ src/mesh/CryptoEngine.h | 31 +++++- src/mesh/NodeDB.cpp | 50 +++++++++- src/mesh/NodeDB.h | 2 +- src/mesh/PhoneAPI.cpp | 4 + src/mesh/Router.cpp | 173 +++++++++++++++++++++------------ src/mesh/aes-ccm.cpp | 157 ++++++++++++++++++++++++++++++ src/mesh/aes-ccm.h | 10 ++ src/meshUtils.h | 4 +- src/modules/AdminModule.cpp | 38 +++++++- test/test_crypto/test_main.cpp | 50 ++++++++++ userPrefs.h | 6 ++ 19 files changed, 641 insertions(+), 79 deletions(-) create mode 100644 src/mesh/aes-ccm.cpp create mode 100644 src/mesh/aes-ccm.h create mode 100644 test/test_crypto/test_main.cpp diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 58c1302da..0dd6cbc1d 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -48,6 +48,7 @@ lib_deps = https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f + rweather/Crypto@^0.4.0 lib_ignore = segger_rtt diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index f3e7c3036..503da2aab 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -20,6 +20,7 @@ build_src_filter = lib_deps= ${arduino_base.lib_deps} + rweather/Crypto@^0.4.0 lib_ignore = BluetoothOTA \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e60f0d7b9..5ad7d60a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,6 +45,7 @@ extra_configs = variants/*/platformio.ini [env] +test_build_src = true extra_scripts = bin/platformio-custom.py ; note: we add src to our include search path so that lmic_project_config can override diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 05d349de9..02cd8b309 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -39,7 +39,7 @@ size_t RedirectablePrint::write(uint8_t c) SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); #endif - if (!config.has_lora || config.device.serial_enabled) + if (!config.has_lora || config.security.serial_enabled) dest->write(c); return 1; // We always claim one was written, rather than trusting what the @@ -180,7 +180,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg) { #if !MESHTASTIC_EXCLUDE_BLUETOOTH - if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) { + if (config.security.bluetooth_logging_enabled && !pauseBluetoothLogging) { bool isBleConnected = false; #ifdef ARCH_ESP32 isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected(); diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index d25b81da7..b911e15da 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -83,7 +83,7 @@ bool SerialConsole::checkIsConnected() bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { // only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled. - if (config.has_lora && config.device.serial_enabled) { + if (config.has_lora && config.security.serial_enabled) { // Switch to protobufs for log messages usingProtobufs = true; canWrite = true; @@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len) void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg) { - if (usingProtobufs && config.device.debug_log_enabled) { + if (usingProtobufs && config.security.debug_log_api_enabled) { meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset switch (logLevel[0]) { case 'D': diff --git a/src/configuration.h b/src/configuration.h index 9148f1d37..c9a5d7fb0 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -193,6 +193,10 @@ along with this program. If not, see . #define DEFAULT_SHUTDOWN_SECONDS 2 #endif +#ifndef MINIMUM_SAFE_FREE_HEAP +#define MINIMUM_SAFE_FREE_HEAP 1500 +#endif + /* Step #3: mop up with disabled values for HAS_ options not handled by the above two */ #ifndef HAS_WIFI @@ -256,6 +260,7 @@ along with this program. If not, see . #define MESHTASTIC_EXCLUDE_MQTT 1 #define MESHTASTIC_EXCLUDE_POWERMON 1 #define MESHTASTIC_EXCLUDE_I2C 1 +#define MESHTASTIC_EXCLUDE_PKI 1 #define MESHTASTIC_EXCLUDE_POWER_FSM 1 #define MESHTASTIC_EXCLUDE_TZ 1 #endif diff --git a/src/main.cpp b/src/main.cpp index 7f45905d1..b6af60d2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -227,7 +227,7 @@ void printInfo() { LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION)); } - +#ifndef PIO_UNIT_TESTING void setup() { concurrency::hasBeenSetup = true; @@ -1052,7 +1052,7 @@ void setup() powerFSMthread = new PowerFSMThread(); setCPUFast(false); // 80MHz is fine for our slow peripherals } - +#endif uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes) uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client) @@ -1075,7 +1075,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled; return deviceMetadata; } - +#ifndef PIO_UNIT_TESTING void loop() { runASAP = false; @@ -1120,4 +1120,5 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 1e44cb9b7..677667aef 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -1,6 +1,176 @@ #include "CryptoEngine.h" +#include "NodeDB.h" +#include "RadioInterface.h" #include "configuration.h" +#if !(MESHTASTIC_EXCLUDE_PKI) +#include "aes-ccm.h" +#include "meshUtils.h" +#include +#include +#include +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) +/** + * Create a public/private key pair with Curve25519. + * + * @param pubKey The destination for the public key. + * @param privKey The destination for the private key. + */ +void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) +{ + LOG_DEBUG("Generating Curve25519 key pair...\n"); + Curve25519::dh1(public_key, private_key); + memcpy(pubKey, public_key, sizeof(public_key)); + memcpy(privKey, private_key, sizeof(private_key)); +} +#endif +uint8_t shared_key[32]; +void CryptoEngine::clearKeys() +{ + memset(public_key, 0, sizeof(public_key)); + memset(private_key, 0, sizeof(private_key)); +} + +/** + * Encrypt a packet's payload using a key generated with Curve25519 and SHA256 + * for a specific node. + * + * @param bytes is updated in place + */ +bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, + uint8_t *bytesOut) +{ + uint8_t *auth; + auth = bytesOut + numBytes; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode); + if (node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node %d or their public_key not found\n", toNode); + return false; + } + if (!crypto->setDHKey(toNode)) { + return false; + } + initNonce(fromNode, packetNum); + + // Calculate the shared secret with the destination node and encrypt + printBytes("Attempting encrypt using nonce: ", nonce, 16); + printBytes("Attempting encrypt using shared_key: ", shared_key, 32); + aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth); + return true; +} + +/** + * Decrypt a packet's payload using a key generated with Curve25519 and SHA256 + * for a specific node. + * + * @param bytes is updated in place + */ +bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut) +{ + uint8_t *auth; // set to last 8 bytes of text? + auth = bytes + numBytes - 8; + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode); + + if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node or its public key not found in database\n"); + return false; + } + + // Calculate the shared secret with the sending node and decrypt + if (!crypto->setDHKey(fromNode)) { + return false; + } + initNonce(fromNode, packetNum); + printBytes("Attempting decrypt using nonce: ", nonce, 16); + printBytes("Attempting decrypt using shared_key: ", shared_key, 32); + return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); +} + +void CryptoEngine::setPrivateKey(uint8_t *_private_key) +{ + memcpy(private_key, _private_key, 32); +} +/** + * Set the PKI key used for encrypt, decrypt. + * + * @param nodeNum the node number of the node who's public key we want to use + */ +bool CryptoEngine::setDHKey(uint32_t nodeNum) +{ + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum); + if (node->num < 1 || node->user.public_key.size == 0) { + LOG_DEBUG("Node %d or their public_key not found\n", nodeNum); + return false; + } + + uint8_t *pubKey = node->user.public_key.bytes; + uint8_t local_priv[32]; + memcpy(shared_key, pubKey, 32); + memcpy(local_priv, private_key, 32); + // Calculate the shared secret with the specified node's public key and our private key + // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. + if (!Curve25519::dh2(shared_key, local_priv)) { + LOG_WARN("Curve25519DH step 2 failed!\n"); + return false; + } + + printBytes("DH Output: ", shared_key, 32); + + /** + * D.J. Bernstein reccomends hashing the shared key. We want to do this because there are + * at least 128 bits of entropy in the 256-bit output of the DH key exchange, but we don't + * really know where. If you extract, for instance, the first 128 bits with basic truncation, + * then you don't know if you got all of your 128 entropy bits, or less, possibly much less. + * + * No exploitable bias is really known at that point, but we know enough to be wary. + * Hashing the DH output is a simple and safe way to gather all the entropy and spread + * it around as needed. + */ + crypto->hash(shared_key, 32); + return true; +} + +/** + * Hash arbitrary data using SHA256. + * + * @param bytes + * @param numBytes + */ +void CryptoEngine::hash(uint8_t *bytes, size_t numBytes) +{ + SHA256 hash; + size_t posn, len; + uint8_t size = numBytes; + uint8_t inc = 16; + hash.reset(); + for (posn = 0; posn < size; posn += inc) { + len = size - posn; + if (len > inc) + len = inc; + hash.update(bytes + posn, len); + } + hash.finalize(bytes, 32); +} + +void CryptoEngine::aesSetKey(const uint8_t *key_bytes, size_t key_len) +{ + if (aes) { + delete aes; + aes = nullptr; + } + if (key_len != 0) { + aes = new AESSmall256(); + aes->setKey(key_bytes, key_len); + } +} + +void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) +{ + aes->encryptBlock(out, in); +} + +#endif + concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 2737dab2d..51080fd59 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -1,6 +1,8 @@ #pragma once - +#include "AES.h" #include "concurrency/LockGuard.h" +#include "configuration.h" +#include "mesh-pb-constants.h" #include extern concurrency::Lock *cryptLock; @@ -26,9 +28,34 @@ class CryptoEngine uint8_t nonce[16] = {0}; CryptoKey key = {}; +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t private_key[32] = {0}; +#endif public: +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t public_key[32] = {0}; +#endif + virtual ~CryptoEngine() {} +#if !(MESHTASTIC_EXCLUDE_PKI) +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); +#endif + void clearKeys(); + void setPrivateKey(uint8_t *_private_key); + virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, + uint8_t *bytesOut); + virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); + virtual bool setDHKey(uint32_t nodeNum); + virtual void hash(uint8_t *bytes, size_t numBytes); + + virtual void aesSetKey(const uint8_t *key, size_t key_len); + + virtual void aesEncrypt(uint8_t *in, uint8_t *out); + AESSmall256 *aes = NULL; + +#endif /** * Set the key used for encrypt, decrypt. @@ -61,4 +88,4 @@ class CryptoEngine void initNonce(uint32_t fromNode, uint64_t packetId); }; -extern CryptoEngine *crypto; +extern CryptoEngine *crypto; \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 000da335f..fb7926977 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -19,6 +19,7 @@ #include "error.h" #include "main.h" #include "mesh-pb-constants.h" +#include "meshUtils.h" #include "modules/NeighborInfoModule.h" #include #include @@ -123,6 +124,31 @@ NodeDB::NodeDB() // Include our owner in the node db under our nodenum meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); +#if !(MESHTASTIC_EXCLUDE_PKI) + // Calculate Curve25519 public and private keys + printBytes("Old Pubkey", config.security.public_key.bytes, 32); + if (config.security.private_key.size == 32 && config.security.public_key.size == 32) { + LOG_INFO("Using saved PKI keys\n"); + owner.public_key.size = config.security.public_key.size; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + crypto->setPrivateKey(config.security.private_key.bytes); + } else { +#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + LOG_INFO("Generating new PKI keys\n"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + config.security.public_key.size = 32; + config.security.private_key.size = 32; + + printBytes("New Pubkey", config.security.public_key.bytes, 32); + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); +#else + LOG_INFO("No PKI keys set, and generation disabled!\n"); +#endif + } + +#endif + info->user = owner; info->has_user = true; @@ -237,6 +263,7 @@ void NodeDB::installDefaultConfig() config.has_power = true; config.has_network = true; config.has_bluetooth = (HAS_BLUETOOTH ? true : false); + config.has_security = true; config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL; config.lora.sx126x_rx_boosted_gain = true; @@ -259,6 +286,14 @@ void NodeDB::installDefaultConfig() #else config.lora.ignore_mqtt = false; #endif +#ifdef ADMIN_KEY_USERPREFS + memcpy(config.security.admin_key.bytes, admin_key_userprefs, 32); + config.security.admin_key.size = 32; +#else + config.security.admin_key.size = 0; +#endif + config.security.public_key.size = 0; + config.security.private_key.size = 0; #ifdef PIN_GPS_EN config.position.gps_en_gpio = PIN_GPS_EN; #endif @@ -282,7 +317,8 @@ void NodeDB::installDefaultConfig() config.position.broadcast_smart_minimum_interval_secs = 30; if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER) config.device.node_info_broadcast_secs = default_node_info_broadcast_secs; - config.device.serial_enabled = true; + config.security.serial_enabled = true; + config.security.admin_channel_enabled = false; resetRadioConfig(); strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32); // FIXME: Default to bluetooth capability of platform as default @@ -778,6 +814,7 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat) config.has_power = true; config.has_network = true; config.has_bluetooth = true; + config.has_security = true; success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config); } @@ -957,7 +994,7 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS /** Update user info and channel for this node based on received user data */ -bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex) +bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex) { meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); if (!info) { @@ -965,6 +1002,12 @@ bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t chann } LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel); +#if !(MESHTASTIC_EXCLUDE_PKI) + if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one + printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); + memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + } +#endif // Both of info->user and p start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); @@ -1042,7 +1085,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) meshtastic_NodeInfoLite *lite = getMeshNode(n); if (!lite) { - if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) { + if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP)) { if (screen) screen->print("Warn: node database full!\nErasing oldest entry\n"); LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes, @@ -1068,6 +1111,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // everything is missing except the nodenum memset(lite, 0, sizeof(*lite)); lite->num = n; + LOG_INFO("Adding node to database with %i nodes and %i bytes free!\n", numMeshNodes, memGet.getFreeHeap()); } return lite; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 447ce10d4..a71f3e134 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -98,7 +98,7 @@ class NodeDB /** Update user info and channel for this node based on received user data */ - bool updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex = 0); + bool updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex = 0); /// @return our node number NodeNum getNodeNum() { return myNodeInfo.my_node_num; } diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index fc0099e87..0a9bb5b10 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -255,6 +255,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) fromRadioScratch.config.which_payload_variant = meshtastic_Config_bluetooth_tag; fromRadioScratch.config.payload_variant.bluetooth = config.bluetooth; break; + case meshtastic_Config_security_tag: + fromRadioScratch.config.which_payload_variant = meshtastic_Config_security_tag; + fromRadioScratch.config.payload_variant.security = config.security; + break; default: LOG_ERROR("Unknown config type %d\n", config_state); } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index f59d61ea2..1fecef6d7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -37,6 +37,7 @@ static MemoryDynamic staticPool; Allocator &packetPool = staticPool; static uint8_t bytes[MAX_RHPACKETLEN]; +static uint8_t ScratchEncrypted[MAX_RHPACKETLEN]; /** * Constructor @@ -307,70 +308,105 @@ bool perhapsDecode(meshtastic_MeshPacket *p) if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) return true; // If packet was already decoded just return - // assert(p->which_payloadVariant == MeshPacket_encrypted_tag); + size_t rawSize = p->encrypted.size; + if (rawSize > sizeof(bytes)) { + LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize); + return false; + } + bool decrypted = false; + ChannelIndex chIndex = 0; + memcpy(bytes, p->encrypted.bytes, + rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf + memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize); +#if !(MESHTASTIC_EXCLUDE_PKI) + // Attempt PKI decryption first + if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && nodeDB->getMeshNode(p->from) != nullptr && + nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && + rawSize > 8) { + LOG_DEBUG("Attempting PKI decryption\n"); - // Try to find a channel that works with this hash - for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { - // Try to use this hash/channel pair - if (channels.decryptForHash(chIndex, p->channel)) { - // Try to decrypt the packet if we can - size_t rawSize = p->encrypted.size; - if (rawSize > sizeof(bytes)) { - LOG_ERROR("Packet too large to attempt decription! (rawSize=%d > 256)\n", rawSize); - return false; - } - memcpy(bytes, p->encrypted.bytes, - rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf - crypto->decrypt(p->from, p->id, rawSize, bytes); - - // printBytes("plaintext", bytes, p->encrypted.size); - - // Take those raw bytes and convert them back into a well structured protobuf we can understand + if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) { + LOG_INFO("PKI Decryption worked!\n"); memset(&p->decoded, 0, sizeof(p->decoded)); - if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { - LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n"); - } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { - LOG_ERROR("Invalid portnum (bad psk?)!\n"); + rawSize -= 8; + if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) && + p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) { + decrypted = true; + LOG_INFO("Packet decrypted using PKI!\n"); + p->pki_encrypted = true; + memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32); + p->public_key.size = 32; + // memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers + // chIndex = 8; } else { - // parsing was successful - p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded - p->channel = chIndex; // change to store the index instead of the hash - - /* Not actually ever used. - // Decompress if needed. jm - if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) { - // Decompress the payload - char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; - char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; - int decompressed_len; - - memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); - - decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); - - // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); - - memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); - - // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP - p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; - } */ - - printPacket("decoded message", p); -#if ENABLE_JSON_LOGGING - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); -#elif ARCH_PORTDUINO - if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { - LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); - } -#endif - return true; + return false; } } } +#endif - LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); - return false; + // assert(p->which_payloadVariant == MeshPacket_encrypted_tag); + if (!decrypted) { + // Try to find a channel that works with this hash + for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) { + // Try to use this hash/channel pair + if (channels.decryptForHash(chIndex, p->channel)) { + // Try to decrypt the packet if we can + crypto->decrypt(p->from, p->id, rawSize, bytes); + + // printBytes("plaintext", bytes, p->encrypted.size); + + // Take those raw bytes and convert them back into a well structured protobuf we can understand + memset(&p->decoded, 0, sizeof(p->decoded)); + if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) { + LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id); + } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) { + LOG_ERROR("Invalid portnum (bad psk?)!\n"); + } else { + decrypted = true; + break; + } + } + } + } + if (decrypted) { + // parsing was successful + p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded + p->channel = chIndex; // change to store the index instead of the hash + + /* Not actually ever used. + // Decompress if needed. jm + if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) { + // Decompress the payload + char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; + char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {}; + int decompressed_len; + + memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size); + + decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out); + + // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len); + + memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len); + + // Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP + p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP; + } */ + + printPacket("decoded message", p); +#if ENABLE_JSON_LOGGING + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); +#elif ARCH_PORTDUINO + if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { + LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str()); + } +#endif + return true; + } else { + LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel); + return false; + } } /** Return 0 for success or a Routing_Errror code for failure @@ -384,7 +420,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); /* Not actually used, so save the cycles - // Only allow encryption on the text message app. // TODO: Allow modules to opt into compression. if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) { @@ -432,10 +467,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Now that we are encrypting the packet channel should be the hash (no longer the index) p->channel = hash; +#if !(MESHTASTIC_EXCLUDE_PKI) + meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); + if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && + p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && + p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag + LOG_DEBUG("Using PKI!\n"); + if (numbytes + 8 > MAX_RHPACKETLEN) + return meshtastic_Routing_Error_TOO_LARGE; + crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); + numbytes += 8; + memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); + p->channel = 0; + } else { + crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + memcpy(p->encrypted.bytes, bytes, numbytes); + } +#else crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + memcpy(p->encrypted.bytes, bytes, numbytes); +#endif // Copy back into the packet and set the variant type - memcpy(p->encrypted.bytes, bytes, numbytes); p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } @@ -539,4 +592,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p) handleReceived(p); packetPool.release(p); -} +} \ No newline at end of file diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp new file mode 100644 index 000000000..cd18ae6c5 --- /dev/null +++ b/src/mesh/aes-ccm.cpp @@ -0,0 +1,157 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#define AES_BLOCK_SIZE 16 +#include "aes-ccm.h" +#if !MESHTASTIC_EXCLUDE_PKI + +static inline void WPA_PUT_BE16(uint8_t *a, uint16_t val) +{ + a[0] = val >> 8; + a[1] = val & 0xff; +} + +static void xor_aes_block(uint8_t *dst, const uint8_t *src) +{ + uint32_t *d = (uint32_t *)dst; + uint32_t *s = (uint32_t *)src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} +static void aes_ccm_auth_start(size_t M, size_t L, const uint8_t *nonce, const uint8_t *aad, size_t aad_len, size_t plain_len, + uint8_t *x) +{ + uint8_t aad_buf[2 * AES_BLOCK_SIZE]; + uint8_t b[AES_BLOCK_SIZE]; + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + crypto->aesEncrypt(b, x); /* X_1 = E(K, B_0) */ + if (!aad_len) + return; + WPA_PUT_BE16(aad_buf, aad_len); + memcpy(aad_buf + 2, aad, aad_len); + memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + xor_aes_block(aad_buf, x); + crypto->aesEncrypt(aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + crypto->aesEncrypt(&aad_buf[AES_BLOCK_SIZE], x); + } +} +static void aes_ccm_auth(const uint8_t *data, size_t len, uint8_t *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + crypto->aesEncrypt(x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + crypto->aesEncrypt(x, x); + } +} +static void aes_ccm_encr_start(size_t L, const uint8_t *nonce, uint8_t *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + memcpy(&a[1], nonce, 15 - L); +} +static void aes_ccm_encr(size_t L, const uint8_t *in, size_t len, uint8_t *out, uint8_t *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + crypto->aesEncrypt(a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + crypto->aesEncrypt(a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} +static void aes_ccm_encr_auth(size_t M, uint8_t *x, uint8_t *a, uint8_t *auth) +{ + size_t i; + uint8_t tmp[AES_BLOCK_SIZE]; + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + crypto->aesEncrypt(a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; +} +static void aes_ccm_decr_auth(size_t M, uint8_t *a, const uint8_t *auth, uint8_t *t) +{ + size_t i; + uint8_t tmp[AES_BLOCK_SIZE]; + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + crypto->aesEncrypt(a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; +} +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth) +{ + const size_t L = 2; + uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + crypto->aesSetKey(key, key_len); + aes_ccm_auth_start(M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(plain, plain_len, x); + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(M, x, a, auth); + return 0; +} +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain) +{ + const size_t L = 2; + uint8_t x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + uint8_t t[AES_BLOCK_SIZE]; + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return false; + crypto->aesSetKey(key, key_len); + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(M, a, auth, t); + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(L, crypt, crypt_len, plain, a); + aes_ccm_auth_start(M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(plain, crypt_len, x); + if (memcmp(x, t, M) != 0) { // FIXME make const comp + return false; + } + return true; +} +#endif \ No newline at end of file diff --git a/src/mesh/aes-ccm.h b/src/mesh/aes-ccm.h new file mode 100644 index 000000000..6b8edcde4 --- /dev/null +++ b/src/mesh/aes-ccm.h @@ -0,0 +1,10 @@ +#pragma once +#include "CryptoEngine.h" +#if !MESHTASTIC_EXCLUDE_PKI + +int aes_ccm_ae(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *plain, size_t plain_len, + const uint8_t *aad, size_t aad_len, uint8_t *crypt, uint8_t *auth); + +bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t M, const uint8_t *crypt, size_t crypt_len, + const uint8_t *aad, size_t aad_len, const uint8_t *auth, uint8_t *plain); +#endif \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index 9dfe9b558..e2d4188d7 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -12,4 +12,6 @@ template constexpr const T &clamp(const T &v, const T &lo, const T &hi #define STRNSTR #include char *strnstr(const char *s, const char *find, size_t slen); -#endif \ No newline at end of file +#endif + +void printBytes(const char *label, const uint8_t *p, size_t numbytes); \ No newline at end of file diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 25450992b..fe426f8f5 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -65,7 +65,29 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta bool handled = false; assert(r); bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum(); - + if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) { + return handled; + } + meshtastic_Channel *ch = &channels.getByIndex(mp.channel); + // Could tighten this up further by tracking the last poblic_key we went an AdminMessage request to + // and only allowing responses from that remote. + if (!((mp.from == 0 && !config.security.is_managed) || + r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag || + r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag || + r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag || + (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) || + (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) { + LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant); + return handled; + } + LOG_INFO("Handling admin payload %i\n", r->which_payload_variant); switch (r->which_payload_variant) { /** @@ -383,8 +405,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) #endif if (config.device.button_gpio == c.payload_variant.device.button_gpio && config.device.buzzer_gpio == c.payload_variant.device.buzzer_gpio && - config.device.debug_log_enabled == c.payload_variant.device.debug_log_enabled && - config.device.serial_enabled == c.payload_variant.device.serial_enabled && config.device.role == c.payload_variant.device.role && config.device.disable_triple_click == c.payload_variant.device.disable_triple_click && config.device.rebroadcast_mode == c.payload_variant.device.rebroadcast_mode) { @@ -501,6 +521,16 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) config.has_bluetooth = true; config.bluetooth = c.payload_variant.bluetooth; break; + case meshtastic_Config_security_tag: + LOG_INFO("Setting config: Security\n"); + config.security = c.payload_variant.security; + owner.public_key.size = config.security.public_key.size; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); + if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled && + config.security.serial_enabled == c.payload_variant.security.serial_enabled) + requiresReboot = false; + + break; } saveChanges(changes, requiresReboot); @@ -896,5 +926,5 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg) { // restrict to the admin channel for rx - boundChannel = Channels::adminChannel; + // boundChannel = Channels::adminChannel; } \ No newline at end of file diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp new file mode 100644 index 000000000..e564d5d0e --- /dev/null +++ b/test/test_crypto/test_main.cpp @@ -0,0 +1,50 @@ +#include "CryptoEngine.h" + +#include + +void setUp(void) +{ + // set stuff up here +} + +void tearDown(void) +{ + // clean stuff up here +} + +void test_SHA256(void) +{ + uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, + 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + uint8_t hash[32] = {0}; + crypto->hash(hash, 0); + TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32); +} +void test_ECB_AES256(void) +{ + uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; + uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; + uint8_t scratch[16] = {0}; + + uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}; + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16); +} + +void setup() +{ + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + UNITY_BEGIN(); // IMPORTANT LINE! + RUN_TEST(test_SHA256); + RUN_TEST(test_ECB_AES256); +} + +void loop() +{ + UNITY_END(); // stop unit testing +} \ No newline at end of file diff --git a/userPrefs.h b/userPrefs.h index 3ebbefcaf..4e80b579f 100644 --- a/userPrefs.h +++ b/userPrefs.h @@ -36,4 +36,10 @@ static unsigned char icon_bits[] = { 0x98, 0x3F, 0xF0, 0x23, 0x00, 0xFC, 0x0F, 0xE0, 0x7F, 0x00, 0xFC, 0x03, 0x80, 0xFF, 0x01, 0xFC, 0x00, 0x00, 0x3E, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00, 0x70, 0x00, 0x00, 0x1C, 0x00}; */ +/* +#define ADMIN_KEY_USERPREFS 1 +static unsigned char admin_key_userprefs[] = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, + 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, + 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c}; +*/ #endif \ No newline at end of file From 26d0b2b4771de39f10f589392fb0351e5d96fdd4 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 15:45:29 -0500 Subject: [PATCH 14/38] Add DH25519 unit test --- src/DebugConfiguration.h | 2 +- src/mesh/CryptoEngine.cpp | 27 ++++++----- src/mesh/CryptoEngine.h | 25 +++++----- src/mesh/NodeDB.cpp | 2 +- test/test_crypto/test_main.cpp | 89 +++++++++++++++++++++++++++++----- 5 files changed, 109 insertions(+), 36 deletions(-) diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index 7987e7fa1..55453ea1e 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -45,7 +45,7 @@ #define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__) #define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else -#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) +#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE) && !defined(PIO_UNIT_TESTING) #define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__) #define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__) #define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 677667aef..d284f3410 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -24,7 +24,6 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey) memcpy(privKey, private_key, sizeof(private_key)); } #endif -uint8_t shared_key[32]; void CryptoEngine::clearKeys() { memset(public_key, 0, sizeof(public_key)); @@ -86,7 +85,7 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut); } -void CryptoEngine::setPrivateKey(uint8_t *_private_key) +void CryptoEngine::setDHPrivateKey(uint8_t *_private_key) { memcpy(private_key, _private_key, 32); } @@ -103,16 +102,8 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum) return false; } - uint8_t *pubKey = node->user.public_key.bytes; - uint8_t local_priv[32]; - memcpy(shared_key, pubKey, 32); - memcpy(local_priv, private_key, 32); - // Calculate the shared secret with the specified node's public key and our private key - // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. - if (!Curve25519::dh2(shared_key, local_priv)) { - LOG_WARN("Curve25519DH step 2 failed!\n"); + if (!setDHPublicKey(node->user.public_key.bytes)) return false; - } printBytes("DH Output: ", shared_key, 32); @@ -171,6 +162,20 @@ void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) #endif +bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) +{ + uint8_t local_priv[32]; + memcpy(shared_key, pubKey, 32); + memcpy(local_priv, private_key, 32); + // Calculate the shared secret with the specified node's public key and our private key + // This includes an internal weak key check, which among other things looks for an all 0 public key and shared key. + if (!Curve25519::dh2(shared_key, local_priv)) { + LOG_WARN("Curve25519DH step 2 failed!\n"); + return false; + } + return true; +} + concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index 51080fd59..fd607c29e 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -23,15 +23,6 @@ struct CryptoKey { class CryptoEngine { - protected: - /** Our per packet nonce */ - uint8_t nonce[16] = {0}; - - CryptoKey key = {}; -#if !(MESHTASTIC_EXCLUDE_PKI) - uint8_t private_key[32] = {0}; -#endif - public: #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t public_key[32] = {0}; @@ -43,11 +34,12 @@ class CryptoEngine virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); #endif void clearKeys(); - void setPrivateKey(uint8_t *_private_key); + void setDHPrivateKey(uint8_t *_private_key); virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut); - virtual bool setDHKey(uint32_t nodeNum); + bool setDHKey(uint32_t nodeNum); + virtual bool setDHPublicKey(uint8_t *publicKey); virtual void hash(uint8_t *bytes, size_t numBytes); virtual void aesSetKey(const uint8_t *key, size_t key_len); @@ -75,8 +67,17 @@ class CryptoEngine */ virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); - +#ifndef PIO_UNIT_TESTING protected: +#endif + /** Our per packet nonce */ + uint8_t nonce[16] = {0}; + + CryptoKey key = {}; +#if !(MESHTASTIC_EXCLUDE_PKI) + uint8_t shared_key[32] = {0}; + uint8_t private_key[32] = {0}; +#endif /** * Init our 128 bit nonce for a new packet * diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index fb7926977..468bffab3 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -131,7 +131,7 @@ NodeDB::NodeDB() LOG_INFO("Using saved PKI keys\n"); owner.public_key.size = config.security.public_key.size; memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size); - crypto->setPrivateKey(config.security.private_key.bytes); + crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) LOG_INFO("Generating new PKI keys\n"); diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index e564d5d0e..e9aee928e 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -2,6 +2,18 @@ #include +void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0) +{ + if (len) { + memset(result, 0, len); + } + for (unsigned int i = 0; i < hex.length(); i += 2) { + std::string byteString = hex.substr(i, 2); + result[i / 2] = (uint8_t)strtol(byteString.c_str(), NULL, 16); + } + return; +} + void setUp(void) { // set stuff up here @@ -14,25 +26,79 @@ void tearDown(void) void test_SHA256(void) { - uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, - 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + uint8_t expected[32]; uint8_t hash[32] = {0}; + + HexToBytes(expected, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); crypto->hash(hash, 0); - TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); + + HexToBytes(hash, "d3", 32); + HexToBytes(expected, "28969cdfa74a12c82f3bad960b0b000aca2ac329deea5c2328ebc6f2ba9802c1"); + crypto->hash(hash, 1); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); + + HexToBytes(hash, "11af", 32); + HexToBytes(expected, "5ca7133fa735326081558ac312c620eeca9970d1e70a4b95533d956f072d1f98"); + crypto->hash(hash, 2); + TEST_ASSERT_EQUAL_MEMORY(hash, expected, 32); } void test_ECB_AES256(void) { - uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, - 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; - uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; - uint8_t scratch[16] = {0}; + // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_ECB.pdf - uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}; + uint8_t key[32] = {0}; + uint8_t plain[16] = {0}; + uint8_t result[16] = {0}; + uint8_t expected[16] = {0}; + + HexToBytes(key, "603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4"); + + HexToBytes(plain, "6BC1BEE22E409F96E93D7E117393172A"); + HexToBytes(expected, "F3EED1BDB5D2A03C064B5A7E3DB181F8"); crypto->aesSetKey(key, 32); - crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time - TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16); -} + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); + HexToBytes(plain, "AE2D8A571E03AC9C9EB76FAC45AF8E51"); + HexToBytes(expected, "591CCB10D410ED26DC5BA74A31362870"); + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); + + HexToBytes(plain, "30C81C46A35CE411E5FBC1191A0A52EF"); + HexToBytes(expected, "B6ED21B99CA6F4F9F153E7B1BEAFED1D"); + crypto->aesSetKey(key, 32); + crypto->aesEncrypt(plain, result); // Does 16 bytes at a time + TEST_ASSERT_EQUAL_MEMORY(expected, result, 16); +} +void test_DH25519(void) +{ + // test vectors from wycheproof x25519 + // https://github.com/C2SP/wycheproof/blob/master/testvectors/x25519_test.json + uint8_t private_key[32]; + uint8_t public_key[32]; + uint8_t expected_shared[32]; + + HexToBytes(public_key, "504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"); + HexToBytes(private_key, "c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475"); + HexToBytes(expected_shared, "436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); + + HexToBytes(public_key, "63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733"); + HexToBytes(private_key, "d85d8c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211958"); + HexToBytes(expected_shared, "279df67a7c4611db4708a0e8282b195e5ac0ed6f4b2f292c6fbd0acac30d1332"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(crypto->setDHPublicKey(public_key)); + TEST_ASSERT_EQUAL_MEMORY(expected_shared, crypto->shared_key, 32); + + HexToBytes(public_key, "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"); + HexToBytes(private_key, "18630f93598637c35da623a74559cf944374a559114c7937811041fc8605564a"); + crypto->setDHPrivateKey(private_key); + TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key +} void setup() { // NOTE!!! Wait for >2 secs @@ -42,6 +108,7 @@ void setup() UNITY_BEGIN(); // IMPORTANT LINE! RUN_TEST(test_SHA256); RUN_TEST(test_ECB_AES256); + RUN_TEST(test_DH25519); } void loop() From 192af05a25ef219c14eec702129bdc800ce31721 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 20:04:38 -0500 Subject: [PATCH 15/38] Fix compile on STM32 --- src/mesh/CryptoEngine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index d284f3410..fd7246fa9 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -160,8 +160,6 @@ void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out) aes->encryptBlock(out, in); } -#endif - bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) { uint8_t local_priv[32]; @@ -176,6 +174,7 @@ bool CryptoEngine::setDHPublicKey(uint8_t *pubKey) return true; } +#endif concurrency::Lock *cryptLock; void CryptoEngine::setKey(const CryptoKey &k) From c3aa56ef30ff4069065c1819d8ab0ac8ca8d7aba Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 22:38:05 -0500 Subject: [PATCH 16/38] Refactor platform cryptography, add tests --- src/mesh/CryptoEngine.cpp | 42 +++++++++- src/mesh/CryptoEngine.h | 6 +- src/mesh/Router.cpp | 2 +- src/platform/esp32/ESP32CryptoEngine.cpp | 41 ++-------- src/platform/esp32/architecture.h | 3 + src/platform/nrf52/NRF52CryptoEngine.cpp | 29 ++----- src/platform/nrf52/architecture.h | 3 + .../portduino/CrossPlatformCryptoEngine.cpp | 78 ------------------- src/platform/rp2040/rp2040CryptoEngine.cpp | 66 ---------------- src/platform/stm32wl/STM32WLCryptoEngine.cpp | 67 ---------------- test/test_crypto/test_main.cpp | 27 +++++++ 11 files changed, 88 insertions(+), 276 deletions(-) delete mode 100644 src/platform/portduino/CrossPlatformCryptoEngine.cpp delete mode 100644 src/platform/rp2040/rp2040CryptoEngine.cpp delete mode 100644 src/platform/stm32wl/STM32WLCryptoEngine.cpp diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index fd7246fa9..e83236eab 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -1,6 +1,7 @@ #include "CryptoEngine.h" #include "NodeDB.h" #include "RadioInterface.h" +#include "architecture.h" #include "configuration.h" #if !(MESHTASTIC_EXCLUDE_PKI) @@ -188,14 +189,44 @@ void CryptoEngine::setKey(const CryptoKey &k) * * @param bytes is updated in place */ -void CryptoEngine::encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) +void CryptoEngine::encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) { - LOG_WARN("noop encryption!\n"); + if (key.length > 0) { + initNonce(fromNode, packetId); + if (numBytes <= MAX_BLOCKSIZE) { + encryptAESCtr(key, nonce, numBytes, bytes); + } else { + LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); + } + } } void CryptoEngine::decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) { - LOG_WARN("noop decryption!\n"); + // For CTR, the implementation is the same + encryptPacket(fromNode, packetId, numBytes, bytes); +} + +// Generic implementation of AES-CTR encryption. +void CryptoEngine::encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) +{ + if (ctr) { + delete ctr; + ctr = nullptr; + } + if (_key.length == 16) + ctr = new CTR(); + else + ctr = new CTR(); + ctr->setKey(_key.bytes, _key.length); + static uint8_t scratch[MAX_BLOCKSIZE]; + memcpy(scratch, bytes, numBytes); + memset(scratch + numBytes, 0, + sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) + + ctr->setIV(_nonce, 16); + ctr->setCounterSize(4); + ctr->encrypt(bytes, scratch, numBytes); } /** @@ -208,4 +239,7 @@ void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId) // use memcpy to avoid breaking strict-aliasing memcpy(nonce, &packetId, sizeof(uint64_t)); memcpy(nonce + sizeof(uint64_t), &fromNode, sizeof(uint32_t)); -} \ No newline at end of file +} +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +CryptoEngine *crypto = new CryptoEngine; +#endif \ No newline at end of file diff --git a/src/mesh/CryptoEngine.h b/src/mesh/CryptoEngine.h index fd607c29e..5ca9db7c1 100644 --- a/src/mesh/CryptoEngine.h +++ b/src/mesh/CryptoEngine.h @@ -1,5 +1,6 @@ #pragma once #include "AES.h" +#include "CTR.h" #include "concurrency/LockGuard.h" #include "configuration.h" #include "mesh-pb-constants.h" @@ -65,15 +66,16 @@ class CryptoEngine * * @param bytes is updated in place */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); + virtual void encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); + virtual void encryptAESCtr(CryptoKey key, uint8_t *nonce, size_t numBytes, uint8_t *bytes); #ifndef PIO_UNIT_TESTING protected: #endif /** Our per packet nonce */ uint8_t nonce[16] = {0}; - CryptoKey key = {}; + CTRCommon *ctr = NULL; #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t shared_key[32] = {0}; uint8_t private_key[32] = {0}; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 1fecef6d7..b00b66a47 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -480,7 +480,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; } else { - crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); } #else diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp index 998419df8..230139036 100644 --- a/src/platform/esp32/ESP32CryptoEngine.cpp +++ b/src/platform/esp32/ESP32CryptoEngine.cpp @@ -13,58 +13,29 @@ class ESP32CryptoEngine : public CryptoEngine ~ESP32CryptoEngine() { mbedtls_aes_free(&aes); } - /** - * Set the key used for encrypt, decrypt. - * - * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. - * - * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) - * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the - * provided pointer) - */ - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - - if (key.length != 0) { - auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8); - assert(!res); - } - } - /** * Encrypt a packet * * @param bytes is updated in place + * TODO: return bool, and handle graciously when something fails */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override + virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override { - if (key.length > 0) { - LOG_DEBUG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); - initNonce(fromNode, packetId); + if (_key.length > 0) { if (numBytes <= MAX_BLOCKSIZE) { + mbedtls_aes_setkey_enc(&aes, _key.bytes, _key.length * 8); static uint8_t scratch[MAX_BLOCKSIZE]; uint8_t stream_block[16]; size_t nc_off = 0; memcpy(scratch, bytes, numBytes); memset(scratch + numBytes, 0, sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes); - assert(!res); + mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes); } else { LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); } } } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: }; -CryptoEngine *crypto = new ESP32CryptoEngine(); +CryptoEngine *crypto = new ESP32CryptoEngine(); \ No newline at end of file diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index fd3f92a9c..b6def5b01 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -42,6 +42,9 @@ #ifndef DEFAULT_VREF #define DEFAULT_VREF 1100 #endif +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +#define HAS_CUSTOM_CRYPTO_ENGINE 1 +#endif #if defined(HAS_AXP192) || defined(HAS_AXP2101) #define HAS_PMU diff --git a/src/platform/nrf52/NRF52CryptoEngine.cpp b/src/platform/nrf52/NRF52CryptoEngine.cpp index a7cf3d5bf..5de13c58b 100644 --- a/src/platform/nrf52/NRF52CryptoEngine.cpp +++ b/src/platform/nrf52/NRF52CryptoEngine.cpp @@ -9,41 +9,24 @@ class NRF52CryptoEngine : public CryptoEngine ~NRF52CryptoEngine() {} - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override + virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override { - if (key.length > 16) { - LOG_DEBUG("Software encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); + if (_key.length > 16) { AES_ctx ctx; - initNonce(fromNode, packetId); - AES_init_ctx_iv(&ctx, key.bytes, nonce); + AES_init_ctx_iv(&ctx, _key.bytes, _nonce); AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes); - } else if (key.length > 0) { - LOG_DEBUG("nRF52 encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t)packetId, numBytes); + } else if (_key.length > 0) { nRFCrypto.begin(); nRFCrypto_AES ctx; uint8_t myLen = ctx.blockLen(numBytes); char encBuf[myLen] = {0}; - initNonce(fromNode, packetId); ctx.begin(); - ctx.Process((char *)bytes, numBytes, nonce, key.bytes, key.length, encBuf, ctx.encryptFlag, ctx.ctrMode); + ctx.Process((char *)bytes, numBytes, _nonce, _key.bytes, _key.length, encBuf, ctx.encryptFlag, ctx.ctrMode); ctx.end(); nRFCrypto.end(); memcpy(bytes, encBuf, numBytes); } } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: }; -CryptoEngine *crypto = new NRF52CryptoEngine(); +CryptoEngine *crypto = new NRF52CryptoEngine(); \ No newline at end of file diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index d5685d611..8781347c3 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -32,6 +32,9 @@ #ifndef HAS_CPU_SHUTDOWN #define HAS_CPU_SHUTDOWN 1 #endif +#ifndef HAS_CUSTOM_CRYPTO_ENGINE +#define HAS_CUSTOM_CRYPTO_ENGINE 1 +#endif // // set HW_VENDOR diff --git a/src/platform/portduino/CrossPlatformCryptoEngine.cpp b/src/platform/portduino/CrossPlatformCryptoEngine.cpp deleted file mode 100644 index 46ef942f0..000000000 --- a/src/platform/portduino/CrossPlatformCryptoEngine.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -/** A platform independent AES engine implemented using Tiny-AES - */ -class CrossPlatformCryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - CrossPlatformCryptoEngine() {} - - ~CrossPlatformCryptoEngine() {} - - /** - * Set the key used for encrypt, decrypt. - * - * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. - * - * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) - * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the - * provided pointer) - */ - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new CrossPlatformCryptoEngine(); diff --git a/src/platform/rp2040/rp2040CryptoEngine.cpp b/src/platform/rp2040/rp2040CryptoEngine.cpp deleted file mode 100644 index 5486e51e5..000000000 --- a/src/platform/rp2040/rp2040CryptoEngine.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -class RP2040CryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - RP2040CryptoEngine() {} - - ~RP2040CryptoEngine() {} - - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new RP2040CryptoEngine(); diff --git a/src/platform/stm32wl/STM32WLCryptoEngine.cpp b/src/platform/stm32wl/STM32WLCryptoEngine.cpp deleted file mode 100644 index 4debdf78e..000000000 --- a/src/platform/stm32wl/STM32WLCryptoEngine.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#undef RNG -#include "AES.h" -#include "CTR.h" -#include "CryptoEngine.h" -#include "configuration.h" - -class STM32WLCryptoEngine : public CryptoEngine -{ - - CTRCommon *ctr = NULL; - - public: - STM32WLCryptoEngine() {} - - ~STM32WLCryptoEngine() {} - - virtual void setKey(const CryptoKey &k) override - { - CryptoEngine::setKey(k); - LOG_DEBUG("Installing AES%d key!\n", key.length * 8); - if (ctr) { - delete ctr; - ctr = NULL; - } - if (key.length != 0) { - if (key.length == 16) - ctr = new CTR(); - else - ctr = new CTR(); - - ctr->setKey(key.bytes, key.length); - } - } - /** - * Encrypt a packet - * - * @param bytes is updated in place - */ - virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - if (key.length > 0) { - initNonce(fromNode, packetId); - if (numBytes <= MAX_BLOCKSIZE) { - static uint8_t scratch[MAX_BLOCKSIZE]; - memcpy(scratch, bytes, numBytes); - memset(scratch + numBytes, 0, - sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it) - - ctr->setIV(nonce, sizeof(nonce)); - ctr->setCounterSize(4); - ctr->encrypt(bytes, scratch, numBytes); - } else { - LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!\n", numBytes); - } - } - } - - virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override - { - // For CTR, the implementation is the same - encrypt(fromNode, packetId, numBytes, bytes); - } - - private: -}; - -CryptoEngine *crypto = new STM32WLCryptoEngine(); diff --git a/test/test_crypto/test_main.cpp b/test/test_crypto/test_main.cpp index e9aee928e..129c88283 100644 --- a/test/test_crypto/test_main.cpp +++ b/test/test_crypto/test_main.cpp @@ -99,6 +99,32 @@ void test_DH25519(void) crypto->setDHPrivateKey(private_key); TEST_ASSERT(!crypto->setDHPublicKey(public_key)); // Weak public key results in 0 shared key } +void test_AES_CTR(void) +{ + uint8_t expected[32]; + uint8_t plain[32]; + uint8_t nonce[32]; + CryptoKey k; + + // vectors from https://www.rfc-editor.org/rfc/rfc3686#section-6 + k.length = 32; + HexToBytes(k.bytes, "776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104"); + HexToBytes(nonce, "00000060DB5672C97AA8F0B200000001"); + HexToBytes(expected, "145AD01DBF824EC7560863DC71E3E0C0"); + memcpy(plain, "Single block msg", 16); + + crypto->encryptAESCtr(k, nonce, 16, plain); + TEST_ASSERT_EQUAL_MEMORY(expected, plain, 16); + + k.length = 16; + memcpy(plain, "Single block msg", 16); + HexToBytes(k.bytes, "AE6852F8121067CC4BF7A5765577F39E"); + HexToBytes(nonce, "00000030000000000000000000000001"); + HexToBytes(expected, "E4095D4FB7A7B3792D6175A3261311B8"); + crypto->encryptAESCtr(k, nonce, 16, plain); + TEST_ASSERT_EQUAL_MEMORY(expected, plain, 16); +} + void setup() { // NOTE!!! Wait for >2 secs @@ -109,6 +135,7 @@ void setup() RUN_TEST(test_SHA256); RUN_TEST(test_ECB_AES256); RUN_TEST(test_DH25519); + RUN_TEST(test_AES_CTR); } void loop() From c86a3200f04fc90ea045988cdbbb62078390f115 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 10 Aug 2024 23:11:04 -0500 Subject: [PATCH 17/38] Add missed function rename. (Thanks VSCode) --- src/mesh/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index b00b66a47..2a6fb31fc 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -484,7 +484,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) memcpy(p->encrypted.bytes, bytes, numbytes); } #else - crypto->encrypt(getFrom(p), p->id, numbytes, bytes); + crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); #endif From 185eb318ad274fd95315ebd2da3aafe20e5802ca Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 11 Aug 2024 14:12:20 -0500 Subject: [PATCH 18/38] Manual protobuf update --- src/mesh/generated/meshtastic/admin.pb.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index bef2abf9b..13093839d 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -30,7 +30,9 @@ typedef enum _meshtastic_AdminMessage_ConfigType { /* TODO: REPLACE */ meshtastic_AdminMessage_ConfigType_LORA_CONFIG = 5, /* TODO: REPLACE */ - meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6 + meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6, + /* TODO: REPLACE */ + meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG = 7 } meshtastic_AdminMessage_ConfigType; /* TODO: REPLACE */ @@ -194,8 +196,8 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG -#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG -#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG+1)) +#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG +#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG+1)) #define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG #define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG From e7dfabc20fa5518103821c7ca95bd77cdcea9768 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 11 Aug 2024 14:18:33 -0500 Subject: [PATCH 19/38] Exclude position packets from PKI (at least for now) --- src/mesh/Router.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 2a6fb31fc..945f92bb7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -470,8 +470,9 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) #if !(MESHTASTIC_EXCLUDE_PKI) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && - p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && - p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag + numbytes <= MAX_RHPACKETLEN - 8 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && + p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && + p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; From 8f3614d66c2671d5160c3bb95374c0ba712dc2f6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 11 Aug 2024 17:22:11 -0500 Subject: [PATCH 20/38] User to UserLite in NodeDB (#4438) * User to UserLite in the nodedb * Tronkdor the burninator --- protobufs | 2 +- src/RedirectablePrint.cpp | 5 +- src/mesh/NodeDB.cpp | 14 +++-- src/mesh/TypeConversions.cpp | 32 +++++++++- src/mesh/TypeConversions.h | 2 + .../generated/meshtastic/deviceonly.pb.cpp | 3 + src/mesh/generated/meshtastic/deviceonly.pb.h | 62 +++++++++++++++++-- 7 files changed, 106 insertions(+), 14 deletions(-) diff --git a/protobufs b/protobufs index 778667d93..c7a5a410b 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 778667d93b9769d51c386c461456bdec4f14f433 +Subproject commit c7a5a410b9652a91533f44c5fa4c28f0a6c96429 diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 02cd8b309..0eab0de0a 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -38,8 +38,9 @@ size_t RedirectablePrint::write(uint8_t c) #ifdef USE_SEGGER SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); #endif - - if (!config.has_lora || config.security.serial_enabled) + // Account for legacy config transition + bool serialEnabled = config.has_security ? config.security.serial_enabled : config.device.serial_enabled; + if (!config.has_lora || serialEnabled) dest->write(c); return 1; // We always claim one was written, rather than trusting what the diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 468bffab3..a9c8bbb44 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -134,6 +134,10 @@ NodeDB::NodeDB() crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) + config.has_security = true; + config.security.serial_enabled = config.device.serial_enabled; + config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; + config.security.is_managed = config.device.is_managed; LOG_INFO("Generating new PKI keys\n"); crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); config.security.public_key.size = 32; @@ -149,7 +153,7 @@ NodeDB::NodeDB() #endif - info->user = owner; + info->user = TypeConversions::ConvertToUserLite(owner); info->has_user = true; #ifdef ARCH_ESP32 @@ -1001,7 +1005,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde return false; } - LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel); + LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); @@ -1012,11 +1016,11 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde // Both of info->user and p start as filled with zero so I think this is okay bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); - info->user = p; + info->user = TypeConversions::ConvertToUserLite(p); if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) - LOG_DEBUG("updating changed=%d user %s/%s/%s, channel=%d\n", changed, info->user.id, info->user.long_name, - info->user.short_name, info->channel); + LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, + info->channel); info->has_user = true; if (changed) { diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index bcd600f24..30b06d0ad 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -24,7 +24,7 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo } if (lite->has_user) { info.has_user = true; - info.user = lite->user; + info.user = ConvertToUser(lite->num, lite->user); } if (lite->has_device_metrics) { info.has_device_metrics = true; @@ -55,4 +55,34 @@ meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite l position.time = lite.time; return position; +} + +meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) +{ + meshtastic_UserLite lite = meshtastic_UserLite_init_default; + + strncpy(lite.long_name, user.long_name, sizeof(lite.long_name)); + strncpy(lite.short_name, user.short_name, sizeof(lite.short_name)); + lite.hw_model = user.hw_model; + lite.role = user.role; + lite.is_licensed = user.is_licensed; + memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); + memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); + return lite; +} + +meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite) +{ + meshtastic_User user = meshtastic_User_init_default; + + snprintf(user.id, sizeof(user.id), "!%08x", nodeNum); + strncpy(user.long_name, lite.long_name, sizeof(user.long_name)); + strncpy(user.short_name, lite.short_name, sizeof(user.short_name)); + user.hw_model = lite.hw_model; + user.role = lite.role; + user.is_licensed = lite.is_licensed; + memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); + memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); + + return user; } \ No newline at end of file diff --git a/src/mesh/TypeConversions.h b/src/mesh/TypeConversions.h index ffc3c12a7..19e471f98 100644 --- a/src/mesh/TypeConversions.h +++ b/src/mesh/TypeConversions.h @@ -10,4 +10,6 @@ class TypeConversions static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite); static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position); static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite); + static meshtastic_UserLite ConvertToUserLite(meshtastic_User user); + static meshtastic_User ConvertToUser(uint32_t nodeNum, meshtastic_UserLite lite); }; diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp index 672192f67..2747ac9d9 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp +++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp @@ -9,6 +9,9 @@ PB_BIND(meshtastic_PositionLite, meshtastic_PositionLite, AUTO) +PB_BIND(meshtastic_UserLite, meshtastic_UserLite, AUTO) + + PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO) diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 2c91fe30e..343e5f48a 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -9,6 +9,7 @@ #include "meshtastic/localonly.pb.h" #include "meshtastic/mesh.pb.h" #include "meshtastic/telemetry.pb.h" +#include "meshtastic/config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -45,12 +46,37 @@ typedef struct _meshtastic_PositionLite { meshtastic_Position_LocSource location_source; } meshtastic_PositionLite; +typedef PB_BYTES_ARRAY_T(32) meshtastic_UserLite_public_key_t; +typedef struct _meshtastic_UserLite { + /* This is the addr of the radio. */ + pb_byte_t macaddr[6]; + /* A full name for this user, i.e. "Kevin Hester" */ + char long_name[40]; + /* A VERY short name, ideally two characters. + Suitable for a tiny OLED screen */ + char short_name[5]; + /* TBEAM, HELTEC, etc... + Starting in 1.2.11 moved to hw_model enum in the NodeInfo object. + Apps will still need the string here for older builds + (so OTA update can find the right image), but if the enum is available it will be used instead. */ + meshtastic_HardwareModel hw_model; + /* In some regions Ham radio operators have different bandwidth limitations than others. + If this user is a licensed operator, set this flag. + Also, "long_name" should be their licence number. */ + bool is_licensed; + /* Indicates that the user's role in the mesh */ + meshtastic_Config_DeviceConfig_Role role; + /* The public key of the user's device. + This is sent out to other nodes on the mesh to allow them to compute a shared secret key. */ + meshtastic_UserLite_public_key_t public_key; +} meshtastic_UserLite; + typedef struct _meshtastic_NodeInfoLite { /* The node number */ uint32_t num; /* The user info for this node */ bool has_user; - meshtastic_User user; + meshtastic_UserLite user; /* This position data. Note: before 1.2.14 we would also store the last time we've heard from this node in position.time, that is no longer true. Position.time now indicates the last time we received a POSITION from that node. */ bool has_position; @@ -164,6 +190,9 @@ extern "C" { #define meshtastic_PositionLite_location_source_ENUMTYPE meshtastic_Position_LocSource +#define meshtastic_UserLite_hw_model_ENUMTYPE meshtastic_HardwareModel +#define meshtastic_UserLite_role_ENUMTYPE meshtastic_Config_DeviceConfig_Role + @@ -172,12 +201,14 @@ extern "C" { /* Initializer values for message structs */ #define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} -#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_User_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} +#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} +#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0} #define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}} #define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0} #define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default} #define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN} -#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} +#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}} +#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0} #define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}} #define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0} #define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero} @@ -188,6 +219,13 @@ extern "C" { #define meshtastic_PositionLite_altitude_tag 3 #define meshtastic_PositionLite_time_tag 4 #define meshtastic_PositionLite_location_source_tag 5 +#define meshtastic_UserLite_macaddr_tag 1 +#define meshtastic_UserLite_long_name_tag 2 +#define meshtastic_UserLite_short_name_tag 3 +#define meshtastic_UserLite_hw_model_tag 4 +#define meshtastic_UserLite_is_licensed_tag 5 +#define meshtastic_UserLite_role_tag 6 +#define meshtastic_UserLite_public_key_tag 7 #define meshtastic_NodeInfoLite_num_tag 1 #define meshtastic_NodeInfoLite_user_tag 2 #define meshtastic_NodeInfoLite_position_tag 3 @@ -229,6 +267,17 @@ X(a, STATIC, SINGULAR, UENUM, location_source, 5) #define meshtastic_PositionLite_CALLBACK NULL #define meshtastic_PositionLite_DEFAULT NULL +#define meshtastic_UserLite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 1) \ +X(a, STATIC, SINGULAR, STRING, long_name, 2) \ +X(a, STATIC, SINGULAR, STRING, short_name, 3) \ +X(a, STATIC, SINGULAR, UENUM, hw_model, 4) \ +X(a, STATIC, SINGULAR, BOOL, is_licensed, 5) \ +X(a, STATIC, SINGULAR, UENUM, role, 6) \ +X(a, STATIC, SINGULAR, BYTES, public_key, 7) +#define meshtastic_UserLite_CALLBACK NULL +#define meshtastic_UserLite_DEFAULT NULL + #define meshtastic_NodeInfoLite_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ @@ -242,7 +291,7 @@ X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \ X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) #define meshtastic_NodeInfoLite_CALLBACK NULL #define meshtastic_NodeInfoLite_DEFAULT NULL -#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_User +#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_UserLite #define meshtastic_NodeInfoLite_position_MSGTYPE meshtastic_PositionLite #define meshtastic_NodeInfoLite_device_metrics_MSGTYPE meshtastic_DeviceMetrics @@ -290,6 +339,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, oem_local_module_config, 8) #define meshtastic_OEMStore_oem_local_module_config_MSGTYPE meshtastic_LocalModuleConfig extern const pb_msgdesc_t meshtastic_PositionLite_msg; +extern const pb_msgdesc_t meshtastic_UserLite_msg; extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg; extern const pb_msgdesc_t meshtastic_DeviceState_msg; extern const pb_msgdesc_t meshtastic_ChannelFile_msg; @@ -297,6 +347,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg +#define meshtastic_UserLite_fields &meshtastic_UserLite_msg #define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg #define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg #define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg @@ -306,9 +357,10 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg; /* meshtastic_DeviceState_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define meshtastic_ChannelFile_size 718 -#define meshtastic_NodeInfoLite_size 200 +#define meshtastic_NodeInfoLite_size 183 #define meshtastic_OEMStore_size 3502 #define meshtastic_PositionLite_size 28 +#define meshtastic_UserLite_size 96 #ifdef __cplusplus } /* extern "C" */ From 884bc529f0ce5c52ec3402f10446023a75fbd9e5 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sun, 11 Aug 2024 18:25:32 -0500 Subject: [PATCH 21/38] protos --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index c7a5a410b..8063f8026 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit c7a5a410b9652a91533f44c5fa4c28f0a6c96429 +Subproject commit 8063f80260a5af438d5b68c6587b4fed77d6c46d From 67ddae2851a6d51a059baaf70540f4770b37c320 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 06:43:54 -0500 Subject: [PATCH 22/38] Add logic to nodeDB to prefer evicting boring nodes (#4441) --- src/mesh/NodeDB.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a9c8bbb44..f2a0520e3 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1097,11 +1097,24 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) // look for oldest node and erase it uint32_t oldest = UINT32_MAX; int oldestIndex = -1; + int oldestIndex = -1; + int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { + // Simply the oldest non-favorite node if (!meshNodes->at(i).is_favorite && meshNodes->at(i).last_heard < oldest) { oldest = meshNodes->at(i).last_heard; oldestIndex = i; } + // The oldest "boring" node + if (!meshNodes->at(i).is_favorite && meshNodes->at(i).user.public_key.size == 0 && + meshNodes->at(i).last_heard < oldestBoring) { + oldestBoring = meshNodes->at(i).last_heard; + oldestBoringIndex = i; + } + } + // if we found a "boring" node, evict it + if (oldestBoringIndex != -1) { + oldestIndex = oldestBoringIndex; } // Shove the remaining nodes down the chain for (int i = oldestIndex; i < numMeshNodes - 1; i++) { @@ -1142,4 +1155,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} \ No newline at end of file +} From 2d1813023500dccb2f42763aec8810a3448c849c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 11:26:43 -0500 Subject: [PATCH 23/38] Don't goober public_key in Userlite conversion --- src/mesh/NodeDB.cpp | 15 ++++++++++++--- src/mesh/TypeConversions.cpp | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index f2a0520e3..3fdc22685 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1007,9 +1007,15 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde LOG_DEBUG("old user %s/%s, channel=%d\n", info->user.long_name, info->user.short_name, info->channel); #if !(MESHTASTIC_EXCLUDE_PKI) - if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one - printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32); - memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + if (p.public_key.size > 0) { + printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); + if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one + LOG_INFO("Public Key set for node, not updateing!\n"); + // we copy the key into the incoming packet, to prevent overwrite + memcpy(p.public_key.bytes, info->user.public_key.bytes, 32); + } else { + LOG_INFO("Updating Node Pubkey!\n"); + } } #endif @@ -1017,6 +1023,9 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex); info->user = TypeConversions::ConvertToUserLite(p); + if (info->user.public_key.size == 32) { + printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32); + } if (nodeId != getNodeNum()) info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel) LOG_DEBUG("updating changed=%d user %s/%s, channel=%d\n", changed, info->user.long_name, info->user.short_name, diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 30b06d0ad..5303dfb49 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -68,6 +68,7 @@ meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user) lite.is_licensed = user.is_licensed; memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr)); memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes)); + lite.public_key.size = user.public_key.size; return lite; } From 7537b55586aaaa526098a798cc3a9da59aedb589 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 11:37:50 -0500 Subject: [PATCH 24/38] Ungoober oldestBoring --- src/mesh/NodeDB.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 3fdc22685..ef649288e 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1105,7 +1105,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) memGet.getFreeHeap()); // look for oldest node and erase it uint32_t oldest = UINT32_MAX; - int oldestIndex = -1; + uint32_t oldestBoring = UINT32_MAX; int oldestIndex = -1; int oldestBoringIndex = -1; for (int i = 1; i < numMeshNodes; i++) { From b91d66b436d11f98963f5a6d93390d7517cb5c53 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 16:20:07 -0500 Subject: [PATCH 25/38] Don't forget public_key.size in converting back --- src/mesh/TypeConversions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp index 5303dfb49..d8ee6afc7 100644 --- a/src/mesh/TypeConversions.cpp +++ b/src/mesh/TypeConversions.cpp @@ -84,6 +84,7 @@ meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_User user.is_licensed = lite.is_licensed; memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr)); memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes)); + user.public_key.size = lite.public_key.size; return user; } \ No newline at end of file From 0e7253d309d75a048abb59459d8dd5b037673727 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 12 Aug 2024 19:37:39 -0500 Subject: [PATCH 26/38] Protos --- protobufs | 2 +- src/mesh/generated/meshtastic/mesh.pb.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/protobufs b/protobufs index 8063f8026..6307b44cd 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 8063f80260a5af438d5b68c6587b4fed77d6c46d +Subproject commit 6307b44cd98ef1d86d5f39edefe9361e79d9ece4 diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 70423f673..3800e6c0c 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -300,7 +300,9 @@ typedef enum _meshtastic_Routing_Error { meshtastic_Routing_Error_BAD_REQUEST = 32, /* The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) */ - meshtastic_Routing_Error_NOT_AUTHORIZED = 33 + meshtastic_Routing_Error_NOT_AUTHORIZED = 33, + /* This message is not a failure, and indicates that the message was sent via PKI */ + meshtastic_Routing_Error_NONE_PKI = 34 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -993,8 +995,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NOT_AUTHORIZED -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NOT_AUTHORIZED+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NONE_PKI +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NONE_PKI+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX From b4cbea1b3d63c759eb771c1f752b88e4dca66661 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:29:54 -0500 Subject: [PATCH 27/38] Move security migrate to if has_security --- src/mesh/NodeDB.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ef649288e..521a9d6d7 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -124,6 +124,12 @@ NodeDB::NodeDB() // Include our owner in the node db under our nodenum meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum()); + if (!config.has_security) { + config.has_security = true; + config.security.serial_enabled = config.device.serial_enabled; + config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; + config.security.is_managed = config.device.is_managed; + } #if !(MESHTASTIC_EXCLUDE_PKI) // Calculate Curve25519 public and private keys printBytes("Old Pubkey", config.security.public_key.bytes, 32); From c16f20de21cded1d56ea9992b86ebe9b2ce3c816 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:30:34 -0500 Subject: [PATCH 28/38] Make "Alloc an error" a LOG_WARN --- src/mesh/MeshModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 604ac9dc4..3fe4c85d5 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -54,8 +54,8 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; - if (err != meshtastic_Routing_Error_NONE) - LOG_ERROR("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); + if (err != meshtastic_Routing_Error_NONE && err != meshtastic_Routing_Error_NONE_PKI) + LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); return p; } From 754db3f2bcff99e38aea1443934bc64416549e9a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:32:56 -0500 Subject: [PATCH 29/38] Finish fixing config migrate --- src/mesh/NodeDB.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 521a9d6d7..7f403c53a 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -140,10 +140,6 @@ NodeDB::NodeDB() crypto->setDHPrivateKey(config.security.private_key.bytes); } else { #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) - config.has_security = true; - config.security.serial_enabled = config.device.serial_enabled; - config.security.bluetooth_logging_enabled = config.bluetooth.device_logging_enabled; - config.security.is_managed = config.device.is_managed; LOG_INFO("Generating new PKI keys\n"); crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); config.security.public_key.size = 32; From 308c0a6bb8e5f4366e079b9c767af049b4263ef5 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 12 Aug 2024 23:39:45 -0500 Subject: [PATCH 30/38] Add Routing_Error_NONE --- src/mesh/MeshService.cpp | 3 ++- src/mesh/MeshTypes.h | 2 +- src/mesh/ReliableRouter.cpp | 23 ++++++++++------------- src/mesh/Router.cpp | 8 +++++--- src/modules/CannedMessageModule.cpp | 3 ++- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index ac97d51a7..f22949576 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -253,7 +253,8 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh LOG_DEBUG("Can't send status to phone"); } - if (res == ERRNO_OK && ccToPhone) { // Check if p is not released in case it couldn't be sent + if ((res == ERRNO_OK || res == meshtastic_Routing_Error_NONE_PKI) && + ccToPhone) { // Check if p is not released in case it couldn't be sent sendToPhone(packetPool.allocCopy(*p)); } } diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index c0919bf5d..ecac626a8 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -15,7 +15,7 @@ typedef uint32_t PacketId; // A packet sequence number #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -#define ERRNO_DISABLED 34 // the interface is disabled +#define ERRNO_DISABLED 35 // the interface is disabled /* * Source of a received message diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index c91ce50c5..6b1cf92e7 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -117,20 +117,17 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error - PacketId ackId = ((c && c->error_reason == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; + if ((c && (c->error_reason == meshtastic_Routing_Error_NONE || c->error_reason == meshtastic_Routing_Error_NONE_PKI)) || + !c) { + LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", p->decoded.request_id); + stopRetransmission(p->to, p->decoded.request_id); + // } else if (c && (c->error_reason == meshtastic_Routing_Error_NO_CHANNEL)) { + // noop? + } else if (c && + (c->error_reason != meshtastic_Routing_Error_NONE && c->error_reason != meshtastic_Routing_Error_NONE_PKI)) { - // A nak is a routing packt that has an error code - PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0; - - // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records - if (ackId || nakId) { - if (ackId) { - LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId); - stopRetransmission(p->to, ackId); - } else { - LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId); - stopRetransmission(p->to, nakId); - } + LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", p->decoded.request_id); + stopRetransmission(p->to, p->decoded.request_id); } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 945f92bb7..64fa4cca2 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -258,7 +258,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p); auto encodeResult = perhapsEncode(p); - if (encodeResult != meshtastic_Routing_Error_NONE) { + if (encodeResult != meshtastic_Routing_Error_NONE && encodeResult != meshtastic_Routing_Error_NONE_PKI) { packetPool.release(p_decoded); abortSendAndNak(encodeResult, p); return encodeResult; // FIXME - this isn't a valid ErrorCode @@ -493,8 +493,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } - - return meshtastic_Routing_Error_NONE; + if (p->pki_encrypted) + return meshtastic_Routing_Error_NONE_PKI; + else + return meshtastic_Routing_Error_NONE; } NodeNum Router::getNodeNum() diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 4df5a03fc..2ec914668 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -1105,7 +1105,8 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket & this->incoming = service->getNodenumFromRequestId(mp.decoded.request_id); meshtastic_Routing decoded = meshtastic_Routing_init_default; pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); - this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; + this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE || + decoded.error_reason == meshtastic_Routing_Error_NONE_PKI; waitingForAck = false; // No longer want routing packets this->notifyObservers(&e); // run the next time 2 seconds later From bcd77c45232c2b05c63f7f646452ef53182e7dad Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 06:31:05 -0500 Subject: [PATCH 31/38] Cleanup public_keys (#4450) --- src/mesh/NodeDB.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 7f403c53a..1caaaf39b 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -554,10 +554,21 @@ void NodeDB::cleanupMeshDB() { int newPos = 0, removed = 0; for (int i = 0; i < numMeshNodes; i++) { - if (meshNodes->at(i).has_user) + if (meshNodes->at(i).has_user) { + if (meshNodes->at(i).user.public_key.size > 0) { + for (int j = 0; j < numMeshNodes; j++) { + if (meshNodes->at(i).user.public_key.bytes[j] != 0) { + break; + } + if (j == 31) { + meshNodes->at(i).user.public_key.size = 0; + } + } + } meshNodes->at(newPos++) = meshNodes->at(i); - else + } else { removed++; + } } numMeshNodes -= removed; std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + removed, @@ -1166,4 +1177,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co LOG_ERROR("A critical failure occurred, portduino is exiting..."); exit(2); #endif -} +} \ No newline at end of file From f3fa8daedf9da40ca9e2f4c92880e349da5fe36a Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 09:15:20 -0500 Subject: [PATCH 32/38] Revert "Add Routing_Error_NONE" This reverts commit e1985fa0f90db0cbc346db18cecbf3604f6973c1. --- src/mesh/MeshService.cpp | 3 +-- src/mesh/MeshTypes.h | 2 +- src/mesh/ReliableRouter.cpp | 23 +++++++++++++---------- src/mesh/Router.cpp | 8 +++----- src/modules/CannedMessageModule.cpp | 3 +-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index f22949576..ac97d51a7 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -253,8 +253,7 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh LOG_DEBUG("Can't send status to phone"); } - if ((res == ERRNO_OK || res == meshtastic_Routing_Error_NONE_PKI) && - ccToPhone) { // Check if p is not released in case it couldn't be sent + if (res == ERRNO_OK && ccToPhone) { // Check if p is not released in case it couldn't be sent sendToPhone(packetPool.allocCopy(*p)); } } diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index ecac626a8..c0919bf5d 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -15,7 +15,7 @@ typedef uint32_t PacketId; // A packet sequence number #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -#define ERRNO_DISABLED 35 // the interface is disabled +#define ERRNO_DISABLED 34 // the interface is disabled /* * Source of a received message diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 6b1cf92e7..c91ce50c5 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -117,17 +117,20 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error - if ((c && (c->error_reason == meshtastic_Routing_Error_NONE || c->error_reason == meshtastic_Routing_Error_NONE_PKI)) || - !c) { - LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", p->decoded.request_id); - stopRetransmission(p->to, p->decoded.request_id); - // } else if (c && (c->error_reason == meshtastic_Routing_Error_NO_CHANNEL)) { - // noop? - } else if (c && - (c->error_reason != meshtastic_Routing_Error_NONE && c->error_reason != meshtastic_Routing_Error_NONE_PKI)) { + PacketId ackId = ((c && c->error_reason == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; - LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", p->decoded.request_id); - stopRetransmission(p->to, p->decoded.request_id); + // A nak is a routing packt that has an error code + PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0; + + // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records + if (ackId || nakId) { + if (ackId) { + LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId); + stopRetransmission(p->to, ackId); + } else { + LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId); + stopRetransmission(p->to, nakId); + } } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 64fa4cca2..945f92bb7 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -258,7 +258,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p) meshtastic_MeshPacket *p_decoded = packetPool.allocCopy(*p); auto encodeResult = perhapsEncode(p); - if (encodeResult != meshtastic_Routing_Error_NONE && encodeResult != meshtastic_Routing_Error_NONE_PKI) { + if (encodeResult != meshtastic_Routing_Error_NONE) { packetPool.release(p_decoded); abortSendAndNak(encodeResult, p); return encodeResult; // FIXME - this isn't a valid ErrorCode @@ -493,10 +493,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) p->encrypted.size = numbytes; p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag; } - if (p->pki_encrypted) - return meshtastic_Routing_Error_NONE_PKI; - else - return meshtastic_Routing_Error_NONE; + + return meshtastic_Routing_Error_NONE; } NodeNum Router::getNodeNum() diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 2ec914668..4df5a03fc 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -1105,8 +1105,7 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket & this->incoming = service->getNodenumFromRequestId(mp.decoded.request_id); meshtastic_Routing decoded = meshtastic_Routing_init_default; pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); - this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE || - decoded.error_reason == meshtastic_Routing_Error_NONE_PKI; + this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; waitingForAck = false; // No longer want routing packets this->notifyObservers(&e); // run the next time 2 seconds later From 80fd121d87eac1dbb447828ada7c4a5122795832 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 12:10:46 -0500 Subject: [PATCH 33/38] Add meshtastic_Routing_Error_NO_CHANNEL --- src/mesh/MeshModule.cpp | 2 +- src/mesh/Router.cpp | 20 +++++++++++++++++--- src/mesh/generated/meshtastic/mesh.pb.h | 8 ++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index 3fe4c85d5..3b137d4bd 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -54,7 +54,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod p->to = to; p->decoded.request_id = idFrom; p->channel = chIndex; - if (err != meshtastic_Routing_Error_NONE && err != meshtastic_Routing_Error_NONE_PKI) + if (err != meshtastic_Routing_Error_NONE) LOG_WARN("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id); return p; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 945f92bb7..832d7b91f 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -461,9 +461,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it auto hash = channels.setActiveByIndex(chIndex); - if (hash < 0) - // No suitable channel could be found for sending - return meshtastic_Routing_Error_NO_CHANNEL; // Now that we are encrypting the packet channel should be the hash (no longer the index) p->channel = hash; @@ -480,11 +477,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) numbytes += 8; memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); p->channel = 0; + p->pki_encrypted = true; } else { + if (p->pki_encrypted == true) { + // Client specifically requested PKI encryption + return meshtastic_Routing_Error_PKI_FAILED; + } + if (hash < 0) { + // No suitable channel could be found for sending + return meshtastic_Routing_Error_NO_CHANNEL; + } crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); } #else + if (p->pki_encrypted == true) { + // Client specifically requested PKI encryption + return meshtastic_Routing_Error_PKI_FAILED; + } + if (hash < 0) { + // No suitable channel could be found for sending + return meshtastic_Routing_Error_NO_CHANNEL; + } crypto->encryptPacket(getFrom(p), p->id, numbytes, bytes); memcpy(p->encrypted.bytes, bytes, numbytes); #endif diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 3800e6c0c..7625fbf92 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -301,8 +301,8 @@ typedef enum _meshtastic_Routing_Error { /* The application layer service on the remote node received your request, but considered your request not authorized (i.e you did not send the request on the required bound channel) */ meshtastic_Routing_Error_NOT_AUTHORIZED = 33, - /* This message is not a failure, and indicates that the message was sent via PKI */ - meshtastic_Routing_Error_NONE_PKI = 34 + /* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */ + meshtastic_Routing_Error_PKI_FAILED = 34 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -995,8 +995,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_NONE_PKI -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_NONE_PKI+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_FAILED +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_FAILED+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX From ff89dca5b39db03defbbfe4fd9e2ca5d6b9fe9b0 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 14:50:35 -0500 Subject: [PATCH 34/38] Add PKI indicator to printPacket --- src/mesh/RadioInterface.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 262d2d6a9..968b9d251 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -283,6 +283,9 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p) if (s.want_response) out += DEBUG_PORT.mt_sprintf(" WANTRESP"); + if (p->pki_encrypted) + out += DEBUG_PORT.mt_sprintf(" PKI"); + if (s.source != 0) out += DEBUG_PORT.mt_sprintf(" source=%08x", s.source); From b528290fde246205d7888c9d276d66f0d8a4403d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 17:16:40 -0500 Subject: [PATCH 35/38] Failure returns PKI_FAILED message if client requested PKI --- src/mesh/Router.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 832d7b91f..015c2c809 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -415,6 +415,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) { concurrency::LockGuard g(cryptLock); + int16_t hash; + // If the packet is not yet encrypted, do so now if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded); @@ -460,19 +462,20 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // printBytes("plaintext", bytes, numbytes); ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it - auto hash = channels.setActiveByIndex(chIndex); - // Now that we are encrypting the packet channel should be the hash (no longer the index) - p->channel = hash; #if !(MESHTASTIC_EXCLUDE_PKI) meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to); - if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 && - numbytes <= MAX_RHPACKETLEN - 8 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && + if (!owner.is_licensed && config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr && + node->user.public_key.size > 0 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) { LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; + if (memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { + LOG_WARN("Client public key for client differs from requested!\n"); + return meshtastic_Routing_Error_PKI_FAILED; + } crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); numbytes += 8; memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes); @@ -483,6 +486,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Client specifically requested PKI encryption return meshtastic_Routing_Error_PKI_FAILED; } + hash = channels.setActiveByIndex(chIndex); + + // Now that we are encrypting the packet channel should be the hash (no longer the index) + p->channel = hash; if (hash < 0) { // No suitable channel could be found for sending return meshtastic_Routing_Error_NO_CHANNEL; @@ -495,6 +502,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // Client specifically requested PKI encryption return meshtastic_Routing_Error_PKI_FAILED; } + hash = channels.setActiveByIndex(chIndex); + + // Now that we are encrypting the packet channel should be the hash (no longer the index) + p->channel = hash; if (hash < 0) { // No suitable channel could be found for sending return meshtastic_Routing_Error_NO_CHANNEL; From 2661fc694f21c0692bc7d18f0b3e3d0ed5f4765d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 20:06:36 -0500 Subject: [PATCH 36/38] sync protobufs --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index 6307b44cd..97fa34517 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 6307b44cd98ef1d86d5f39edefe9361e79d9ece4 +Subproject commit 97fa34517f80332a11046a73f26d55100fbee9e2 From 8ce1c07c4ea36bc53ede1c77219fe5d5fc4e3e61 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 13 Aug 2024 22:34:21 -0500 Subject: [PATCH 37/38] Check for blank key coming from client --- src/mesh/Router.cpp | 6 ++++-- src/meshUtils.cpp | 9 +++++++++ src/meshUtils.h | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 015c2c809..a96773042 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -472,8 +472,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) LOG_DEBUG("Using PKI!\n"); if (numbytes + 8 > MAX_RHPACKETLEN) return meshtastic_Routing_Error_TOO_LARGE; - if (memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { - LOG_WARN("Client public key for client differs from requested!\n"); + if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) && + memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) { + LOG_WARN("Client public key for client differs from requested! Requested 0x%02x, but stored key begins 0x%02x\n", + *p->public_key.bytes, *node->user.public_key.bytes); return meshtastic_Routing_Error_PKI_FAILED; } crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted); diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp index 86d237129..99fcd2a57 100644 --- a/src/meshUtils.cpp +++ b/src/meshUtils.cpp @@ -64,4 +64,13 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes) for (size_t i = 0; i < numbytes; i++) LOG_DEBUG("%02x ", p[i]); LOG_DEBUG("\n"); +} + +bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) +{ + for (int i = 0; i < numbytes; i++) { + if (mem[i] != find) + return false; + } + return true; } \ No newline at end of file diff --git a/src/meshUtils.h b/src/meshUtils.h index e2d4188d7..ce063cb6a 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -14,4 +14,7 @@ template constexpr const T &clamp(const T &v, const T &lo, const T &hi char *strnstr(const char *s, const char *find, size_t slen); #endif -void printBytes(const char *label, const uint8_t *p, size_t numbytes); \ No newline at end of file +void printBytes(const char *label, const uint8_t *p, size_t numbytes); + +// is the memory region filled with a single character? +bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes); \ No newline at end of file From 8ef72a5c080086d6ced5bd276ea1a098b16c62e3 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 14 Aug 2024 17:17:53 -0500 Subject: [PATCH 38/38] Shorter nodeinfo timeout redux (#4458) * Add shorterTimeout bool to sendOurNodeInfo * Respond to likely PKI decode errors with a quick nodeinfo * Protbufs * Move to PKI_UNKNOWN_PUBKEY for PKI decode error --- protobufs | 2 +- src/mesh/ReliableRouter.cpp | 14 +++++++++++++- src/mesh/generated/meshtastic/mesh.pb.h | 8 +++++--- src/modules/NodeInfoModule.cpp | 16 ++++++++++++---- src/modules/NodeInfoModule.h | 4 +++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/protobufs b/protobufs index 97fa34517..8b5b2faf6 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 97fa34517f80332a11046a73f26d55100fbee9e2 +Subproject commit 8b5b2faf662b364754809f923271022f4f1492ed diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index c91ce50c5..0c9180eb5 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -4,6 +4,7 @@ #include "MeshTypes.h" #include "configuration.h" #include "mesh-pb-constants.h" +#include "modules/NodeInfoModule.h" // ReliableRouter::ReliableRouter() {} @@ -109,13 +110,24 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n"); } else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit); + } else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 && + (nodeDB->getMeshNode(p->from) != nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { + // This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY + sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), + p->hop_start, p->hop_limit); } else { // Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start, p->hop_limit); } } - + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c && + c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) { + if (owner.public_key.size == 32) { + LOG_INFO("This seems like a remote PKI decrypt failure, so send a NodeInfo"); + nodeInfoModule->sendOurNodeInfo(p->from, false, p->channel, true); + } + } // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error PacketId ackId = ((c && c->error_reason == meshtastic_Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 7625fbf92..1f0621f9a 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -302,7 +302,9 @@ typedef enum _meshtastic_Routing_Error { (i.e you did not send the request on the required bound channel) */ meshtastic_Routing_Error_NOT_AUTHORIZED = 33, /* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */ - meshtastic_Routing_Error_PKI_FAILED = 34 + meshtastic_Routing_Error_PKI_FAILED = 34, + /* The receiving node does not have a Public Key to decode with */ + meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35 } meshtastic_Routing_Error; /* The priority of this message for sending. @@ -995,8 +997,8 @@ extern "C" { #define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1)) #define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE -#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_FAILED -#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_FAILED+1)) +#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY +#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY+1)) #define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET #define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 62cf9d2a1..cb047a4dc 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -32,19 +32,22 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes return false; // Let others look at this message also if they want } -void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel) +void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel, bool _shorterTimeout) { // cancel any not yet sent (now stale) position packets if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal) service->cancelSending(prevPacketId); - + shorterTimeout = _shorterTimeout; meshtastic_MeshPacket *p = allocReply(); if (p) { // Check whether we didn't ignore it p->to = dest; p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER && config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && wantReplies; - p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; + if (_shorterTimeout) + p->priority = meshtastic_MeshPacket_Priority_DEFAULT; + else + p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; if (channel > 0) { LOG_DEBUG("sending ourNodeInfo to channel %d\n", channel); p->channel = channel; @@ -53,6 +56,7 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha prevPacketId = p->id; service->sendToMesh(p); + shorterTimeout = false; } } @@ -65,10 +69,14 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply() } uint32_t now = millis(); // If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway. - if (lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { + if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n"); ignoreRequest = true; // Mark it as ignored for MeshModule return NULL; + } else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) { + LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n"); + ignoreRequest = true; // Mark it as ignored for MeshModule + return NULL; } else { ignoreRequest = false; // Don't ignore requests anymore meshtastic_User &u = owner; diff --git a/src/modules/NodeInfoModule.h b/src/modules/NodeInfoModule.h index b10cccdf1..c1fb9ccce 100644 --- a/src/modules/NodeInfoModule.h +++ b/src/modules/NodeInfoModule.h @@ -20,7 +20,8 @@ class NodeInfoModule : public ProtobufModule, private concurren /** * Send our NodeInfo into the mesh */ - void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0); + void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0, + bool _shorterTimeout = false); protected: /** Called to handle a particular incoming message @@ -38,6 +39,7 @@ class NodeInfoModule : public ProtobufModule, private concurren private: uint32_t lastSentToMesh = 0; // Last time we sent our NodeInfo to the mesh + bool shorterTimeout = false; }; extern NodeInfoModule *nodeInfoModule;