01Encryption at a Glance

Every communication channel in Loqa uses encryption purpose-built for its trust model. The table below summarizes what protects each surface.

Surface Protocol Cipher Forward Secrecy Post-Compromise Security
1 : 1 DMs X3DH + Double Ratchet AES-256-GCM โœ“ โœ“
Group DMs Per-msg key + Ratchet wrap AES-256-GCM โœ“ โœ“
Server Channels MLS (RFC 9420) AES-256-GCM โœ“ โœ“
File Attachments Per-file key wrapping AES-256-GCM โœ“ Via channel protocol
PII (Emails) Server-side envelope AES-256-GCM + HMAC N/A N/A
Passwords Argon2id (PHC winner) โ€” N/A N/A
Stored Files Server-managed encryption AES-256-GCM + HKDF N/A N/A

02Direct Message Encryption

1:1 DMs use a Signal-style protocol with two phases: X3DH for initial key agreement and the Double Ratchet for ongoing message encryption.

X3DH Key Agreement

When Alice first messages Bob, the Extended Triple Diffie-Hellman (X3DH) protocol establishes a shared secret without requiring Bob to be online.

Alice (Initiator)
Identity Key (IKA)
Ephemeral Key (EKA)
DH1 IKA โ†” SPKB
DH2 EKA โ†” IKB
DH3 EKA โ†” SPKB
DH4 EKA โ†” OPKB
Shared Secret = HKDF(DH1 โ€– DH2 โ€– DH3 โ€– DH4)
Bob (Responder)
Identity Key (IKB)
Signed PreKey (SPKB)
One-Time PreKey (OPKB)

Curve

X25519 (Curve25519) with automatic P-256 ECDH fallback for browsers that don't yet support X25519 in WebCrypto.

KDF

HKDF-SHA256 with the info label loqa-x3dh. The raw DH outputs are concatenated and passed through HKDF to derive the initial root key.

Signed PreKey

Bob's signed prekey is authenticated by signing the raw public key bytes with his identity key, preventing key substitution attacks.

One-Time PreKeys

Uploaded in batches of 100, replenished when fewer than 30 remain. Each is consumed once to provide an additional layer of key separation.

Double Ratchet Protocol

After X3DH establishes the shared secret, every subsequent message uses the Double Ratchet โ€” a combination of a symmetric chain ratchet and a DH ratchet that provides forward secrecy and post-compromise security.

Root Key (RK)
โ†“ DH Ratchet (on reply)
Send Chain Key (CKs)
โ†“ HMAC-SHA256
Message Key (MK1)
โ†“
Message Key (MK2)
โ†“ ...
Recv Chain Key (CKr)
โ†“ HMAC-SHA256
Message Key (MK1)
โ†“
Message Key (MK2)
โ†“ ...

Chain KDF

Each chain key is advanced using HMAC-SHA256 with two constant inputs: 0x01 produces the message key, 0x02 produces the next chain key. Message keys are used once and discarded.

DH Ratchet

When a reply is sent, a new DH key pair is generated. The new DH output is combined with the root key via HKDF to derive a fresh root key and chain key โ€” providing post-compromise security.

Out-of-Order Messages

Up to 256 skipped message keys are cached per session, enabling decryption of out-of-order messages without breaking the ratchet state.

Multi-Device

Each device maintains its own ratchet session. Messages are encrypted for all of the recipient's devices, with per-device ciphertext carried in the message header.

03Group DM Encryption

Group DMs use a per-message random key architecture. Each message gets a fresh AES-256-GCM key, which is then wrapped (encrypted) individually for each participant using their ratchet session.

1
Generate random per-message AES-256 key
โ†“
2
Encrypt plaintext โ†’ ciphertext + nonce
โ†“
3
Wrap key for Recipient A
via Ratchet session
Wrap key for Recipient B
via Ratchet session
Wrap key for Recipient C
via Ratchet session
โ†“
4
Send: ciphertext + nonce + { A: wrappedKey, B: wrappedKey, C: wrappedKey }

Ratchet-Wrapped Keys

Each recipient's copy of the message key is encrypted using their active Double Ratchet session โ€” inheriting the same forward secrecy and post-compromise security as 1:1 DMs. The wrapped key format is a JSON object containing the ratchet header, ciphertext, and nonce.

Legacy Fallback

If no ratchet session can be established for a recipient (e.g., they haven't uploaded prekeys), the key is wrapped using static ECDH with ciphertext:nonce format. This path will be phased out as all clients upgrade.

04Server Channel Encryption (MLS)

For server channels that opt into E2EE, Loqa implements Messaging Layer Security (MLS) per RFC 9420 via an OpenMLS WASM module running entirely in the browser.

Ratchet Tree (Binary)
Group Secret
Interior
Interior
Member A
Member B
Member C
Member D
Each member holds only their leaf private key. Interior secrets are derived from child DH outputs. Adding or removing a member updates only the path from the affected leaf to the root โ€” O(log n) cost.

Server Role

The Loqa server acts as a "dumb pipe" Delivery Service โ€” it stores opaque encrypted bytes and fans them out to group members. It never sees plaintext or group secrets.

KeyPackages

Each client pre-uploads KeyPackages (minimum 10, replenished in batches of 20) so other members can add them to groups asynchronously.

Epochs

Every membership change (join, leave, remove) advances the group epoch. All keys from previous epochs are discarded, ensuring forward secrecy for the group.

Commits

Membership changes are applied via MLS Commit messages, which atomically update the ratchet tree and derive new application secrets.

05File Encryption

Every file attachment โ€” in DMs, Group DMs, or E2EE server channels โ€” is encrypted client-side before upload. The storage provider never sees plaintext.

Generate random per-file
AES-256-GCM key
โ†’
Encrypt file bytes
with per-file key
โ†’
Upload ciphertext
to storage
Key wrapping varies by channel type:
1:1 DM
Wrap file key with
pairwise shared secret
Group DM
Wrap file key per-recipient
via Ratchet sessions
MLS Channel
MLS-encrypt file key + nonce
as a group message

Key Wrapping (DM)

The per-file key is exported to raw bytes, then encrypted with the pairwise AES-256-GCM shared secret using a fresh 12-byte nonce. The wrapped key and wrap nonce are sent as metadata alongside the message.

Server-Managed File Encryption

For non-E2EE server channels, the backend encrypts files at rest using AES-256-GCM with per-file keys derived from the server master key via HMAC-SHA256 (loqa-file:{fileId}). Files are stored as nonce (12 bytes) โ€– ciphertext.

06PII & At-Rest Protection

Loqa never stores PII in plaintext. A master encryption key protects all server-side sensitive data, with domain-separated sub-keys ensuring cryptographic isolation between systems.

Master PII Key (32 bytes)
HMAC-SHA256("loqa-email-enc" โ€– 0x01)
Email Encryption Key
AES-256-GCM encrypt/decrypt
HMAC-SHA256("loqa-email-hmac" โ€– 0x01)
Email Blind Index Key
HMAC-SHA256 deterministic lookup
HMAC-SHA256("loqa-file:{id}")
Per-File Encryption Key
AES-256-GCM for stored files

Email Encryption

Email addresses are encrypted with AES-256-GCM using a derived sub-key and a random 12-byte nonce. The ciphertext and nonce are stored base64-encoded. We can authenticate you without ever storing your email in plaintext.

Blind Index Lookup

Emails are normalized (lowercase + trim), then hashed with HMAC-SHA256 using an isolated sub-key. This deterministic blind index enables login and deduplication queries without revealing the actual address.

Password Hashing

Argon2id โ€” the Password Hashing Competition winner โ€” protects all user passwords and OAuth2 client secrets. Each hash uses a unique random salt via OsRng. API tokens and webhook secrets use SHA-256 with constant-time verification.

Key Isolation

The master key is never used directly. All operations use domain-separated sub-keys derived via HMAC-SHA256 (equivalent to HKDF-Expand). Compromising one key domain does not affect the others.

07Key Storage & Lifecycle

Private keys are generated, stored, and used entirely on-device via the Web Crypto API and IndexedDB. They never leave the device and are never transmitted to Loqa's servers.

๐Ÿ”‘

Identity Key Pair

Non-extractable X25519 key pair stored in IndexedDB (loqa_e2ee/keypairs). Generated once on first login; public key uploaded to the server.

๐Ÿ”„

Ratchet Sessions

Per-(peer, device) session state stored in loqa_e2ee/ratchet_sessions. Contains root key, chain keys, ratchet key JWK, and up to 256 skipped message keys.

โœ๏ธ

Signed PreKeys

Stored in loqa_e2ee/signed_prekeys. Public key is signed by the identity key and uploaded to the server. Private JWK stays local for X3DH responder operations.

๐ŸŽŸ๏ธ

One-Time PreKeys

Batched in loqa_e2ee/otp_prekeys (100 at a time, replenished at 30). Each public key is uploaded; private JWK stays local and is consumed on first use.

๐ŸŒฒ

MLS Group State

Persisted in a separate loqa-mls IndexedDB. Tracks group epoch and identity initialization state. WASM manages in-memory tree state.

๐Ÿ“ฑ

Device Identity

Each device generates a unique ID stored in loqa_e2ee/device_info. This allows per-device ratchet sessions and multi-device message delivery.

08Reproducible Audit Scope

Every cryptographic claim in this whitepaper can be independently verified by reading 8 self-contained source files. These files have zero business logic dependencies โ€” they rely only on the Web Crypto API (browser) and standard Rust crates (server).

File Language Purpose Lines
crypto.ts TypeScript X25519/P-256 ECDH key exchange + AES-256-GCM encrypt/decrypt primitives 138
doubleRatchet.ts TypeScript Full X3DH key agreement + Double Ratchet protocol implementation 615
e2eeManager.ts TypeScript Orchestration โ€” DM, Group DM, and MLS file encryption flows 927
fileCrypto.ts TypeScript Per-file AES-256-GCM encryption with key wrapping 145
keystore.ts TypeScript IndexedDB private key storage (proves keys never leave device) 289
mlsManager.ts TypeScript OpenMLS WASM wrapper โ€” group management, encrypt, decrypt 333
mlsStorage.ts TypeScript IndexedDB persistence for MLS group & identity state 114
crypto.rs Rust Server-side PII encryption, blind indexes, Argon2id, file encryption 208
Total auditable surface 2,762
๐Ÿ”ฌ

For security researchers: These 8 files contain 100% of the cryptographic logic. No encryption decisions are made outside this boundary. To validate any claim in this whitepaper, search for the relevant function in these files.

๐Ÿ“‚ View source on GitHub โ†’ loqachat/loqa-encryption-audit

Questions About Our Encryption?

We welcome review from security researchers, cryptographers, and enterprise security teams. We're happy to provide additional technical detail or discuss integration requirements.