M
MeshTRX
All articles

Encryption in 39 bytes

9 min read

First, plainly: nothing in MeshTRX is encrypted today. Not voice, not messages, not files, not the coordinates in beacons. Anyone with the same radio on the same channel hears the whole conversation. The Bluetooth link between phone and radio is open too — the PIN there protects against someone connecting by accident, not against eavesdropping.

That is worth knowing before you decide what to use this kind of link for. Now, why it turned out this way and what we intend to do about it.

Why there is still no encryption

This was a matter of order, not a forgotten task. First we had to learn whether live voice actually travels over LoRa at a sensible distance, and whether anyone besides the author needed it. Encrypting a link that does not exist yet makes no sense.

That condition is met: the link works, strangers run it in the field, the range has been measured. So the task came back into the queue. And that is when it turned out that "turn on encryption" is not one task but three.

Hide the contents. So an outsider hears noise instead of speech.

Make sure the packet was not tampered with. A cipher alone does not do this: an attacker can flip bits, and the receiver will decrypt garbage without noticing. That needs an authentication tag.

Know who is one of us. That is about keys: where they come from, how they get onto devices, and what to do when one radio goes missing.

The first is nearly free. The second costs air time. The third is the hardest — and it is exactly what the community asked for: "encryption based on key exchange, so private channels can be created".

The budget: 39 bytes, not one more

A MeshTRX voice packet is 39 bytes: 7 of header and 32 of compressed speech. It goes out every 80 milliseconds, and that is not our choice but the codec's requirement.

Here is what happens to the air if cryptographic fields are added:

PacketTime on airChannel used
today, 39 bytes53.4 ms67%
+ 4-byte tag57.0 ms71%
+ 8-byte tag60.5 ms76%
+ 16-byte tag (full)71.3 ms89%
+ 32-byte public key85.6 ms107%

The last line is not a typo. A modern key-exchange public key (X25519) is 32 bytes, and a packet carrying it simply does not fit the channel: a hundred and seven per cent means the transmission cannot finish before the next one is due.

From which the first conclusions follow.

The encryption itself is free. A stream cipher does not change the length: 32 bytes of speech stay 32 bytes. Not a single extra byte on air.

A random nonce cannot be sent with every packet. Normally a cipher needs a unique nonce, carried alongside the data — that is 8 to 12 bytes more. But we already have a packet number in the header, and it is unique within a transmission: from that number, the sender address and the channel, both sides derive the same counter without transmitting anything.

A full tag is too expensive. Sixteen bytes is 89% of the channel for a single speaker: nothing is left for the repeater, which already runs at its limit. A truncated 4–8 byte tag is weaker but realistic in price. For text, files and commands, where packets are rare, the full tag is affordable.

Losses change the rules

There is another constraint that ordinary systems never face: our packets get lost. Half the voice through a repeater does not arrive, and that is normal operation.

So modes where each block depends on the previous one are out: lose one packet and the receiver cannot decrypt anything after it. We need a mode where each packet stands alone — losing one does not prevent reading the next.

Fortunately that matches how voice already works: every packet is self-contained, because otherwise speech would fall apart at the first loss. The packet number in the header, which today serves for reassembly and duplicate rejection, turns out to be a ready-made counter for the cipher.

What a shared channel key buys — and what it does not

The minimum workable option, and where we will start: one key per channel, set in the settings, stored in the device's memory.

What it closes off:

  • the neighbour with the same radio on the same channel no longer hears your conversation;
  • a random person who bought a module and flashed the firmware does not land in someone else's group by sharing a channel number;
  • recorded traffic cannot be decrypted after the fact without the key.

What it does not close off, and this must be said plainly:

  • anyone who has the key. There is one key for the group; when someone leaves, everyone has to change it;
  • a leak through the device. Physical access to the board is access to the key in its memory;
  • the fact of transmission. Radio direction finding sees that someone came on air, on what frequency and for how long, without understanding a word. Against triangulation, encryption does not help at all;
  • tampering, if there is no tag. Without an authentication code an outsider can inject garbage that turns into a crackle at your end.

This is an honest "the neighbour will not overhear" level, not "someone determined will not read it". Confusing the two is dangerous: a person who believes in protection that is not there takes more risk than one who knows the air is open.

Private channels: a separate problem

The request from the community went beyond a shared key: key exchange and private channels that only your own people join.

Here we hit that table row again. Classic key exchange means transmitting 32 bytes of public key, which is more than one packet. Splitting it across two or three is possible, but the exchange then has to be made reliable: confirmations, retries, and protection against a man in the middle. On air, where half the packets are lost, that is real work.

So it makes sense to separate the methods by situation:

  • the key is typed in or read off a screen. Boring, but reliable: the exchange happens off air, so there is nothing to overhear. For a group that gathers before heading out, that is enough;
  • exchange on first meeting over Bluetooth. The radios are side by side and the phone sees both — the key travels the short way, bypassing the radio channel;
  • exchange over the air — the most convenient and the most expensive. It is needed when people cannot be gathered in one place, and it should be built last, once the simple methods already work.

And one detail that ties encryption to an entirely different task. To stop repeaters forwarding foreign traffic, the network needs zones — a notion of which station is one of ours. The group identifier that requires, and the group key, are the same field if they are built together. So zones and encryption are better not kept in separate corners: one task gives the other almost everything it needs.

What comes next

The order we consider right: first a shared channel key with a truncated tag for voice and a full one for text and files. Then key entry by hand and from a screen. Then groups and zones on the same identifier. Key exchange over the air, last.

We name no dates: the project is built in the evenings, and promising deadlines would be a lie. But as long as there is no encryption, we will keep saying so plainly — in the documentation, on the project page, and here.

How the network this will live in is built is in the repeater article. What the product can already do is in the documentation. The best place to argue about what key exchange should look like is the Telegram group: this article was largely written from its questions.