Skip to content
Browse the docs

Security model

How encryption works

How Crunu derives account keys from a password and Secret Key, encrypts every environment with its own key, and signs access lists, versions and releases.

Last updated

On this page

Crunu encrypts everything on your devices: in the browser and in the crunu agent. The server stores ciphertext, public keys and signed documents, and it holds no decryption key at any point. This page explains how the keys fit together. What the server sees lists exactly what the server stores, and Limitations lists what this design doesn't protect against.

Three layers give the guarantees, and the product always says which layer a guarantee rests on:

  • Cryptography: who holds a key. It holds even if the server is compromised.
  • Server: whom it sends ciphertext to and whose writes it accepts. It holds while the server is honest.
  • Client: what the app shows and checks. It protects against accidents, not malice.

Primitives

PurposeAlgorithm
Encrypting dataAES-256-GCM
Sealing a key to a recipientX25519 sealed box with a fresh ephemeral key for every message
SignaturesEd25519
Deriving keysHKDF-SHA256
Stretching the passwordPBKDF2-SHA256 with 1,000,000 iterations
Paddingcontents padded to a multiple of 4,096 bytes

The browser uses WebCrypto and the agent uses Go's standard library. Both implementations are checked against the same test vectors.

Your account key has two halves

Your account key comes from two secrets, and neither one ever leaves your device:

  • your password, which only you know;
  • your Secret Key, a random string of about 128.8 bits that starts with K1-. It's created on your device and stored there and in your Emergency Kit.

The password is stretched with PBKDF2, the Secret Key is expanded with HKDF, and the two are combined. From the result your device derives the key that opens your keyset, a separate key used to prove your identity when you sign in, and the key for offline copies.

Because the Secret Key never reaches the server, a stolen copy of the database is useless for guessing passwords. Every guess would also have to guess about 2^128 Secret Key values.

Your keyset

Your keyset holds two key pairs: an X25519 pair for receiving keys sealed to you, and an Ed25519 pair for signing. The server stores the keyset only in encrypted form. Changing your password or Secret Key re-encrypts only the wrapper around it. Your key pairs, certificates and the trust that agents have in you stay the same.

When you sign in, your device signs a one-time challenge for this exact server address. That signature can't be replayed on another server or reused.

The organization keys

Each organization has two keys, held only by its owners and admins, sealed to their keysets:

  • the organization signing key, the root of trust. It certifies members and machines and signs the revocation list;
  • the organization recovery key. Every shared environment key is also sealed to it, so owners and admins can reach shared environments and help someone who lost access.

Members and machines get certificates signed by the organization signing key. Certificates last 2 years. When an owner or admin signs in, their app reissues certificates that have less than 90 days left. Removed people and machines are listed in a signed revocation list whose number only grows.

When an owner or admin leaves, the organization moves to a new key generation: new signing and recovery keys, reissued certificates and new keys for every shared environment.

Every environment has its own key

An environment is the unit of encryption and access. Each has its own random key, and each saved version is encrypted with it. The environment key is then sealed separately to the public key of every recipient: people with a role that reads values, machines that receive the key, and the organization recovery key.

Members with the Specific files role and machines with Selected files delivery don't get the environment key. They get a file copy sealed to them that contains only their files. It's rebuilt whenever those files change, so their access can be removed without rotating the key.

Rotating an environment key creates a new key and seals it to everyone who should keep access. Older versions stay readable for those who had the old key, but nobody without the new key can read a new version.

Signed access lists and versions

Who can access an environment is written in a document called the environment head, signed by an owner or admin. It lists every recipient and role and commits to the current environment key. Each saved version is signed by its author.

Clients and agents check these signatures before they trust anything. The server can't add itself as a recipient, swap an environment key or alter a version without breaking a signature. It can't quietly rename prod to dev either, because the names are part of the signed head.

Rollback and fork protection

Clients and agents remember the highest numbers they've seen: of environment heads, versions, key epochs, revocation lists and organization key generations. They reject anything lower. Two different documents with the same number are rejected as a fork. A device that has never seen the newer state can't detect a rollback, which is the limit of this protection.

A new device has no memory yet. Your organization key pin, a record of the organization's signing key that you signed yourself, tells the new device which organization key to trust, instead of taking the server's word for it.

Signed and reproducible releases

The agent and the web app are released with Ed25519 signatures. The web app, the installer and crunu self-update check those signatures. The web app build is reproducible: the same commit produces the same bytes, so a release can be checked by rebuilding it.

The web app can also run from a local file, crunu.html. Its code comes from your disk rather than the server, and it checks the site against the signed release. The pages use a Content Security Policy that blocks third-party scripts, and load their own code with integrity checksums.