Skip to main content
Version: 2.8

Security Implementation

Meshtastic firmware protects packets with several mechanisms, each with its own key and its own cost in bytes. This page describes how each one is built. For the reasoning behind them and their limits, see Meshtastic Encryption and Known Limitations.

MechanismKeyBytes added to a packet
Channel encryption, AES-CTRChannel PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel.0
Channel encryption, AES-CCMChannel PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel.12
Direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. encryption, AES-256-CCMPairwise key from X2551912
Packet signature, XEdDSANode's X25519 key pair66
Acknowledgment proof, HMAC-SHA256Pairwise key from X2551910

Every mechanism's overhead counts against the 255-byte LoRaLoRaA low-power, long-range wireless communication technology used by Meshtastic devices to enable communication over distances of several kilometers without the need for cellular, Wi-Fi, or other traditional network infrastructures. frame, which also carries the 16-byte packet header.

Channel encryption​

Channel messages are encrypted with AES-CTR under the channel's pre-shared keyPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel. (PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel.). A 16-byte key selects AES-128 and a 32-byte key selects AES-256. A key of length 0 disables encryption.

A single-byte PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel. is shorthand. 0 means no encryption, 1 selects the published default key, and 2 through 255 select the default key with that value minus one added to its last byte. These are public keys, not private ones. A secondary channel with no PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel. uses the primary channelPrimary ChannelThe channel used for a node's default traffic and its name in the mesh. It's channel 0, and its name helps set the node's frequency slot.'s key.

The 16-byte nonce is the packet ID as a 64-bit value in bytes 0–7, the sender's node number in bytes 8–11, and the block counter in bytes 12–15. AES-CTR adds no bytes and has no integrity check.

The one-byte channel hash in the packet header is the XOR of the channel name's bytes with the XOR of the key's bytes. When the stored name is empty, the hash uses the modem presetModem PresetThe tradeoff between range and speed for LoRa transmissions. Each preset sets the bandwidth, spreading factor, and coding rate, and nodes need the same preset to hear each other.'s name.

Authenticated channels (AES-CCM)​

On firmware 2.8.1 and later, setting use_aead (ChannelSettings field 8) switches a channel to AES-CCM under the same PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel.. The setting is experimental and off by default.

  • A 12-byte authentication tag follows the ciphertext.
  • The nonce is the same as for AES-CTR.
  • The associated data is the sender and destination node numbers, 4 bytes each. Hop fields are left out because relays change them.
  • The channel hash is XORed with 0xAE, so an AES-CCM channel and an AES-CTR channel with the same name and key have different hashes.

A receiver doesn't fall back to AES-CTR when the tag fails to verify. A node without the setting can't match the packet to a channel, so it relays the packet as it would any packet it can't decrypt. AES-CCM requires a PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel.: the firmware turns use_aead off on a channel with no key.

Direct messages​

Each node has an X25519 key pair. The firmware generates it once a LoRaLoRaA low-power, long-range wireless communication technology used by Meshtastic devices to enable communication over distances of several kilometers without the need for cellular, Wi-Fi, or other traditional network infrastructures. region is set.

To encrypt a direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it., the sender computes the X25519 shared secret from its private key and the recipient's public key, and hashes it with SHA-256 to get a 32-byte key. The firmware recomputes this key for every packet rather than storing it. The message is encrypted with AES-256-CCM under that key, with an 8-byte tag and no associated data. A 4-byte random extra nonce goes into the AES-CCM nonce and is appended after the tag, so the packet grows by 12 bytes.

A direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. is sent with a channel hash of 0 and pki_encrypted set. The firmware encrypts a packet this way when all of these hold:

  • The packet originates on this node and is addressed to one node.
  • The node has a private key and isn't in licensed mode.
  • The portnum isn't TRACEROUTE_APP, NODEINFO_APP, ROUTING_APP, or POSITION_APP, which use channel encryption.

If the firmware has no public key for the destination, it doesn't send the packet and returns PKI_SEND_FAIL_PUBLIC_KEY. The first KEY_VERIFICATION_APP packet is the exception: it goes out on the channel so the two nodes can exchange keys.

An acknowledgment for a direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. that arrived on a channel the node doesn't hold is sent encrypted as a direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. instead, so the sender still gets it. Relays can't read that acknowledgment.

A node that isn't in licensed mode rejects a TEXT_MESSAGE_APP packet addressed to it that was encrypted with a channel key.

Direct messagesDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. aren't signed. The AES-CCM tag authenticates them, because only the two nodes can derive the key. Either node could have produced the tag, so it doesn't prove authorship to anyone else.

Packet signing​

Nodes sign packets with XEdDSA, which produces Ed25519 signatures from the node's X25519 key pair, so there is no second key pair to manage. A verifier converts the sender's X25519 public key to the matching Ed25519 key.

The signature is 64 bytes in Data.xeddsa_signature (field 10), which encodes to 66 bytes. It sits inside Data, so it's encrypted along with the payload.

The signed bytes are:

version(1) | from(4) | id(4) | to(4) | portnum(4) | request_id(4) | reply_id(4) | emoji(4) | bitfield(4) | flags(1) | payload(N)

version is 0x01. In flags, bit 0 is want_response and bit 1 records whether bitfield is present. The signature doesn't cover the channel, the hop fields, next_hop, or relay_node, because relays change them.

The firmware signs a packet it originates when the packet isn't encrypted as a direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it., it's a broadcast or the node is in licensed mode, and the encoded Data with a signature plus the 16-byte header fits in 255 bytes. A packet that doesn't fit is sent unsigned. Any signature a client supplies is discarded first.

On receipt:

  • With a stored key for the sender, the firmware verifies the signature and drops the packet if it fails, under every policy. A valid signature marks the sender as a signer.
  • With no stored key, a signed NodeInfo is accepted as first contact only if the CRC-32 of its public key equals the sender's node number and the signature verifies under that key. The key is then stored.
  • A signature field that is neither empty nor 64 bytes is dropped.
  • An unsigned packet is handled by packet_signature_policy. Packets encrypted as direct messagesDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. pass every policy.

Clients see the result in MeshPacket.xeddsa_signed and NodeInfo.has_xeddsa_signed. DeviceMetadata.has_xeddsa reports whether the firmware build includes signing. STM32WL builds don't.

Acknowledgment proof​

On firmware 2.8.1 and later, an acknowledgment can prove that it came from the node that received the packet. Without a proof, anyone holding the channel key can forge one.

The proof is 8 bytes in Routing.ack_proof (field 4), which encodes to 10 bytes:

ack_proof = HMAC-SHA256(shared_key, "ack" | LE32(from) | LE32(to) | LE32(request_id) | routing)[0..8)

shared_key is the same SHA-256 of the X25519 secret used for direct messagesDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it.. routing is the encoded Routing message without the proof. Covering the Routing bytes stops a key holder from turning a successful acknowledgment into a failure.

The recipient attaches a proof when it holds the sender's public key. The sender checks it against the key of the node it addressed, not the key of whoever the acknowledgment claims to be from. The firmware reports the result to the client in MeshPacket.ack_proof_status: ACK_PROOF_ABSENT, ACK_PROOF_VALID, ACK_PROOF_INVALID, or ACK_PROOF_NO_KEY. Only ACK_PROOF_VALID means the recipient received the packet.

The proof is advisory. The firmware still accepts an acknowledgment with a missing or invalid proof, and an overheard rebroadcast still stops retransmission without one.

Node identity​

A node's number is the CRC-32 of its 32-byte public key. Until the node has a key, for example while its region is unset, the number comes from its MAC address. Generating a new key gives the node a new number.

The firmware checks that a node number matches its key only for a signed first-contact NodeInfo. When a known node advertises a different public key, the firmware drops that NodeInfo. When a remote node advertises this node's own public key, the firmware notifies the client that the key may be compromised.

Admin messages​

A node accepts a remote admin message only if it arrives encrypted as a direct messageDirect MessageA message sent to one node rather than broadcast to a channel. It's encrypted with a key that only the sender and the recipient can compute, so other nodes can relay it but can't read it. from a node whose public key matches one of up to three admin_key entries. A message that changes state must also carry the node's 8-byte session passkey. A passkey is valid for 300 seconds after it's issued, and the node issues a new one once the current one is older than 150 seconds.

The legacy admin channel, a channel named admin in any letter case, works only when admin_channel_enabled is set. Managed mode requires at least one admin key.

Trust model and operational security​

The cryptographic protections described above defend messages as they traverse the LoRaLoRaA low-power, long-range wireless communication technology used by Meshtastic devices to enable communication over distances of several kilometers without the need for cellular, Wi-Fi, or other traditional network infrastructures. mesh. They sit on top of two assumptions that operators should understand: the link between a node and a directly connected client is trusted, and node identity is established via Trust On First Use rather than a signing authority.

Local trust boundary​

A client connected to a node over Bluetooth, USBUSB (Universal Serial Bus). The wired connection used to power a device, flash firmware onto it, and communicate with it over serial. serial, Wi-Fi, or Ethernet has full local API access. From that local connection, a client can read decrypted channel traffic, send messages as the node, change configuration (subject to managed mode), and read the node's private key for backup. The on-air cryptography does not extend below this boundary. Anything inside scope of the local trust relationship (a shared LAN, a USBUSB (Universal Serial Bus). The wired connection used to power a device, flash firmware onto it, and communicate with it over serial. cable to an untrusted host, a phone paired over Bluetooth) should be treated as an extension of the node itself.

Node identity: trust on first use​

Meshtastic uses a Trust On First Use (TOFU) model for node identity. There is no central authority that signs node keys, so the first public key a node hears for a given NodeNum is the one it binds to that NodeNum going forward. This is a hard requirement of operating as a decentralized ad-hoc mesh.

Client applications and the firmware include mitigations for the consequences of TOFU, such as favoriting nodes to prevent NodeDB rolloff and surfacing public-key changes in the UI. Deriving the node number from the public key and signing broadcasts narrow it further. For NodeDB limits and spoofing edge cases, see Known Limitations.

Local administration over Wi-Fi​

warning

Administering a Meshtastic node over an untrusted Wi-Fi network, including setting Wi-Fi credentials on an ESP32ESP32A chipset of microcontroller made/designed by Espressif, used by a number of devices. Higher power usage than nRF52, but often cheaper and supports Wi-Fi if desired., is not recommended. All local admin traffic, not just the initial credential write, is exposed to that network.

When a client reaches a node over Wi-Fi or Ethernet, that connection is part of the trust boundary described above. If the underlying network is untrusted (open Wi-Fi, a network shared with parties you do not control), config writes, key reads, decrypted message traffic, and the Wi-Fi PSKPSK (Pre-Shared Key). A secret code or passphrase used in Meshtastic channels for encryption, ensuring that only devices with the matching PSK can communicate within that specific channel. itself during provisioning are all exposed on that network. Setting network.wifi_ssid and network.wifi_psk over an untrusted Wi-Fi connection is a common case of this exposure but not the only one.

The recommended pattern is to provision and administer nodes over a trusted channel: Bluetooth from a phone, USBUSB (Universal Serial Bus). The wired connection used to power a device, flash firmware onto it, and communicate with it over serial. serial to a trusted host, or Remote Administration over the mesh itself.