Skip to content

First-party web encryption format

This document specifies the format written by the first-party AnonPaste web app. It is a compatibility specification for the web client and backend, not a claim that every paste field or every AnonPaste API is encrypted.

Encrypted and Secret-link modes encrypt selected text values and file bytes in the browser before upload. The server stores and returns the resulting ciphertext.

The envelope does not hide everything required to operate a paste. Depending on the workflow, the server still receives fields such as the paste reference, encrypted-mode choice, encryption version, expiry, burn setting, content/file type, ordering and size information, account or anonymous authorization, and request metadata used for delivery, abuse prevention, and service operation.

Encryption does not prevent a recipient from copying, saving, photographing, or forwarding plaintext after decryption. AnonPaste cannot recover a lost Secret-link key or password.

Let secret be either:

  • the password supplied by the creator, or
  • a random Secret-link value generated by the web client.

The first-party web client derives:

encryption_key = SHA-256(UTF-8(secret)) // 32 raw bytes, represented as lowercase hex internally
lookup_key = SHA-256(hex(encryption_key)) // lowercase hex string

encryption_key remains in the browser and is used with AES-256-GCM. lookup_key is sent where the API needs a stable protected-paste lookup credential. These values are purpose-separated by the second hash, but the password path is still only a direct SHA-256 derivation: use a strong, unique passphrase.

The share reference is formatted as:

<paste-reference>#<secret>

The URL fragment beginning with # is not sent in the HTTP request by normal browsers. The recipient must receive the complete link. The browser derives both keys locally from the fragment secret.

The password is not included in the share URL. The creator must send it separately. The viewer derives the same keys after the recipient enters the password.

New encrypted text values use:

algorithm AES-256-GCM
key encryption_key (32 bytes)
IV 12 random bytes per text value
authentication 128-bit GCM tag
AAD UTF-8("anonpaste:v2:text")
plaintext raw UTF-8 text; no legacy pre-encryption compression

Serialized form:

apenc:2:text:<base64(iv)>:<base64(ciphertext || tag)>

Each encrypted text value gets a fresh random IV. A version 2 record that does not contain a valid version 2 envelope fails decryption; it does not fall back to CBC.

New encrypted files use:

algorithm AES-256-GCM
key encryption_key (32 bytes)
IV 12 random bytes per file
authentication 128-bit GCM tag
AAD UTF-8("anonpaste:v2:file")
plaintext original file bytes

Binary serialized form:

offset length value
0 7 ASCII "APENC2F"
7 12 IV
19 rest ciphertext || 16-byte GCM tag

The encrypted blob keeps the original MIME type for delivery. MIME type, filename, size, and other file-management fields are therefore not implied to be encrypted by this byte envelope.

The web client writes encryptionVersion: 2 for new encrypted full and Quick pastes. The backend validates and persists that version with the paste record.

  • Expiry makes the stored paste unavailable after its configured time.
  • Burn after view removes the stored paste after the successful retrieval defined by that workflow.
  • Deletion removes the server-side record through the supported owner or management path.
  • None of these operations can revoke plaintext or ciphertext already copied, cached, downloaded, or forwarded elsewhere.

Quick-paste preview integrations do not receive password-protected or burn-after-view content. Full-paste link-preview bots receive placeholder content for burn-after-view pastes instead of consuming the permitted read. Unknown third-party scanners are outside this guarantee.

Existing version 1 web content remains decryptable. It used AES-256-CBC, PKCS#7 padding, the SHA-256-derived encryption key, and the historical fixed IV:

be410fea41df7162a679875ec131cf2c

Version 1 is decrypt-only in the current first-party web path. New encrypted writes use version 2. Version selection comes from the persisted encryption version and the v2 envelope marker, not a paste-ID date cutoff.

These vectors use a fixed IV only to make independent implementations testable. Production encryption must generate a fresh unpredictable 12-byte IV for every value or file.

secret: correct horse battery staple
encryption_key: c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a
IV (hex): 000102030405060708090a0b
AAD: anonpaste:v2:text
plaintext UTF-8: AnonPaste v2 test vector
ciphertext+tag: VqOzPTzMkZ6utM7qZd7xzhJSlteSNXgIlprOJFcUT2swullCIFyuMA==
envelope: apenc:2:text:AAECAwQFBgcICQoL:VqOzPTzMkZ6utM7qZd7xzhJSlteSNXgIlprOJFcUT2swullCIFyuMA==
secret: correct horse battery staple
encryption_key: c4bbcb1fbec99d65bf59d85c8cb62ee2db963f0fe106f483d9afa73bd4e39a8a
IV (hex): 000102030405060708090a0b
AAD: anonpaste:v2:file
plaintext hex: 00010203feff
ciphertext+tag: F8zeUJJSrcIia+gfbbweAYw85xi2vQ==
envelope hex: 4150454e433246000102030405060708090a0b17ccde509252adc2226be81f6dbc1e018c3ce718b6bd
  • Reject malformed prefixes, IV lengths, authentication tags, and trailing text-envelope fields.
  • Never reuse an IV with the same key.
  • Authenticate the exact AAD string for the envelope type.
  • Treat GCM authentication failure as a hard decryption error.
  • Do not silently downgrade a record marked version 2.
  • Keep keys out of logs, analytics, error reports, and server-rendered markup.