mirror of
https://github.com/meshtastic/firmware.git
synced 2026-09-29 17:55:25 -04:00
fix: don't wipe admin keys when regenerating the keypair (#11088)
A client's "regenerate keys" action sends a blank SecurityConfig carrying only the new private key rather than the config it read from the device, so assigning it wholesale cleared admin_key, is_managed, serial_enabled, debug_log_api_enabled, admin_channel_enabled and packet_signature_policy. Losing the admin keys locks the owner out of remote admin with no recourse but a physical connection to the node. Detect that bare-rotation shape and swap in just the keypair, leaving the rest of the security config intact. Deliberately clearing admin keys still works through a SET that leaves the private key alone. Fixes #11073
This commit is contained in:
1 parent
290967f739
commit
5cf346311c
2 files changed
+103
No files matched your search
@@ -815,6 +815,23 @@ static void reconcileAccelerometerThread(bool wasOn, bool nowOn, bool otherFeatu
|
||||
}
|
||||
#endif
|
||||
|
||||
// A "regenerate keys" client sends a blank SecurityConfig holding only the new private key, rather than the
|
||||
// config it read from us. Detect that shape - new private key, every other field at its proto default - so it
|
||||
// isn't mistaken for "and clear everything else".
|
||||
static bool isBareKeypairRotation(const meshtastic_Config_SecurityConfig &incoming,
|
||||
const meshtastic_Config_SecurityConfig ¤t)
|
||||
{
|
||||
if (incoming.private_key.size != 32)
|
||||
return false;
|
||||
if (current.private_key.size == 32 && memcmp(incoming.private_key.bytes, current.private_key.bytes, 32) == 0)
|
||||
return false;
|
||||
|
||||
return incoming.admin_key_count == 0 && !incoming.is_managed && !incoming.serial_enabled && !incoming.debug_log_api_enabled &&
|
||||
!incoming.admin_channel_enabled &&
|
||||
incoming.packet_signature_policy ==
|
||||
meshtastic_Config_SecurityConfig_PacketSignaturePolicy_PACKET_SIGNATURE_POLICY_COMPATIBLE;
|
||||
}
|
||||
|
||||
void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
||||
{
|
||||
auto changes = SEGMENT_CONFIG;
|
||||
@@ -1104,6 +1121,16 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
||||
incoming.private_key = config.security.private_key;
|
||||
incoming.public_key = config.security.public_key;
|
||||
}
|
||||
// Rotating the keypair must not drop the admin keys - that locks the owner out of remote admin with no
|
||||
// recourse but a physical connection. Clearing admin keys still works via a SET that leaves the private
|
||||
// key alone and sends an empty list.
|
||||
if (isBareKeypairRotation(incoming, config.security)) {
|
||||
LOG_INFO("Security set is a bare keypair rotation; preserving remaining security config");
|
||||
meshtastic_Config_SecurityConfig rotated = config.security;
|
||||
rotated.public_key = incoming.public_key; // usually empty; derived from the private key below
|
||||
rotated.private_key = incoming.private_key;
|
||||
incoming = rotated;
|
||||
}
|
||||
config.security = incoming;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// First provisioning (no key) generates one; a private key supplied without its public key derives it.
|
||||
|
||||
Reference in new issue
Block a user