mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-13 23:21:19 +10:00
* Test commit for XEdDSA support * Update to Crypto lib in Meshtatic org * Generate a new node identity on key generation (#7628) * Generate a new node identity on key generation * Fixes * Fixes * Fixes * Messed up * Fixes * Update src/modules/AdminModule.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/mesh/NodeDB.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Figured it out! * Cleanup * Update src/mesh/NodeDB.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/mesh/NodeDB.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/modules/AdminModule.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update crypto commit hash * Some fixes for xeddsa pr (#9610) * fix: add null check for getMeshNode() in NodeInfoModule getMeshNode() can return nullptr for unknown nodes. Dereferencing without a check crashes the firmware when receiving NodeInfo from a node not yet in the database. * fix: enforce XEdDSA signature verification and prevent stripping Previously, failed signature verification still allowed the packet through, making signatures purely cosmetic. Now: - Failed verification drops the packet (DECODE_FAILURE) - Successfully verified nodes get HAS_XEDDSA_SIGNED bitfield set - Unsigned packets from previously-signing nodes are rejected - Log levels reduced from WARN/ERROR to DEBUG/WARN as appropriate * fix: include packet metadata in XEdDSA signature The signature now covers [fromNode | packetId | portnum | payload] instead of just the payload bytes. This prevents: - Replay attacks (different packetId fails verification) - Reattribution (different fromNode fails verification) - Portnum redirection (different portnum fails verification) Also adds a key initialization check to xeddsa_sign (returns false if XEdDSA keys are all zeros) and checks the return value in the encode path. * fix: handle existing key pair in AdminModule security config When a user provides both a valid private key and public key via admin config, the crypto engine's DH private key and owner public key were never loaded. DMs and XEdDSA signing would silently break. Add an else branch to load both keys into the crypto engine. * perf: cache Ed25519 public key conversion in xeddsa_verify curve_to_ed_pub() performs field element parsing, inversion, and multiplication on every call. Since packets from the same node tend to arrive in bursts, a single-entry cache avoids repeating this expensive conversion for consecutive packets from one sender. * fix: skip identity cleanup when node number is unchanged createNewIdentity() was called on every generateCryptoKeyPair(), including normal boots where the same key is regenerated. This caused unnecessary NodeDB writes and old-node cleanup logic to run when the node number hadn't actually changed. Also fixes only zeroing byte[0] of the old node's public key instead of clearing the entire array. * fix: replace hardcoded 120 with derived XEDDSA_SIGNATURE_SIZE constant The payload size check for XEdDSA signing used a magic number (120). Replace with a derivation from DATA_PAYLOAD_LEN and XEDDSA_SIGNATURE_SIZE so the limit adjusts automatically if constants change. This also increases the max signable payload from 120 to 169 bytes, which is still safe since the actual encoded size is checked after pb_encode. * fix: add const qualifiers to XEdDSA verify and curve_to_ed_pub inputs pubKey, payload, and signature parameters in xeddsa_verify are input-only and should not be modified. Same for curve_pubkey in curve_to_ed_pub. * chore: remove commented-out old Crypto dependency in portduino.ini * Leave out the admin module change for now --------- Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> * trunk * protobuf re-update * Protobufs * Merge resolution fix * Put XEDDSA on the right bit * NodeDB update to new nodeInfoLite accessors, etc * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Refine unsigned packet rejection logic in Router (#10534) * use hardware random to fill the first 32 signature bytes with entropy prior to signing. * Add XEdDSA packet-signing policy tests and update dependencies for macos * Minor fixes * integrate XEdDSA support and update dependencies across multiple modules --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Wessel <github@weebl.me> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
114 lines
4.2 KiB
C++
114 lines
4.2 KiB
C++
#pragma once
|
|
#include "AES.h"
|
|
#include "CTR.h"
|
|
#include "concurrency/LockGuard.h"
|
|
#include "configuration.h"
|
|
#include "mesh-pb-constants.h"
|
|
#include <Arduino.h>
|
|
#include <memory>
|
|
|
|
extern concurrency::Lock *cryptLock;
|
|
|
|
struct CryptoKey {
|
|
uint8_t bytes[32];
|
|
|
|
/// # of bytes, or -1 to mean "invalid key - do not use"
|
|
int8_t length;
|
|
};
|
|
|
|
/**
|
|
* see docs/software/crypto.md for details.
|
|
*
|
|
*/
|
|
|
|
#define MAX_BLOCKSIZE 256
|
|
#define TEST_CURVE25519_FIELD_OPS // Exposes Curve25519::isWeakPoint() for testing keys
|
|
#define XEDDSA_SIGNATURE_SIZE 64
|
|
|
|
class CryptoEngine
|
|
{
|
|
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);
|
|
virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey);
|
|
virtual bool ensurePkiKeys(meshtastic_Config_SecurityConfig &security, meshtastic_User &user);
|
|
#endif
|
|
#if !(MESHTASTIC_EXCLUDE_XEDDSA)
|
|
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
|
|
void setDHPrivateKey(uint8_t *_private_key);
|
|
// The remotePublic key parameter takes the public_key bytes container from
|
|
// a stored node header. NodeInfoLite is the on-device storage type since
|
|
// the slim refactor flattened UserLite into it.
|
|
virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtastic_NodeInfoLite_public_key_t remotePublic,
|
|
uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut);
|
|
virtual bool decryptCurve25519(uint32_t fromNode, meshtastic_NodeInfoLite_public_key_t remotePublic, uint64_t packetNum,
|
|
size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut);
|
|
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);
|
|
std::unique_ptr<AESSmall256> aes = nullptr;
|
|
|
|
#endif
|
|
|
|
/**
|
|
* 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);
|
|
|
|
/**
|
|
* Encrypt a packet
|
|
*
|
|
* @param bytes is updated in place
|
|
*/
|
|
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 = {};
|
|
#if !(MESHTASTIC_EXCLUDE_PKI)
|
|
uint8_t shared_key[32] = {0};
|
|
uint8_t private_key[32] = {0};
|
|
#if !(MESHTASTIC_EXCLUDE_XEDDSA)
|
|
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
|
|
*
|
|
* The NONCE is constructed by concatenating (from MSB to LSB):
|
|
* a 64 bit packet number (stored in little endian order)
|
|
* 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, uint32_t extraNonce = 0);
|
|
};
|
|
|
|
extern CryptoEngine *crypto; |