Skip to content

mindcrypt/crypto-telegramEncryptionFirefoxExtension

Repository files navigation

πŸ” Telegram Crypto

A Firefox extension that adds end-to-end AES-256-GCM encryption on top of Telegram Web. Per-chat toggle, automatic encryption on send, automatic decryption on receive, clickable links, mentions and hashtags preserved in decrypted view.

Manifest Browser Crypto KDF License


✨ Features

  • Per-chat toggle β€” encryption activates only in chats you explicitly enable; other chats remain unchanged
  • Automatic encryption on send β€” just press Enter as usual; the extension encrypts before the message leaves your browser
  • Automatic decryption on receive β€” incoming encrypted messages are decrypted in place
  • View mode toggle β€” switch between the decrypted view and the raw ciphertext to verify what's actually being sent
  • Clickable URLs, @mentions, #hashtags in decrypted view, with HTML escaping to prevent injection
  • Passphrase strength meter with entropy estimation (~bits)
  • Strong cryptography defaults
    • AES-256-GCM (authenticated encryption)
    • PBKDF2-HMAC-SHA256 with 600,000 iterations (OWASP 2023)
    • Random 16-byte salt and 12-byte IV per message
    • GCM authentication tag detects tampering

πŸ“¦ Installation

There are three ways to install the extension, ranked from easiest to most permanent.

Option 1 β€” Temporary install (works on any Firefox, lost on restart)

  1. Download telegram-crypto.xpi (or clone this repo and zip the source folder)
  2. Open about:debugging#/runtime/this-firefox in Firefox
  3. Click Load Temporary Add-on…
  4. Select the manifest.json file inside the extension folder
  5. Reload Telegram Web with Ctrl+F5

The extension stays loaded until you close Firefox.

Option 2 β€” Permanent install in Firefox Developer Edition or Nightly

Regular release Firefox requires Mozilla-signed extensions. The easiest workaround is to use Firefox Developer Edition or Nightly, where unsigned extensions can be enabled:

  1. Install Firefox Developer Edition or Nightly
  2. Open about:config, accept the warning, and set xpinstall.signatures.required to false
  3. Drag telegram-crypto.xpi into the Firefox window
  4. Confirm the install dialog

The extension stays installed across restarts.

Option 3 β€” Self-signed via Mozilla AMO (for regular Firefox)

For installation in stable Firefox, the extension must be signed by Mozilla:

  1. Create a free account at addons.mozilla.org
  2. Submit the extension as self-distribution (unlisted) for signing
  3. After Mozilla's automated review you receive a signed .xpi
  4. Install the signed .xpi in any Firefox by drag-and-drop

This is the only way to get a permanent install in regular release Firefox.


βš™οΈ Configuration

Setting the passphrase

  1. Click the πŸ” icon in the Firefox toolbar to open the extension popup (if the icon is not visible, click the puzzle 🧩 icon and pin Telegram Crypto)

  2. Enter a passphrase. The popup shows a strength meter:

    Bits Label Meaning
    <40 Very weak Crackable in seconds
    40–59 Weak Crackable in hours by a motivated attacker
    60–79 Acceptable Reasonable for most uses
    80–99 Strong Infeasible to brute force
    β‰₯100 Very strong Effectively impossible
  3. The recommended approach is 5–6 random words separated by spaces, e.g. correct horse battery staple pine. This is easier to remember than a complex string and reaches β‰₯80 bits of entropy with minimal effort.

  4. The passphrase is saved automatically as you type, and persists across sessions.

Sharing the passphrase

The other party must have the extension installed with the same passphrase.

⚠️ Share the passphrase via a separate secure channel β€” in person, by phone call, via another encrypted messenger β€” never through Telegram itself. Anyone who can read the channel where the passphrase is shared can read all your encrypted messages.


πŸš€ Usage

Floating controls

When you open a Telegram chat, two floating controls appear to the left of the message input:

Button State Meaning
πŸ”‘ No Key yellow No passphrase configured. Set one in the extension popup.
πŸ”“ Clear Channel grey Passphrase set, encryption disabled in this chat. Messages send and receive in clear.
πŸ”’ Encrypted Channel blue Encryption enabled in this chat. Messages send encrypted; incoming encrypted messages decrypt automatically.
πŸ‘ Eye (icon) blue circle Showing decrypted text. Click to hide. Only visible when Encrypted Channel is active.
πŸ‘β€πŸ—¨ Eye-off (icon) grey circle Showing raw ciphertext. Click to reveal decrypted text. Only visible when Encrypted Channel is active.

The state is remembered per chat (keyed by the chat's URL hash), so it persists across page reloads and chat switches.

Sending an encrypted message

  1. Open the target chat
  2. Click the Clear Channel button β†’ it turns into Encrypted Channel (blue)
  3. Type your message and press Enter as usual
  4. The extension encrypts the message before Telegram sends it
  5. The recipient sees the encrypted form unless they also have the extension with the same passphrase

Receiving an encrypted message

If the other party has the same setup, their encrypted messages appear in your chat as:

πŸ” 5kF2bN9pQwVxYr3z...   (long base64 string)

The extension automatically detects these and displays the decrypted text with a πŸ”“ prefix and a blue dashed outline. URLs, @mentions and #hashtags become clickable.

To toggle between decrypted view and raw view, click the eye / eye-off icon.

What the recipient sees without the extension

If the recipient does not have the extension installed (or has a different passphrase), they will see the raw ciphertext as a long base64 string prefixed by πŸ”. They cannot read the content.


🧠 How it works

Message format

Every encrypted message is sent through Telegram as:

πŸ”<base64-encoded payload>

The payload, after base64 decoding, is:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  salt (16B)  β”‚  IV (12B) β”‚  ciphertext + GCM tag       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Encryption pipeline

passphrase ─▢ PBKDF2-HMAC-SHA256 (600k iter, random salt) ─▢ AES-256 key
                                                                  β”‚
              plaintext ─▢ AES-256-GCM (key, random IV) ─▢ ciphertext + tag
                                                                  β”‚
                                                                  β–Ό
                                base64(salt + IV + ct + tag) ─▢ πŸ” prefix added ─▢ Telegram

Each message uses a fresh random salt and IV, so encrypting the same plaintext twice produces completely different ciphertexts. The GCM authentication tag detects any modification to the ciphertext.

Decryption pipeline

The extension scans the chat DOM looking for text nodes that either:

  1. Start directly with πŸ”, or
  2. Are pure base64 strings whose nearest DOM ancestor contains a πŸ” emoji (Telegram often renders the emoji as a separate <img> element)

When a match is found:

  1. The base64 is decoded back into salt | IV | ciphertext + tag
  2. PBKDF2 derives the AES key from the passphrase and the message's salt
  3. AES-GCM verifies the tag and decrypts
  4. The original text node is replaced with a <span> containing the decrypted plaintext with URLs, mentions and hashtags converted to clickable elements

If decryption fails (wrong passphrase, corrupted data, tampered ciphertext), the message remains visible in its raw form with a tooltip explaining the failure.

Backward compatibility

Messages encrypted by older versions of the extension (0.2.x, which used 100,000 PBKDF2 iterations) are still decryptable. The extension tries the current iteration count first and falls back to the legacy value if needed.


πŸ›‘οΈ Threat model

This is an honest assessment of what the extension protects against and what it does not.

Designed against

Adversary Protection
Telegram (the company, servers, employees with DB access) Message content is opaque ciphertext on Telegram's servers
Network observers (ISPs, governments, MITM) TLS already protects in transit; this adds another encryption layer
Compelled disclosure of Telegram's stored data Stored ciphertext is useless without the passphrase
Active modification of stored ciphertext GCM authentication tag detects tampering

NOT designed against

Adversary Why not
Local malware, OS-level keyloggers Plaintext exists in memory before encryption
Other browser extensions with access to web.telegram.org They can read the input field's DOM directly
A malicious Telegram Web client modifying its own JS to capture plaintext The plaintext exists in Telegram-controlled DOM before the extension cifrates it
Metadata analysis (who, when, message size) All visible to Telegram regardless of payload encryption
Weak or leaked passphrases No passphrase = no security
Forward secrecy The same passphrase is used for all messages; if leaked, all past messages become readable

Honest summary

This extension provides reasonable defense against Telegram as a passive adversary (reading stored messages on their servers, complying with subpoenas, or being breached).

It is not a defense against Telegram as an active adversary that could modify their own web client to capture plaintext before the extension encrypts it. The plaintext exists in a contenteditable element controlled by Telegram's own JavaScript before our key handler fires; in principle, Telegram could intercept it there if motivated.


πŸ”§ Technical details

Cryptography

Component Choice
Cipher AES-256-GCM (AEAD: confidentiality + authenticity)
Key derivation PBKDF2-HMAC-SHA256, 600,000 iterations (OWASP 2023 recommendation)
Salt 16 random bytes per message (crypto.getRandomValues)
IV / Nonce 12 random bytes per message (crypto.getRandomValues)
Authentication tag 16 bytes (default), appended to ciphertext
Encoding Base64
Random source crypto.getRandomValues (CSPRNG)

All primitives use the browser's Web Crypto API. No external cryptographic libraries are used.

Project structure

telegram-crypto/
β”œβ”€β”€ manifest.json     # Manifest V3, host permissions for web.telegram.org
β”œβ”€β”€ content.js        # Main content script: crypto + DOM manipulation + UI
β”œβ”€β”€ content.css       # Floating button styles
β”œβ”€β”€ popup.html        # Configuration popup (passphrase, strength meter)
β”œβ”€β”€ popup.js          # Popup logic (storage, entropy estimation)
β”œβ”€β”€ README.md         # This file
└── icons/
    β”œβ”€β”€ icon48.png
    └── icon96.png

Storage

Configuration is persisted in browser.storage.local:

Key Type Description
passphrase string The user's passphrase (stored unencrypted at rest)
showDecrypted boolean View mode preference
tc_chat_<hash> boolean Per-chat enable/disable state

⚠️ The passphrase is stored unencrypted in the extension's local storage. Anyone with filesystem access to the Firefox profile can read it. This is acceptable for the documented threat model (Telegram as adversary) but is a real concern if your local machine is at risk.

Browser compatibility

  • Firefox 109+ (Manifest V3 with host_permissions)
  • Other Chromium-based browsers (Chrome, Edge, Brave) β€” should work with minor manifest adjustments, not officially tested

πŸ› οΈ Development

Build the .xpi from source

git clone https://github.com/<your-user>/telegram-crypto.git
cd telegram-crypto
zip -r ../telegram-crypto.xpi \
    manifest.json content.js content.css \
    popup.html popup.js README.md icons/

Test locally

  1. Open about:debugging#/runtime/this-firefox in Firefox
  2. Click Load Temporary Add-on…
  3. Select manifest.json from the source folder

Diagnostic console output

Open DevTools (F12) on a Telegram Web tab. If the extension is active, you will see:

[Telegram Crypto] v5.7 loaded (AES-256-GCM, PBKDF2 600k)

Modifying the prefix marker

The encrypted message marker πŸ” is defined as the constant PREFIX near the top of content.js. It can be changed to any string, but both sender and recipient must use the same prefix.

Adjusting button position

If the floating buttons overlap with Telegram's UI on your screen layout, edit the margin constant in the positionUI function in content.js (default 60). Larger values push the buttons further left.


🀝 Contributing

Pull requests welcome, especially for:

  • Support for Telegram Web versions A (/a/) and Z (/z/) β€” currently optimized for K
  • Markdown formatting in decrypted view (bold, italic, code)
  • Argon2id as an alternative key derivation function (currently PBKDF2 only)
  • Per-chat passphrases instead of a single global one
  • Chromium/Chrome compatibility shim

When reporting bugs, please include:

  • Firefox version
  • Telegram Web version (/k/, /a/, /z/)
  • Console output from DevTools
  • Steps to reproduce

πŸ“œ License

MIT. See LICENSE file for details.

This software is provided "as is", without warranty of any kind, express or implied.

This extension was written as a learning project to explore browser-based end-to-end encryption using AI vibecoding. Audit the source code yourself before relying on it for anything that matters.

πŸ“Έ Screenshots

Configure passphrase Clear Channel Encrypted Channel + eye on Encrypted Channel + eye off

About

Telegram Encryption Firefox Extension

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors