← Todas las extensiones

Interoperabilidad · Root

AGM Encryption

por Mango

Cifrado AES-256-GCM compatible con AGM v1, con nonces autenticados por mensaje y protección contra ataques de repetición.

Instalar en Mango 3.116 KB · Mango 3.1
Versión
0.1.9
Tipo de ejecución
Root de servidor
Grove API
1
Actualizado
2026-09-06

Permisos solicitados

6 permisos

  • Agregar comandos de barra inclinadaRegistra sus propios comandos y puede mostrar tarjetas de resultados de comandos nativos.
  • Filtrar mensajes recibidosPuede transformar o ocultar el texto de los mensajes entrantes antes de que Mango los muestre.
  • Filtrar mensajes enviadosPuede transformar o bloquear el texto de los mensajes salientes antes de llegar a IRC.
  • messages.protectUn permiso de Grove declarado por esta extensión.
  • Mantener datos localesAlmacena datos de extensión en este dispositivo en su propio espacio aislado.
  • Mantener secretos en el llaveroAlmacena valores privados en un espacio de nombres Keychain reservado para esta extensión.

Acceso a la red

Sin acceso a Internet

Su manifiesto no declara hosts externos. El sandbox de Grove bloquea las solicitudes de red fuera del paquete.

Desde la extensión

README · Inglés

AGM Encryption — a Grove Root

AGM adds AGM v1-compatible +AGM encryption to Mango. It uses a shared 32-byte key per channel or private conversation and sends standard AGM v1 AES-256-GCM payloads that other AGM implementations can read.

Quick start with another AGM client

  1. Open the same channel in Mango and the other client, or open a private query with the other device’s nickname on each client.
  2. Run /agm gen in Mango.
  3. Copy the Base64 key from the card and import it for that target in the other client.
  4. Compare the XXXX-XXXX safety number out of band.
  5. Send an ordinary message from either client.

If the other client generated the key, copy its Base64 value and run /agm set <base64-key> in the matching Mango conversation instead. Target names are per side: in a query, each client assigns the key to the other person’s nickname.

Commands

Run these in the channel or query whose key you want to manage:

/agm gen
/agm set <base64-key>
/agm export
/agm clear
/agm clearconflicts
/agm status
/agm list

The target index is stored in the Grove WebKit store. Actual key bytes are stored in Keychain through storage.secure. /agm list and /agm status never reveal keys; /agm export deliberately reveals the current key so it can be transferred to another client.

Interoperability and behavior

  • Wire format: +AGM <base64(version || nonce || ciphertext || tag)>.
  • Cipher: AES-256-GCM with a fresh random 96-bit nonce per message.
  • AAD: lowercased channel name, or the UTF-8-sorted pair of lowercased nicknames joined with NUL for a query.
  • Key: exactly 32 random bytes, imported/exported as standard RFC 4648 Base64.
  • Safety number: the AGM-compatible first 40 bits of SHA-256(0x00 || key), rendered with its unambiguous Base32 alphabet.
  • Long messages are split at UTF-8 scalar boundaries before encryption. Every chunk has a distinct nonce and is sent atomically by Mango.
  • Authenticated live nonces are remembered in a bounded 2,048-entry replay cache. Historical and local transcript replay remain decryptable.
  • Query keys follow authoritative nickname changes. Previous authenticated conversation identities are retained so pre-rename cached history still decrypts. A rename colliding with another saved key disables both entries until the user explicitly chooses or clears the key for the new nickname. Legacy unlinked conflicts from 0.1.3 require /agm clearconflicts before their keys can be configured again.
  • Keychain changes are guarded by a durable recovery marker. If a key update is interrupted, AGM stays fail closed across app restarts until the affected key is set again or cleared.
  • Channel keys fail closed for outbound messages, but they do not require every participant to use AGM. Plain inbound channel messages remain visible without an AGM indicator; only successfully authenticated frames receive the AGM lock.
  • Incoming encrypted ACTION (/me) text is decrypted. Mango’s current outbound /me path does not pass through Grove filters, so outgoing ACTION encryption is not included in this release.

Malformed, tampered, wrong-key, or cross-conversation payloads stay visible as raw +AGM text with an error indicator. A repeated authenticated live nonce is dropped. With no key configured for a target, AGM payloads pass through unchanged.

Diagnostics

The Grove console records key lifecycle events and live encryption/decryption activity, including the target, conversation type, message kind, delivery type, and encrypted frame count. It also reports authentication failures and replay rejections. Logs never include message plaintext, keys, or ciphertext.

Security warning

AGM is authenticated pre-shared-key encryption and a substantial improvement over FiSH, but it is not OTR or Signal. It has no key exchange, peer identity, deniability, forward secrecy, or post-compromise security. Anyone who obtains the static key can decrypt recorded traffic encrypted with that key. Share keys through a trusted private channel, compare safety numbers out of band, rotate keys when membership changes, and keep IRC transport TLS enabled.

This implementation is independently written against the public AGM v1 wire-format documentation. Its bundled cryptographic primitives come from the MIT-licensed Noble Ciphers and Noble Hashes projects.

Development

npm install
npm test
npm run package
manifest.jsonapp.mango.grove.agm
{
  "grove": 1,
  "id": "app.mango.grove.agm",
  "name": "AGM Encryption",
  "version": "0.1.9",
  "description": "AGM v1-compatible AES-256-GCM encryption with authenticated per-message nonces and replay protection.",
  "author": {
    "name": "Mango",
    "url": "https://mangoirc.chat"
  },
  "readme": "README.md",
  "entry": "index.html",
  "presentation": {
    "surfaces": [
      "headless"
    ],
    "scope": "server"
  },
  "capabilities": [
    "commands.register",
    "messages.filter.inbound",
    "messages.filter.outbound",
    "messages.protect",
    "storage",
    "storage.secure"
  ],
  "hostPermissions": []
}