Security Whitepaper
Last updated:
Threat model
Ciphek's threat model assumes an adversary with full read access to Ciphek's servers, all subprocessors, all network paths between client and server, and the ability to compel Ciphek by lawful process. The protected asset is the plaintext of media files (video, photo, thumbnail), filenames, tags, and the search index. The protection guarantee is confidentiality of those assets — the adversary obtains ciphertext only.
The threat model does not defend against an attacker who controls the user's browser at the moment of decryption (e.g., malicious browser extension), an attacker who knows the user's master password, or an attacker who can run code on the user's device. These attacks reach plaintext at the boundary where Ciphek's protections end.
Ciphek protects content, not all metadata. An adversary with persistent network observation can infer rough upload sizes and timing. The Storage and Transport section discusses the chunking strategy that bounds those inferences.
Key derivation hierarchy
The master password is stretched into a 256-bit master key by Argon2id with 64 MiB of memory, 3 iterations, and parallelism p=1. The salt is a 32-byte client-generated random value stored on the server alongside the account record. p=1 reflects the single-threaded execution model of browser WASM; iterations and memory follow OWASP 2024 minimum recommendations for interactive login.
From the master key, HKDF-SHA256 derives subkeys with distinct info labels: an authentication verifier key, a metadata-encryption key, and a per-account file-wrapping key. The exact label strings are "video-cloud-v1-auth-key", "video-cloud-v1-metadata-key", "video-cloud-v1-file-wrapping-key", and "video-cloud-v1-recovery-wrap".
Each uploaded file gets a freshly random 256-bit file key, generated by the browser's CSPRNG. The file key encrypts the file's chunks with XChaCha20-Poly1305. The file key is then wrapped with AES-KW under the account's wrapping key and stored alongside the file's metadata blob. Decryption requires re-deriving the wrapping key (master password → master key → HKDF → wrapping key) and unwrapping the file key.
Encryption pipeline
Files are split into 4 MiB chunks before encryption. Chunking enables progressive upload, resumability after network failure, and time-to-first-frame playback for video without buffering the full ciphertext.
Each chunk is encrypted with XChaCha20-Poly1305 using the file key and a fresh 192-bit random nonce. The 192-bit nonce space (vs AES-GCM's 96-bit) lets us use random nonces per chunk without the birthday-bound concerns that motivate counter-based AES-GCM nonce schemes. Each ciphertext chunk carries its 16-byte Poly1305 authentication tag.
Filename, tags, and the search-index entries for a file are encrypted as a single metadata blob with the per-account metadata key. The blob is decrypted client-side at vault load time. The minisearch index is built from the decrypted metadata in the browser; the index never reaches the server in plaintext.
Storage and transport
Encrypted chunks land in Cloudflare R2 via S3-compatible multipart upload. The browser obtains presigned upload URLs from Ciphek's API; the API never sees the chunk payload.
Range requests retrieve chunks for streaming playback. The browser fetches encrypted chunks through Cloudflare R2 presigned URLs, decrypts them in a TransformStream pipeline, and feeds plaintext to the MediaSource API.
All transport between client and server, and between server and R2, uses TLS 1.3. HSTS is set with a one-year max-age and includeSubDomains. The Content-Security-Policy is strict-dynamic with per-request nonces on application routes; legal pages use a nonce-free static CSP because they render at build time.
TOTP secret encryption
Two-factor authentication uses TOTP per RFC 6238 with the otpauth library. The TOTP secret is generated server-side at enrollment, stored encrypted with a server-side AES-256-GCM key, and only decrypted in process memory during the verification step. The encryption key for the TOTP secret is held in Ciphek's secret manager; this key is the boundary on the TOTP threat model — Ciphek staff with access to the secret manager could in principle decrypt TOTP secrets, which is why we treat the second factor as supplementary to the master password rather than as a primary cryptographic boundary.
Recovery codes for 2FA bypass are generated client-side, encrypted under the same metadata key as other vault content, and stored only in the encrypted metadata blob. Loss of all recovery codes plus loss of the TOTP device requires master-password-only sign-in to be re-enabled by deleting and recreating the account, since the cryptographic boundary cannot be reset by the server.
Recovery mnemonic
At account creation Ciphek displays a 24-word BIP39 mnemonic encoding 256 bits of entropy. The mnemonic acts as an alternate Argon2id input — entering the mnemonic in the recovery flow lets the browser re-derive the master key and re-establish encryption. The mnemonic is shown once, never sent to the server, and never stored in any form by Ciphek.
The user's responsibility is to write the mnemonic down on paper and store it offline. Loss of both the master password and the mnemonic permanently destroys access to the encrypted vault. Ciphek cannot recover from this loss — the keys do not exist on Ciphek's systems.
Audit results
No third-party security audit of Ciphek's integration code has been completed as of v1 launch. A third-party audit is planned post-launch.
The cryptographic primitives Ciphek depends on carry their own audit lineage: @noble/ciphers (Cure53 audit, 2024, funded by OpenSats), Argon2id (RFC 9106 reference implementation), Web Crypto API (browser-platform-audited), and @noble/hashes (same audit lineage as @noble/ciphers).
Internal pen-test results from milestone v1.0 reported zero critical and zero high-severity findings. Internal crypto-audit results from v1.0 reported 41 of 44 checks PASS; the 3 non-PASS items related to documentation and threat-model write-up rather than implementation defects, and have been addressed in subsequent milestones. These results are internal and do not substitute for an independent audit.
Attacks outside scope
Ciphek does not protect against the following attack classes. We disclose them here because honest scope is part of a credible security claim.
- Browser compromise — a malicious browser extension or compromised browser binary can read decrypted plaintext at the moment of decryption.
- Endpoint malware — keyloggers, screen recorders, or memory-scraping malware on the user's device defeat the cryptographic boundary.
- Side-channel attacks (timing, cache, EM emanation) against the user's device — the browser's WebCrypto and WASM implementations are the relevant defense surface, not Ciphek's code.
- Server-side malicious code injection at build time or runtime — if Ciphek's deployment pipeline is compromised and serves a tampered JavaScript bundle, that bundle can exfiltrate keys before encryption. Subresource Integrity is not currently enforced on Vercel's edge network because Vercel rewrites bundles in transit; mitigation is restricted to access controls on the deployment pipeline.
- Quantum cryptanalysis of asymmetric primitives — Ciphek does not currently use post-quantum key exchange. Symmetric primitives (XChaCha20, AES) are considered robust against known quantum attacks at the chosen key sizes.
Where these limits become user-relevant, the Privacy Policy and Terms of Service describe the operational implications.