Overview
HexDroid supports optional end-to-end encryption (E2EE) of message content, configured per channel or per private conversation. This is separate from, and layered on top of, your TLS connection to the server.
TLS (transport)
Encrypts the link between your phone and the IRC server. The server, and any bouncer in between sees your messages in the clear. Protects against network eavesdroppers only.
E2EE (content)
Encrypts the message text itself before it leaves your device. The server, the bouncer, and other channel members without the key see only ciphertext. Only holders of the shared key can read it.
Two schemes
Pick per target in the encryption dialog. The padlock glyph on each message tells you which scheme delivered it.
| Scheme | Wire prefix | Use it for |
|---|---|---|
| AES-256-GCM 🔒 padlock |
+AGM |
The modern default. HexDroid-to-HexDroid, or HexDroid-to-HexChat via the companion plugin. Authenticated encryption with a fresh random nonce per message and replay-binding to the channel name. Recommended for all new conversations. |
| Blowfish 🐟 fish |
+OK |
Legacy FiSH compatibility, for talking to people using HexChat's fishlim plugin or other old clients. Reads both ECB and CBC FiSH formats; sends CBC. Use only when the other side can't speak +AGM. |
+AGM whenever both sides support it.Set up AES-GCM (+AGM)
For you
- Open the channel or private conversation.
- Tap the menu (⋮) and choose Secure Chat.
- Leave the cipher set to AES-256-GCM and tap Generate key.
- Tap Reveal key, then Share to send the key to your contact through a channel you trust (Signal, in person via QR-from-another-app, etc.). The shared text includes the safety number for verification.
For other users
- Open the same channel/conversation and the same Encryption dialog.
- Paste the key into the import field (the Paste button pulls from the clipboard) and tap Import.
- Confirm the safety number shown on both devices is identical.
+AGM <base64>.Set up Blowfish (+OK / FiSH)
Blowfish uses a shared passphrase rather than a generated key, the same string both clients type. This matches how fishlim and other FiSH clients work.
- Open the Encryption dialog and switch the cipher to Blowfish.
- Type the agreed passphrase (4–56 characters) and tap Set.
- On the HexChat side, in the same channel, run
/setkey <passphrase>(fishlim).
HexChat interoperability
You can chat encrypted with desktop HexChat users in both schemes:
+AGM via plugin
Install the hexdroid_agm.py plugin (requires the Python cryptography package). It adds /AGM-GEN, /AGM-SET, /AGM-INFO and decrypts/encrypts +AGM messages transparently.
Generate on one side, /AGM-SET <target> <base64> on the other, verify safety numbers match.
+OK via fishlim
HexChat ships with the fishlim plugin for FiSH. Set the same passphrase on both sides: /setkey in HexChat, Blowfish passphrase in HexDroid.
HexDroid reads both the ECB and CBC FiSH wire formats and sends CBC.
+AGM wire-format specification are published in the HexDroid repository, so any client author can add +AGM support.Safety numbers
Each key has a short safety number (also called a fingerprint) such as K4XR-T9BS, derived from the key. It appears in the encryption dialog on every device that holds the key.
Compare it with your contact over a separate, trusted channel If both devices show the same safety number, you have the same key and no one tampered with it in transit. If they differ, do not send anything sensitive: re-share the key and investigate.
What it protects, and what it doesn't
Be realistic about the guarantees. HexDroid's E2EE is a pragmatic upgrade over plaintext IRC and FiSH
Protects against
- Passive eavesdropping by the IRC server, bouncer, or network operators
- Other channel members who don't have the key
- Message tampering, an altered
+AGMmessage fails its integrity check and is shown as a decryption failure rather than fake content - Replay of a ciphertext into a different channel (the channel name is bound into each
+AGMmessage)
Does not protect
- Identity anyone holding the shared key can read and send as if they had it; the key does not prove who you are
- Forward secrecy if a key is later compromised, past messages encrypted with it can be read
- Metadata who is talking to whom, when, and how often remains visible
- A compromised device decrypted text and the key live in memory on each device
Key storage & backup
- Keys are stored on-device in
EncryptedSharedPreferences, wrapped by the Android Keystore. TThe same protection used for SASL passwords. - Keys are excluded from backups. A backup/restore or reinstall will not carry your encryption keys, so you'll re-share them after moving devices. This is deliberate: it keeps keys from ever leaving the device in a portable form.
- Deleting a network profile clears all of its encryption keys.
- Changing your device lock screen can invalidate the Keystore; if that happens, affected keys become unrecoverable and the channel quietly reverts to "no key configured" rather than misbehaving, just re-import.
Troubleshooting
| Symptom | Cause & fix |
|---|---|
You see +AGM <base64> instead of text | No key configured on your side for that target, or your key doesn't match the sender's. Open the Encryption dialog, import the correct key, and confirm safety numbers match. |
| Messages show a decryption-failure indicator | The keys differ between sender and receiver, or the message was tampered with. Re-share the key and compare safety numbers. |
| Safety numbers don't match | You don't have identical keys. Do not send anything sensitive. re-share the key over a trusted channel. |
| Works on one phone, not another | Each device needs the key imported separately,. keys are per device and never travel through backups. |
HexChat user can't read your +AGM | They need the hexdroid_agm.py plugin and the cryptography Python package, plus the same key set via /AGM-SET. |