Quill security whitepaper
1. Cryptographic primitives
| Purpose | Algorithm | Implementation |
|---|---|---|
| Key agreement | X25519 (Curve25519 ECDH), 32-byte keys | BouncyCastle (managed) |
| Signatures | Ed25519, 64-byte signatures | BouncyCastle (managed) |
| Identity-key conversion | Ed25519 → X25519 (birational map) for DH | Quill.Crypto |
| AEAD | AES-256-GCM; 12-byte random nonce, 16-byte tag; wire format nonce‖ct‖tag | .NET AesGcm |
| KDF | HKDF-SHA256 (info QuillX3DH, QuillRatchet) | .NET HKDF |
| Chain KDF | HMAC-SHA256, 0x01 → message key, 0x02 → next chain key | .NET HMACSHA256 |
| CSPRNG | OS entropy | .NET RandomNumberGenerator |
| Password hashing | Argon2id, per-user random salt | Konscious.Security.Cryptography |
| Device PIN hashing | PBKDF2-SHA256, 100,000 iterations, random salt, device-local | .NET, client only |
| Local database | SQLCipher (AES) over SQLite | sqlite-net-sqlcipher |
| Safety numbers | SHA-512 fingerprint over both identity keys, 12 × 5-digit blocks | Quill.Client |
| Transport | TLS 1.2/1.3 + application-level certificate pinning | Platform TLS + Quill pin check |
| Call media | WebRTC DTLS-SRTP, peer-to-peer or TURN-relayed | SIPSorcery |
Secret comparisons use constant-time equality; intermediate Diffie-Hellman outputs and ephemeral private keys are zeroed after use.
2. Session establishment (X3DH)
Each account publishes a prekey bundle to the server: an Ed25519 identity
public key, a signed X25519 prekey with its Ed25519 signature, and a pool
of one-time X25519 prekeys (client target pool 100; the server rejects an
upload of fewer than 10 or more than 1,000). A one-time prekey handed out
to a requester is deleted server-side in the same operation, so it is used
at most once. If the pool is exhausted the exchange proceeds without
DH4.
The signature on the signed prekey is verified against the responder's identity key before any of the above runs; a bundle that fails verification is rejected outright, which is what stops a malicious or compromised server from substituting its own prekey.
Identity continuity. The client pins a peer's identity key on first successful session and compares it on every later exchange. A change raises a "security key changed" warning in the conversation list and invalidates the cached session; the pin is then updated (warn-and-continue). Users who need certainty compare safety numbers out of band.
3. Message encryption (Double Ratchet)
The X3DH shared secret seeds a Double Ratchet session. Every message carries a header with the sender's current ratchet public key, the previous chain length and the message number.
- Symmetric ratchet. Each message key is derived from the chain key by HMAC-SHA256 and the chain key is advanced; keys are not retained after use. This gives forward secrecy: compromising current state does not reveal earlier messages.
- DH ratchet. When a message arrives bearing a new ratchet public key, the receiver performs an X25519 exchange, re-derives the root key via HKDF-SHA256 and generates a fresh sending key pair. This gives post-compromise security: once both parties have exchanged messages after a compromise, the attacker's captured state is no longer sufficient.
- Out-of-order delivery. Skipped message keys are cached per ratchet key up to a bounded window (1,000 messages); a gap beyond that is refused rather than allowing unbounded key retention.
- Payload. The message key encrypts the plaintext with AES-256-GCM. A failed authentication tag is a decryption failure, not a partial result.
- Groups. A group message is encrypted once per member — the sender fans out one individually-encrypted copy per recipient session. The server relays each copy; there is no group key held anywhere central.
- Attachments. The file is encrypted with AES-256-GCM on the sender's device before upload; the file key is carried inside the end-to-end encrypted message. The server enforces per-recipient access control on the stored blob, but could not read it in any case.
4. What the server holds
| Data | Retention | Server can read? |
|---|---|---|
| Username, Argon2id password hash, workspace, role | Until the account is deleted | Yes (it is metadata) |
| Public keys: identity, signed prekey, one-time prekeys | Until replaced, consumed or the account is deleted | Yes — they are public by design |
| Undelivered message envelopes | Deleted on the recipient's delivery acknowledgement; hard expiry of at most 30 days, swept every 5 minutes | No — ciphertext only |
| Delivered messages | Not stored at all | n/a |
| Encrypted attachments | Until unsent, the account is wiped, or destroy-all | No |
| Device registration and push token | Until the device is removed, wiped or the token is replaced | Yes (opaque token) |
| Sessions / refresh tokens | Refresh window of 30 days; revoked on wipe, logout or password change | Hashed server-side |
| Audit log | Operational retention, then rotated; erased by destroy-all | Yes — metadata only |
An audit entry is {event, userId, ip, timestamp, short detail,
workspace}. For a relay the detail is the recipient id and the
ciphertext length in bytes. No message content, plaintext or
decryptable form is written to any log, database column or telemetry
stream. Push payloads carry only the item kind
(text | image | video | voice | file | location | reaction | task),
the sender's username and, for a group, the group name.
5. Multi-tenancy and authorisation
- Isolation source. Every user and group row carries an
OrganizationId. That column — never the username string — determines isolation. Usernames carry the workspace slug as a suffix (name-workspace) for human legibility only and are never parsed for authorisation. - Single chokepoint. One server-side resolver converts any client-supplied identifier (GUID, full username or bare name) into a user, and returns a result only inside the caller's workspace; a foreign identifier is indistinguishable from a non-existent one. Every relay, presence check, key lookup, group operation and call signal goes through it.
- Roles.
Member,OrgAdmin(their own workspace) andSuperAdmin(platform). Destroy-all, IP whitelist management and server logs are platform-only. - Tokens. JWT access tokens valid 15 minutes, carrying workspace and role claims, with zero clock-skew tolerance; refresh tokens are stored server-side and revocable. SignalR connections authenticate with the same token.
- Account creation. No member self-signup. A workspace is created once by its owner (e-mail-verified) and every subsequent account is created by an administrator; the server, not the admin, applies the workspace suffix.
- Brute-force resistance. Five consecutive failed sign-ins lock the account for 30 minutes; optional TOTP two-factor authentication can be enforced workspace-wide.
6. Client hardening
Access to the app
- PIN gate before any chat data is rendered; biometric unlock via Android
BiometricPromptor iOSLocalAuthentication(Windows: PIN only). - Idle auto-lock, default 30 s, user-configurable; re-locks on background.
- Panic PIN wipes the local database and sessions and presents an empty state.
Data at rest
- SQLCipher database per user. The key is derived from a 256-bit random per-device secret in Android Keystore / iOS Keychain / Windows DPAPI, so a copied database file cannot be opened from public facts.
- Ephemeral conversations persist nothing; self-destruct timers and one-time-view media limit what is retained.
Screen and clipboard
- Android
FLAG_SECURE; iOS window cover on backgrounding (keeps chats out of the app switcher); WindowsWDA_EXCLUDEFROMCAPTURE. - Clipboard contents copied from Quill are cleared after 30 seconds.
Network
- Certificate pinning: platform hostname and chain validation first, then a check of the presented chain against pinned SHA-256 public-key hashes (leaf key plus issuing-CA backups, so a same-key renewal does not break the fleet).
- No third-party analytics, attribution or advertising SDKs; no advertising identifier is read.
7. Administrative operations
| Action | Effect | Scope |
|---|---|---|
| Suspend | Account deactivated, sign-in refused, seat released | Workspace admin |
| Delete user | Account and its server-side key material removed | Workspace admin |
| Remote wipe | Wipe signal pushed to the user's devices; server deletes all sessions, public keys, one-time prekeys, queued envelopes and push tokens for that user, forcing re-registration | Workspace admin (plan-gated) |
| Destroy all | Deletes users, sessions, public keys, one-time prekeys, offline queue, groups and membership, device tokens, IP whitelist and the audit log; purges the attachment store. Irreversible; rate-limited to once per 10 minutes; audited before execution | Platform super admin |
| IP whitelist | Restricts client access to listed source addresses (admin API and the public site remain reachable so the list itself can be managed and app-store review can fetch the policy) | Platform super admin |
| Audit log review | Metadata events, scoped to the caller's workspace | Workspace admin (plan-gated) |
8. Infrastructure and abuse controls
- Region. AWS
ap-south-1(Mumbai), two availability zones. Containerised ASP.NET Core server and Blazor admin panel. - Transport. TLS terminated at an application load balancer with the
ELBSecurityPolicy-TLS13-1-2-2021-06policy on a certificate for our domain; clients additionally pin (§6). - Storage. Managed PostgreSQL with encrypted storage and automated daily backups (7-day window). Attachment bucket: all public access blocked, server-side encryption enabled, contents additionally end-to-end encrypted.
- Rate limiting. Per-IP sliding windows on HTTP — 100 req/min default, 10 req/min on auth endpoints, 60 req/min on token refresh, 1 destroy-all per 10 minutes — plus per-user fixed windows on the real-time hub (e.g. 60 message relays per 10 s) so one authenticated client cannot flood relays, pushes or the queue.
- Reliability without retention. Every message is queued before relay and the queue row is deleted only on the recipient's delivery acknowledgement. The cost of not losing messages is a short-lived ciphertext copy, never a plaintext one.
- Third parties in the message path. Firebase Cloud Messaging (Android), Apple Push Notification service (iOS), Windows Notification Service (Windows) — opaque tokens and content-free payloads only. A TURN server relays call media when peer-to-peer fails; that media is DTLS-SRTP encrypted between the two devices and the relay sees only ciphertext (see §9 for the current limit on call set-up).
9. Threat model — and what Quill does not defend against
In scope. A hostile or compelled server operator; passive or active network attackers including TLS interception; theft of the server database or attachment store; loss or brief seizure of an unlocked-but-idle device; a departing employee's device; casual shoulder-surfing and screenshotting.
Out of scope.
- A compromised endpoint. Malware with sufficient privilege on an unlocked device can read what the user can read. No messenger solves this.
- Metadata concealment. Quill minimises metadata; it is not an anonymity network. The server necessarily learns who is exchanging messages with whom, when, and roughly how large each ciphertext is, and it logs IP addresses for security and rate limiting.
- The other participant. Anyone you message can photograph the screen, quote you, or retain their own copy. Disappearing messages express intent; they do not enforce it against a determined recipient.
- Denial of service. Rate limits blunt abuse; they are not a DDoS defence.
- Root and jailbreak detection. Where present, such checks are a speed bump against casual tampering, not a security boundary.
- Active interception of a call by the server (current limitation). Call media keys come from a DTLS handshake between the two devices, so neither we nor the TURN relay can decrypt a call by recording it. The certificate fingerprints that authenticate that handshake, however, are exchanged through our signalling server and are not yet bound to the participants' identity keys, so a compromised server could substitute them and actively intercept a call as it is set up. Messages and files are not affected. Moving call set-up into the end-to-end-encrypted message channel, verified against the pinned identity keys, will close this.
Non-claims. Quill holds no security certification or compliance attestation (no ISO 27001, no SOC 2, and no DPDP certification — no such scheme exists). No third-party security audit or penetration test has been commissioned. We publish no customer names, user counts or uptime figures we have not measured. Every statement in this document describes implemented behaviour in the shipping product; where a control is partial or platform-specific, we have said so instead of rounding it up.
This document is technical information, not legal advice. Deploying Quill does not by itself make an organisation compliant with India's Digital Personal Data Protection Act, 2023 or any other regulation; obligations remain with you as the data fiduciary. What Quill contributes is data minimisation (no phone numbers, no contact upload, no profiling), encryption of content in transit and at rest on the device, in-region hosting for the managed service, and administrator tools that make deletion immediate and verifiable.
10. Consequences we accept
- We cannot read, recover or produce the contents of any message, file or past call — calls are never recorded and we hold no call keys — including in response to a lawful demand. What we can be compelled to produce is what we hold: account records, connection and audit metadata, and any envelopes still undelivered at that moment.
- We cannot restore a user's history to a new device. History lives in the device's encrypted database; an administrator can restore access to the account, never to past conversations.
- We cannot recover a forgotten device PIN or reverse a panic-PIN wipe.
- We cannot silently add a device or reader to a conversation: a new device means a new identity key, and the other side is warned that the key changed.
11. Self-hosted deployment
Enterprise customers run the same server and admin-panel images on their own infrastructure with their own database, object storage, TURN and mail. Entitlement is a licence string signed with an Ed25519 key and verified offline against a public key in the deployment — the server never contacts us to validate it, and an invalid or expired licence stops the process at startup rather than degrading silently. Self-serve signup is disabled on private deployments. In this mode the metadata described in §4 lives entirely in the customer's environment.
12. Reporting a vulnerability
Send findings to security@clareinfotech.com with reproduction steps and your impact assessment. Please allow a reasonable window for a fix before publishing, test against your own trial workspace rather than other people's data, and avoid denial-of-service testing against production. We acknowledge reports, share an honest severity assessment, and credit reporters in release notes on request. There is no paid bounty programme.
Quill is published by Clare Infotech. Product overview: quillchat.app/security · Privacy policy: quillchat.app/privacy · Sales: sales@clareinfotech.com · Security: security@clareinfotech.com. This summary reflects the product as shipped at the revision date above; the implementation continues to evolve and this page is updated with it.