← Back to Papers Privacy

LLM Chat Privacy & Encryption Architecture

December 2025 Topics: Privacy, Encryption, Client-Side Security, Web Crypto API

Abstract

This paper describes a privacy-first architecture for LLM chat conversations. Unlike traditional encryption approaches, user messages are transmitted to LLM providers using standard TLS 1.3 transport encryption, allowing the server to process and forward requests. Privacy is achieved through two complementary mechanisms: (1) unique, ephemeral identifiers per conversation, making it harder for LLM providers to build cross-conversation profiles, and (2) zero-knowledge storage of conversations after processing.

Once a response is received from the LLM provider, the conversation is encrypted using envelope encryption (hybrid RSA + AES-GCM) before storage. Cryptographic operations leverage a combination of the W3C Web Crypto API and supplementary libraries (OpenPGP.js for RSA, Argon2id via WebAssembly for key derivation), with SharedWorker providing secure in-memory key management across browser tabs. IndexedDB and Origin Private File System (OPFS) provide encrypted local storage.

No existing solution combines zero-knowledge storage with local search, multi-tab session management, and cross-device index synchronization for this use case. Non-extractable WebCrypto keys held in SharedWorker memory provide session persistence, while encrypted search index backups enable efficient bootstrapping on new devices.

In a nutshell: ProtonMail-style encryption for LLM chats with full local search capability.

Important: By the nature of an LLM chat system, message content must be transmitted in plaintext to downstream LLM providers for processing. This architecture provides zero-knowledge storage — once a response is received and stored, conversations are asymmetrically encrypted and the server cannot access them. Conversation messages are not logged by the system regardless of encryption status. When encryption is enabled, no unencrypted copy is stored except during active abuse review (see Section 7.3). The server sees plaintext during processing but cannot access stored conversations.

1. The Problem

Traditional SaaS applications store user data in plaintext on servers. LLM chat conversations represent a uniquely intimate form of data - more personal than search histories, often containing sensitive thoughts, questions, and information that users would never share publicly. This creates significant risks:

  • Data breaches expose all user content
  • Insider access enables unauthorized reading
  • Legal subpoenas can access unencrypted data
  • Marketing and advertising profiles - LLM providers can use chat histories to build detailed profiles about individual users

Requirements

  1. Once zero-knowledge storage is enabled, the server cannot decrypt stored conversations
  2. Users must be able to search their encrypted data locally
  3. Session persistence across browser tabs and navigation without re-authentication
  4. Recoverability through forgot password reset capability and password changes without re-encrypting all existing data
  5. No consistent user identifier sent across separate conversations, reducing LLM providers' ability to build cross-conversation profiles

Threat Model

This architecture is designed to protect against specific threats while acknowledging its boundaries:

What this protects against:

  • Data breaches of stored data - A database breach exposes only ciphertext that cannot be decrypted without the user's private key
  • Insider access - Server operators and database administrators cannot read stored conversations
  • Legal subpoenas for stored data - The server genuinely cannot produce plaintext conversations; only ciphertext can be provided
  • Cross-conversation profiling by LLM providers - Unique, ephemeral identifiers per conversation make it harder to build unified user profiles

What this does NOT protect against:

  • Compromised server at request time - Messages pass through the server in plaintext during processing; a compromised server could intercept messages before encryption
  • Cross-site scripting (XSS) - Malicious scripts running in the browser origin could intercept decrypted content after it is returned from the SharedWorker to the UI layer
  • Malicious browser extensions - Extensions with page access can read rendered content in the DOM regardless of encryption
  • Within-conversation correlation - Multi-turn conversations necessarily share context with the LLM provider; identifier obfuscation operates between conversations, not within them

2. Inspirations & Industry Best Practices

This architecture draws from multiple established cryptographic systems and industry standards.

Proton Mail

What we adopted:

  • Two-tier key hierarchy model: user password derives a Key Encryption Key (KEK), which wraps the private key
  • Argon2id for password-based key derivation with memory-hard parameters
  • Dual key wrapping: both password-derived and recovery-phrase-derived KEKs wrap the same private key
  • Zero-knowledge storage principle: server stores only wrapped keys and ciphertext

How we adapted it:

  • SharedWorker combined with Web Crypto API's non-extractable keys provides session persistence
  • Added local search capability via separately encrypted search indices with cross-device synchronization

W3C Web Crypto API

What we adopted:

  • Native browser cryptography with hardware acceleration for AES-GCM operations
  • Non-extractable key properties preventing JavaScript extraction of raw key material
  • Structured cloning for CryptoKey persistence in IndexedDB
  • Standardized interfaces avoiding library vendoring for symmetric operations

Supplementary Libraries: The Web Crypto API does not include all required primitives. RSA key generation and OpenPGP-format encryption use OpenPGP.js (maintained by ProtonMail). Password-based key derivation uses Argon2id via a WebAssembly module, as the Web Crypto API's built-in PBKDF2 does not provide equivalent memory-hard properties. BIP39 mnemonic generation is used for recovery phrases.

Key Security Properties: Keys marked as non-extractable cannot be exported via any Web Crypto API method. This prevents direct extraction of raw key material by scripts, though it does not prevent authorized use of the key through the Web Crypto API by any script with access to the CryptoKey handle.

3. Architecture Overview

Data Flow: Message Encryption and Storage

+-----------------------------------------------------------------------------+
|  1. USER SENDS MESSAGE                                                      |
|     Browser Tab (UI Layer) - User composes message                          |
+-----------------------------------------------------------------------------+
                                    |
                                    v  TLS 1.3
+-----------------------------------------------------------------------------+
|  2. SERVER RECEIVES MESSAGE                                                 |
|     Message arrives via TLS 1.3 transport encryption                        |
+-----------------------------------------------------------------------------+
                                    |
                                    v  TLS 1.3
+-----------------------------------------------------------------------------+
|  3. FORWARD TO LLM PROVIDER                                                |
|     Server forwards message with ephemeral conversation identifier          |
|     (no persistent user ID sent to provider)                                |
+-----------------------------------------------------------------------------+
                                    |
                                    v  TLS 1.3
+-----------------------------------------------------------------------------+
|  4. LLM PROVIDER RESPONDS                                                  |
|     Response returned to server via TLS 1.3                                 |
+-----------------------------------------------------------------------------+
                                    |
                                    v
+-----------------------------------------------------------------------------+
|  5. ENCRYPTION & STORAGE (Envelope Encryption)                              |
|     * Generate random AES session key per message                           |
|     * Encrypt message + response with AES session key                       |
|     * Wrap session key with user's RSA-4096 public key (OpenPGP)            |
|     * Store ciphertext + wrapped key in database                            |
+-----------------------------------------------------------------------------+
                                    |
                                    v  TLS 1.3
+-----------------------------------------------------------------------------+
|  6. SharedWorker (Client-Side)                                              |
|  +-----------------------------------------------------------------------+  |
|  |  Cryptographic Bridge (in-memory keys)                                |  |
|  |  * privateKey: RSA-4096 key object (OpenPGP)                          |  |
|  |  * kekDerived: Non-extractable CryptoKey (AES-256)                    |  |
|  |  * indexKey: Non-extractable CryptoKey (AES-256)                      |  |
|  +-----------------------------------------------------------------------+  |
|                              |                                              |
|                              v                                              |
|  +-----------------------------------------------------------------------+  |
|  |  7. DECRYPTION & LOCAL PROCESSING                                     |  |
|  |     a. Decrypt session key using RSA private key (OpenPGP)            |  |
|  |     b. Decrypt message content using AES session key                  |  |
|  |     c. Add to in-memory search index (FlexSearch)                     |  |
|  |     d. Encrypt search index with separate index key (AES-GCM)         |  |
|  |     e. Store encrypted index in OPFS (sync to server)                 |  |
|  +-----------------------------------------------------------------------+  |
+-----------------------------------------------------------------------------+

4. Technical Implementation

Key Generation and Wrapping

The architecture uses a three-stage process:

  1. RSA-4096 Keypair Generation - Generate asymmetric encryption keys using OpenPGP.js (maintained by ProtonMail), which provides a mature, well-audited RSA implementation in the OpenPGP format. While OpenPGP.js also supports ECC (Curve25519), RSA-4096 was chosen for its well-understood security properties and broad interoperability with existing OpenPGP tooling. The key wrapping architecture supports future migration to ECC without re-encrypting stored data — only the session key wrapping layer would change
  2. Password-Based Key Derivation - Use Argon2id (WebAssembly) to derive KEK from user password
  3. Key Wrapping - Encrypt private key with KEK using AES-GCM via Web Crypto API
// 1. Generate RSA-4096 keypair via OpenPGP.js (ProtonMail library)
// Note: RSA generation uses OpenPGP.js, not the Web Crypto API directly,
// to produce keys in OpenPGP armored format for compatibility.
const { privateKey, publicKey } = await openpgp.generateKey({
    type: 'rsa',
    rsaBits: 4096,
    userIDs: [{ name: userID, email: email }],
    format: 'armored'
});

// 2. Derive KEK from password using Argon2id (WebAssembly module)
// Argon2id is not a Web Crypto API primitive. We use argon2-browser
// (WASM) for memory-hard key derivation, as the Web Crypto API's
// built-in PBKDF2 lacks equivalent memory-hardness properties.
const kekBytes = await argon2.hash({
    pass: userPassword,
    salt: saltBytes,                // 32 bytes from crypto.getRandomValues()
    type: argon2.ArgonType.Argon2id,
    time: 3,                        // iterations
    mem: 65536,                     // 64 MB memory cost
    parallelism: 4,
    hashLen: 32                     // 256-bit output
});

// 3. Import as non-extractable Web Crypto API key
const kek = await crypto.subtle.importKey(
    'raw',
    kekBytes.hash,
    { name: 'AES-GCM', length: 256 },
    false,                          // NON-EXTRACTABLE
    ['encrypt', 'decrypt']
);

// 4. Wrap private key with KEK using AES-GCM (Web Crypto API)
const nonce = crypto.getRandomValues(new Uint8Array(12)); // 96 bits
const wrappedPrivateKey = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv: nonce },
    kek,
    new TextEncoder().encode(privateKey)  // armored PGP private key
);

Envelope Encryption for Messages

RSA-4096 cannot directly encrypt payloads larger than approximately 446 bytes. All message content is encrypted using envelope encryption (hybrid encryption):

  1. A random AES session key is generated for each message
  2. Message content is encrypted with AES using the session key
  3. The session key is wrapped (encrypted) with the user's RSA public key
  4. Both the wrapped session key and the AES ciphertext are stored together

When using OpenPGP-format encryption (the primary path), this envelope encryption is handled internally by the OpenPGP library. For file attachments, the system performs explicit envelope encryption using the Web Crypto API for the AES-GCM layer and OpenPGP for the RSA key wrapping.

5. Protecting the User's Decryption Key

The user's decryption key (RSA private key) is the most sensitive component. The architecture provides multiple layers of protection:

Server-Side Storage

  • The private key is never stored in plaintext
  • Encrypted (wrapped) using a Key Encryption Key derived from the user's password
  • The server cannot decrypt without the user's password

SharedWorker Isolation

  • SharedWorker runs in a separate execution context from browser tabs
  • Tabs interact with the worker exclusively through a predefined message bus interface (MessagePort)
  • The worker exposes specific operations (unlock, encrypt, decrypt) but raw key objects never leave the worker context
  • This limits the attack surface — a script in the main thread cannot directly invoke cryptographic operations on the key objects, only request specific operations through the message interface

Limitation: SharedWorker isolation reduces exposure but does not eliminate all risk. A malicious script could still invoke worker operations through the message bus and intercept decrypted content returned to the main thread. SharedWorker isolation is a defense-in-depth measure, not a complete security boundary against same-origin attacks.

Session Persistence

  • All tabs share a single SharedWorker instance
  • When one tab unlocks keys, all tabs have access
  • Keys persist via IndexedDB structured cloning — the browser's native serialization mechanism can store CryptoKey objects directly while preserving their non-extractable property

Browser Compatibility

SharedWorker support varies across browsers, with the primary gap being mobile devices. On mobile, SharedWorker is less critical: users typically work in a single tab, and mobile clients do not perform full search index synchronization or other operations that benefit from cross-tab coordination. On devices without SharedWorker support, the system falls back to a standard Web Worker, providing the same cryptographic isolation within a single-tab context.

Argon2id Performance Considerations

The Argon2id key derivation uses a 64 MB memory cost, which is intentionally expensive (~600ms) to resist brute-force attacks. Critically, this cost is incurred only once per session — when the user first unlocks their encryption keys (e.g., on login or after a browser restart). The derived KEK is used to unwrap the RSA private key, and the resulting key objects are then cached as non-extractable CryptoKey handles in the SharedWorker for the duration of the session.

Individual message encryption and decryption operations use the already-unlocked RSA key and do not re-invoke Argon2id. This makes the per-session cost practical even on mobile devices, while maintaining strong resistance to offline password-guessing attacks against the wrapped private key.

6. Recoverability

Password Changes

When changing passwords, the underlying private key remains unchanged - only the wrapping changes:

  1. Old password derives the old KEK
  2. Old KEK decrypts the private key
  3. New password derives a new KEK
  4. Private key is re-wrapped with the new KEK

Key insight: No re-encryption of stored conversations required.

Multi-Word Recovery Phrase

For forgot-password scenarios, users generate a recovery phrase during account setup. This follows the same dual-wrapping approach used by ProtonMail:

  1. Phrase generation - A 12-word mnemonic is generated using the BIP39 standard (128 bits of entropy from crypto.getRandomValues(), mapped to a 2048-word English dictionary with SHA-256 checksum validation)
  2. Seed derivation - The mnemonic is converted to a 512-bit seed using BIP39's mnemonicToSeed function
  3. Recovery KEK derivation - The seed is passed through Argon2id with a separate random salt (identical parameters to the password-derived KEK: 3 iterations, 64 MB memory, parallelism of 4) to produce a 256-bit AES key
  4. Private key wrapping - The same RSA private key is encrypted a second time using AES-GCM with the recovery KEK

The server stores both wrapped copies with their respective salts and nonces:

  • private_key_encrypted + kdf_params — the password-wrapped copy
  • backup_private_key_encrypted + backup_kdf_params — the recovery-wrapped copy

During recovery, the user enters their 12-word phrase, which re-derives the recovery KEK and unwraps the private key. The user then sets a new password, which generates a new password-derived KEK and re-wraps the private key — all without affecting stored conversations or the recovery-wrapped copy. Changing the recovery phrase similarly only re-wraps the backup copy, leaving the password-wrapped copy untouched.

Key Rotation

The system maintains a key versioning mechanism. Each time a user's encryption keys are changed — whether through a password change, recovery phrase reset, or explicit key rotation — the KeyVersion is incremented. The last 3 key versions are retained in a UserEncryptionKeyHistory record, along with metadata about how each change was triggered (ChangedByMethod).

Password and recovery phrase changes only re-wrap the existing RSA private key with a new KEK — no re-encryption of stored conversation data is needed, since the underlying RSA keypair remains the same.

For full key rotation (generating a new RSA keypair), the envelope encryption architecture makes this feasible without re-encrypting all message ciphertext. Each message's AES session key (256 bits) is wrapped with the user's RSA public key. Re-wrapping these small session keys with a new public key is computationally inexpensive, while the AES-encrypted message ciphertext remains untouched. This separation — a core benefit of envelope encryption — means that even large conversation histories can be migrated to a new keypair efficiently.

7. Enhancing Privacy from LLM Providers

The Profiling Threat

LLM providers could potentially build profiles based on conversation content, tracking interests and behaviors across all of a user's conversations over time.

7.1 Cross-Conversation Identifier Obfuscation

The system assigns unique, ephemeral identifiers at the conversation level:

  • No consistent user ID is transmitted across separate conversations
  • Each new conversation appears to originate from a different user
  • This makes it harder for providers to build cross-conversation profiles linking a user's disparate topics together

Scope and limitations: Within a single multi-turn conversation, the LLM provider necessarily sees the conversation history as context. Identifier obfuscation operates between conversations — preventing the provider from easily linking a user's medical questions in one conversation to their financial questions in another. It does not hide the content within a conversation.

7.2 Residual Correlation Risks

Ephemeral identifier obfuscation prevents straightforward cross-conversation profile building by LLM providers. However, it does not guarantee full unlinkability. Advanced correlation techniques — such as IP address analysis, request timing patterns, or session-level behavioral fingerprinting — could theoretically be used to re-link conversations. In practice, such techniques are typically employed only in abuse investigation or law enforcement contexts, not routine provider analytics.

The goal is to raise the bar significantly: casual or automated profiling across conversations becomes impractical, even if determined, targeted correlation remains theoretically possible.

7.3 Abuse Prevention & Legal Compliance

The system includes an abuse prevention mechanism to satisfy legal obligations and protect the platform. When content is flagged — either by internal automated detection systems or by the LLM provider's own safety filters — the affected conversation remains unencrypted on the server until the review process completes.

  • If no abuse is found, the conversation is encrypted normally under the standard zero-knowledge flow
  • If abuse is confirmed, the unencrypted content is retained as required for legal compliance
  • Users are notified directly in their conversation when a review is triggered
  • Conversation messages are not logged by the system regardless of encryption status — the distinction is whether the stored copy is encrypted or held in plaintext during review

Transparency notice regarding ephemeral identifiers: Ephemeral conversation identifiers are stored unencrypted on the server (they must be, since the server generates and assigns them). In cases of illegal abuse or pursuant to a valid court order, the system can provide a list of a user's ephemeral identifiers to the LLM provider. The provider can then look up those identifiers on their side to reconstruct the full picture of flagged conversations. Even in this scenario, encrypted conversation content cannot be read by the server — only the provider's own retained data (if any) would be accessible via the identifiers. This mechanism is reserved exclusively for illegal abuse cases or court orders.

8. Local Search and Cross-Device Synchronization

A key requirement is enabling users to search their encrypted conversations without exposing plaintext to the server. The system uses a separately encrypted search index with cross-device synchronization.

Index Architecture

  • Indexing library - FlexSearch with forward tokenization, running inside the SharedWorker
  • Separate encryption key - A dedicated AES-256-GCM index key, independent from the conversation encryption keys. This key is wrapped with the user's RSA public key and stored on the server
  • Local storage - The encrypted index is stored in the Origin Private File System (OPFS), with metadata tracked in IndexedDB

Cross-Device Synchronization

When a user accesses their account from a new device, the encrypted search index can be downloaded from the server rather than requiring a full re-index of all conversations:

  1. The client requests the latest encrypted index backup via a presigned URL
  2. The encrypted index is downloaded and decrypted locally using the index key (which is unwrapped using the user's private key)
  3. The index is imported into FlexSearch and saved to OPFS for subsequent local use

Synchronization uses a server-authoritative model. The client calls ListSince with its last-known sync timestamp. The server maintains SyncMetadata per device — tracking LastSyncTime, a Version counter, and an IsStale flag — using DynamoDB GSI-backed updated_at timestamps for ordering. When conflicts arise (e.g., concurrent edits from multiple devices), a ConflictResolver applies a server-wins strategy by default, ensuring deterministic resolution without user intervention.

Each device is tracked independently via a DeviceID, with separate sync timestamps for conversations and messages. When a device goes offline, pending operations are queued locally and retried automatically (up to 3 attempts with backoff) when connectivity is restored. The server stores only encrypted index data and never has access to the index key or plaintext search terms.

Browser Storage Limits

Client-side encrypted storage relies on IndexedDB and the Origin Private File System (OPFS), which typically provide gigabyte-level storage quotas in modern browsers. The system monitors available quota via navigator.storage.estimate() and warns users when usage exceeds 90% of the available space. To prevent the browser from evicting stored data under storage pressure, the system requests persistent storage via navigator.storage.persist(). As a long-term measure, AI-assisted conversation cleanup will help users identify and archive older conversations to reclaim space.

9. Key Insights and Recommendations

Insights

  1. Non-extractable keys limit exposure - Raw key material cannot be exported via the Web Crypto API, which prevents a class of exfiltration attacks. SharedWorker isolation adds an additional layer by restricting direct access to key handles behind a message bus interface. However, this does not prevent a same-origin script from invoking worker operations or intercepting decrypted responses
  2. Separate index encryption key enables safe backup - The search index can be backed up and synced across devices without exposing conversation content, since the index key is independent from the message encryption keys
  3. Structured cloning enables persistence - IndexedDB's structured clone algorithm can serialize CryptoKey objects while preserving their non-extractable property, enabling session persistence across page navigations
  4. User ID obfuscation complements encryption - Addresses the cross-conversation profiling threat that encryption alone cannot solve

Gotchas

  • IndexedDB keyPath is immutable - schema planning critical
  • Argon2id is slow by design (~600ms) - cache the derived key
  • CryptoKey structured cloning support varies by browser - test persistence on target platforms
  • Password strength is paramount - weak passwords undermine everything
  • OpenPGP.js and argon2-browser are external dependencies outside the Web Crypto API - they must be loaded from trusted sources and integrity-checked

References

  1. OWASP. "Password Storage Cheat Sheet." https://cheatsheetseries.owasp.org/
  2. NIST Special Publication 800-57 Part 1 Revision 5 - Key Management
  3. W3C. "Web Cryptography API." W3C Recommendation, 2017
  4. Biryukov, A., Dinu, D., & Khovratovich, D. (2016). "Argon2: the memory-hard function for password hashing"
  5. Bonneau, J., et al. (2012). "The Science of Guessing: Analyzing an Anonymized Corpus of 70 Million Passwords"
  6. Boneh, D., & Shoup, V. "A Graduate Course in Applied Cryptography" - Key Management
  7. ProtonMail. "ProtonMail Security Features and Infrastructure." https://proton.me/mail/security
  8. Bitcoin BIP39. "Mnemonic code for generating deterministic keys." https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki