diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index 58c1302da8..0dd6cbc1d7 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 f3e7c30368..503da2aab6 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 e60f0d7b93..5ad7d60a24 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/protobufs b/protobufs
index 666b481ae0..4eb4f42517 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 666b481ae02f1f88ec30f10a2d80184b31c0fc4e
+Subproject commit 4eb4f425170f08839abc6ececd13e8db30094ad5
diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h
index 7987e7fa14..55453ea1e7 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/DisplayFormatters.cpp b/src/DisplayFormatters.cpp
index f15052de6a..0718ffcbdf 100644
--- a/src/DisplayFormatters.cpp
+++ b/src/DisplayFormatters.cpp
@@ -3,6 +3,9 @@
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
{
switch (preset) {
+ case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
+ return useShortName ? "ShortT" : "ShortTurbo";
+ break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
return useShortName ? "ShortS" : "ShortSlow";
break;
diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp
index c2b0c55dbe..96cf851e4c 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.device.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
@@ -212,7 +213,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 d25b81da7b..b911e15dad 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 9148f1d377..2e0efffd49 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
@@ -280,6 +285,7 @@ along with this program. If not, see .
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
#define MESHTASTIC_EXCLUDE_SERIAL 1
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
+#define MESHTASTIC_EXCLUDE_ADMIN 1
#endif
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
diff --git a/src/main.cpp b/src/main.cpp
index 7f45905d15..b6af60d2cd 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 1e44cb9b7b..eef9f74b19 100644
--- a/src/mesh/CryptoEngine.cpp
+++ b/src/mesh/CryptoEngine.cpp
@@ -1,6 +1,191 @@
#include "CryptoEngine.h"
+#include "NodeDB.h"
+#include "RadioInterface.h"
+#include "architecture.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
+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;
+ uint32_t *extraNonce;
+ long extraNonceTmp = random();
+ auth = bytesOut + numBytes;
+ extraNonce = (uint32_t *)(auth + 8);
+ *extraNonce = extraNonceTmp;
+ LOG_INFO("Random nonce value: %d\n", *extraNonce);
+ 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, *extraNonce);
+
+ // Calculate the shared secret with the destination node and encrypt
+ printBytes("Attempting encrypt using nonce: ", nonce, 13);
+ printBytes("Attempting encrypt using shared_key: ", shared_key, 32);
+ aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut,
+ auth); // this can write up to 15 bytes longer than numbytes past bytesOut
+ *extraNonce = extraNonceTmp;
+ 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?
+ uint32_t *extraNonce;
+ auth = bytes + numBytes - 12;
+ extraNonce = (uint32_t *)(auth + 8);
+ LOG_INFO("Random nonce value: %d\n", *extraNonce);
+ 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, *extraNonce);
+ printBytes("Attempting decrypt using nonce: ", nonce, 13);
+ printBytes("Attempting decrypt using shared_key: ", shared_key, 32);
+ return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 12, nullptr, 0, auth, bytesOut);
+}
+
+void CryptoEngine::setDHPrivateKey(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;
+ }
+
+ if (!setDHPublicKey(node->user.public_key.bytes))
+ 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);
+}
+
+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;
+}
+
+#endif
concurrency::Lock *cryptLock;
void CryptoEngine::setKey(const CryptoKey &k)
@@ -14,24 +199,59 @@ 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);
}
/**
* Init our 128 bit nonce for a new packet
*/
-void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId)
+void CryptoEngine::initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce)
{
memset(nonce, 0, sizeof(nonce));
// 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
+ if (extraNonce)
+ memcpy(nonce + sizeof(uint32_t), &extraNonce, sizeof(uint32_t));
+}
+#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 2737dab2d9..64382f6a77 100644
--- a/src/mesh/CryptoEngine.h
+++ b/src/mesh/CryptoEngine.h
@@ -1,6 +1,9 @@
#pragma once
-
+#include "AES.h"
+#include "CTR.h"
#include "concurrency/LockGuard.h"
+#include "configuration.h"
+#include "mesh-pb-constants.h"
#include
extern concurrency::Lock *cryptLock;
@@ -21,14 +24,31 @@ struct CryptoKey {
class CryptoEngine
{
- protected:
- /** Our per packet nonce */
- uint8_t nonce[16] = {0};
-
- CryptoKey key = {};
-
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 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);
+ 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);
+
+ virtual void aesEncrypt(uint8_t *in, uint8_t *out);
+ AESSmall256 *aes = NULL;
+
+#endif
/**
* Set the key used for encrypt, decrypt.
@@ -46,10 +66,20 @@ 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};
+#endif
/**
* Init our 128 bit nonce for a new packet
*
@@ -58,7 +88,7 @@ class CryptoEngine
* a 32 bit sending node number (stored in little endian order)
* a 32 bit block counter (starts at zero)
*/
- void initNonce(uint32_t fromNode, uint64_t packetId);
+ void initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce = 0);
};
-extern CryptoEngine *crypto;
+extern CryptoEngine *crypto;
\ No newline at end of file
diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp
index 604ac9dc4f..3b137d4bda 100644
--- a/src/mesh/MeshModule.cpp
+++ b/src/mesh/MeshModule.cpp
@@ -55,7 +55,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod
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);
+ 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/MeshService.cpp b/src/mesh/MeshService.cpp
index 697644a4f5..ac97d51a71 100644
--- a/src/mesh/MeshService.cpp
+++ b/src/mesh/MeshService.cpp
@@ -48,14 +48,19 @@ 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 +329,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 528adb1379..ea1c4e345c 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/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 000da335fe..1caaaf39be 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,7 +124,38 @@ NodeDB::NodeDB()
// Include our owner in the node db under our nodenum
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
- info->user = owner;
+ 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);
+ 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->setDHPrivateKey(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 = TypeConversions::ConvertToUserLite(owner);
info->has_user = true;
#ifdef ARCH_ESP32
@@ -237,6 +269,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 +292,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 +323,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
@@ -512,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,
@@ -778,6 +831,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,23 +1011,38 @@ 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) {
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 (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
// 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 (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/%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) {
@@ -1042,19 +1111,32 @@ 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,
memGet.getFreeHeap());
// look for oldest node and erase it
uint32_t oldest = UINT32_MAX;
+ uint32_t oldestBoring = UINT32_MAX;
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++) {
@@ -1068,6 +1150,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 447ce10d4f..a71f3e134b 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 fc0099e87f..0a9bb5b108 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/PhoneAPI.h b/src/mesh/PhoneAPI.h
index 3c3668300a..5feb1c4bfb 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/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index 262d2d6a99..f0048dd3d2 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);
@@ -409,67 +412,93 @@ void RadioInterface::applyModemConfig()
// Set up default configuration
// No Sync Words in LORA mode
meshtastic_Config_LoRaConfig &loraConfig = config.lora;
- if (loraConfig.use_preset) {
-
- switch (loraConfig.modem_preset) {
- case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
- bw = (myRegion->wideLora) ? 812.5 : 250;
- cr = 5;
- sf = 7;
- break;
- case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
- bw = (myRegion->wideLora) ? 812.5 : 250;
- cr = 5;
- sf = 8;
- break;
- case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
- bw = (myRegion->wideLora) ? 812.5 : 250;
- cr = 5;
- sf = 9;
- break;
- case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
- bw = (myRegion->wideLora) ? 812.5 : 250;
- cr = 5;
- sf = 10;
- break;
- default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
- bw = (myRegion->wideLora) ? 812.5 : 250;
- cr = 5;
- sf = 11;
- break;
- case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
- bw = (myRegion->wideLora) ? 406.25 : 125;
- cr = 8;
- sf = 11;
- break;
- case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
- bw = (myRegion->wideLora) ? 406.25 : 125;
- cr = 8;
- sf = 12;
- break;
- case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
- bw = (myRegion->wideLora) ? 203.125 : 62.5;
- cr = 8;
- sf = 12;
- break;
+ bool validConfig = false; // We need to check for a valid configuration
+ while (!validConfig) {
+ if (loraConfig.use_preset) {
+
+ switch (loraConfig.modem_preset) {
+ case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
+ bw = (myRegion->wideLora) ? 812.5 : 500;
+ cr = 5;
+ sf = 7;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
+ bw = (myRegion->wideLora) ? 812.5 : 250;
+ cr = 5;
+ sf = 7;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
+ bw = (myRegion->wideLora) ? 812.5 : 250;
+ cr = 5;
+ sf = 8;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
+ bw = (myRegion->wideLora) ? 812.5 : 250;
+ cr = 5;
+ sf = 9;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
+ bw = (myRegion->wideLora) ? 812.5 : 250;
+ cr = 5;
+ sf = 10;
+ break;
+ default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
+ bw = (myRegion->wideLora) ? 812.5 : 250;
+ cr = 5;
+ sf = 11;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
+ bw = (myRegion->wideLora) ? 406.25 : 125;
+ cr = 8;
+ sf = 11;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
+ bw = (myRegion->wideLora) ? 406.25 : 125;
+ cr = 8;
+ sf = 12;
+ break;
+ case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
+ bw = (myRegion->wideLora) ? 203.125 : 62.5;
+ cr = 8;
+ sf = 12;
+ break;
+ }
+ } else {
+ sf = loraConfig.spread_factor;
+ cr = loraConfig.coding_rate;
+ bw = loraConfig.bandwidth;
+
+ if (bw == 31) // This parameter is not an integer
+ bw = 31.25;
+ if (bw == 62) // Fix for 62.5Khz bandwidth
+ bw = 62.5;
+ if (bw == 200)
+ bw = 203.125;
+ if (bw == 400)
+ bw = 406.25;
+ if (bw == 800)
+ bw = 812.5;
+ if (bw == 1600)
+ bw = 1625.0;
+ }
+
+ if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) {
+ static const char *err_string =
+ "Regional frequency range is smaller than bandwidth. Falling back to default preset.\n";
+ LOG_ERROR(err_string);
+ RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
+
+ meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
+ cn->level = meshtastic_LogRecord_Level_ERROR;
+ sprintf(cn->message, err_string);
+ service->sendClientNotification(cn);
+
+ // Set to default modem preset
+ loraConfig.use_preset = true;
+ loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
+ } else {
+ validConfig = true;
}
- } else {
- sf = loraConfig.spread_factor;
- cr = loraConfig.coding_rate;
- bw = loraConfig.bandwidth;
-
- if (bw == 31) // This parameter is not an integer
- bw = 31.25;
- if (bw == 62) // Fix for 62.5Khz bandwidth
- bw = 62.5;
- if (bw == 200)
- bw = 203.125;
- if (bw == 400)
- bw = 406.25;
- if (bw == 800)
- bw = 812.5;
- if (bw == 1600)
- bw = 1625.0;
}
power = loraConfig.tx_power;
@@ -512,6 +541,7 @@ void RadioInterface::applyModemConfig()
saveChannelNum(channel_num);
saveFreq(freq + loraConfig.frequency_offset);
+ slotTimeMsec = computeSlotTimeMsec(bw, sf);
preambleTimeMsec = getPacketTime((uint32_t)0);
maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader));
diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h
index b965328e46..f1016e3d87 100644
--- a/src/mesh/RadioInterface.h
+++ b/src/mesh/RadioInterface.h
@@ -5,6 +5,7 @@
#include "Observer.h"
#include "PointerQueue.h"
#include "airtime.h"
+#include "error.h"
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
@@ -71,18 +72,20 @@ class RadioInterface
- roundtrip air propagation time (assuming max. 30km between nodes);
- Tx/Rx turnaround time (maximum of SX126x and SX127x);
- MAC processing time (measured on T-beam) */
- uint32_t slotTimeMsec = 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7;
+ uint32_t slotTimeMsec = computeSlotTimeMsec(bw, sf);
uint16_t preambleLength = 16; // 8 is default, but we use longer to increase the amount of sleep time when receiving
uint32_t preambleTimeMsec = 165; // calculated on startup, this is the default for LongFast
uint32_t maxPacketTimeMsec = 3246; // calculated on startup, this is the default for LongFast
const uint32_t PROCESSING_TIME_MSEC =
4500; // time to construct, process and construct a packet again (empirically determined)
const uint8_t CWmin = 2; // minimum CWsize
- const uint8_t CWmax = 8; // maximum CWsize
+ const uint8_t CWmax = 7; // maximum CWsize
meshtastic_MeshPacket *sendingPacket = NULL; // The packet we are currently sending
uint32_t lastTxStart = 0L;
+ uint32_t computeSlotTimeMsec(float bw, float sf) { return 8.5 * pow(2, sf) / bw + 0.2 + 0.4 + 7; }
+
/**
* A temporary buffer used for sending/receiving packets, sized to hold the biggest buffer we might need
* */
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index dd01d2037f..edcbb394fb 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -61,11 +61,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
static void isrTxLevel0(), isrLevel0Common(PendingISR code);
- /**
- * Debugging counts
- */
- uint32_t rxBad = 0, rxGood = 0, txGood = 0;
-
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
protected:
@@ -109,6 +104,11 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
virtual void enableInterrupt(void (*)()) = 0;
+ /**
+ * Debugging counts
+ */
+ uint32_t rxBad = 0, rxGood = 0, txGood = 0;
+
public:
RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy, PhysicalLayer *iface = NULL);
diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp
index c91ce50c5b..4c8c1e1e76 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/Router.cpp b/src/mesh/Router.cpp
index db968ce515..bdd8c4e6c9 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"
@@ -36,6 +37,7 @@ static MemoryDynamic staticPool;
Allocator &packetPool = staticPool;
static uint8_t bytes[MAX_RHPACKETLEN];
+static uint8_t ScratchEncrypted[MAX_RHPACKETLEN];
/**
* Constructor
@@ -209,6 +211,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
@@ -299,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);
-
- // 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);
+ 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 && p->to != NODENUM_BROADCAST &&
+ nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 &&
+ nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 12) {
+ LOG_DEBUG("Attempting PKI decryption\n");
+
+ if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) {
+ LOG_INFO("PKI Decryption worked!\n");
+ memset(&p->decoded, 0, sizeof(p->decoded));
+ rawSize -= 12;
+ 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 {
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);
+ }
+ }
+#endif
- // 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 (bad psk?)!\n");
- } else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
- LOG_ERROR("Invalid portnum (bad psk?)!\n");
- } 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
+ // 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;
+ /* 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);
+ memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
- decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
+ decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
- // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
+ // LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
- memcpy(p->decoded.payload.bytes, decompressed_out, 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;
- } */
+ // 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);
+ printPacket("decoded message", p);
#if ENABLE_JSON_LOGGING
- LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
+ 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;
- }
+ 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;
}
-
- 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
@@ -371,12 +415,13 @@ 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);
/* 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) {
@@ -417,17 +462,61 @@ 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);
- if (hash < 0)
- // No suitable channel could be found for sending
- return meshtastic_Routing_Error_NO_CHANNEL;
+
+#if !(MESHTASTIC_EXCLUDE_PKI)
+ meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
+ 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 + 12 > MAX_RHPACKETLEN)
+ return meshtastic_Routing_Error_TOO_LARGE;
+ 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);
+ numbytes += 12;
+ 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;
+ }
+ 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;
+ }
+ 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;
+ }
+ hash = channels.setActiveByIndex(chIndex);
// Now that we are encrypting the packet channel should be the hash (no longer the index)
p->channel = hash;
- crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
+ 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
// 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 +628,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
// cache/learn of the existence of nodes (i.e. FloodRouter) that they should not
handleReceived(p);
packetPool.release(p);
-}
+}
\ No newline at end of file
diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp
index bcd600f242..d8ee6afc74 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,36 @@ 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));
+ lite.public_key.size = user.public_key.size;
+ 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));
+ user.public_key.size = lite.public_key.size;
+
+ return user;
}
\ No newline at end of file
diff --git a/src/mesh/TypeConversions.h b/src/mesh/TypeConversions.h
index ffc3c12a78..19e471f988 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/aes-ccm.cpp b/src/mesh/aes-ccm.cpp
new file mode 100644
index 0000000000..cd18ae6c57
--- /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 0000000000..6b8edcde49
--- /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/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h
index bef2abf9b1..99b8fd8c3d 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 */
@@ -85,6 +87,7 @@ typedef struct _meshtastic_NodeRemoteHardwarePinsResponse {
meshtastic_NodeRemoteHardwarePin node_remote_hardware_pins[16];
} meshtastic_NodeRemoteHardwarePinsResponse;
+typedef PB_BYTES_ARRAY_T(8) meshtastic_AdminMessage_session_passkey_t;
/* This message is handled by the Admin module and is responsible for all settings/channel read/write operations.
This message is used to do settings operations to both remote AND local nodes.
(Prior to 1.2 these operations were done via special ToRadio operations) */
@@ -163,6 +166,9 @@ typedef struct _meshtastic_AdminMessage {
meshtastic_Position set_fixed_position;
/* Clear fixed position coordinates and then set position.fixed_position = false */
bool remove_fixed_position;
+ /* Set time only on the node
+ Convenience method to set the time on the node (as Net quality) without any other position data */
+ uint32_t set_time_only;
/* Begins an edit transaction for config, module config, owner, and channel settings changes
This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) */
bool begin_edit_settings;
@@ -185,6 +191,10 @@ typedef struct _meshtastic_AdminMessage {
/* Tell the node to reset the nodedb. */
int32_t nodedb_reset;
};
+ /* The node generates this key and sends it with any get_x_response packets.
+ The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
+ Prevents replay attacks for admin messages. */
+ meshtastic_AdminMessage_session_passkey_t session_passkey;
} meshtastic_AdminMessage;
@@ -194,8 +204,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
@@ -208,10 +218,10 @@ extern "C" {
/* Initializer values for message structs */
-#define meshtastic_AdminMessage_init_default {0, {0}}
+#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
#define meshtastic_NodeRemoteHardwarePinsResponse_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, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
-#define meshtastic_AdminMessage_init_zero {0, {0}}
+#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
#define meshtastic_NodeRemoteHardwarePinsResponse_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, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
@@ -254,6 +264,7 @@ extern "C" {
#define meshtastic_AdminMessage_remove_favorite_node_tag 40
#define meshtastic_AdminMessage_set_fixed_position_tag 41
#define meshtastic_AdminMessage_remove_fixed_position_tag 42
+#define meshtastic_AdminMessage_set_time_only_tag 43
#define meshtastic_AdminMessage_begin_edit_settings_tag 64
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
#define meshtastic_AdminMessage_factory_reset_device_tag 94
@@ -263,6 +274,7 @@ extern "C" {
#define meshtastic_AdminMessage_shutdown_seconds_tag 98
#define meshtastic_AdminMessage_factory_reset_config_tag 99
#define meshtastic_AdminMessage_nodedb_reset_tag 100
+#define meshtastic_AdminMessage_session_passkey_tag 101
/* Struct field encoding specification for nanopb */
#define meshtastic_AdminMessage_FIELDLIST(X, a) \
@@ -299,6 +311,7 @@ X(a, STATIC, ONEOF, UINT32, (payload_variant,set_favorite_node,set_favori
X(a, STATIC, ONEOF, UINT32, (payload_variant,remove_favorite_node,remove_favorite_node), 40) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed_position), 41) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \
+X(a, STATIC, ONEOF, FIXED32, (payload_variant,set_time_only,set_time_only), 43) \
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) \
@@ -307,7 +320,8 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulato
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,nodedb_reset,nodedb_reset), 100)
+X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100) \
+X(a, STATIC, SINGULAR, BYTES, session_passkey, 101)
#define meshtastic_AdminMessage_CALLBACK NULL
#define meshtastic_AdminMessage_DEFAULT NULL
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
@@ -349,7 +363,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size
-#define meshtastic_AdminMessage_size 500
+#define meshtastic_AdminMessage_size 511
#define meshtastic_HamParameters_size 31
#define meshtastic_NodeRemoteHardwarePinsResponse_size 496
diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp
index bb82198c05..c6274aed41 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 44a86f4d64..dbb0deb001 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.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp
index 672192f672..2747ac9d94 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 eb37f4f957..343e5f48af 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 166
-#define meshtastic_OEMStore_size 3388
+#define meshtastic_NodeInfoLite_size 183
+#define meshtastic_OEMStore_size 3502
#define meshtastic_PositionLite_size 28
+#define meshtastic_UserLite_size 96
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h
index 983f48ad3f..c612b24abd 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 3fa81e1312..8c8b9ded72 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 955484cb09..f32f865db7 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -187,6 +187,8 @@ typedef enum _meshtastic_HardwareModel {
/* RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module
SSD1306 OLED and No GPS */
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74,
+ /* Minewsemi ME25LS01 (ME25LE01_V1.0). NRF52840 w/ LR1110 radio, buttons and leds and pins. */
+ meshtastic_HardwareModel_ME25LS01_4Y10TD = 75,
/* ------------------------------------------------------------------------------------------------------------------------------------------
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.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -300,7 +302,11 @@ 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,
+ /* 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,
+ /* 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.
@@ -374,10 +380,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 +402,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 +427,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 +452,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,13 +499,25 @@ 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) */
+/* A message used in a traceroute */
typedef struct _meshtastic_RouteDiscovery {
- /* The list of nodenums this packet has visited so far */
+ /* The list of nodenums this packet has visited so far to the destination. */
pb_size_t route_count;
uint32_t route[8];
+ /* The list of SNRs (in dB, scaled by 4) in the route towards the destination. */
+ pb_size_t snr_towards_count;
+ int8_t snr_towards[8];
+ /* The list of nodenums the packet has visited on the way back from the destination. */
+ pb_size_t route_back_count;
+ uint32_t route_back[8];
+ /* The list of SNRs (in dB, scaled by 4) in the route back from the destination. */
+ pb_size_t snr_back_count;
+ int8_t snr_back[8];
} meshtastic_RouteDiscovery;
/* A Routing control Data packet handled by the routing module */
@@ -546,8 +572,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 +607,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 +678,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 +771,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 +901,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;
@@ -957,8 +1008,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_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
@@ -994,6 +1045,8 @@ extern "C" {
+#define meshtastic_ClientNotification_level_ENUMTYPE meshtastic_LogRecord_Level
+
#define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum
@@ -1010,19 +1063,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_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
+#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}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 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 +1088,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_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
+#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}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 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,7 +1145,11 @@ 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_RouteDiscovery_snr_towards_tag 2
+#define meshtastic_RouteDiscovery_route_back_tag 3
+#define meshtastic_RouteDiscovery_snr_back_tag 4
#define meshtastic_Routing_route_request_tag 1
#define meshtastic_Routing_route_reply_tag 2
#define meshtastic_Routing_error_reason_tag 3
@@ -1129,6 +1188,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 +1211,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 +1252,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 +1273,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,12 +1306,16 @@ 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
#define meshtastic_RouteDiscovery_FIELDLIST(X, a) \
-X(a, STATIC, REPEATED, FIXED32, route, 1)
+X(a, STATIC, REPEATED, FIXED32, route, 1) \
+X(a, STATIC, REPEATED, INT32, snr_towards, 2) \
+X(a, STATIC, REPEATED, FIXED32, route_back, 3) \
+X(a, STATIC, REPEATED, INT32, snr_back, 4)
#define meshtastic_RouteDiscovery_CALLBACK NULL
#define meshtastic_RouteDiscovery_DEFAULT NULL
@@ -1272,8 +1342,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 +1375,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 +1437,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 +1453,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 +1567,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 +1594,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 +1612,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 +1620,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_RouteDiscovery_size 256
+#define meshtastic_Routing_size 259
#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/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h
index 7fd57fe006..2e1985660a 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.h
+++ b/src/mesh/generated/meshtastic/module_config.pb.h
@@ -341,7 +341,7 @@ typedef struct _meshtastic_ModuleConfig_CannedMessageConfig {
/* Enable/disable CannedMessageModule. */
bool enabled;
/* Input event origin accepted by the canned message module.
- Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any" */
+ Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any" */
char allow_input_source[16];
/* CannedMessageModule also sends a bell character with the messages.
ExternalNotificationModule can benefit from this feature. */
diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h
index 6cc82352ab..b9e537ddfb 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.h
+++ b/src/mesh/generated/meshtastic/portnums.pb.h
@@ -113,7 +113,7 @@ typedef enum _meshtastic_PortNum {
ENCODING: Protobuf (?) */
meshtastic_PortNum_SIMULATOR_APP = 69,
/* Provides a traceroute functionality to show the route a packet towards
- a certain destination would take on the mesh.
+ a certain destination would take on the mesh. Contains a RouteDiscovery message as payload.
ENCODING: Protobuf */
meshtastic_PortNum_TRACEROUTE_APP = 70,
/* Aggregates edge info for the network by sending out a list of each node's neighbors
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp
index c93483a152..90859c98e3 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.cpp
+++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp
@@ -18,6 +18,9 @@ PB_BIND(meshtastic_PowerMetrics, meshtastic_PowerMetrics, AUTO)
PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO)
+PB_BIND(meshtastic_LocalStats, meshtastic_LocalStats, AUTO)
+
+
PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO)
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index a4acd3f4a4..2d3eb407aa 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -76,101 +76,161 @@ 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;
+/* Local device mesh statistics */
+typedef struct _meshtastic_LocalStats {
+ /* How long the device has been running since the last reboot (in seconds) */
+ uint32_t uptime_seconds;
+ /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */
+ float channel_utilization;
+ /* Percent of airtime for transmission used within the last hour. */
+ float air_util_tx;
+ /* Number of packets sent */
+ uint32_t num_packets_tx;
+ /* Number of packets received good */
+ uint32_t num_packets_rx;
+ /* Number of packets received that are malformed or violate the protocol */
+ uint32_t num_packets_rx_bad;
+ /* Number of nodes online (in the past 2 hours) */
+ uint16_t num_online_nodes;
+ /* Number of nodes total */
+ uint16_t num_total_nodes;
+} meshtastic_LocalStats;
+
/* Types of Measurements the telemetry module is equipped to handle */
typedef struct _meshtastic_Telemetry {
/* Seconds since 1970 - or 0 for unknown/unset */
@@ -185,6 +245,8 @@ typedef struct _meshtastic_Telemetry {
meshtastic_AirQualityMetrics air_quality_metrics;
/* Power Metrics */
meshtastic_PowerMetrics power_metrics;
+ /* Local device mesh statistics */
+ meshtastic_LocalStats local_stats;
} variant;
} meshtastic_Telemetry;
@@ -213,17 +275,20 @@ 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_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 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_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
#define meshtastic_Nau7802Config_init_zero {0, 0}
@@ -268,83 +333,106 @@ extern "C" {
#define meshtastic_AirQualityMetrics_particles_25um_tag 10
#define meshtastic_AirQualityMetrics_particles_50um_tag 11
#define meshtastic_AirQualityMetrics_particles_100um_tag 12
+#define meshtastic_LocalStats_uptime_seconds_tag 1
+#define meshtastic_LocalStats_channel_utilization_tag 2
+#define meshtastic_LocalStats_air_util_tx_tag 3
+#define meshtastic_LocalStats_num_packets_tx_tag 4
+#define meshtastic_LocalStats_num_packets_rx_tag 5
+#define meshtastic_LocalStats_num_packets_rx_bad_tag 6
+#define meshtastic_LocalStats_num_online_nodes_tag 7
+#define meshtastic_LocalStats_num_total_nodes_tag 8
#define meshtastic_Telemetry_time_tag 1
#define meshtastic_Telemetry_device_metrics_tag 2
#define meshtastic_Telemetry_environment_metrics_tag 3
#define meshtastic_Telemetry_air_quality_metrics_tag 4
#define meshtastic_Telemetry_power_metrics_tag 5
+#define meshtastic_Telemetry_local_stats_tag 6
#define meshtastic_Nau7802Config_zeroOffset_tag 1
#define meshtastic_Nau7802Config_calibrationFactor_tag 2
/* 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
+#define meshtastic_LocalStats_FIELDLIST(X, a) \
+X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 1) \
+X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 2) \
+X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 3) \
+X(a, STATIC, SINGULAR, UINT32, num_packets_tx, 4) \
+X(a, STATIC, SINGULAR, UINT32, num_packets_rx, 5) \
+X(a, STATIC, SINGULAR, UINT32, num_packets_rx_bad, 6) \
+X(a, STATIC, SINGULAR, UINT32, num_online_nodes, 7) \
+X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8)
+#define meshtastic_LocalStats_CALLBACK NULL
+#define meshtastic_LocalStats_DEFAULT NULL
+
#define meshtastic_Telemetry_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FIXED32, time, 1) \
X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \
X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) \
X(a, STATIC, ONEOF, MESSAGE, (variant,air_quality_metrics,variant.air_quality_metrics), 4) \
-X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5)
+X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5) \
+X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6)
#define meshtastic_Telemetry_CALLBACK NULL
#define meshtastic_Telemetry_DEFAULT NULL
#define meshtastic_Telemetry_variant_device_metrics_MSGTYPE meshtastic_DeviceMetrics
#define meshtastic_Telemetry_variant_environment_metrics_MSGTYPE meshtastic_EnvironmentMetrics
#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics
#define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics
+#define meshtastic_Telemetry_variant_local_stats_MSGTYPE meshtastic_LocalStats
#define meshtastic_Nau7802Config_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \
@@ -356,6 +444,7 @@ extern const pb_msgdesc_t meshtastic_DeviceMetrics_msg;
extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg;
extern const pb_msgdesc_t meshtastic_PowerMetrics_msg;
extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg;
+extern const pb_msgdesc_t meshtastic_LocalStats_msg;
extern const pb_msgdesc_t meshtastic_Telemetry_msg;
extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
@@ -364,6 +453,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_EnvironmentMetrics_fields &meshtastic_EnvironmentMetrics_msg
#define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg
#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg
+#define meshtastic_LocalStats_fields &meshtastic_LocalStats_msg
#define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg
#define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg
@@ -372,6 +462,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_AirQualityMetrics_size 72
#define meshtastic_DeviceMetrics_size 27
#define meshtastic_EnvironmentMetrics_size 85
+#define meshtastic_LocalStats_size 42
#define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 30
#define meshtastic_Telemetry_size 92
diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp
index 86d2371291..99fcd2a57f 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 9dfe9b5583..ce063cb6a8 100644
--- a/src/meshUtils.h
+++ b/src/meshUtils.h
@@ -12,4 +12,9 @@ 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);
+
+// 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
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index cf22470315..81d595d29e 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -3,6 +3,8 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
+#include "RTC.h"
+#include "meshUtils.h"
#include
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "BleOta.h"
@@ -65,7 +67,28 @@ 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 public_key we went an AdminMessage request to
+ // and only allowing responses from that remote.
+ if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) ||
+ (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);
+
+ // all of the get and set messages, including those for other modules, flow through here first.
+ // any message that changes state, we want to check the passkey for
+ if (mp.from != 0 && !messageIsRequest(r) && !messageIsResponse(r)) {
+ if (!checkPassKey(r)) {
+ LOG_WARN("Admin message without session_key!\n");
+ return handled;
+ }
+ }
switch (r->which_payload_variant) {
/**
@@ -257,6 +280,15 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
break;
}
+ case meshtastic_AdminMessage_set_time_only_tag: {
+ LOG_INFO("Client is receiving a set_time_only command.\n");
+ struct timeval tv;
+ tv.tv_sec = r->set_time_only;
+ tv.tv_usec = 0;
+
+ perhapsSetRTC(RTCQualityFromNet, &tv, false);
+ break;
+ }
case meshtastic_AdminMessage_enter_dfu_mode_request_tag: {
LOG_INFO("Client is requesting to enter DFU mode.\n");
#if defined(ARCH_NRF52) || defined(ARCH_RP2040)
@@ -287,6 +319,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
AdminMessageHandleResult handleResult = MeshModule::handleAdminMessageForAllModules(mp, r, &res);
if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE) {
+ setPassKey(&res);
myReply = allocDataProtobuf(res);
} else if (mp.decoded.want_response) {
LOG_DEBUG("We did not responded to a request that wanted a respond. req.variant=%d\n", r->which_payload_variant);
@@ -383,8 +416,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 +532,19 @@ 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 !MESHTASTIC_EXCLUDE_PKI
+ crypto->setDHPrivateKey(config.security.private_key.bytes);
+#endif
+ 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);
@@ -602,6 +646,7 @@ void AdminModule::handleGetOwner(const meshtastic_MeshPacket &req)
res.get_owner_response = owner;
res.which_payload_variant = meshtastic_AdminMessage_get_owner_response_tag;
+ setPassKey(&res);
myReply = allocDataProtobuf(res);
}
}
@@ -649,6 +694,11 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32
res.get_config_response.which_payload_variant = meshtastic_Config_bluetooth_tag;
res.get_config_response.payload_variant.bluetooth = config.bluetooth;
break;
+ case meshtastic_AdminMessage_ConfigType_SECURITY_CONFIG:
+ LOG_INFO("Getting config: Security\n");
+ res.get_config_response.which_payload_variant = meshtastic_Config_security_tag;
+ res.get_config_response.payload_variant.security = config.security;
+ break;
}
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
@@ -658,6 +708,7 @@ void AdminModule::handleGetConfig(const meshtastic_MeshPacket &req, const uint32
// and useful for users to know current provisioning) hideSecret(r.get_radio_response.preferences.wifi_password);
// r.get_config_response.which_payloadVariant = Config_ModuleConfig_telemetry_tag;
res.which_payload_variant = meshtastic_AdminMessage_get_config_response_tag;
+ setPassKey(&res);
myReply = allocDataProtobuf(res);
}
}
@@ -743,6 +794,7 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
// and useful for users to know current provisioning) hideSecret(r.get_radio_response.preferences.wifi_password);
// r.get_config_response.which_payloadVariant = Config_ModuleConfig_telemetry_tag;
res.which_payload_variant = meshtastic_AdminMessage_get_module_config_response_tag;
+ setPassKey(&res);
myReply = allocDataProtobuf(res);
}
}
@@ -766,6 +818,7 @@ void AdminModule::handleGetNodeRemoteHardwarePins(const meshtastic_MeshPacket &r
nodePin.pin = moduleConfig.remote_hardware.available_pins[i];
r.get_node_remote_hardware_pins_response.node_remote_hardware_pins[i + 12] = nodePin;
}
+ setPassKey(&r);
myReply = allocDataProtobuf(r);
}
@@ -774,6 +827,7 @@ void AdminModule::handleGetDeviceMetadata(const meshtastic_MeshPacket &req)
meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default;
r.get_device_metadata_response = getDeviceMetadata();
r.which_payload_variant = meshtastic_AdminMessage_get_device_metadata_response_tag;
+ setPassKey(&r);
myReply = allocDataProtobuf(r);
}
@@ -837,6 +891,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
r.get_device_connection_status_response = conn;
r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag;
+ setPassKey(&r);
myReply = allocDataProtobuf(r);
}
@@ -847,6 +902,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
meshtastic_AdminMessage r = meshtastic_AdminMessage_init_default;
r.get_channel_response = channels.getByIndex(channelIndex);
r.which_payload_variant = meshtastic_AdminMessage_get_channel_response_tag;
+ setPassKey(&r);
myReply = allocDataProtobuf(r);
}
}
@@ -900,5 +956,61 @@ 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;
+}
+
+void AdminModule::setPassKey(meshtastic_AdminMessage *res)
+{
+ if (session_time == 0 || millis() / 1000 > session_time + 150) {
+ for (int i = 0; i < 8; i++) {
+ session_passkey[i] = random();
+ }
+ session_time = millis() / 1000;
+ }
+ memcpy(res->session_passkey.bytes, session_passkey, 8);
+ res->session_passkey.size = 8;
+ printBytes("Setting admin key to ", res->session_passkey.bytes, 8);
+ // if halfway to session_expire, regenerate session_passkey, reset the timeout
+ // set the key in the packet
+}
+
+bool AdminModule::checkPassKey(meshtastic_AdminMessage *res)
+{ // check that the key in the packet is still valid
+ printBytes("Incoming session key: ", res->session_passkey.bytes, 8);
+ printBytes("Expected session key: ", session_passkey, 8);
+ return (session_time + 300 > millis() / 1000 && res->session_passkey.size == 8 &&
+ memcmp(res->session_passkey.bytes, session_passkey, 8) == 0);
+}
+
+bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r)
+{
+ if (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)
+ return true;
+ else
+ return false;
+}
+
+bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r)
+{
+ if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_owner_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_config_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_module_config_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag ||
+ r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag)
+ return true;
+ else
+ return false;
}
\ No newline at end of file
diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h
index a5ffeb7d60..61c54d1b15 100644
--- a/src/modules/AdminModule.h
+++ b/src/modules/AdminModule.h
@@ -25,6 +25,9 @@ class AdminModule : public ProtobufModule, public Obser
private:
bool hasOpenEditTransaction = false;
+ uint8_t session_passkey[8] = {0};
+ uint session_time = 0;
+
void saveChanges(int saveWhat, bool shouldReboot = true);
/**
@@ -48,6 +51,12 @@ class AdminModule : public ProtobufModule, public Obser
void handleSetChannel();
void handleSetHamMode(const meshtastic_HamParameters &req);
void reboot(int32_t seconds);
+
+ void setPassKey(meshtastic_AdminMessage *res);
+ bool checkPassKey(meshtastic_AdminMessage *res);
+
+ bool messageIsResponse(meshtastic_AdminMessage *r);
+ bool messageIsRequest(meshtastic_AdminMessage *r);
};
extern AdminModule *adminModule;
\ No newline at end of file
diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp
index 62cf9d2a1f..cb047a4dc0 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 b10cccdf1d..c1fb9cccec 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;
diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp
index 87015032db..b7be4abc9f 100644
--- a/src/modules/RoutingModule.cpp
+++ b/src/modules/RoutingModule.cpp
@@ -13,6 +13,19 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
+ bool maybePKI =
+ mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag && mp.channel == 0 && mp.to != NODENUM_BROADCAST;
+ // Beginning of logic whether to drop the packet based on Rebroadcast mode
+ if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
+ (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY ||
+ config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY)) {
+ if (!maybePKI)
+ return false;
+ if ((nodeDB->getMeshNode(mp.from) == NULL || !nodeDB->getMeshNode(mp.from)->has_user) &&
+ (nodeDB->getMeshNode(mp.to) == NULL || !nodeDB->getMeshNode(mp.to)->has_user))
+ return false;
+ }
+
// FIXME - move this to a non promsicious PhoneAPI module?
// Note: we are careful not to send back packets that started with the phone back to the phone
if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB->getNodeNum()) && (mp.from != 0)) {
@@ -65,6 +78,9 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{
isPromiscuous = true;
- encryptedOk = config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY &&
- config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY;
+
+ // moved the ReboradcastMode logic into handleReceivedProtobuf
+ // LocalOnly requires either the from or to to be a known node
+ // knownOnly specifically requires the from to be a known node.
+ encryptedOk = true;
}
\ No newline at end of file
diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp
index 1104e6c4a4..f22685d43c 100644
--- a/src/modules/Telemetry/DeviceTelemetry.cpp
+++ b/src/modules/Telemetry/DeviceTelemetry.cpp
@@ -5,6 +5,7 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RTC.h"
+#include "RadioLibInterface.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
@@ -31,6 +32,10 @@ int32_t DeviceTelemetryModule::runOnce()
// Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true);
+ if (lastSentStatsToPhone == 0 || (uptimeLastMs - lastSentStatsToPhone) >= sendStatsToPhoneIntervalMs) {
+ sendLocalStatsToPhone();
+ lastSentStatsToPhone = uptimeLastMs;
+ }
}
return sendToPhoneIntervalMs;
}
@@ -84,6 +89,13 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
t.time = getTime();
+ t.variant.device_metrics = meshtastic_DeviceMetrics_init_zero;
+ t.variant.device_metrics.has_air_util_tx = true;
+ t.variant.device_metrics.has_battery_level = true;
+ t.variant.device_metrics.has_channel_utilization = true;
+ t.variant.device_metrics.has_voltage = true;
+ t.variant.device_metrics.has_uptime_seconds = true;
+
t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent();
#if ARCH_PORTDUINO
t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL;
@@ -98,6 +110,40 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
return t;
}
+void DeviceTelemetryModule::sendLocalStatsToPhone()
+{
+ meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
+ telemetry.which_variant = meshtastic_Telemetry_local_stats_tag;
+ telemetry.variant.local_stats = meshtastic_LocalStats_init_zero;
+ telemetry.time = getTime();
+ telemetry.variant.local_stats.uptime_seconds = getUptimeSeconds();
+ telemetry.variant.local_stats.channel_utilization = airTime->channelUtilizationPercent();
+ telemetry.variant.local_stats.air_util_tx = airTime->utilizationTXPercent();
+ telemetry.variant.local_stats.num_online_nodes = numOnlineNodes;
+ telemetry.variant.local_stats.num_total_nodes = nodeDB->getNumMeshNodes();
+ if (RadioLibInterface::instance) {
+ telemetry.variant.local_stats.num_packets_tx = RadioLibInterface::instance->txGood;
+ telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood;
+ telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
+ }
+
+ LOG_INFO(
+ "(Sending local stats): uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i\n",
+ telemetry.variant.local_stats.uptime_seconds, telemetry.variant.local_stats.channel_utilization,
+ telemetry.variant.local_stats.air_util_tx, telemetry.variant.local_stats.num_online_nodes,
+ telemetry.variant.local_stats.num_total_nodes);
+
+ LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i\n", telemetry.variant.local_stats.num_packets_tx,
+ telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad);
+
+ meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
+ p->to = NODENUM_BROADCAST;
+ p->decoded.want_response = false;
+ p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
+
+ service->sendToPhone(p);
+}
+
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry telemetry = getDeviceTelemetry();
diff --git a/src/modules/Telemetry/DeviceTelemetry.h b/src/modules/Telemetry/DeviceTelemetry.h
index baaf59f280..6d7f698911 100644
--- a/src/modules/Telemetry/DeviceTelemetry.h
+++ b/src/modules/Telemetry/DeviceTelemetry.h
@@ -42,7 +42,10 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu
private:
meshtastic_Telemetry getDeviceTelemetry();
- uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
+ void sendLocalStatsToPhone();
+ uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
+ uint32_t sendStatsToPhoneIntervalMs = 15 * SECONDS_IN_MINUTE * 1000; // Send stats to phone every 15 minutes
+ uint32_t lastSentStatsToPhone = 0;
uint32_t lastSentToMesh = 0;
void refreshUptime()
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index db56fb1a50..4755a5be5a 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -289,6 +289,7 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
bool hasSensor = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
+ m->variant.environment_metrics = meshtastic_EnvironmentMetrics_init_zero;
#ifdef T1000X_SENSOR_EN // add by WayenWeng
valid = valid && t1000xSensor.getMetrics(m);
diff --git a/src/modules/Telemetry/Sensor/AHT10.cpp b/src/modules/Telemetry/Sensor/AHT10.cpp
index a5212b39b7..f9e8ba18af 100644
--- a/src/modules/Telemetry/Sensor/AHT10.cpp
+++ b/src/modules/Telemetry/Sensor/AHT10.cpp
@@ -32,6 +32,9 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
sensors_event_t humidity, temp;
aht10.getEvent(&humidity, &temp);
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
diff --git a/src/modules/Telemetry/Sensor/BME280Sensor.cpp b/src/modules/Telemetry/Sensor/BME280Sensor.cpp
index aea6f2c3d6..55bc167441 100644
--- a/src/modules/Telemetry/Sensor/BME280Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/BME280Sensor.cpp
@@ -31,6 +31,10 @@ void BME280Sensor::setup() {}
bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+ measurement->variant.environment_metrics.has_barometric_pressure = true;
+
LOG_DEBUG("BME280Sensor::getMetrics\n");
bme280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bme280.readTemperature();
diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp
index 411cbbf69a..328ec827d2 100644
--- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp
@@ -54,6 +54,13 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
return false;
+
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+ measurement->variant.environment_metrics.has_barometric_pressure = true;
+ measurement->variant.environment_metrics.has_gas_resistance = true;
+ measurement->variant.environment_metrics.has_iaq = true;
+
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
measurement->variant.environment_metrics.relative_humidity =
bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;
diff --git a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp
index 0c4d0b5ca0..15951126fa 100644
--- a/src/modules/Telemetry/Sensor/BMP085Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/BMP085Sensor.cpp
@@ -26,6 +26,9 @@ void BMP085Sensor::setup() {}
bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_barometric_pressure = true;
+
LOG_DEBUG("BMP085Sensor::getMetrics\n");
measurement->variant.environment_metrics.temperature = bmp085.readTemperature();
measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F;
diff --git a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp
index 8d0e4c1805..6b0743d757 100644
--- a/src/modules/Telemetry/Sensor/BMP280Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/BMP280Sensor.cpp
@@ -31,6 +31,9 @@ void BMP280Sensor::setup() {}
bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_barometric_pressure = true;
+
LOG_DEBUG("BMP280Sensor::getMetrics\n");
bmp280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bmp280.readTemperature();
diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp
index 830552023a..4b01eb4442 100644
--- a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp
+++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp
@@ -35,6 +35,12 @@ void DFRobotLarkSensor::setup() {}
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+ measurement->variant.environment_metrics.has_wind_speed = true;
+ measurement->variant.environment_metrics.has_wind_direction = true;
+ measurement->variant.environment_metrics.has_barometric_pressure = true;
+
measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();
diff --git a/src/modules/Telemetry/Sensor/INA219Sensor.cpp b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
index 040e595750..f70d3705ed 100644
--- a/src/modules/Telemetry/Sensor/INA219Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA219Sensor.cpp
@@ -32,6 +32,9 @@ void INA219Sensor::setup() {}
bool INA219Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_voltage = true;
+ measurement->variant.environment_metrics.has_current = true;
+
measurement->variant.environment_metrics.voltage = ina219.getBusVoltage_V();
measurement->variant.environment_metrics.current = ina219.getCurrent_mA() * INA219_MULTIPLIER;
return true;
diff --git a/src/modules/Telemetry/Sensor/INA260Sensor.cpp b/src/modules/Telemetry/Sensor/INA260Sensor.cpp
index f156a9abad..751608c823 100644
--- a/src/modules/Telemetry/Sensor/INA260Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA260Sensor.cpp
@@ -26,6 +26,9 @@ void INA260Sensor::setup() {}
bool INA260Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_voltage = true;
+ measurement->variant.environment_metrics.has_current = true;
+
// mV conversion to V
measurement->variant.environment_metrics.voltage = ina260.readBusVoltage() / 1000;
measurement->variant.environment_metrics.current = ina260.readCurrent();
diff --git a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
index dec99c551c..549346d729 100644
--- a/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/INA3221Sensor.cpp
@@ -67,6 +67,9 @@ bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurement m = getMeasurement(ENV_CH);
+ measurement->variant.environment_metrics.has_voltage = true;
+ measurement->variant.environment_metrics.has_current = true;
+
measurement->variant.environment_metrics.voltage = m.voltage;
measurement->variant.environment_metrics.current = m.current;
@@ -77,6 +80,13 @@ bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurements m = getMeasurements();
+ measurement->variant.power_metrics.has_ch1_voltage = true;
+ measurement->variant.power_metrics.has_ch1_current = true;
+ measurement->variant.power_metrics.has_ch2_voltage = true;
+ measurement->variant.power_metrics.has_ch2_current = true;
+ measurement->variant.power_metrics.has_ch3_voltage = true;
+ measurement->variant.power_metrics.has_ch3_current = true;
+
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
diff --git a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp
index c3c994cfa3..111d86d1a6 100644
--- a/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp
+++ b/src/modules/Telemetry/Sensor/LPS22HBSensor.cpp
@@ -27,6 +27,9 @@ void LPS22HBSensor::setup()
bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_barometric_pressure = true;
+
sensors_event_t temp;
sensors_event_t pressure;
lps22hb.getEvent(&pressure, &temp);
diff --git a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp
index b01a192918..c1cda72275 100644
--- a/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/MCP9808Sensor.cpp
@@ -26,6 +26,8 @@ void MCP9808Sensor::setup()
bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+
LOG_DEBUG("MCP9808Sensor::getMetrics\n");
measurement->variant.environment_metrics.temperature = mcp9808.readTempC();
return true;
diff --git a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp
index 4c459c3655..0568a46522 100644
--- a/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/MLX90632Sensor.cpp
@@ -32,6 +32,7 @@ void MLX90632Sensor::setup() {}
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit
return true;
diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
index 3560c6580f..d7dcbd09f0 100644
--- a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
@@ -45,6 +45,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
return false;
}
}
+ measurement->variant.environment_metrics.has_weight = true;
// Check if we have correct calibration values after powerup
LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg
diff --git a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp
index 338bd9e2c1..3e4376d55b 100644
--- a/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/OPT3001Sensor.cpp
@@ -38,6 +38,7 @@ void OPT3001Sensor::setup()
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_lux = true;
OPT3001 result = opt3001.readResult();
measurement->variant.environment_metrics.lux = result.lux;
diff --git a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp
index 49a509d382..b9a29ab7d2 100644
--- a/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/RCWL9620Sensor.cpp
@@ -23,6 +23,7 @@ void RCWL9620Sensor::setup() {}
bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_distance = true;
LOG_DEBUG("RCWL9620Sensor::getMetrics\n");
measurement->variant.environment_metrics.distance = getDistance();
return true;
diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp
index aa2b5dcfcd..c372f79861 100644
--- a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp
@@ -27,6 +27,8 @@ void SHT31Sensor::setup()
bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.temperature = sht31.readTemperature();
measurement->variant.environment_metrics.relative_humidity = sht31.readHumidity();
diff --git a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp
index 7f37327c61..94367cba41 100644
--- a/src/modules/Telemetry/Sensor/SHT4XSensor.cpp
+++ b/src/modules/Telemetry/Sensor/SHT4XSensor.cpp
@@ -39,6 +39,9 @@ void SHT4XSensor::setup()
bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+
sensors_event_t humidity, temp;
sht4x.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;
diff --git a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp
index 37685fed7d..64ebfb472b 100644
--- a/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/SHTC3Sensor.cpp
@@ -26,6 +26,9 @@ void SHTC3Sensor::setup()
bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_relative_humidity = true;
+
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);
diff --git a/src/modules/Telemetry/Sensor/T1000xSensor.cpp b/src/modules/Telemetry/Sensor/T1000xSensor.cpp
index e544d0dc5b..4079d8ae3f 100644
--- a/src/modules/Telemetry/Sensor/T1000xSensor.cpp
+++ b/src/modules/Telemetry/Sensor/T1000xSensor.cpp
@@ -108,6 +108,9 @@ float T1000xSensor::getTemp()
bool T1000xSensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_temperature = true;
+ measurement->variant.environment_metrics.has_lux = true;
+
measurement->variant.environment_metrics.temperature = getTemp();
measurement->variant.environment_metrics.lux = getLux();
return true;
diff --git a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp
index d20e48dce1..9002874b3d 100644
--- a/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/TSL2591Sensor.cpp
@@ -29,6 +29,7 @@ void TSL2591Sensor::setup()
bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_lux = true;
uint32_t lum = tsl.getFullLuminosity();
uint16_t ir, full;
ir = lum >> 16;
diff --git a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp
index cbeaf4c2e6..c176ed21ba 100644
--- a/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/VEML7700Sensor.cpp
@@ -53,6 +53,9 @@ float VEML7700Sensor::getResolution(void)
bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
+ measurement->variant.environment_metrics.has_lux = true;
+ measurement->variant.environment_metrics.has_white_lux = true;
+
int16_t white;
measurement->variant.environment_metrics.lux = veml7700.readLux(VEML_LUX_AUTO);
white = veml7700.readWhite(true);
diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp
index f390aafcd6..dd3d0b4f98 100644
--- a/src/modules/TraceRouteModule.cpp
+++ b/src/modules/TraceRouteModule.cpp
@@ -5,71 +5,141 @@ TraceRouteModule *traceRouteModule;
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
{
- // Only handle a response
- if (mp.decoded.request_id) {
- printRoute(r, mp.to, mp.from);
- }
-
+ // We only alter the packet in alterReceivedProtobuf()
return false; // let it be handled by RoutingModule
}
void TraceRouteModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
{
auto &incoming = p.decoded;
- // Only append IDs for the request (one way)
- if (!incoming.request_id) {
- // Insert unknown hops if necessary
- insertUnknownHops(p, r);
-
- // Don't add ourselves if we are the destination (the reply will have our NodeNum already)
- if (p.to != nodeDB->getNodeNum()) {
- appendMyID(r);
- printRoute(r, p.from, NODENUM_BROADCAST);
- }
- // Set updated route to the payload of the to be flooded packet
- p.decoded.payload.size =
- pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r);
- }
+
+ // Insert unknown hops if necessary
+ insertUnknownHops(p, r, !incoming.request_id);
+
+ // Append ID and SNR. For the last hop (p.to == nodeDB->getNodeNum()), we only need to append the SNR
+ appendMyIDandSNR(r, p.rx_snr, !incoming.request_id, p.to == nodeDB->getNodeNum());
+ if (!incoming.request_id)
+ printRoute(r, p.from, p.to, true);
+ else
+ printRoute(r, p.to, p.from, false);
+
+ // Set updated route to the payload of the to be flooded packet
+ p.decoded.payload.size =
+ pb_encode_to_bytes(p.decoded.payload.bytes, sizeof(p.decoded.payload.bytes), &meshtastic_RouteDiscovery_msg, r);
}
-void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r)
+void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination)
{
+ pb_size_t *route_count;
+ uint32_t *route;
+ pb_size_t *snr_count;
+ int8_t *snr_list;
+
+ // Pick the correct route array and SNR list
+ if (isTowardsDestination) {
+ route_count = &r->route_count;
+ route = r->route;
+ snr_count = &r->snr_towards_count;
+ snr_list = r->snr_towards;
+ } else {
+ route_count = &r->route_back_count;
+ route = r->route_back;
+ snr_count = &r->snr_back_count;
+ snr_list = r->snr_back;
+ }
+
// Only insert unknown hops if hop_start is valid
if (p.hop_start != 0 && p.hop_limit <= p.hop_start) {
uint8_t hopsTaken = p.hop_start - p.hop_limit;
- int8_t diff = hopsTaken - r->route_count;
+ int8_t diff = hopsTaken - *route_count;
+ for (uint8_t i = 0; i < diff; i++) {
+ if (*route_count < sizeof(*route) / sizeof(route[0])) {
+ route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
+ *route_count += 1;
+ }
+ }
+ // Add unknown SNR values if necessary
+ diff = *route_count - *snr_count;
for (uint8_t i = 0; i < diff; i++) {
- if (r->route_count < sizeof(r->route) / sizeof(r->route[0])) {
- r->route[r->route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
- r->route_count += 1;
+ if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) {
+ snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR
+ *snr_count += 1;
}
}
}
}
-void TraceRouteModule::appendMyID(meshtastic_RouteDiscovery *updated)
+void TraceRouteModule::appendMyIDandSNR(meshtastic_RouteDiscovery *updated, float snr, bool isTowardsDestination, bool SNRonly)
{
+ pb_size_t *route_count;
+ uint32_t *route;
+ pb_size_t *snr_count;
+ int8_t *snr_list;
+
+ // Pick the correct route array and SNR list
+ if (isTowardsDestination) {
+ route_count = &updated->route_count;
+ route = updated->route;
+ snr_count = &updated->snr_towards_count;
+ snr_list = updated->snr_towards;
+ } else {
+ route_count = &updated->route_back_count;
+ route = updated->route_back;
+ snr_count = &updated->snr_back_count;
+ snr_list = updated->snr_back;
+ }
+
+ if (*snr_count < sizeof(*snr_list) / sizeof(snr_list[0])) {
+ snr_list[*snr_count] = (int8_t)(snr * 4); // Convert SNR to 1 byte
+ *snr_count += 1;
+ }
+ if (SNRonly)
+ return;
+
// Length of route array can normally not be exceeded due to the max. hop_limit of 7
- if (updated->route_count < sizeof(updated->route) / sizeof(updated->route[0])) {
- updated->route[updated->route_count] = myNodeInfo.my_node_num;
- updated->route_count += 1;
+ if (*route_count < sizeof(*route) / sizeof(route[0])) {
+ route[*route_count] = myNodeInfo.my_node_num;
+ *route_count += 1;
} else {
LOG_WARN("Route exceeded maximum hop limit, are you bridging networks?\n");
}
}
-void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest)
+void TraceRouteModule::printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination)
{
#ifdef DEBUG_PORT
LOG_INFO("Route traced:\n");
LOG_INFO("0x%x --> ", origin);
for (uint8_t i = 0; i < r->route_count; i++) {
- LOG_INFO("0x%x --> ", r->route[i]);
+ if (i < r->snr_towards_count && r->snr_towards[i] != INT8_MIN)
+ LOG_INFO("0x%x (%.2fdB) --> ", r->route[i], (float)r->snr_towards[i] / 4);
+ else
+ LOG_INFO("0x%x (?dB) --> ", r->route[i]);
}
- if (dest != NODENUM_BROADCAST)
- LOG_INFO("0x%x\n", dest);
- else
+ // If we are the destination, or it has already reached the destination, print it
+ if (dest == nodeDB->getNodeNum() || !isTowardsDestination) {
+ if (r->snr_towards_count > 0 && r->snr_towards[r->snr_towards_count - 1] != INT8_MIN)
+ LOG_INFO("0x%x (%.2fdB)\n", dest, (float)r->snr_towards[r->snr_towards_count - 1] / 4);
+ else
+ LOG_INFO("0x%x (?dB)\n", dest);
+ } else
LOG_INFO("...\n");
+
+ // If there's a route back (or we are the destination as then the route is complete), print it
+ if (r->route_back_count > 0 || origin == nodeDB->getNodeNum()) {
+ if (r->snr_towards_count > 0 && origin == nodeDB->getNodeNum())
+ LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[r->snr_back_count - 1] / 4, origin);
+ else
+ LOG_INFO("...");
+
+ for (int8_t i = r->route_back_count - 1; i >= 0; i--) {
+ if (i < r->snr_back_count && r->snr_back[i] != INT8_MIN)
+ LOG_INFO("(%.2fdB) 0x%x <-- ", (float)r->snr_back[i] / 4, r->route_back[i]);
+ else
+ LOG_INFO("(?dB) 0x%x <-- ", r->route_back[i]);
+ }
+ LOG_INFO("0x%x\n", dest);
+ }
#endif
}
@@ -86,8 +156,6 @@ meshtastic_MeshPacket *TraceRouteModule::allocReply()
pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_RouteDiscovery_msg, &scratch);
updated = &scratch;
- printRoute(updated, req.from, req.to);
-
// Create a MeshPacket with this payload and set it as the reply
meshtastic_MeshPacket *reply = allocDataProtobuf(*updated);
diff --git a/src/modules/TraceRouteModule.h b/src/modules/TraceRouteModule.h
index 18a5ac0cb7..fe69300dea 100644
--- a/src/modules/TraceRouteModule.h
+++ b/src/modules/TraceRouteModule.h
@@ -20,15 +20,15 @@ class TraceRouteModule : public ProtobufModule
private:
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
- void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r);
+ void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
// Call to add your ID to the route array of a RouteDiscovery message
- void appendMyID(meshtastic_RouteDiscovery *r);
+ void appendMyIDandSNR(meshtastic_RouteDiscovery *r, float snr, bool isTowardsDestination, bool SNRonly);
/* Call to print the route array of a RouteDiscovery message.
Set origin to where the request came from.
Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */
- void printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest);
+ void printRoute(meshtastic_RouteDiscovery *r, uint32_t origin, uint32_t dest, bool isTowardsDestination);
};
extern TraceRouteModule *traceRouteModule;
\ No newline at end of file
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 4bb9cd5ebe..22f68bac8d 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -152,7 +152,8 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
LOG_INFO("Ignoring downlink message we originally sent.\n");
} else {
// Find channel by channel_id and check downlink_enabled
- if (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled) {
+ if ((strcmp(e.channel_id, "PKI") == 0 && e.packet) ||
+ (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled)) {
LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length);
meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet);
p->via_mqtt = true; // Mark that the packet was received via MQTT
@@ -161,8 +162,16 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
p->channel = ch.index;
}
- // ignore messages if we don't have the channel key
- if (router && perhapsDecode(p))
+ // PKI messages get accepted even if we can't decrypt
+ if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag &&
+ strcmp(e.channel_id, "PKI") == 0) {
+ meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p));
+ meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
+ // Only accept PKI messages if we have both the sender and receiver in our nodeDB, as then it's likely
+ // they discovered each other via a channel we have downlink enabled for
+ if (tx && tx->has_user && rx && rx->has_user)
+ router->enqueueReceivedMessage(p);
+ } else if (router && perhapsDecode(p)) // ignore messages if we don't have the channel key
router->enqueueReceivedMessage(p);
else
packetPool.release(p);
@@ -361,10 +370,12 @@ void MQTT::reconnect()
void MQTT::sendSubscriptions()
{
#if HAS_NETWORKING
+ bool hasDownlink = false;
size_t numChan = channels.getNumChannels();
for (size_t i = 0; i < numChan; i++) {
const auto &ch = channels.getByIndex(i);
if (ch.settings.downlink_enabled) {
+ hasDownlink = true;
std::string topic = cryptTopic + channels.getGlobalId(i) + "/#";
LOG_INFO("Subscribing to %s\n", topic.c_str());
pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right?
@@ -377,6 +388,13 @@ void MQTT::sendSubscriptions()
#endif // ARCH_NRF52
}
}
+#if !MESHTASTIC_EXCLUDE_PKI
+ if (hasDownlink) {
+ std::string topic = cryptTopic + "PKI/#";
+ LOG_INFO("Subscribing to %s\n", topic.c_str());
+ pubSub.subscribe(topic.c_str(), 1);
+ }
+#endif
#endif
}
@@ -452,8 +470,12 @@ void MQTT::publishQueuedMessages()
meshtastic_ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
-
- std::string topic = cryptTopic + env->channel_id + "/" + owner.id;
+ std::string topic;
+ if (env->packet->pki_encrypted) {
+ topic = cryptTopic + "PKI/" + owner.id;
+ } else {
+ topic = cryptTopic + env->channel_id + "/" + owner.id;
+ }
LOG_INFO("publish %s, %u bytes from queue\n", topic.c_str(), numBytes);
publish(topic.c_str(), bytes, numBytes, false);
@@ -463,7 +485,12 @@ void MQTT::publishQueuedMessages()
// handle json topic
auto jsonString = MeshPacketSerializer::JsonSerialize(env->packet);
if (jsonString.length() != 0) {
- std::string topicJson = jsonTopic + env->channel_id + "/" + owner.id;
+ std::string topicJson;
+ if (env->packet->pki_encrypted) {
+ topicJson = jsonTopic + "PKI/" + owner.id;
+ } else {
+ topicJson = jsonTopic + env->channel_id + "/" + owner.id;
+ }
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
jsonString.c_str());
publish(topicJson.c_str(), jsonString.c_str(), false);
@@ -478,7 +505,13 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
{
if (mp.via_mqtt)
return; // Don't send messages that came from MQTT back into MQTT
-
+ bool uplinkEnabled = false;
+ for (int i = 0; i <= 7; i++) {
+ if (channels.getByIndex(i).settings.uplink_enabled)
+ uplinkEnabled = true;
+ }
+ if (!uplinkEnabled)
+ return; // no channels have an uplink enabled
auto &ch = channels.getByIndex(chIndex);
if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
@@ -493,7 +526,7 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
return;
}
- if (ch.settings.uplink_enabled) {
+ if (ch.settings.uplink_enabled || mp.pki_encrypted) {
const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel
meshtastic_ServiceEnvelope *env = mqttPool.allocZeroed();
@@ -513,8 +546,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
static uint8_t bytes[meshtastic_MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_ServiceEnvelope_msg, env);
-
- std::string topic = cryptTopic + channelId + "/" + owner.id;
+ std::string topic;
+ if (mp.pki_encrypted) {
+ topic = cryptTopic + "PKI/" + owner.id;
+ } else {
+ topic = cryptTopic + channelId + "/" + owner.id;
+ }
LOG_DEBUG("MQTT Publish %s, %u bytes\n", topic.c_str(), numBytes);
publish(topic.c_str(), bytes, numBytes, false);
@@ -524,7 +561,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
// handle json topic
auto jsonString = MeshPacketSerializer::JsonSerialize((meshtastic_MeshPacket *)&mp_decoded);
if (jsonString.length() != 0) {
- std::string topicJson = jsonTopic + channelId + "/" + owner.id;
+ std::string topicJson;
+ if (mp.pki_encrypted) {
+ topicJson = jsonTopic + "PKI/" + owner.id;
+ } else {
+ topicJson = jsonTopic + channelId + "/" + owner.id;
+ }
LOG_INFO("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(),
jsonString.c_str());
publish(topicJson.c_str(), jsonString.c_str(), false);
diff --git a/src/platform/esp32/ESP32CryptoEngine.cpp b/src/platform/esp32/ESP32CryptoEngine.cpp
index 998419df84..2301390363 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 fd3f92a9c3..b6def5b01f 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 a7cf3d5bff..5de13c58bb 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 ebb847dc81..895525f5ab 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 46ef942f0b..0000000000
--- 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 5486e51e5a..0000000000
--- 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 4debdf78e1..0000000000
--- 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
new file mode 100644
index 0000000000..129c882839
--- /dev/null
+++ b/test/test_crypto/test_main.cpp
@@ -0,0 +1,144 @@
+#include "CryptoEngine.h"
+
+#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
+}
+
+void tearDown(void)
+{
+ // clean stuff up here
+}
+
+void test_SHA256(void)
+{
+ uint8_t expected[32];
+ uint8_t hash[32] = {0};
+
+ HexToBytes(expected, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ crypto->hash(hash, 0);
+ 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)
+{
+ // https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_ECB.pdf
+
+ 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(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 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
+ // 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);
+ RUN_TEST(test_DH25519);
+ RUN_TEST(test_AES_CTR);
+}
+
+void loop()
+{
+ UNITY_END(); // stop unit testing
+}
\ No newline at end of file
diff --git a/userPrefs.h b/userPrefs.h
index 52209deaad..3b32bf79b0 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