Harden XEdDSA unsigned-packet policy and add coverage (#10858)
Audit of the XEdDSA packet-signing implementation (#10478) surfaced several issues in when unsigned packets are accepted on receive or emitted on send. This fixes them and adds regression coverage. - Unicast NodeInfo exchange no longer breaks against signer nodes: the NodeInfoModule downgrade drop is gated to broadcasts, since senders never sign unicast (want_response replies, directed exchanges). - Replace the payload-size sign heuristic with an exact encoded-size gate (signedDataFits) and mirror it on the receive side, removing a dead band where 167-168 B broadcasts were signed then failed TOO_LARGE. - Extract the receive policy into checkXeddsaReceivePolicy() and apply it to plaintext-MQTT decoded downlink, which previously skipped signature verification and downgrade protection entirely. - Reject signatures whose length is neither 0 nor 64 as malformed, so a crafted partial signature can't inflate the size estimate and dodge the unsigned-downgrade drop. - Hold cryptLock on the MQTT verify path (shared Ed25519 key cache). - Clear any client-preset signature on packets we originate, on all builds. - Randomized (hedged) signing per the Signal XEdDSA spec: bump the meshtastic/Crypto pin to the build where XEdDSA::sign mixes 32 bytes of caller randomness into the nonce as Z (meshtastic/Crypto#3), and seed those bytes in xeddsa_sign from HardwareRNG (checked, with a seeded-CSPRNG fallback). test_crypto pins that repeated signs differ and both verify. Adds test coverage: test_packet_signing groups A-E (receive matrix, send policy, NodeInfo backstop, encoding invariants, decoded-ingress policy), test_mqtt end-to-end downlink cases, and a test_crypto randomization check.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "mesh/Channels.h"
|
||||
#include "mesh/CryptoEngine.h"
|
||||
#include "mesh/Router.h"
|
||||
#include "mesh/generated/meshtastic/mqtt.pb.h"
|
||||
#include "mesh/generated/meshtastic/telemetry.pb.h"
|
||||
@@ -131,6 +132,21 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
return;
|
||||
}
|
||||
p->channel = ch.index;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI) && !(MESHTASTIC_EXCLUDE_XEDDSA)
|
||||
// Already-decoded downlink skips perhapsDecode's crypto path entirely, so enforce the
|
||||
// signature policy here: verify a carried signature and apply unsigned-downgrade
|
||||
// protection for known signers. Without this, a peer on a plaintext broker could
|
||||
// impersonate a signing node with unsigned broadcasts. Hold cryptLock like the RF path
|
||||
// (perhapsDecode) does - checkXeddsaReceivePolicy -> xeddsa_verify mutates shared
|
||||
// CryptoEngine cache state, and MQTT ingress can run on a different task.
|
||||
{
|
||||
concurrency::LockGuard g(cryptLock);
|
||||
if (!checkXeddsaReceivePolicy(p.get())) {
|
||||
LOG_INFO("Ignore decoded message failing XEdDSA policy");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// PKI messages get accepted even if we can't decrypt
|
||||
|
||||
Reference in New Issue
Block a user