1. Client-Side Key Derivation:
Your PIN is stretched into a 256-bit symmetric key using PBKDF2-HMAC-SHA256 with 100,000
iterations and a local salt, ensuring keys are never transmitted to our servers.
2. Authenticated Encryption:
Entries are locked via AES-256-GCM using a cryptographically random, non-reusable 12-byte
Initialization Vector (IV) for every write event to block correlation and manipulation.
3. Zero-Knowledge Synchronization:
Firestore stores only Base64 encoded payload blocks. Keys remain only in temporary volatile
memory (RAM) and are wiped instantly upon logout.
4. Local Search Indexing:
Plaintext search queries never touch the cloud. RozVibe extracts words locally, hashes them via
HMAC-SHA256, and maps them inside an on-device SQLite database for blind index matching.
Table of Contents
- 1. The Threat Model of Digital Diaries
- 2. Client-Side vs. Server-Side Encryption
- 3. Step 1: Key Derivation (PBKDF2-HMAC-SHA256)
- 4. Step 2: Symmetric Encryption (AES-256-GCM)
- 5. Step 3: Payload Packaging and Base64 Encoding
- 6. Step 4: Zero-Knowledge Firestore Synchronization
- 7. Step 5: On-Device Search via Blind Indexing
- 8. Visualizing the Cryptographic Pipeline
- 9. Cryptographic Comparison Matrix
- 10. Physical Security & OS Limitations
- 11. Verifiability: Auditing RozVibe's Claims
- 12. Conclusion
1. The Threat Model of Digital Diaries
Traditional messaging applications protect conversations between multiple people. But journaling is fundamentally different: it is a private conversation with yourself. The thoughts committed to a digital journal are inherently more sensitive, raw, and intimate than typical social chat logs. They detail relationship issues, mental health challenges, professional strategies, and deeply personal anxieties. Consequently, the threat model of a secure journaling application is uniquely demanding.
To build a secure journaling system, we must protect against several categories of threat actors:
- Database Administrators & Cloud Providers: Engineers with access to production servers, or infrastructure managers at cloud providers (such as Google Cloud or AWS), who could access raw database tables.
- External Intruders & Hackers: Malicious actors who exploit cloud configuration bugs, weak employee credentials, or platform-level vulnerabilities to download entire storage disks.
- Government & Legal Compulsion: Authorities issuing subpoenas or court orders demanding the disclosure of a user's diary records.
- Compromised Client Hardware: Malware, debug logs, or unauthorized physical access by local actors looking to read the device screen or extract databases.
Standard cloud architectures rely on server-side encryption , where the server encrypts data prior to writing it to disk. However, this offers zero protection if the server itself is compromised, subpoenaed, or managed by a curious insider. True privacy requires that the plaintext of your memories is mathematically unreadable to any actor except you. This brings us to client-side, zero-knowledge security models.
2. Client-Side vs. Server-Side Encryption
Many digital journals market their services as "securely encrypted at rest and in transit." While technically true, this phrasing often masks a severe structural flaw.
In a Server-Side Encryption model, the journey of your journal entry looks like this:
- You type an entry on your phone: "Today I decided to resign from my job."
- The app encrypts this plaintext in transit using TLS (HTTPS) and transmits it to the server.
- The server receives the data, decrypts the TLS layer, and reads the plaintext: "Today I decided..."
- The server encrypts the text using an encryption key managed by the service provider (e.g., Google Cloud Key Management Service) and writes it to a physical database.
The critical vulnerability occurs at Step 3. Because the server processes the raw text to store, index, or parse it, the server must possess the keys. If the service provider is compromised, your entries are exposed. For a deeper look, check out our analysis on why most digital journals aren't truly private .
In a Client-Side, Zero-Knowledge model (the paradigm used by RozVibe), the architecture is fundamentally different:
- You type your entry on your phone: "Today I decided..."
- Your phone's local processor uses a cryptographically derived key (unknown to the server) to encrypt the text.
-
The entry becomes an unreadable block of bytes:
dKx8mPvQr1Zy0fT3h... - This encrypted blob is transmitted to the server via TLS.
- The server receives the blob, cannot decrypt it, and writes the raw ciphertext directly to Firestore.
Here, the server has zero knowledge of your data. The mathematical keys never leave the volatile memory (RAM) of your physical phone. The rest of this article details the five cryptographic steps that make this possible.
3. Step 1: Password-Based Key Derivation (PBKDF2-HMAC-SHA256)
An encryption key is not a standard password. A secure AES-256 key is a sequence of 256 bits (32 bytes) with high entropy, meaning it must look completely random. Humans, however, cannot memorize 32 random bytes. Instead, they memorize short numeric PINs or passphrases.
If we used a user's raw PIN directly as an encryption key, it would be vulnerable to a trivial brute-force attack. A four-digit PIN has only 10,000 combinations. An attacker with a database copy could try every PIN in milliseconds. To prevent this, we use a Key Derivation Function (KDF) to "stretch" a weak password into a strong cryptographic key.
RozVibe implements PBKDF2 (Password-Based Key Derivation Function 2) configured with **HMAC-SHA256**. The process takes three inputs:
- The User Passcode: A combination of the user's PIN and their account ID (to ensure uniqueness across accounts).
-
A Cryptographic Salt:
A 16-byte random value generated locally on the device when the account is initialized. The salt
ensures that two users with the same PIN (e.g.,
1234) do not derive the same key. It also renders pre-computed dictionary attacks (rainbow tables) useless. - An Iteration Count: The number of times the hashing algorithm is run sequentially. RozVibe uses **100,000 iterations**.
Running 100,000 rounds of HMAC-SHA256 introduces a deliberate, minor computational delay (typically around 100-200 milliseconds on a modern mobile device). While imperceptible to a user logging in, it is a massive barrier for an attacker. If an attacker downloads the database, they cannot simply guess PINs; each guess requires running 100,000 rounds of HMAC-SHA256. Testing a million guesses on a GPU cluster becomes computationally expensive and impractical.
Dart Implementation: Key Derivation FlowThe 76-byte output from the key derivator is split into three specific segments:
- Bytes 0–31 (32 bytes): The primary AES-256 encryption key.
- Bytes 32–43 (12 bytes): A legacy fallback IV (only used to decrypt older legacy entries; modern saves generate fresh random IVs).
- Bytes 44–75 (32 bytes): The HMAC-SHA256 search key used to calculate blind indexing search tokens locally.
4. Step 2: Symmetric Encryption (AES-256-GCM)
With the 32-byte (256-bit) encryption key derived in RAM, the application can now encrypt the journal entry. RozVibe uses AES-256-GCM (Advanced Encryption Standard in Galois/Counter Mode).
AES-256-GCM is an Authenticated Encryption with Associated Data (AEAD) cipher. This means it provides two fundamental security guarantees:
- Confidentiality: Encrypts the plaintext data so it is unreadable.
- Authenticity and Integrity: Generates a 16-byte (128-bit) **Authentication Tag** that verifies the ciphertext has not been tampered with or modified. If even a single bit of the encrypted file is altered in storage, decryption fails immediately.
Unlike older cipher modes like CBC (Cipher Block Chaining), GCM does not require padding block sizes and is highly resistant to padding oracle attacks. However, GCM has a critical constraint: the **Initialization Vector (IV)**.
The IV is a 12-byte nonce (number used once). In Galois/Counter Mode, you must never reuse the same IV with the same key. If you do, it triggers a catastrophic cryptographic failure (known as GCM nonce reuse). An attacker who obtains two different entries encrypted with the same key and the same IV can XOR the ciphertexts together. This reveals the XOR of the plaintexts, allowing them to recover both entries.
To prevent this, RozVibe generates a **fresh 12-byte random IV** using a cryptographically secure random number generator (CSPRNG) for every save event. Even if you edit a single journal entry multiple times, each save creates a brand-new IV. The encrypted output will look completely different every time, blocking attackers from correlating your changes.
Dart Implementation: AES-256-GCM Encryption5. Step 3: Payload Packaging and Base64 Encoding
Once encryption completes, the device holds three separate components:
- The 12-byte random Initialization Vector (IV).
- The raw ciphertext bytes.
- The 16-byte Galois/Counter Mode authentication tag.
To transmit and store this securely without corrupting the raw binary data, these pieces are combined into a single packet. RozVibe packages the data by appending the components together sequentially in memory. The structure is:
[ 12 Bytes: IV ] + [ Variable Bytes: Ciphertext ] + [ 16 Bytes: GCM Auth Tag ]
Because binary bytes are incompatible with JSON text payloads used by database APIs, this combined
array is converted into a standard
Base64 ASCII string
. This string is then saved to the
data
field in Firestore.
6. Step 4: Zero-Knowledge Firestore Synchronization
The packaged Base64 string is synced to Google Cloud Firestore. Because the encryption happened on-device, our servers never see the plaintext, the derived encryption key, or your PIN.
For synchronization to work across multiple devices (e.g., setting up the app on a new tablet), the application needs access to the user's unique cryptographic salt. Without it, the new device cannot reconstruct the exact encryption key from the PIN.
RozVibe handles this by storing the cryptographic salt alongside your account metadata in Firestore. Here is a comparison of what is synced to the database versus what remains local:
| Data Field | Stored on Server? | Cryptographic Visibility | Purpose |
|---|---|---|---|
| User ID | Yes | Plaintext | Associates documents with an authenticated user account. |
| Cryptographic Salt | Yes | Plaintext Metadata | Allows a new device to derive the same key from the PIN. |
| Journal Content (Text) | Yes | AES-256-GCM Encrypted String | Backup and multi-device cloud synchronization. |
| Encryption Key (Derived) | ✗ No | Local Memory (RAM) Only | Wiped instantly upon application exit or logout. |
| User PIN | ✗ No | Local input session only | Never saved to disk or sent to the cloud. |
Storing the salt on the server is a standard, secure practice. A salt is not secret; its job is to provide uniqueness to block pre-computed table attacks. Knowing the salt provides no advantage to an attacker unless they also know the user's PIN.
A note on cloud sync: If you're interested in the details of how local state changes sync to the server, read our guide on how secure cloud sync works .
To restore data on a new device, the sync cycle follows these steps:
- The user logs into their account using credentials.
- The app requests the account metadata from Firestore and retrieves the 16-byte salt.
- The user is prompted to enter their PIN.
- The app combines the PIN, user ID, and salt, running 100,000 iterations of PBKDF2 locally to re-derive the key.
- The app pulls down the encrypted Firestore documents, extracts the IV, and decrypts the entries in-memory.
7. Step 5: On-Device Search via Blind Indexing
Client-side encryption introduces a major technical challenge: search functionality. In a standard
cloud app, search works by running database queries (e.g.,
SELECT * WHERE content LIKE '%sad%'
). But since the server only sees encrypted gibberish, it cannot perform text indexing.
If a user wants to find every entry containing the word "sad" , how does the app search without decrypting every document?
Some apps download the entire database and decrypt it in memory to search locally. This is slow, drains battery, and does not scale if a user has thousands of entries.
RozVibe solves this by implementing **Blind Indexing** using **HMAC-SHA256 tokens** mapped in a
local SQLite database (
rozvibe_search.db
). Here is how the indexing pipeline works:
- Tokenization: When you save a journal entry, the app splits the text into individual words, normalizes them (lowercasing, removing punctuation), and filters out common stop words (e.g., "the", "and").
- Hashing: Each unique word is hashed using HMAC-SHA256 with the dedicated 32-byte search key derived in Step 1.
- Index Mapping: The resulting secure token hashes are saved to the local SQLite database alongside the corresponding entry ID.
For example, if the word is
"anxiety"
, the token hash might be
9b5e8...
. This hash is written to the local index table. The word
"anxiety"
is never saved to the database in plaintext.
When you search for "anxiety" :
- You type "anxiety" into the app's search bar.
-
The app computes the token hash using the local search key:
BlindIndexer.generateSearchToken("anxiety", searchKey). -
It queries the local SQLite table for matching hashes:
SELECT entryId FROM search_index WHERE token = '9b5e8...'. - The matched entries are decrypted in memory and displayed in your search results.
Because the search key is derived from your PIN, the blind index hashes are unique to you. The index database is kept entirely offline, meaning your search terms never leak to cloud providers.
8. Visualizing the Cryptographic Pipeline
To understand how these components interact, follow this flowchart tracing the flow of data from raw text down to the encrypted cloud document:
9. Cryptographic Comparison Matrix
Choosing a digital journal involves understanding how different apps balance security guarantees with user features. The table below compares the security architectures of major platforms:
| Platform | Encryption Model | Key Management | Search Method | Compromise Risk |
|---|---|---|---|---|
| RozVibe | Client-Side Zero-Knowledge (AES-256-GCM) | Derived locally (PBKDF2-HMAC-SHA256, 100k rounds) from PIN. Never sent to server. | On-device blind indexing using SQLite token hashes. | Minimal. Database breaches or subpoenaed servers yield only encrypted blobs. |
| Standard Notes | Client-Side Zero-Knowledge (XChaCha20-Poly1305 / AES-GCM) | Derived locally using Argon2. Never sent to server. | On-device decryption and local indexing. | Minimal. Strong mathematical protections similar to RozVibe. |
| Day One | Opt-In End-to-End Encryption (AES-GCM) | Key stored in iCloud Key-Value Store or server wrapper. Default is server-side encryption. | Server-side indexing for default accounts; client-side for encrypted journals. | Moderate. Security depends on cloud credentials and encryption settings. |
| Penzu | Server-Side Encryption (Opt-in client passphrase) | Server holds or wraps encryption keys unless legacy custom password lock is configured. | Server-side text matching. | Higher. Database compromise or administrative access can expose entries. |
| Reflectly / Journey | Server-Side Encryption | Provider manages encryption keys. Plaintext visible during transit decryption. | Server-side database processing. | Higher. Rely entirely on policy promises and cloud infrastructure security. |
While apps like Day One offer richer editing tools and cross-platform desktop clients, their default setup uses server-controlled keys. RozVibe prioritizes security, sacrificing server-side AI parsing and password recovery to guarantee that your thoughts remain private.
10. Physical Security & OS Limitations
While client-side cryptography is highly effective against server breaches and legal orders, it cannot protect against threats that originate on the device itself.
A zero-knowledge security model assumes that your phone's operating system (iOS or Android) is secure. If the physical hardware is compromised, the cryptographic protections can be bypassed:
1. Local Malware & Keyloggers:
If your phone is infected with keylogging malware, characters can be captured as you type,
before encryption occurs.
2. Memory Extraction:
If an operating system is jailbroken or rooted, malicious apps with root access can read the RAM
of other running apps, potentially capturing the encryption key.
3. Physical Access:
If an unauthorized person gains access to your phone while it is unlocked and RozVibe is open,
they can read your entries directly.
4. PIN Compromise:
If someone watches you enter your PIN, they can duplicate your credentials and re-derive your
key on another device.
To mitigate local threats, RozVibe includes an optional local PIN lock that enforces application-level lockouts, and the derived encryption key is wiped from RAM immediately upon logout or when the app is minimized.
11. Verifiability: Auditing RozVibe's Claims
A core tenet of security engineering is that you should not have to trust a developer's claims. Privacy claims should be auditable and verifiable.
Any security auditor or technically inclined user can verify RozVibe's client-side encryption using standard tools. Here is how:
- Network Auditing: Run a local network proxy (such as mitmproxy or Charles Proxy ) on your computer, route your mobile device's traffic through it, and inspect the requests sent to Firestore. You will see that the payload in the HTTP requests is already Base64 ciphertext, and your plaintext entries are never transmitted.
-
Local Database Inspection:
Root your device or run the app in an emulator, export the SQLite database files (e.g.,
rozvibe_search.db), and inspect them. You will find that only HMAC hashes are present in the search mapping tables. - Code Review: Examine our security documentation detailing the precise functions and iteration parameters used. For more information, read our guide on how RozVibe encrypts journal entries .
12. Conclusion
Digital journaling requires a level of privacy that traditional cloud applications are not designed to provide. Standard databases and server-side encryption models leave your personal thoughts exposed to cloud administrators, compromised systems, and legal subpoenas.
By moving key derivation, encryption, and search processing to your physical device, RozVibe establishes a zero-knowledge architecture. The server becomes a simple storage utility for encrypted blocks, ensuring your data is protected by mathematics rather than policy.
If you want to learn more about client-side cryptography, explore our technical guide on why client-side encryption matters .