Skip to main content

Designing Recovery Keys Without Giving the Server the Master Key

See how Inpages.me exports an account-bound Recovery Key in the browser, verifies it without uploading the key, and restores access without server escrow.

Recovery is where an encryption promise is easiest to weaken. If support can silently reconstruct the key, the server has more access than the product suggests. If no recovery path exists, one forgotten Vault Password can make years of protected writing unreadable. Inpages.me uses a browser-exported Recovery Key to preserve a second route without server key escrow.

Separate sign-in from decryption

Inpages.me has two doors with different secrets. Account authentication establishes who may request a Journal and its stored ciphertext. Vault unlocking recovers the Master Key that can turn protected content back into plaintext. Resetting a login password can restore the first door without changing the second.

Under the normal vault flow, a Vault Password and salt are processed by Argon2id to derive a Key Encryption Key. That key decrypts the server-stored Encrypted Master Key in the browser. The server can return the encrypted envelope and derivation parameters, but it cannot perform the final unlock without the password-derived key.

A Recovery Key provides another route to the same Master Key. It must work on a new device where no unlocked browser state remains, while avoiding a recovery database that contains readable vault keys.

The portable key format

The Recovery Key is the Master Key in a portable envelope. In the current INPAGES-RK1 format, the browser serializes three values: a version number, an account binding, and the 32-byte Master Key. The JSON payload is Base64url-encoded and prefixed with a recognizable format marker.

INPAGES-RK1.
base64url({
  v: 1,
  a: first16Bytes(SHA256(identityId)),
  k: base64url(masterKey)
}).
base64url(first8Bytes(SHA256(prefix + payload)))

The final checksum detects truncation, transcription errors, and accidental corruption before the browser tries to use the key. It is not a password hash, a signature, or a defense against an attacker who can deliberately rewrite the whole string. The actual proof comes later, when the candidate key must decrypt the account's vault verifier.

Account binding is also not encryption. It prevents a valid key exported from one account from being casually accepted for another, while avoiding the need to place an email address or other readable account label inside the format. Anyone holding the complete Recovery Key still holds the Master Key material.

Export without uploading the key

Export is available only while the browser vault is unlocked. The controller reads the existing Master Key from the in-memory vault, builds the versioned string locally, and offers it for copy or download. The downloaded text file is created with a temporary browser Blob URL and is never posted as an attachment.

Before showing the export, the browser ensures that the account has a vault verifier. If none exists, it encrypts a fixed, account-specific sentence with the Master Key and sends only that ciphertext and a format version to Rails. The sentence is not a secret; the proof is that only the correct Master Key can authenticate and decrypt its ciphertext. Rails stores the verifier but does not receive the key used to produce it.

After a copy or download, Inpages.me makes a separate best-effort request that records when recovery material was exported. That metadata supports reminders such as “not yet exported.” The request body contains no Recovery Key. A failure to record the timestamp does not invalidate the key already copied or downloaded.

Verify the account and key locally

Import begins by removing whitespace, checking a strict maximum length, parsing the three-part prefix, and comparing the checksum. The browser then validates the format version, account binding, and decoded key length. Those checks give precise errors for damaged or wrong-account files before any vault state changes.

A structurally valid string is not yet trusted. The browser fetches the encrypted vault verifier from Rails and tries to decrypt it with the candidate Master Key. It accepts the import only when the plaintext exactly matches the expected account-specific verifier sentence. The candidate Recovery Key and decoded Master Key never appear in that verification request.

On failure, the controller overwrites the temporary candidate byte array when it can and leaves the vault locked. On success, the normal browser vault takes ownership of the key and emits the same unlock event used by the password path.

What recovery does not do

Importing a Recovery Key does not rotate the Master Key, change the Vault Password, replace the Encrypted Master Key, or re-encrypt Memories and files. It restores a usable copy of the same key that already protects them. This keeps recovery independent of a potentially large data migration.

The consequence is equally important: an old Recovery Key remains powerful after a successful import. The current design does not offer cryptographic revocation of an exported copy. True revocation would require creating a new Master Key, re-encrypting every protected field and attachment, replacing the EMK and verifier, handling interrupted migration, and retiring old material only after the transition completes.

Recovery also does not restore missing ciphertext. A key can decrypt only the records and blobs that still exist. Backups preserve stored data; the Recovery Key preserves the ability to read it. Neither substitutes for the other.

The failure boundary remains real

A server-held reset secret would be more convenient, but it would create another route for the service—or anyone who compromises that route—to obtain protected content. The browser-exported design gives that responsibility to the owner instead. Losing the Vault Password, every unlocked device, and every valid Recovery Key can make protected writing unrecoverable.

The reverse risk is possession. Anyone who obtains a Recovery Key and access to the corresponding account's ciphertext may be able to unlock the vault. It should not be stored inside the journal it protects, pasted into ordinary chat, or left in a shared downloads folder. Clipboard and downloaded-file hygiene are part of the endpoint boundary.

This is why the interface calls the artifact a key rather than a recovery code. The user-facing article explains how Recovery Keys work after a lost journal password. The browser encryption architecture shows where the Master Key, password-derived key, and Encrypted Master Key fit around it.