mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-22 11:17:49 +10:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7088470b5e | ||
|
|
d71a38078e | ||
|
|
8d08077412 | ||
|
|
86a0d9473f | ||
|
|
9009bbdde8 | ||
|
|
c85f2a215a | ||
|
|
7960144c9b | ||
|
|
089ad58dd3 |
@@ -6,21 +6,16 @@
|
|||||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||||
#include "HardwareRNG.h"
|
#include "HardwareRNG.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "XEdDSA.h"
|
|
||||||
#include "aes-ccm.h"
|
#include "aes-ccm.h"
|
||||||
#include "meshUtils.h"
|
#include "meshUtils.h"
|
||||||
#include <Crypto.h>
|
#include <Crypto.h>
|
||||||
#include <Curve25519.h>
|
#include <Curve25519.h>
|
||||||
#include <Ed25519.h>
|
|
||||||
#include <RNG.h>
|
#include <RNG.h>
|
||||||
#include <SHA256.h>
|
#include <SHA256.h>
|
||||||
|
|
||||||
#ifndef NUM_LIMBS_256BIT
|
|
||||||
#define NUM_LIMBS_BITS(n) (((n) + sizeof(limb_t) * 8 - 1) / (8 * sizeof(limb_t)))
|
|
||||||
#define NUM_LIMBS_256BIT NUM_LIMBS_BITS(256)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||||
|
#if !defined(ARCH_STM32WL)
|
||||||
|
#define CryptRNG RNG
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a public/private key pair with Curve25519.
|
* Create a public/private key pair with Curve25519.
|
||||||
@@ -51,7 +46,6 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey)
|
|||||||
Curve25519::dh1(public_key, private_key);
|
Curve25519::dh1(public_key, private_key);
|
||||||
memcpy(pubKey, public_key, sizeof(public_key));
|
memcpy(pubKey, public_key, sizeof(public_key));
|
||||||
memcpy(privKey, private_key, sizeof(private_key));
|
memcpy(privKey, private_key, sizeof(private_key));
|
||||||
XEdDSA::priv_curve_to_ed_keys(private_key, xeddsa_private_key, xeddsa_public_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +65,6 @@ bool CryptoEngine::regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey)
|
|||||||
}
|
}
|
||||||
memcpy(private_key, privKey, sizeof(private_key));
|
memcpy(private_key, privKey, sizeof(private_key));
|
||||||
memcpy(public_key, pubKey, sizeof(public_key));
|
memcpy(public_key, pubKey, sizeof(public_key));
|
||||||
XEdDSA::priv_curve_to_ed_keys(private_key, xeddsa_private_key, xeddsa_public_key);
|
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("X25519 key generation failed due to blank private key");
|
LOG_WARN("X25519 key generation failed due to blank private key");
|
||||||
return false;
|
return false;
|
||||||
@@ -79,91 +72,6 @@ bool CryptoEngine::regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a signing buffer that covers packet metadata and payload:
|
|
||||||
* [fromNode(4) | packetId(4) | portnum(4) | payload(N)]
|
|
||||||
* This prevents replay, reattribution, and portnum redirection attacks.
|
|
||||||
*/
|
|
||||||
static size_t buildSigningBuffer(uint8_t *buf, size_t bufSize, uint32_t fromNode, uint32_t packetId, uint32_t portnum,
|
|
||||||
const uint8_t *payload, size_t payloadLen)
|
|
||||||
{
|
|
||||||
const size_t headerLen = sizeof(uint32_t) * 3;
|
|
||||||
size_t totalLen = headerLen + payloadLen;
|
|
||||||
if (totalLen > bufSize)
|
|
||||||
return 0;
|
|
||||||
memcpy(buf, &fromNode, sizeof(uint32_t));
|
|
||||||
memcpy(buf + sizeof(uint32_t), &packetId, sizeof(uint32_t));
|
|
||||||
memcpy(buf + sizeof(uint32_t) * 2, &portnum, sizeof(uint32_t));
|
|
||||||
memcpy(buf + headerLen, payload, payloadLen);
|
|
||||||
return totalLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CryptoEngine::xeddsa_sign(uint32_t fromNode, uint32_t packetId, uint32_t portnum, const uint8_t *payload, size_t payloadLen,
|
|
||||||
uint8_t *signature)
|
|
||||||
{
|
|
||||||
if (memfll(xeddsa_private_key, 0, sizeof(xeddsa_private_key)))
|
|
||||||
return false;
|
|
||||||
uint8_t sigBuf[MAX_BLOCKSIZE];
|
|
||||||
size_t sigLen = buildSigningBuffer(sigBuf, sizeof(sigBuf), fromNode, packetId, portnum, payload, payloadLen);
|
|
||||||
if (sigLen == 0)
|
|
||||||
return false;
|
|
||||||
XEdDSA::sign(signature, xeddsa_private_key, xeddsa_public_key, sigBuf, sigLen);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CryptoEngine::xeddsa_verify(const uint8_t *pubKey, uint32_t fromNode, uint32_t packetId, uint32_t portnum,
|
|
||||||
const uint8_t *payload, size_t payloadLen, const uint8_t *signature)
|
|
||||||
{
|
|
||||||
// Use cached Ed25519 key if the Curve25519 key matches, avoiding expensive field inversion
|
|
||||||
if (memcmp(pubKey, cached_curve_pubkey, 32) != 0) {
|
|
||||||
curve_to_ed_pub(pubKey, cached_ed_pubkey);
|
|
||||||
memcpy(cached_curve_pubkey, pubKey, 32);
|
|
||||||
}
|
|
||||||
uint8_t sigBuf[MAX_BLOCKSIZE];
|
|
||||||
size_t sigLen = buildSigningBuffer(sigBuf, sizeof(sigBuf), fromNode, packetId, portnum, payload, payloadLen);
|
|
||||||
if (sigLen == 0)
|
|
||||||
return false;
|
|
||||||
return XEdDSA::verify(signature, cached_ed_pubkey, sigBuf, sigLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CryptoEngine::curve_to_ed_pub(const uint8_t *curve_pubkey, uint8_t *ed_pubkey)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Apply the birational map defined in RFC 7748, section 4.1 "Curve25519" to calculate an Ed25519 public
|
|
||||||
// key from a Curve25519 public key. Because the serialization format of Curve25519 public keys only
|
|
||||||
// contains the u coordinate, the x coordinate of the corresponding Ed25519 public key can't be uniquely
|
|
||||||
// calculated as defined by the birational map. The x coordinate is represented in the serialization
|
|
||||||
// format of Ed25519 public keys only in a single sign bit. This function assumes that the sign bit is
|
|
||||||
// known to the user and is passed accordingly.
|
|
||||||
fe u, y;
|
|
||||||
fe one;
|
|
||||||
fe u_minus_one, u_plus_one, u_plus_one_inv;
|
|
||||||
|
|
||||||
// Parse the Curve25519 public key input as a field element containing the u coordinate. RFC 7748,
|
|
||||||
// section 5 "The X25519 and X448 Functions", mandates that the most significant bit of the Curve25519
|
|
||||||
// public key has to be zeroized. This is handled by fe_frombytes internally.
|
|
||||||
fe_frombytes(u, curve_pubkey);
|
|
||||||
|
|
||||||
// Calculate the parameters (u - 1) and (u + 1)
|
|
||||||
fe_1(one);
|
|
||||||
fe_sub(u_minus_one, u, one);
|
|
||||||
fe_add(u_plus_one, u, one);
|
|
||||||
|
|
||||||
// Invert u + 1
|
|
||||||
fe_invert(u_plus_one_inv, u_plus_one);
|
|
||||||
|
|
||||||
// Calculate y = (u - 1) * inv(u + 1) (mod p)
|
|
||||||
fe_mul(y, u_minus_one, u_plus_one_inv);
|
|
||||||
|
|
||||||
// Serialize the field element containing the y coordinate to the Ed25519 public key output
|
|
||||||
fe_tobytes(ed_pubkey, y);
|
|
||||||
|
|
||||||
// Set the sign bit to zero
|
|
||||||
ed_pubkey[31] &= 0x7f;
|
|
||||||
|
|
||||||
// need to convert the pubkey y = ( u - 1) * inv( u + 1) (mod p).
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CryptoEngine::ensurePkiKeys(meshtastic_Config_SecurityConfig &security, meshtastic_User &user)
|
bool CryptoEngine::ensurePkiKeys(meshtastic_Config_SecurityConfig &security, meshtastic_User &user)
|
||||||
{
|
{
|
||||||
if (user.is_licensed) {
|
if (user.is_licensed) {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ struct CryptoKey {
|
|||||||
|
|
||||||
#define MAX_BLOCKSIZE 256
|
#define MAX_BLOCKSIZE 256
|
||||||
#define TEST_CURVE25519_FIELD_OPS // Exposes Curve25519::isWeakPoint() for testing keys
|
#define TEST_CURVE25519_FIELD_OPS // Exposes Curve25519::isWeakPoint() for testing keys
|
||||||
#define XEDDSA_SIGNATURE_SIZE 64
|
|
||||||
|
|
||||||
class CryptoEngine
|
class CryptoEngine
|
||||||
{
|
{
|
||||||
@@ -38,10 +37,6 @@ class CryptoEngine
|
|||||||
virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey);
|
virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey);
|
||||||
virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey);
|
virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey);
|
||||||
virtual bool ensurePkiKeys(meshtastic_Config_SecurityConfig &security, meshtastic_User &user);
|
virtual bool ensurePkiKeys(meshtastic_Config_SecurityConfig &security, meshtastic_User &user);
|
||||||
bool xeddsa_sign(uint32_t fromNode, uint32_t packetId, uint32_t portnum, const uint8_t *payload, size_t payloadLen,
|
|
||||||
uint8_t *signature);
|
|
||||||
bool xeddsa_verify(const uint8_t *pubKey, uint32_t fromNode, uint32_t packetId, uint32_t portnum, const uint8_t *payload,
|
|
||||||
size_t payloadLen, const uint8_t *signature);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
void setDHPrivateKey(uint8_t *_private_key);
|
void setDHPrivateKey(uint8_t *_private_key);
|
||||||
@@ -90,12 +85,6 @@ class CryptoEngine
|
|||||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||||
uint8_t shared_key[32] = {0};
|
uint8_t shared_key[32] = {0};
|
||||||
uint8_t private_key[32] = {0};
|
uint8_t private_key[32] = {0};
|
||||||
uint8_t xeddsa_public_key[32] = {0};
|
|
||||||
uint8_t xeddsa_private_key[32] = {0};
|
|
||||||
void curve_to_ed_pub(const uint8_t *curve_pubkey, uint8_t *ed_pubkey);
|
|
||||||
// Single-entry cache for curve_to_ed_pub conversion (avoids expensive field inversion per packet)
|
|
||||||
uint8_t cached_curve_pubkey[32] = {0};
|
|
||||||
uint8_t cached_ed_pubkey[32] = {0};
|
|
||||||
#endif
|
#endif
|
||||||
/**
|
/**
|
||||||
* Init our 128 bit nonce for a new packet
|
* Init our 128 bit nonce for a new packet
|
||||||
|
|||||||
+21
-95
@@ -431,6 +431,8 @@ NodeDB::NodeDB()
|
|||||||
|
|
||||||
// likewise - we always want the app requirements to come from the running appload
|
// likewise - we always want the app requirements to come from the running appload
|
||||||
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
|
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
|
||||||
|
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||||
|
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||||
pickNewNodeNum();
|
pickNewNodeNum();
|
||||||
|
|
||||||
// Set our board type so we can share it with others
|
// Set our board type so we can share it with others
|
||||||
@@ -450,18 +452,31 @@ NodeDB::NodeDB()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
||||||
// Generate crypto keys if needed using consolidated function
|
|
||||||
// Set my node num uint32 value to bytes from the public key (if we have one)
|
if (!owner.is_licensed && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
// Generate identity and crypto keys if needed; this will create a new identity if one does not exist
|
bool keygenSuccess = false;
|
||||||
generateCryptoKeyPair(nullptr);
|
keyIsLowEntropy = checkLowEntropyPublicKey(config.security.public_key);
|
||||||
|
if (config.security.private_key.size == 32 && !keyIsLowEntropy) {
|
||||||
|
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||||
|
keygenSuccess = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||||
|
keygenSuccess = true;
|
||||||
|
}
|
||||||
|
if (keygenSuccess) {
|
||||||
|
config.security.public_key.size = 32;
|
||||||
|
config.security.private_key.size = 32;
|
||||||
|
owner.public_key.size = 32;
|
||||||
|
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
#elif !(MESHTASTIC_EXCLUDE_PKI)
|
#elif !(MESHTASTIC_EXCLUDE_PKI)
|
||||||
// Calculate Curve25519 public and private keys
|
// Calculate Curve25519 public and private keys
|
||||||
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
|
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
|
||||||
owner.public_key.size = config.security.public_key.size;
|
owner.public_key.size = config.security.public_key.size;
|
||||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 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);
|
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
||||||
// Set my node num uint32 value to bytes from the new public key
|
|
||||||
myNodeInfo.my_node_num = crc32Buffer(config.security.public_key.bytes, config.security.public_key.size);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Include our owner in the node db under our nodenum
|
// Include our owner in the node db under our nodenum
|
||||||
@@ -2766,95 +2781,6 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool NodeDB::generateCryptoKeyPair(const uint8_t *privateKey)
|
|
||||||
{
|
|
||||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
|
||||||
// Only generate keys for non-licensed users and if LoRa region is set
|
|
||||||
if (owner.is_licensed || config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool keygenSuccess = false;
|
|
||||||
bool lowEntropy = checkLowEntropyPublicKey(config.security.public_key);
|
|
||||||
|
|
||||||
// If a specific private key was provided, use it
|
|
||||||
if (privateKey != nullptr) {
|
|
||||||
LOG_INFO("Using provided private key for PKI");
|
|
||||||
memcpy(config.security.private_key.bytes, privateKey, 32);
|
|
||||||
config.security.private_key.size = 32;
|
|
||||||
config.security.public_key.size = 32;
|
|
||||||
|
|
||||||
// Generate public key from the provided private key
|
|
||||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
|
||||||
keygenSuccess = true;
|
|
||||||
} else {
|
|
||||||
LOG_ERROR("Failed to generate public key from provided private key");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Try to regenerate public key from existing private key if it's valid and not low entropy
|
|
||||||
else if (config.security.private_key.size == 32 && !lowEntropy) {
|
|
||||||
config.security.public_key.size = 32;
|
|
||||||
LOG_DEBUG("Regenerate PKI public key from existing private key");
|
|
||||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
|
||||||
keygenSuccess = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Generate a new key pair
|
|
||||||
LOG_INFO("Generate new PKI keys");
|
|
||||||
config.security.public_key.size = 32;
|
|
||||||
config.security.private_key.size = 32;
|
|
||||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
|
||||||
keygenSuccess = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update sizes and copy to owner if successful
|
|
||||||
if (keygenSuccess) {
|
|
||||||
owner.public_key.size = 32;
|
|
||||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
|
||||||
|
|
||||||
// Update global entropy flag for UI display
|
|
||||||
keyIsLowEntropy = false;
|
|
||||||
|
|
||||||
// Set the DH private key for crypto operations
|
|
||||||
LOG_DEBUG("Set DH private key for crypto operations");
|
|
||||||
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
|
||||||
|
|
||||||
// Conditionally create new identity based on parameter
|
|
||||||
createNewIdentity();
|
|
||||||
}
|
|
||||||
return keygenSuccess;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeDB::createNewIdentity()
|
|
||||||
{
|
|
||||||
uint32_t oldNodeNum = getNodeNum();
|
|
||||||
uint32_t newNodeNum = crc32Buffer(config.security.public_key.bytes, config.security.public_key.size);
|
|
||||||
|
|
||||||
// If the key hasn't changed, nothing to do
|
|
||||||
if (newNodeNum == oldNodeNum)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Retire the old node entry
|
|
||||||
meshtastic_NodeInfoLite *node = getMeshNode(oldNodeNum);
|
|
||||||
if (node != NULL) {
|
|
||||||
LOG_DEBUG("Old node num %u is now %u", oldNodeNum, newNodeNum);
|
|
||||||
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_IS_IGNORED_MASK, true);
|
|
||||||
node->public_key.size = 0;
|
|
||||||
memset(node->public_key.bytes, 0, sizeof(node->public_key.bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
myNodeInfo.my_node_num = newNodeNum;
|
|
||||||
|
|
||||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
|
||||||
TypeConversions::CopyUserToNodeInfoLite(info, owner);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|||||||
@@ -376,12 +376,6 @@ class NodeDB
|
|||||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Consolidate crypto key generation logic used across multiple modules
|
|
||||||
/// @param privateKey Optional 32-byte private key to use. If nullptr, generates new random keys.
|
|
||||||
bool generateCryptoKeyPair(const uint8_t *privateKey = nullptr);
|
|
||||||
|
|
||||||
bool createNewIdentity();
|
|
||||||
|
|
||||||
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
|
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
|
||||||
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||||
int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
|
int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
|
||||||
@@ -493,8 +487,6 @@ extern uint32_t error_address;
|
|||||||
#define NODEINFO_BITFIELD_IS_UNMESSAGABLE_MASK (1u << NODEINFO_BITFIELD_IS_UNMESSAGABLE_SHIFT)
|
#define NODEINFO_BITFIELD_IS_UNMESSAGABLE_MASK (1u << NODEINFO_BITFIELD_IS_UNMESSAGABLE_SHIFT)
|
||||||
#define NODEINFO_BITFIELD_HAS_IS_UNMESSAGABLE_SHIFT 8
|
#define NODEINFO_BITFIELD_HAS_IS_UNMESSAGABLE_SHIFT 8
|
||||||
#define NODEINFO_BITFIELD_HAS_IS_UNMESSAGABLE_MASK (1u << NODEINFO_BITFIELD_HAS_IS_UNMESSAGABLE_SHIFT)
|
#define NODEINFO_BITFIELD_HAS_IS_UNMESSAGABLE_MASK (1u << NODEINFO_BITFIELD_HAS_IS_UNMESSAGABLE_SHIFT)
|
||||||
#define NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_SHIFT 9
|
|
||||||
#define NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK (1u << NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_SHIFT)
|
|
||||||
// Bits 9..31 reserved for future single-bit flags.
|
// Bits 9..31 reserved for future single-bit flags.
|
||||||
|
|
||||||
// Convenience accessors so call sites read like the old struct fields.
|
// Convenience accessors so call sites read like the old struct fields.
|
||||||
@@ -534,10 +526,6 @@ inline bool nodeInfoLiteIsKeyManuallyVerified(const meshtastic_NodeInfoLite *n)
|
|||||||
{
|
{
|
||||||
return n && (n->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK);
|
return n && (n->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK);
|
||||||
}
|
}
|
||||||
inline bool nodeInfoLiteHasXeddsaSigned(const meshtastic_NodeInfoLite *n)
|
|
||||||
{
|
|
||||||
return n && (n->bitfield & NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void nodeInfoLiteSetBit(meshtastic_NodeInfoLite *n, uint32_t mask, bool value)
|
inline void nodeInfoLiteSetBit(meshtastic_NodeInfoLite *n, uint32_t mask, bool value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -529,32 +529,6 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
|||||||
if (p->decoded.has_bitfield)
|
if (p->decoded.has_bitfield)
|
||||||
p->decoded.want_response |= p->decoded.bitfield & BITFIELD_WANT_RESPONSE_MASK;
|
p->decoded.want_response |= p->decoded.bitfield & BITFIELD_WANT_RESPONSE_MASK;
|
||||||
|
|
||||||
if (p->decoded.xeddsa_signature.size == XEDDSA_SIGNATURE_SIZE) {
|
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->from);
|
|
||||||
if (node && node->public_key.size == 32) {
|
|
||||||
p->xeddsa_signed =
|
|
||||||
crypto->xeddsa_verify(node->public_key.bytes, p->from, p->id, p->decoded.portnum, p->decoded.payload.bytes,
|
|
||||||
p->decoded.payload.size, p->decoded.xeddsa_signature.bytes);
|
|
||||||
if (p->xeddsa_signed) {
|
|
||||||
// Mark this node as a signer so future unsigned packets from it are rejected
|
|
||||||
nodeInfoLiteSetBit(node, NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK, true);
|
|
||||||
LOG_DEBUG("Verified XEdDSA signature from 0x%08x", p->from);
|
|
||||||
} else {
|
|
||||||
LOG_WARN("XEdDSA signature verification failed from 0x%08x, dropping", p->from);
|
|
||||||
return DecodeState::DECODE_FAILURE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG("No public key for 0x%08x, cannot verify XEdDSA signature", p->from);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unsigned packet — reject if this node previously sent signed packets
|
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->from);
|
|
||||||
if (node && nodeInfoLiteHasXeddsaSigned(node)) {
|
|
||||||
LOG_WARN("Dropping unsigned packet from 0x%08x that previously signed", p->from);
|
|
||||||
return DecodeState::DECODE_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not actually ever used.
|
/* Not actually ever used.
|
||||||
// Decompress if needed. jm
|
// Decompress if needed. jm
|
||||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||||
@@ -627,16 +601,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
|||||||
p->decoded.has_bitfield = true;
|
p->decoded.has_bitfield = true;
|
||||||
p->decoded.bitfield |= (config.lora.config_ok_to_mqtt << BITFIELD_OK_TO_MQTT_SHIFT);
|
p->decoded.bitfield |= (config.lora.config_ok_to_mqtt << BITFIELD_OK_TO_MQTT_SHIFT);
|
||||||
p->decoded.bitfield |= (p->decoded.want_response << BITFIELD_WANT_RESPONSE_SHIFT);
|
p->decoded.bitfield |= (p->decoded.want_response << BITFIELD_WANT_RESPONSE_SHIFT);
|
||||||
// Sign broadcast packets if payload + signature fits within the max Data payload.
|
|
||||||
// The actual encoded size is checked after pb_encode (TOO_LARGE).
|
|
||||||
if (!p->pki_encrypted && isBroadcast(p->to) &&
|
|
||||||
p->decoded.payload.size + XEDDSA_SIGNATURE_SIZE < meshtastic_Constants_DATA_PAYLOAD_LEN) {
|
|
||||||
if (crypto->xeddsa_sign(p->from, p->id, p->decoded.portnum, p->decoded.payload.bytes, p->decoded.payload.size,
|
|
||||||
p->decoded.xeddsa_signature.bytes)) {
|
|
||||||
p->decoded.xeddsa_signature.size = XEDDSA_SIGNATURE_SIZE;
|
|
||||||
LOG_DEBUG("XEdDSA signed packet 0x%08x", p->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
|
|||||||
info.is_ignored = nodeInfoLiteIsIgnored(lite);
|
info.is_ignored = nodeInfoLiteIsIgnored(lite);
|
||||||
info.is_key_manually_verified = nodeInfoLiteIsKeyManuallyVerified(lite);
|
info.is_key_manually_verified = nodeInfoLiteIsKeyManuallyVerified(lite);
|
||||||
info.is_muted = nodeInfoLiteIsMuted(lite);
|
info.is_muted = nodeInfoLiteIsMuted(lite);
|
||||||
info.has_xeddsa_signed = nodeInfoLiteHasXeddsaSigned(lite);
|
|
||||||
|
|
||||||
if (lite->has_hops_away) {
|
if (lite->has_hops_away) {
|
||||||
info.has_hops_away = true;
|
info.has_hops_away = true;
|
||||||
|
|||||||
+18
-10
@@ -895,14 +895,22 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
|||||||
LOG_INFO("Set config: Security");
|
LOG_INFO("Set config: Security");
|
||||||
config.security = c.payload_variant.security;
|
config.security = c.payload_variant.security;
|
||||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI)
|
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI)
|
||||||
// Only regenerate keys if the private key is not 32 bytes
|
// If the client set the key to blank, go ahead and regenerate so long as we're not in ham mode
|
||||||
if (config.security.private_key.size != 32) {
|
if (!owner.is_licensed && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
nodeDB->generateCryptoKeyPair();
|
if (config.security.private_key.size != 32) {
|
||||||
}
|
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||||
// If user provided a private key of correct size but no public key, generate the public key from private key
|
|
||||||
else if (config.security.private_key.size == 32 && config.security.public_key.size == 0) {
|
} else {
|
||||||
nodeDB->generateCryptoKeyPair(config.security.private_key.bytes);
|
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||||
|
config.security.public_key.size = 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
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
|
#endif
|
||||||
if (config.security.is_managed && !(config.security.admin_key[0].size == 32 || config.security.admin_key[1].size == 32 ||
|
if (config.security.is_managed && !(config.security.admin_key[0].size == 32 || config.security.admin_key[1].size == 32 ||
|
||||||
config.security.admin_key[2].size == 32)) {
|
config.security.admin_key[2].size == 32)) {
|
||||||
@@ -912,9 +920,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
|||||||
sendWarning(warning);
|
sendWarning(warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
changes = SEGMENT_CONFIG | SEGMENT_DEVICESTATE | SEGMENT_NODEDATABASE;
|
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 = true;
|
requiresReboot = false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case meshtastic_Config_device_ui_tag:
|
case meshtastic_Config_device_ui_tag:
|
||||||
|
|||||||
@@ -49,12 +49,6 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
|||||||
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
|
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
NodeNum sourceNum = getFrom(&mp);
|
|
||||||
auto node = nodeDB->getMeshNode(sourceNum);
|
|
||||||
if (node && nodeInfoLiteHasXeddsaSigned(node) && !mp.xeddsa_signed) {
|
|
||||||
LOG_WARN("Dropping unsigned NodeInfo from node 0x%08x that previously signed", sourceNum);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Coerce user.id to be derived from the node number
|
// Coerce user.id to be derived from the node number
|
||||||
snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp));
|
snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp));
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include "CryptoEngine.h"
|
#include "CryptoEngine.h"
|
||||||
|
|
||||||
#include "TestUtil.h"
|
#include "TestUtil.h"
|
||||||
#include <XEdDSA.h>
|
|
||||||
#include <unity.h>
|
#include <unity.h>
|
||||||
|
|
||||||
void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0)
|
void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0)
|
||||||
@@ -153,48 +152,6 @@ void test_PKC(void)
|
|||||||
TEST_ASSERT_EQUAL_MEMORY(expected_decrypted, decrypted, 10);
|
TEST_ASSERT_EQUAL_MEMORY(expected_decrypted, decrypted, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_XEdDSA(void)
|
|
||||||
{
|
|
||||||
uint8_t private_key[32];
|
|
||||||
uint8_t x_public_key[32];
|
|
||||||
uint8_t ed_private_key[32];
|
|
||||||
uint8_t ed_public_key[32];
|
|
||||||
uint8_t ed_public_key2[32];
|
|
||||||
uint8_t message[] = "This is a test!";
|
|
||||||
uint8_t message2[] = "This is a test.";
|
|
||||||
uint8_t signature[64];
|
|
||||||
uint32_t fromNode = 0x1234;
|
|
||||||
uint32_t packetId = 0xDEADBEEF;
|
|
||||||
uint32_t portnum = 1;
|
|
||||||
for (int times = 0; times < 10; times++) {
|
|
||||||
printf("Start of time %u\n", times);
|
|
||||||
crypto->generateKeyPair(x_public_key, private_key);
|
|
||||||
XEdDSA::priv_curve_to_ed_keys(private_key, ed_private_key, ed_public_key);
|
|
||||||
crypto->curve_to_ed_pub(x_public_key, ed_public_key2);
|
|
||||||
TEST_ASSERT_EQUAL_MEMORY(ed_public_key, ed_public_key2, 32);
|
|
||||||
|
|
||||||
// Sign and verify with metadata
|
|
||||||
TEST_ASSERT(crypto->xeddsa_sign(fromNode, packetId, portnum, message, sizeof(message), signature));
|
|
||||||
TEST_ASSERT(crypto->xeddsa_verify(x_public_key, fromNode, packetId, portnum, message, sizeof(message), signature));
|
|
||||||
|
|
||||||
// Different payload fails
|
|
||||||
TEST_ASSERT_FALSE(
|
|
||||||
crypto->xeddsa_verify(x_public_key, fromNode, packetId, portnum, message2, sizeof(message2), signature));
|
|
||||||
|
|
||||||
// Different fromNode fails
|
|
||||||
TEST_ASSERT_FALSE(
|
|
||||||
crypto->xeddsa_verify(x_public_key, fromNode + 1, packetId, portnum, message, sizeof(message), signature));
|
|
||||||
|
|
||||||
// Different packetId fails
|
|
||||||
TEST_ASSERT_FALSE(
|
|
||||||
crypto->xeddsa_verify(x_public_key, fromNode, packetId + 1, portnum, message, sizeof(message), signature));
|
|
||||||
|
|
||||||
// Different portnum fails
|
|
||||||
TEST_ASSERT_FALSE(
|
|
||||||
crypto->xeddsa_verify(x_public_key, fromNode, packetId, portnum + 1, message, sizeof(message), signature));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_AES_CTR(void)
|
void test_AES_CTR(void)
|
||||||
{
|
{
|
||||||
uint8_t expected[32];
|
uint8_t expected[32];
|
||||||
@@ -235,7 +192,6 @@ void setup()
|
|||||||
RUN_TEST(test_DH25519);
|
RUN_TEST(test_DH25519);
|
||||||
RUN_TEST(test_AES_CTR);
|
RUN_TEST(test_AES_CTR);
|
||||||
RUN_TEST(test_PKC);
|
RUN_TEST(test_PKC);
|
||||||
RUN_TEST(test_XEdDSA);
|
|
||||||
exit(UNITY_END()); // stop unit testing
|
exit(UNITY_END()); // stop unit testing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ lib_deps =
|
|||||||
https://github.com/mverch67/libpax/archive/6f52ee989301cdabaeef00bcbf93bff55708ce2f.zip
|
https://github.com/mverch67/libpax/archive/6f52ee989301cdabaeef00bcbf93bff55708ce2f.zip
|
||||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||||
lewisxhe/XPowersLib@0.3.3
|
lewisxhe/XPowersLib@0.3.3
|
||||||
# renovate: datasource=git-refs depName=meshtastic/Crypto packageName=https://github.com/meshtastic/Crypto gitBranch=master
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
rweather/Crypto@0.4.0
|
||||||
|
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
segger_rtt
|
segger_rtt
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ build_flags =
|
|||||||
-I variants/esp32s3/ELECROW-ThinkNode-G3
|
-I variants/esp32s3/ELECROW-ThinkNode-G3
|
||||||
-mfix-esp32-psram-cache-issue
|
-mfix-esp32-psram-cache-issue
|
||||||
|
|
||||||
|
custom_sdkconfig =
|
||||||
|
${esp32s3_base.custom_sdkconfig}
|
||||||
|
CONFIG_ETH_ENABLED=y
|
||||||
|
CONFIG_ARDUINO_SELECTIVE_Ethernet=y
|
||||||
|
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
Ethernet
|
Ethernet
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ build_flags =
|
|||||||
-I variants/esp32s3/ELECROW-ThinkNode-M7
|
-I variants/esp32s3/ELECROW-ThinkNode-M7
|
||||||
-mfix-esp32-psram-cache-issue
|
-mfix-esp32-psram-cache-issue
|
||||||
|
|
||||||
|
custom_sdkconfig =
|
||||||
|
${esp32s3_base.custom_sdkconfig}
|
||||||
|
CONFIG_ETH_ENABLED=y
|
||||||
|
CONFIG_ARDUINO_SELECTIVE_Ethernet=y
|
||||||
|
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
Ethernet
|
Ethernet
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef Pins_Arduino_h
|
||||||
|
#define Pins_Arduino_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define USB_VID 0x303a
|
||||||
|
#define USB_PID 0x1001
|
||||||
|
|
||||||
|
static const uint8_t TX = 43;
|
||||||
|
static const uint8_t RX = 44;
|
||||||
|
|
||||||
|
static const uint8_t SDA = 7;
|
||||||
|
static const uint8_t SCL = 6;
|
||||||
|
|
||||||
|
static const uint8_t SS = 39;
|
||||||
|
static const uint8_t MOSI = 47;
|
||||||
|
static const uint8_t MISO = 38;
|
||||||
|
static const uint8_t SCK = 40;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
[thinknode_m9_base]
|
||||||
|
extends = esp32s3_base
|
||||||
|
board = crowpanel
|
||||||
|
board_check = true
|
||||||
|
board_build.partitions = default_16MB.csv
|
||||||
|
upload_protocol = esptool
|
||||||
|
build_src_filter =
|
||||||
|
${esp32s3_base.build_src_filter}
|
||||||
|
+<../variants/esp32s3/ELECROW-ThinkNode-M9>
|
||||||
|
build_flags =
|
||||||
|
${esp32s3_base.build_flags}
|
||||||
|
-I variants/esp32s3/ELECROW-ThinkNode-M9
|
||||||
|
-D ELECROW_ThinkNode_M9
|
||||||
|
-D BOARD_HAS_PSRAM
|
||||||
|
-D HAS_SDCARD=1
|
||||||
|
-D SDCARD_USE_SPI1
|
||||||
|
-D SDCARD_USER_SPI_BEGIN
|
||||||
|
-D HAS_SCREEN=1
|
||||||
|
; -L variants/esp32s3/ELECROW-ThinkNode-M9/lib
|
||||||
|
; -lqst
|
||||||
|
lib_deps = ${esp32s3_base.lib_deps}
|
||||||
|
lovyan03/LovyanGFX@1.2.19
|
||||||
|
lewisxhe/SensorLib@0.3.4
|
||||||
|
|
||||||
|
[env:thinknode_m9]
|
||||||
|
extends = thinknode_m9_base
|
||||||
|
build_flags =
|
||||||
|
${thinknode_m9_base.build_flags}
|
||||||
|
|
||||||
|
[env:thinknode_m9-tft]
|
||||||
|
extends = thinknode_m9_base
|
||||||
|
build_flags =
|
||||||
|
${thinknode_m9_base.build_flags}
|
||||||
|
-D RADIOLIB_SPI_PARANOID=0
|
||||||
|
-D CONFIG_DISABLE_HAL_LOCKS=1
|
||||||
|
-D HAS_TFT=1
|
||||||
|
-D USE_PACKET_API
|
||||||
|
-D USE_PIN_BUZZER=PIN_BUZZER
|
||||||
|
-D LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
-D LV_CONF_INCLUDE_SIMPLE
|
||||||
|
-D LV_COMP_CONF_INCLUDE_SIMPLE
|
||||||
|
-D LV_USE_SYSMON=0
|
||||||
|
-D LV_USE_PROFILER=0
|
||||||
|
-D LV_USE_PERF_MONITOR=0
|
||||||
|
-D LV_USE_MEM_MONITOR=0
|
||||||
|
-D LV_USE_LOG=0
|
||||||
|
-D LV_BUILD_TEST=0
|
||||||
|
-D USE_LOG_DEBUG
|
||||||
|
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||||
|
-D RAM_SIZE=5120
|
||||||
|
-D LGFX_DRIVER_TEMPLATE
|
||||||
|
-D LGFX_DRIVER=LGFX_GENERIC
|
||||||
|
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_GENERIC.h\"
|
||||||
|
-D VIEW_320x240
|
||||||
|
-D LGFX_PANEL=ST7789
|
||||||
|
-D DISPLAY_SIZE=320x240
|
||||||
|
-D LGFX_ROTATION=3
|
||||||
|
-D LGFX_CFG_HOST=SPI3_HOST
|
||||||
|
-D LGFX_PIN_SCK=40
|
||||||
|
-D LGFX_PIN_MOSI=47
|
||||||
|
-D LGFX_PIN_DC=15
|
||||||
|
-D LGFX_PIN_CS=16
|
||||||
|
-D LGFX_PIN_BL=17
|
||||||
|
-D LGFX_PIN_RST=14
|
||||||
|
-D LGFX_SCREEN_WIDTH=240
|
||||||
|
-D LGFX_SCREEN_HEIGHT=320
|
||||||
|
-D LGFX_INVERT_LIGHT=true
|
||||||
|
-D SPI_FREQUENCY=80000000
|
||||||
|
-D MAP_FULL_REDRAW
|
||||||
|
|
||||||
|
lib_deps =
|
||||||
|
${thinknode_m9_base.lib_deps}
|
||||||
|
${device-ui_base.lib_deps}
|
||||||
|
https://github.com/mverch67/ESPWebDAV/archive/a57dedfac243b1c47db90c712db664f896752467.zip
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#include "RadioLib.h"
|
||||||
|
|
||||||
|
static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
|
||||||
|
|
||||||
|
static const Module::RfSwitchMode_t rfswitch_table[] = {
|
||||||
|
// mode DIO5 DIO6
|
||||||
|
{LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
|
||||||
|
{LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
|
||||||
|
{LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
|
||||||
|
{LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
|
||||||
|
};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
void earlyInitVariant()
|
||||||
|
{
|
||||||
|
pinMode(LORA_CS, OUTPUT);
|
||||||
|
digitalWrite(LORA_CS, HIGH);
|
||||||
|
pinMode(SDCARD_CS, OUTPUT);
|
||||||
|
digitalWrite(SDCARD_CS, HIGH);
|
||||||
|
pinMode(TFT_CS, OUTPUT);
|
||||||
|
digitalWrite(TFT_CS, HIGH);
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||||
|
#define PRESET_MESSAGE_MODULE_ENABLE 1
|
||||||
|
|
||||||
|
/*Power*/
|
||||||
|
#define VEXT_ENABLE 18
|
||||||
|
#define VEXT_ON_VALUE LOW
|
||||||
|
#define PIN_GPS_EN 11
|
||||||
|
#define GPS_EN_ACTIVE LOW
|
||||||
|
|
||||||
|
/*Wire Interface*/
|
||||||
|
#define WIRE_INTERFACES_COUNT 2
|
||||||
|
// I2C keybuttons
|
||||||
|
#define I2C_SCL1 21
|
||||||
|
#define I2C_SDA1 20
|
||||||
|
#define KB_INT 12
|
||||||
|
// I2C peripheral`
|
||||||
|
#define I2C_SCL 6
|
||||||
|
#define I2C_SDA 7
|
||||||
|
|
||||||
|
/*LED*/
|
||||||
|
#define PIN_LED 13
|
||||||
|
#define KB_LED 46
|
||||||
|
|
||||||
|
/*BUZZER*/
|
||||||
|
#define PIN_BUZZER 9
|
||||||
|
|
||||||
|
/*CHARGE_CHECK*/
|
||||||
|
#define DONE 8
|
||||||
|
#define EXT_PWR_DETECT 1
|
||||||
|
#define EXT_CHRG_DETECT 1
|
||||||
|
#define EXT_CHRG_DETECT_VALUE LOW
|
||||||
|
|
||||||
|
/*GPS*/
|
||||||
|
#define HAS_GPS 1
|
||||||
|
#define GPS_L76K
|
||||||
|
#define GPS_BAUDRATE 9600
|
||||||
|
#define PIN_GPS_RESET 5
|
||||||
|
#define PIN_GPS_PPS 4
|
||||||
|
#define PIN_GPS_STANDBY 10 // An output to wake GPS, low means allow sleep, high means force wake
|
||||||
|
#define GPS_TX_PIN 3
|
||||||
|
#define GPS_RX_PIN 2
|
||||||
|
#define GPS_THREAD_INTERVAL 50
|
||||||
|
|
||||||
|
/*SPI*/
|
||||||
|
#define SPI_MOSI 47
|
||||||
|
#define SPI_SCK 40
|
||||||
|
#define SPI_MISO 38
|
||||||
|
|
||||||
|
/*SD Card*/
|
||||||
|
#define SDCARD_CS 48
|
||||||
|
#define SD_SPI_FREQUENCY 80000000U
|
||||||
|
|
||||||
|
/*Screen*/
|
||||||
|
// #define USE_ST7789 1
|
||||||
|
#define ST7789_CS 16
|
||||||
|
#define ST7789_RS 15
|
||||||
|
#define ST7789_TE 19
|
||||||
|
#define ST7789_SDA SPI_MOSI // MOSI
|
||||||
|
#define ST7789_SCK SPI_SCK
|
||||||
|
#define ST7789_RESET 14
|
||||||
|
#define ST7789_MISO SPI_MISO
|
||||||
|
#define ST7789_BUSY -1
|
||||||
|
#define ST7789_BL 17
|
||||||
|
#define ST7789_SPI_HOST SPI3_HOST
|
||||||
|
#define SPI_FREQUENCY 80000000
|
||||||
|
#define SPI_READ_FREQUENCY 16000000
|
||||||
|
|
||||||
|
#define USE_TFTDISPLAY 1
|
||||||
|
#define TFT_CS ST7789_CS
|
||||||
|
#define TFT_BL ST7789_BL
|
||||||
|
#define TFT_HEIGHT 320
|
||||||
|
#define TFT_WIDTH 240
|
||||||
|
#define TFT_OFFSET_X 0
|
||||||
|
#define TFT_OFFSET_Y 0
|
||||||
|
#define TFT_OFFSET_ROTATION 0
|
||||||
|
#define TFT_PWM_FREQ 44000
|
||||||
|
#define TFT_PWM_CHANNEL 7
|
||||||
|
#define TFT_INVERT_LIGHT true
|
||||||
|
#define TFT_BACKLIGHT_ON LOW
|
||||||
|
#define SCREEN_ROTATE
|
||||||
|
#define SCREEN_TRANSITION_FRAMERATE 5
|
||||||
|
#define BRIGHTNESS_DEFAULT 128
|
||||||
|
|
||||||
|
/*Lora radio*/
|
||||||
|
#define HW_SPI1_DEVICE
|
||||||
|
#define LORA_SCK SPI_SCK
|
||||||
|
#define LORA_MISO SPI_MISO
|
||||||
|
#define LORA_MOSI SPI_MOSI
|
||||||
|
#define LORA_CS 39
|
||||||
|
#define LORA_RESET 45
|
||||||
|
#define LORA_DIO0 41
|
||||||
|
|
||||||
|
#define USE_LR1110
|
||||||
|
#define LR1110_IRQ_PIN 42
|
||||||
|
#define LR1110_NRESET_PIN LORA_RESET
|
||||||
|
#define LR1110_BUSY_PIN LORA_DIO0
|
||||||
|
#define LR1110_SPI_NSS_PIN LORA_CS
|
||||||
|
#define LR1110_SPI_SCK_PIN LORA_SCK
|
||||||
|
#define LR1110_SPI_MOSI_PIN LORA_MOSI
|
||||||
|
#define LR1110_SPI_MISO_PIN LORA_MISO
|
||||||
|
#define LR11X0_DIO3_TCXO_VOLTAGE 3.3
|
||||||
|
#define LR11X0_DIO_AS_RF_SWITCH
|
||||||
|
|
||||||
|
/*RTC*/
|
||||||
|
#define PCF8563_RTC 0x51
|
||||||
@@ -25,8 +25,8 @@ lib_deps =
|
|||||||
${networking_extra.lib_deps}
|
${networking_extra.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
${environmental_base.lib_deps}
|
${environmental_base.lib_deps}
|
||||||
# renovate: datasource=git-refs depName=meshtastic/Crypto packageName=https://github.com/meshtastic/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
rweather/Crypto@0.4.0
|
||||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||||
lovyan03/LovyanGFX@1.2.21
|
lovyan03/LovyanGFX@1.2.21
|
||||||
; # renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main
|
; # renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ build_src_filter =
|
|||||||
lib_deps=
|
lib_deps=
|
||||||
${arduino_base.lib_deps}
|
${arduino_base.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
# renovate: datasource=github-tags depName=meshtastic/Crypto packageName=meshtastic/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
rweather/Crypto@0.4.0
|
||||||
|
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
BluetoothOTA
|
BluetoothOTA
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ lib_deps =
|
|||||||
${environmental_base.lib_deps}
|
${environmental_base.lib_deps}
|
||||||
${environmental_extra.lib_deps}
|
${environmental_extra.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
# renovate: datasource=github-tags depName=meshtastic/Crypto packageName=meshtastic/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
rweather/Crypto@0.4.0
|
||||||
|
|||||||
@@ -28,5 +28,5 @@ lib_deps =
|
|||||||
${environmental_base.lib_deps}
|
${environmental_base.lib_deps}
|
||||||
${environmental_extra.lib_deps}
|
${environmental_extra.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
# renovate: datasource=github-tags depName=meshtastic/Crypto packageName=meshtastic/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
rweather/Crypto@0.4.0
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ debug_tool = stlink
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
# renovate: datasource=git-refs depName=meshtastic/Crypto packageName=https://github.com/meshtastic/Crypto gitBranch=main
|
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
|
||||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
https://github.com/caveman99/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||||
|
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
OneButton
|
OneButton
|
||||||
|
|||||||
Reference in New Issue
Block a user