Refine unsigned packet rejection logic in Router

This commit is contained in:
Ben Meadors
2026-05-22 12:58:37 -05:00
parent 01bdb91e4e
commit cecd0e94e0
4 changed files with 20 additions and 11 deletions

View File

@@ -133,8 +133,9 @@ void CryptoEngine::curve_to_ed_pub(const uint8_t *curve_pubkey, uint8_t *ed_pubk
// 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.
// format of Ed25519 public keys only in a single sign bit. XEdDSA always normalizes the Ed25519 public
// key to a sign bit of zero (the signer negates its key pair when needed), so this function clears the
// sign bit unconditionally below instead of taking it as an input.
fe u, y;
fe one;
fe u_minus_one, u_plus_one, u_plus_one_inv;

View File

@@ -2775,7 +2775,10 @@ bool NodeDB::generateCryptoKeyPair(const uint8_t *privateKey)
}
bool keygenSuccess = false;
bool lowEntropy = checkLowEntropyPublicKey(config.security.public_key);
// Record whether the stored key is a known compromised/low-entropy key so main.cpp can warn the
// user. A detected low-entropy key is regenerated below, but the flag stays set so the
// "Compromised keys were detected and regenerated" notification still fires.
keyIsLowEntropy = checkLowEntropyPublicKey(config.security.public_key);
// If a specific private key was provided, use it
if (privateKey != nullptr) {
@@ -2793,7 +2796,7 @@ bool NodeDB::generateCryptoKeyPair(const uint8_t *privateKey)
}
}
// 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) {
else if (config.security.private_key.size == 32 && !keyIsLowEntropy) {
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)) {
@@ -2813,9 +2816,6 @@ bool NodeDB::generateCryptoKeyPair(const uint8_t *privateKey)
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);
@@ -2847,6 +2847,10 @@ bool NodeDB::createNewIdentity()
memset(node->public_key.bytes, 0, sizeof(node->public_key.bytes));
}
// Drop satellite-store entries (position/telemetry/environment/status) keyed by the retired
// node number so stale data isn't left attached to the old identity.
eraseNodeSatellites(oldNodeNum);
myNodeInfo.my_node_num = newNodeNum;
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());

View File

@@ -495,7 +495,7 @@ extern uint32_t error_address;
#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 10..31 reserved for future single-bit flags.
// Convenience accessors so call sites read like the old struct fields.
inline bool nodeInfoLiteHasUser(const meshtastic_NodeInfoLite *n)

View File

@@ -548,10 +548,14 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
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
// Unsigned packet — only reject the class of packet a signing node always signs:
// an unencrypted broadcast small enough to also carry a signature (see perhapsEncode()).
// Unicast packets and oversized broadcasts are never signed, so they must not be
// hard-failed here even if this node has signed before.
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->from);
if (node && nodeInfoLiteHasXeddsaSigned(node)) {
LOG_WARN("Dropping unsigned packet from 0x%08x that previously signed", p->from);
if (node && nodeInfoLiteHasXeddsaSigned(node) && isBroadcast(p->to) &&
p->decoded.payload.size + XEDDSA_SIGNATURE_SIZE < meshtastic_Constants_DATA_PAYLOAD_LEN) {
LOG_WARN("Dropping unsigned broadcast from 0x%08x that previously signed", p->from);
return DecodeState::DECODE_FAILURE;
}
}