mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-24 03:31:14 +00:00
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>
This commit is contained in:
@@ -67,25 +67,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
|
||||
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
|
||||
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
||||
if (!owner.is_licensed) {
|
||||
bool keygenSuccess = false;
|
||||
if (config.security.private_key.size == 32) {
|
||||
// public key is derived from private, so this will always have the same result.
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("Generate new PKI keys");
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Use consolidated key generation function
|
||||
nodeDB->generateCryptoKeyPair();
|
||||
config.lora.tx_enabled = true;
|
||||
initRegion();
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
|
||||
@@ -242,8 +242,6 @@ NodeDB::NodeDB()
|
||||
|
||||
// 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
|
||||
// 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();
|
||||
|
||||
// Set our board type so we can share it with others
|
||||
@@ -261,31 +259,18 @@ NodeDB::NodeDB()
|
||||
}
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
||||
|
||||
if (!owner.is_licensed && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
bool keygenSuccess = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Generate crypto keys if needed using consolidated function
|
||||
// Set my node num uint32 value to bytes from the public key (if we have one)
|
||||
// Generate identity and crypto keys if needed; this will create a new identity if one does not exist
|
||||
generateCryptoKeyPair(nullptr);
|
||||
#elif !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// Calculate Curve25519 public and private keys
|
||||
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
|
||||
owner.public_key.size = config.security.public_key.size;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
|
||||
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
||||
// 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
|
||||
// Include our owner in the node db under our nodenum
|
||||
@@ -1876,6 +1861,94 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
|
||||
return false;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// Remove the old node from the NodeDB
|
||||
uint32_t oldNodeNum = getNodeNum();
|
||||
meshtastic_NodeInfoLite *node = getMeshNode(oldNodeNum);
|
||||
|
||||
// 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);
|
||||
|
||||
if (node != NULL && myNodeInfo.my_node_num != oldNodeNum) {
|
||||
LOG_DEBUG("Old node num %u is now %u", oldNodeNum, myNodeInfo.my_node_num);
|
||||
node->is_ignored = true;
|
||||
node->has_device_metrics = false;
|
||||
node->has_position = false;
|
||||
node->user.public_key.size = 0;
|
||||
node->user.public_key.bytes[0] = 0;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
||||
info->user = TypeConversions::ConvertToUserLite(owner);
|
||||
info->has_user = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
@@ -270,6 +270,12 @@ class NodeDB
|
||||
|
||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||
|
||||
/// 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 restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||
int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
|
||||
|
||||
@@ -729,24 +729,8 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
config.lora = c.payload_variant.lora;
|
||||
// If we're setting region for the first time, init the region and regenerate the keys
|
||||
if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
if (!owner.is_licensed) {
|
||||
bool keygenSuccess = false;
|
||||
if (config.security.private_key.size == 32) {
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("Generate new PKI keys");
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Use consolidated key generation function
|
||||
nodeDB->generateCryptoKeyPair();
|
||||
config.lora.tx_enabled = true;
|
||||
initRegion();
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
@@ -754,7 +738,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
}
|
||||
if (strcmp(moduleConfig.mqtt.root, default_mqtt_root) == 0) {
|
||||
sprintf(moduleConfig.mqtt.root, "%s/%s", default_mqtt_root, myRegion->name);
|
||||
changes = SEGMENT_CONFIG | SEGMENT_MODULECONFIG;
|
||||
changes = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_NODEDATABASE | SEGMENT_DEVICESTATE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -767,22 +751,14 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
LOG_INFO("Set config: Security");
|
||||
config.security = c.payload_variant.security;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// If the client set the key to blank, go ahead and regenerate so long as we're not in ham mode
|
||||
if (!owner.is_licensed && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
if (config.security.private_key.size != 32) {
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
|
||||
} else {
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
config.security.public_key.size = 32;
|
||||
}
|
||||
}
|
||||
// Only regenerate keys if the private key is not 32 bytes
|
||||
if (config.security.private_key.size != 32) {
|
||||
nodeDB->generateCryptoKeyPair();
|
||||
}
|
||||
// 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) {
|
||||
nodeDB->generateCryptoKeyPair(config.security.private_key.bytes);
|
||||
}
|
||||
#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
|
||||
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)) {
|
||||
@@ -792,9 +768,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
sendWarning(warning);
|
||||
}
|
||||
|
||||
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;
|
||||
changes = SEGMENT_CONFIG | SEGMENT_DEVICESTATE | SEGMENT_NODEDATABASE;
|
||||
|
||||
requiresReboot = true;
|
||||
|
||||
break;
|
||||
case meshtastic_Config_device_ui_tag:
|
||||
|
||||
Reference in New Issue
Block a user