kopia lustrzana https://github.com/meshtastic/firmware
* Re-implement PKI from #1509 co-authored-by: edinnen <ethanjdinnen@protonmail.com> * Set the key lengnth to actually make PKI work. * Remove unused variable and initialize keys to null * move printBytes() to meshUtils * Don't reset PKI key son reboot unless needed. * Remove double encryption for PKI messages * Cleanup encrypt logic * Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now. * Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32 * Fix a crash when node is null * Don't send PKI encrypted packets while licensed * use chIndex 8 for PKI * Don't be so clever, that you corrupt incoming packets * Pass on channel 8 for now * Typo * Lock keys once non-zero * We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf. * Lighter approach to retaining known key * Attach the public key to PKI decrypted packets in device memory * Turn PKI back off for STM32 :( * Don't just memcp over a protobuf * Don't PKI encrypt nodeinfo packets * Add a bit more memory logging around nodeDB * Use the proper macro to refer to NODENUM_BROADCAST * Typo fix * Don't PKI encrypt ROUTING (naks and acks) * Adds SecurityConfig protobuf * Add admin messages over PKI * Disable PKI for the WIO-e5 * Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k * Add missed "has_security" * Add the admin_channel_enabled option * STM32 again * add missed configuration.h at the top of files * Add EXCLUDE_TZ and RTC * Enable PKI build on STM32 once again * Attempt 1 at moving PKI to aes-ccm * Fix buffers for encrypt/decrypt * Eliminate unused aes variable * Add debugging lines * Set hash to 0 for PKI * Fix debug lines so they don't print pointers. * logic fix and more debug * Rather important typo * Check for short packets before attempting decrypt * Don't forget to give cryptoEngine the keys! * Use the right scratch buffer * Cleanup * moar cleanups * Minor hardening * Remove some in-progress stuff * Turn PKI back off on STM32 * Return false * 2.5 protos * Sync up protos * Add initial cryptography test vector tests * re-add MINIMUM_SAFE_FREE_HEAP * Housekeeping and comment fixes * Add explanatory comment about weak dh25519 keys --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>pull/4436/head
rodzic
a767997cea
commit
74afd13171
|
@ -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
|
||||
|
|
|
@ -16,6 +16,7 @@ build_src_filter =
|
|||
|
||||
lib_deps=
|
||||
${arduino_base.lib_deps}
|
||||
rweather/Crypto@^0.4.0
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
|
@ -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
|
||||
|
|
|
@ -39,7 +39,7 @@ size_t RedirectablePrint::write(uint8_t c)
|
|||
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
if (!config.has_lora || config.device.serial_enabled)
|
||||
if (!config.has_lora || config.security.serial_enabled)
|
||||
dest->write(c);
|
||||
|
||||
return 1; // We always claim one was written, rather than trusting what the
|
||||
|
@ -180,7 +180,7 @@ void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format,
|
|||
void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
|
||||
if (config.security.bluetooth_logging_enabled && !pauseBluetoothLogging) {
|
||||
bool isBleConnected = false;
|
||||
#ifdef ARCH_ESP32
|
||||
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -193,6 +193,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#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 <http://www.gnu.org/licenses/>.
|
|||
#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
|
||||
|
|
|
@ -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;
|
||||
|
@ -1034,7 +1034,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)
|
||||
|
||||
|
@ -1057,7 +1057,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
|
|||
deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled;
|
||||
return deviceMetadata;
|
||||
}
|
||||
|
||||
#ifndef PIO_UNIT_TESTING
|
||||
void loop()
|
||||
{
|
||||
runASAP = false;
|
||||
|
@ -1102,4 +1102,5 @@ void loop()
|
|||
mainDelay.delay(delayMsec);
|
||||
}
|
||||
// if (didWake) LOG_DEBUG("wake!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -1,6 +1,176 @@
|
|||
#include "CryptoEngine.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
#include "aes-ccm.h"
|
||||
#include "meshUtils.h"
|
||||
#include <Crypto.h>
|
||||
#include <Curve25519.h>
|
||||
#include <SHA256.h>
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||
/**
|
||||
* Create a public/private key pair with Curve25519.
|
||||
*
|
||||
* @param pubKey The destination for the public key.
|
||||
* @param privKey The destination for the private key.
|
||||
*/
|
||||
void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey)
|
||||
{
|
||||
LOG_DEBUG("Generating Curve25519 key pair...\n");
|
||||
Curve25519::dh1(public_key, private_key);
|
||||
memcpy(pubKey, public_key, sizeof(public_key));
|
||||
memcpy(privKey, private_key, sizeof(private_key));
|
||||
}
|
||||
#endif
|
||||
uint8_t shared_key[32];
|
||||
void CryptoEngine::clearKeys()
|
||||
{
|
||||
memset(public_key, 0, sizeof(public_key));
|
||||
memset(private_key, 0, sizeof(private_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a packet's payload using a key generated with Curve25519 and SHA256
|
||||
* for a specific node.
|
||||
*
|
||||
* @param bytes is updated in place
|
||||
*/
|
||||
bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes,
|
||||
uint8_t *bytesOut)
|
||||
{
|
||||
uint8_t *auth;
|
||||
auth = bytesOut + numBytes;
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode);
|
||||
if (node->num < 1 || node->user.public_key.size == 0) {
|
||||
LOG_DEBUG("Node %d or their public_key not found\n", toNode);
|
||||
return false;
|
||||
}
|
||||
if (!crypto->setDHKey(toNode)) {
|
||||
return false;
|
||||
}
|
||||
initNonce(fromNode, packetNum);
|
||||
|
||||
// Calculate the shared secret with the destination node and encrypt
|
||||
printBytes("Attempting encrypt using nonce: ", nonce, 16);
|
||||
printBytes("Attempting encrypt using shared_key: ", shared_key, 32);
|
||||
aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut, auth);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a packet's payload using a key generated with Curve25519 and SHA256
|
||||
* for a specific node.
|
||||
*
|
||||
* @param bytes is updated in place
|
||||
*/
|
||||
bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut)
|
||||
{
|
||||
uint8_t *auth; // set to last 8 bytes of text?
|
||||
auth = bytes + numBytes - 8;
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode);
|
||||
|
||||
if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) {
|
||||
LOG_DEBUG("Node or its public key not found in database\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the shared secret with the sending node and decrypt
|
||||
if (!crypto->setDHKey(fromNode)) {
|
||||
return false;
|
||||
}
|
||||
initNonce(fromNode, packetNum);
|
||||
printBytes("Attempting decrypt using nonce: ", nonce, 16);
|
||||
printBytes("Attempting decrypt using shared_key: ", shared_key, 32);
|
||||
return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 8, nullptr, 0, auth, bytesOut);
|
||||
}
|
||||
|
||||
void CryptoEngine::setPrivateKey(uint8_t *_private_key)
|
||||
{
|
||||
memcpy(private_key, _private_key, 32);
|
||||
}
|
||||
/**
|
||||
* Set the PKI key used for encrypt, decrypt.
|
||||
*
|
||||
* @param nodeNum the node number of the node who's public key we want to use
|
||||
*/
|
||||
bool CryptoEngine::setDHKey(uint32_t nodeNum)
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
|
||||
if (node->num < 1 || node->user.public_key.size == 0) {
|
||||
LOG_DEBUG("Node %d or their public_key not found\n", nodeNum);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *pubKey = node->user.public_key.bytes;
|
||||
uint8_t local_priv[32];
|
||||
memcpy(shared_key, pubKey, 32);
|
||||
memcpy(local_priv, private_key, 32);
|
||||
// Calculate the shared secret with the specified node's public key and our private key
|
||||
// This includes an internal weak key check, which among other things looks for an all 0 public key and shared key.
|
||||
if (!Curve25519::dh2(shared_key, local_priv)) {
|
||||
LOG_WARN("Curve25519DH step 2 failed!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printBytes("DH Output: ", shared_key, 32);
|
||||
|
||||
/**
|
||||
* D.J. Bernstein reccomends hashing the shared key. We want to do this because there are
|
||||
* at least 128 bits of entropy in the 256-bit output of the DH key exchange, but we don't
|
||||
* really know where. If you extract, for instance, the first 128 bits with basic truncation,
|
||||
* then you don't know if you got all of your 128 entropy bits, or less, possibly much less.
|
||||
*
|
||||
* No exploitable bias is really known at that point, but we know enough to be wary.
|
||||
* Hashing the DH output is a simple and safe way to gather all the entropy and spread
|
||||
* it around as needed.
|
||||
*/
|
||||
crypto->hash(shared_key, 32);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash arbitrary data using SHA256.
|
||||
*
|
||||
* @param bytes
|
||||
* @param numBytes
|
||||
*/
|
||||
void CryptoEngine::hash(uint8_t *bytes, size_t numBytes)
|
||||
{
|
||||
SHA256 hash;
|
||||
size_t posn, len;
|
||||
uint8_t size = numBytes;
|
||||
uint8_t inc = 16;
|
||||
hash.reset();
|
||||
for (posn = 0; posn < size; posn += inc) {
|
||||
len = size - posn;
|
||||
if (len > inc)
|
||||
len = inc;
|
||||
hash.update(bytes + posn, len);
|
||||
}
|
||||
hash.finalize(bytes, 32);
|
||||
}
|
||||
|
||||
void CryptoEngine::aesSetKey(const uint8_t *key_bytes, size_t key_len)
|
||||
{
|
||||
if (aes) {
|
||||
delete aes;
|
||||
aes = nullptr;
|
||||
}
|
||||
if (key_len != 0) {
|
||||
aes = new AESSmall256();
|
||||
aes->setKey(key_bytes, key_len);
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoEngine::aesEncrypt(uint8_t *in, uint8_t *out)
|
||||
{
|
||||
aes->encryptBlock(out, in);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
concurrency::Lock *cryptLock;
|
||||
|
||||
void CryptoEngine::setKey(const CryptoKey &k)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "AES.h"
|
||||
#include "concurrency/LockGuard.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
extern concurrency::Lock *cryptLock;
|
||||
|
@ -26,9 +28,34 @@ class CryptoEngine
|
|||
uint8_t nonce[16] = {0};
|
||||
|
||||
CryptoKey key = {};
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
uint8_t private_key[32] = {0};
|
||||
#endif
|
||||
|
||||
public:
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
uint8_t public_key[32] = {0};
|
||||
#endif
|
||||
|
||||
virtual ~CryptoEngine() {}
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||
virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey);
|
||||
#endif
|
||||
void clearKeys();
|
||||
void setPrivateKey(uint8_t *_private_key);
|
||||
virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes,
|
||||
uint8_t *bytesOut);
|
||||
virtual bool decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut);
|
||||
virtual bool setDHKey(uint32_t nodeNum);
|
||||
virtual void hash(uint8_t *bytes, size_t numBytes);
|
||||
|
||||
virtual void aesSetKey(const uint8_t *key, size_t key_len);
|
||||
|
||||
virtual void aesEncrypt(uint8_t *in, uint8_t *out);
|
||||
AESSmall256 *aes = NULL;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Set the key used for encrypt, decrypt.
|
||||
|
@ -61,4 +88,4 @@ class CryptoEngine
|
|||
void initNonce(uint32_t fromNode, uint64_t packetId);
|
||||
};
|
||||
|
||||
extern CryptoEngine *crypto;
|
||||
extern CryptoEngine *crypto;
|
|
@ -19,6 +19,7 @@
|
|||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include <ErriezCRC32.h>
|
||||
#include <algorithm>
|
||||
|
@ -144,6 +145,31 @@ NodeDB::NodeDB()
|
|||
|
||||
// Include our owner in the node db under our nodenum
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// Calculate Curve25519 public and private keys
|
||||
printBytes("Old Pubkey", config.security.public_key.bytes, 32);
|
||||
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
|
||||
LOG_INFO("Using saved PKI keys\n");
|
||||
owner.public_key.size = config.security.public_key.size;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
|
||||
crypto->setPrivateKey(config.security.private_key.bytes);
|
||||
} else {
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||
LOG_INFO("Generating new PKI keys\n");
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
config.security.public_key.size = 32;
|
||||
config.security.private_key.size = 32;
|
||||
|
||||
printBytes("New Pubkey", config.security.public_key.bytes, 32);
|
||||
owner.public_key.size = 32;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||
#else
|
||||
LOG_INFO("No PKI keys set, and generation disabled!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
info->user = owner;
|
||||
info->has_user = true;
|
||||
|
||||
|
@ -258,6 +284,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;
|
||||
|
@ -280,6 +307,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
|
||||
|
@ -303,7 +338,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
|
||||
|
@ -796,6 +832,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);
|
||||
}
|
||||
|
@ -975,7 +1012,7 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
|
|||
|
||||
/** Update user info and channel for this node based on received user data
|
||||
*/
|
||||
bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t channelIndex)
|
||||
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
|
@ -983,6 +1020,12 @@ bool NodeDB::updateUser(uint32_t nodeId, const meshtastic_User &p, uint8_t chann
|
|||
}
|
||||
|
||||
LOG_DEBUG("old user %s/%s/%s, channel=%d\n", info->user.id, info->user.long_name, info->user.short_name, info->channel);
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one
|
||||
printBytes("Retaining Old Pubkey: ", info->user.public_key.bytes, 32);
|
||||
memcpy(p.public_key.bytes, info->user.public_key.bytes, 32);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Both of info->user and p start as filled with zero so I think this is okay
|
||||
bool changed = memcmp(&info->user, &p, sizeof(info->user)) || (info->channel != channelIndex);
|
||||
|
@ -1060,7 +1103,7 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
|||
meshtastic_NodeInfoLite *lite = getMeshNode(n);
|
||||
|
||||
if (!lite) {
|
||||
if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
|
||||
if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP)) {
|
||||
if (screen)
|
||||
screen->print("Warn: node database full!\nErasing oldest entry\n");
|
||||
LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes,
|
||||
|
@ -1086,6 +1129,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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ static MemoryDynamic<meshtastic_MeshPacket> staticPool;
|
|||
Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
|
||||
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
static uint8_t ScratchEncrypted[MAX_RHPACKETLEN];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -307,70 +308,105 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
|
|||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
|
||||
return true; // If packet was already decoded just return
|
||||
|
||||
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
|
||||
size_t rawSize = p->encrypted.size;
|
||||
if (rawSize > sizeof(bytes)) {
|
||||
LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize);
|
||||
return false;
|
||||
}
|
||||
bool decrypted = false;
|
||||
ChannelIndex chIndex = 0;
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||
memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize);
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// Attempt PKI decryption first
|
||||
if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && nodeDB->getMeshNode(p->from) != nullptr &&
|
||||
nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 &&
|
||||
rawSize > 8) {
|
||||
LOG_DEBUG("Attempting PKI decryption\n");
|
||||
|
||||
// Try to find a channel that works with this hash
|
||||
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
|
||||
// Try to use this hash/channel pair
|
||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||
// Try to decrypt the packet if we can
|
||||
size_t rawSize = p->encrypted.size;
|
||||
if (rawSize > sizeof(bytes)) {
|
||||
LOG_ERROR("Packet too large to attempt decription! (rawSize=%d > 256)\n", rawSize);
|
||||
return false;
|
||||
}
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||
|
||||
// printBytes("plaintext", bytes, p->encrypted.size);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) {
|
||||
LOG_INFO("PKI Decryption worked!\n");
|
||||
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
|
||||
LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n");
|
||||
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
|
||||
LOG_ERROR("Invalid portnum (bad psk?)!\n");
|
||||
rawSize -= 8;
|
||||
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) &&
|
||||
p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) {
|
||||
decrypted = true;
|
||||
LOG_INFO("Packet decrypted using PKI!\n");
|
||||
p->pki_encrypted = true;
|
||||
memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32);
|
||||
p->public_key.size = 32;
|
||||
// memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers
|
||||
// chIndex = 8;
|
||||
} else {
|
||||
// parsing was successful
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
|
||||
/* Not actually ever used.
|
||||
// Decompress if needed. jm
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
// Decompress the payload
|
||||
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
int decompressed_len;
|
||||
|
||||
memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
|
||||
|
||||
decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
|
||||
|
||||
// LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
|
||||
|
||||
memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len);
|
||||
|
||||
// Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP
|
||||
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
||||
} */
|
||||
|
||||
printPacket("decoded message", p);
|
||||
#if ENABLE_JSON_LOGGING
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
#elif ARCH_PORTDUINO
|
||||
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
|
||||
return false;
|
||||
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
|
||||
if (!decrypted) {
|
||||
// Try to find a channel that works with this hash
|
||||
for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
|
||||
// Try to use this hash/channel pair
|
||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||
// Try to decrypt the packet if we can
|
||||
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||
|
||||
// printBytes("plaintext", bytes, p->encrypted.size);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
|
||||
LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id);
|
||||
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
|
||||
LOG_ERROR("Invalid portnum (bad psk?)!\n");
|
||||
} else {
|
||||
decrypted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decrypted) {
|
||||
// parsing was successful
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
|
||||
/* Not actually ever used.
|
||||
// Decompress if needed. jm
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
// Decompress the payload
|
||||
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
int decompressed_len;
|
||||
|
||||
memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
|
||||
|
||||
decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
|
||||
|
||||
// LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
|
||||
|
||||
memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len);
|
||||
|
||||
// Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP
|
||||
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
||||
} */
|
||||
|
||||
printPacket("decoded message", p);
|
||||
#if ENABLE_JSON_LOGGING
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
#elif ARCH_PORTDUINO
|
||||
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return 0 for success or a Routing_Errror code for failure
|
||||
|
@ -384,7 +420,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
|||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
||||
|
||||
/* Not actually used, so save the cycles
|
||||
// Only allow encryption on the text message app.
|
||||
// TODO: Allow modules to opt into compression.
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) {
|
||||
|
||||
|
@ -432,10 +467,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
|||
|
||||
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||
p->channel = hash;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
|
||||
if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 &&
|
||||
p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP &&
|
||||
p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag
|
||||
LOG_DEBUG("Using PKI!\n");
|
||||
if (numbytes + 8 > MAX_RHPACKETLEN)
|
||||
return meshtastic_Routing_Error_TOO_LARGE;
|
||||
crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted);
|
||||
numbytes += 8;
|
||||
memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes);
|
||||
p->channel = 0;
|
||||
} else {
|
||||
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
}
|
||||
#else
|
||||
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
#endif
|
||||
|
||||
// Copy back into the packet and set the variant type
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
p->encrypted.size = numbytes;
|
||||
p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag;
|
||||
}
|
||||
|
@ -539,4 +592,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
|||
handleReceived(p);
|
||||
|
||||
packetPool.release(p);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Counter with CBC-MAC (CCM) with AES
|
||||
*
|
||||
* Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* 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
|
|
@ -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
|
|
@ -12,4 +12,6 @@ template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi
|
|||
#define STRNSTR
|
||||
#include <string.h>
|
||||
char *strnstr(const char *s, const char *find, size_t slen);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void printBytes(const char *label, const uint8_t *p, size_t numbytes);
|
|
@ -65,7 +65,29 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
|||
bool handled = false;
|
||||
assert(r);
|
||||
bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum();
|
||||
|
||||
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
|
||||
return handled;
|
||||
}
|
||||
meshtastic_Channel *ch = &channels.getByIndex(mp.channel);
|
||||
// Could tighten this up further by tracking the last poblic_key we went an AdminMessage request to
|
||||
// and only allowing responses from that remote.
|
||||
if (!((mp.from == 0 && !config.security.is_managed) ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_config_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_module_config_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_canned_message_module_messages_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_device_metadata_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_response_tag ||
|
||||
r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_response_tag ||
|
||||
r->which_payload_variant == meshtastic_NodeRemoteHardwarePinsResponse_node_remote_hardware_pins_tag ||
|
||||
(strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) ||
|
||||
(mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key.bytes, 32) == 0))) {
|
||||
LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant);
|
||||
return handled;
|
||||
}
|
||||
LOG_INFO("Handling admin payload %i\n", r->which_payload_variant);
|
||||
switch (r->which_payload_variant) {
|
||||
|
||||
/**
|
||||
|
@ -383,8 +405,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
|||
#endif
|
||||
if (config.device.button_gpio == c.payload_variant.device.button_gpio &&
|
||||
config.device.buzzer_gpio == c.payload_variant.device.buzzer_gpio &&
|
||||
config.device.debug_log_enabled == c.payload_variant.device.debug_log_enabled &&
|
||||
config.device.serial_enabled == c.payload_variant.device.serial_enabled &&
|
||||
config.device.role == c.payload_variant.device.role &&
|
||||
config.device.disable_triple_click == c.payload_variant.device.disable_triple_click &&
|
||||
config.device.rebroadcast_mode == c.payload_variant.device.rebroadcast_mode) {
|
||||
|
@ -501,6 +521,16 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
|||
config.has_bluetooth = true;
|
||||
config.bluetooth = c.payload_variant.bluetooth;
|
||||
break;
|
||||
case meshtastic_Config_security_tag:
|
||||
LOG_INFO("Setting config: Security\n");
|
||||
config.security = c.payload_variant.security;
|
||||
owner.public_key.size = config.security.public_key.size;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
|
||||
if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled &&
|
||||
config.security.serial_enabled == c.payload_variant.security.serial_enabled)
|
||||
requiresReboot = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
saveChanges(changes, requiresReboot);
|
||||
|
@ -828,7 +858,8 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
|
|||
conn.serial.is_connected = powerFSM.getState() == &stateSERIAL;
|
||||
#else
|
||||
conn.serial.is_connected = powerFSM.getState();
|
||||
#endif conn.serial.baud = SERIAL_BAUD;
|
||||
#endif
|
||||
conn.serial.baud = SERIAL_BAUD;
|
||||
|
||||
r.get_device_connection_status_response = conn;
|
||||
r.which_payload_variant = meshtastic_AdminMessage_get_device_connection_status_response_tag;
|
||||
|
@ -895,5 +926,5 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p)
|
|||
AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg)
|
||||
{
|
||||
// restrict to the admin channel for rx
|
||||
boundChannel = Channels::adminChannel;
|
||||
// boundChannel = Channels::adminChannel;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#include "CryptoEngine.h"
|
||||
|
||||
#include <unity.h>
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
// set stuff up here
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
// clean stuff up here
|
||||
}
|
||||
|
||||
void test_SHA256(void)
|
||||
{
|
||||
uint8_t hash2[32] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
|
||||
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
|
||||
uint8_t hash[32] = {0};
|
||||
crypto->hash(hash, 0);
|
||||
TEST_ASSERT_EQUAL_MEMORY(hash, hash2, 32);
|
||||
}
|
||||
void test_ECB_AES256(void)
|
||||
{
|
||||
uint8_t key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
|
||||
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4};
|
||||
uint8_t plain1[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a};
|
||||
uint8_t scratch[16] = {0};
|
||||
|
||||
uint8_t cipher1[] = {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8};
|
||||
crypto->aesSetKey(key, 32);
|
||||
crypto->aesEncrypt(plain1, scratch); // Does 16 bytes at a time
|
||||
TEST_ASSERT_EQUAL_MEMORY(scratch, cipher1, 16);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// NOTE!!! Wait for >2 secs
|
||||
// if board doesn't support software reset via Serial.DTR/RTS
|
||||
delay(2000);
|
||||
|
||||
UNITY_BEGIN(); // IMPORTANT LINE!
|
||||
RUN_TEST(test_SHA256);
|
||||
RUN_TEST(test_ECB_AES256);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
UNITY_END(); // stop unit testing
|
||||
}
|
|
@ -33,4 +33,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
|
Ładowanie…
Reference in New Issue