@@ -465,6 +465,8 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
465465
466466 if (!(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag ||
467467 p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)) {
468+ // Error returns from here own the packet, as the position-precision path below does.
469+ packetPool.release (p);
468470 return meshtastic_Routing_Error_BAD_REQUEST;
469471 }
470472
@@ -633,7 +635,9 @@ bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p)
633635 if (p->decoded .xeddsa_signature .size == XEDDSA_SIGNATURE_SIZE ) {
634636 meshtastic_NodeInfoLite_public_key_t senderKey = {0 , {0 }};
635637 meshtastic_NodeInfoLite *node = nodeDB->getMeshNode (p->from );
636- if (nodeDB->copyPublicKey (p->from , senderKey)) {
638+ // Authoritative keys only: verifying against an opportunistic cache key would let a planted
639+ // key mark its own node a signer, the trust loop #11116 closed on the decrypt path.
640+ if (nodeDB->copyPublicKeyAuthoritative (p->from , senderKey)) {
637641 p->xeddsa_signed =
638642 crypto->xeddsa_verify (senderKey.bytes , p->from , p->id , p->decoded .portnum , p->decoded .payload .bytes ,
639643 p->decoded .payload .size , p->decoded .xeddsa_signature .bytes );
@@ -681,20 +685,14 @@ bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p)
681685 if (compatible)
682686 return true ;
683687
684- // In Balanced, preserve legacy unsigned-unicast compatibility and only reject the class a
685- // signing node always signs: a non-PKI broadcast whose signed encoding would still fit the
686- // LoRa frame. Canonical sizing removes unknown protobuf fields before mirroring the
687- // sender-side signedDataFits() gate, so this counts the same fields that gate counted.
688- // Unicast packets and broadcasts too big to carry a signature are never signed, so they
689- // must not be hard-failed here even for a known signer (PKI already returned above).
690- // isKnownXeddsaSigner consults the warm tier too: a signer evicted from the hot store
691- // must not become impersonatable via unsigned broadcasts until it is re-heard.
692- if (nodeDB->isKnownXeddsaSigner (p->from ) && isBroadcast (p->to )) {
688+ // Balanced rejects only what a signer always signs: non-PKI broadcasts whose signed encoding
689+ // would have fit, plus unicasts on ham where licensed senders sign too. Mirrors perhapsEncode.
690+ if (nodeDB->isKnownXeddsaSigner (p->from ) && (isBroadcast (p->to ) || owner.is_licensed )) {
693691 size_t canonicalSize;
694692 if (!canonicalSignableSize (&p->decoded , &canonicalSize))
695693 return true ; // can't size it; never drop on a sizing failure
696694 if (canonicalSize + XEDDSA_SIGNATURE_FIELD_BYTES + MESHTASTIC_HEADER_LENGTH <= MAX_LORA_PAYLOAD_LEN ) {
697- LOG_WARN (" Dropping unsigned broadcast from 0x%08x that previously signed" , p->from );
695+ LOG_WARN (" Dropping unsigned packet from 0x%08x that previously signed" , p->from );
698696 return false ;
699697 }
700698 }
@@ -1172,7 +1170,12 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
11721170 *destKey.bytes );
11731171 return meshtastic_Routing_Error_PKI_FAILED;
11741172 }
1175- crypto->encryptCurve25519 (p->to , getFrom (p), destKey, p->id , numbytes, bytes, p->encrypted .bytes );
1173+ // On failure encrypted.bytes holds no ciphertext, so continuing would put the plaintext
1174+ // on the air labelled pki_encrypted.
1175+ if (!crypto->encryptCurve25519 (p->to , getFrom (p), destKey, p->id , numbytes, bytes, p->encrypted .bytes )) {
1176+ LOG_WARN (" PKI encryption failed for destination node 0x%08x" , p->to );
1177+ return meshtastic_Routing_Error_PKI_FAILED;
1178+ }
11761179 numbytes += MESHTASTIC_PKC_OVERHEAD ;
11771180 p->channel = 0 ;
11781181 p->pki_encrypted = true ;
0 commit comments