Cryptography¶
This page explains the cryptography behind TOVIO in plain terms — enough to understand what protects your files and to look up which primitive does what. It does not reproduce the math; the normative constructions, byte contracts, and test vectors live in the cryptography specification.
The short version: every protected file is sealed in an envelope — the content encrypted once with a fast symmetric cipher, and the key to that content wrapped individually for each authorized reader. Anyone without a wrapping key sees only ciphertext.
The three tiers¶
TOVIO has three permission tiers. They all produce the same envelope object; they differ only in
how the content key is made available to readers. The collaboration mode you pick at tovio init
(--mode simple|team|agentic) selects the tier — Simple Mode is Tier 0, Team and Agentic Mode are
Tier 1 — and tovio identity init upgrades an existing Simple repository to Team later.
For: a single developer protecting files from anything but their own key. No Key Authority; the root of trust is your own X25519 key.
How: content is encrypted with ChaCha20-Poly1305 using age semantics — an ephemeral key
agreement wraps a per-file key to your recipient key. There is no recipient identity in the
envelope, so even the recipient set stays private.
Status: implemented in the source tree. No generally available binary or package has been published.
For: a team where different people read different paths, the relay is untrusted to hold plaintext, and a Key Authority decides membership. This is the v1 target.
How: content is encrypted once with AES-256-GCM under a per-object data encryption key (DEK). That DEK is then wrapped separately for each authorized reader using their X25519 key. Granting access adds a wrapped copy; it does not re-encrypt the content.
Status: implemented in the source tree. No generally available binary or package has been published.
For: many principals with dynamic, policy-driven attributes, where the policy must be enforced by the mathematics of decryption rather than by an oracle choosing recipients.
How: CP-ABE (ciphertext-policy attribute-based encryption). The policy is embedded in the ciphertext; only an identity whose attributes satisfy it can decrypt. This is what removes trust in the Key Authority not to mis-wrap.
Status: implemented behind the separately licensed Enterprise boundary. The CIRCL TKN20 enterprise KA has passed its conformance and internal-review gate, but public-client support remains default-off and no generally available artifact enables it. Threshold/KMS/HSM authority is a later, separately gated profile.
Why Tier 1 remains the public profile
Tier 1 gives you the headline feature — secrets that stay encrypted even to someone who clones the whole repository — using the public, format-v1 X25519 recipient path. Tier 2 is on the TOVIO 1.0 train and its enterprise KA conformance gate has passed, but the implementation remains private and release-gated. Until an accepted Enterprise artifact exists, the honest consequence for the public Tier-1 profile is the malicious-KA residual risk.
The primitives¶
TOVIO uses a small, fixed set of well-understood primitives. They are frozen for format version 1 — changing any one of them is a format-version break.
| Purpose | Primitive | Why it is used |
|---|---|---|
| Content addressing & integrity | BLAKE3-256 | 256-bit collision resistance is the basis of tamper-evidence; fast and tree-structured. Every object is named by its BLAKE3 hash. |
| Identity signing | Ed25519 | Misuse-resistant signatures for commits, audit entries, capability tokens, refs, and rotation statements. |
| Key agreement / key wrap | X25519 | Wraps a content key to a recipient (Tier 1) and is the age recipient key (Tier 0). A fresh ephemeral key is used per wrap. |
| Tier 0 content encryption | ChaCha20-Poly1305 | The production-proven "secrets in version control" envelope; zero-setup solo encryption. |
| Tier 1 content encryption | AES-256-GCM | Fast authenticated encryption of content under a per-object DEK. |
| Key derivation | HKDF (with BLAKE3 as the PRF) | Derives the wrapping key for each recipient, domain-separated so a key from one context can't be reused in another. |
| Transport | TLS 1.3 | A mandatory floor — defense in depth over already-encrypted payloads, not the basis of confidentiality. |
| Randomness | OS CSPRNG | Generates DEKs, nonces, ephemeral keys, and Change IDs — unique per object. |
Two keys per identity
Every TOVIO identity holds two independent keys: an Ed25519 key for signing and an X25519 key for key agreement (receiving wrapped content keys). They are never interchanged — the signing key is never used to decrypt, and vice versa.
The envelope¶
The envelope is the single object shape that holds a protected file across all three tiers. Its logical fields:
policy-blob | policy-chunk {
policy_id reference to the policy that governs this path
policy_expr human-readable policy string — display/audit ONLY, never trusted for access
enc { cipher, nonce } the symmetric cipher + its 96-bit nonce
recipients [ Recipient ] >= 1; how the content key is made available
ciphertext the encrypted content, with its authentication tag appended
}
A few things matter for understanding the model:
policy_expris never trusted. It is a display and audit string. Access is decided only by whether you can unwrap a content key — never by reading the policy text off the object.- The recipient list says who may read, not who did. Each entry is a wrapped copy of the content key, sealed to one reader.
- The ciphertext authenticates its context. The encryption binds the object's type and policy as additional authenticated data, so a ciphertext cannot be cut-and-pasted into a different policy.
Recipient kinds¶
The recipients list is what differs between tiers. Each entry is tagged with a kind:
kind |
Tier | What it carries |
|---|---|---|
age |
Tier 0 | An ephemeral public key and the wrapped file key — age stanza semantics. No recipient identity. |
x25519 |
Tier 1 | The recipient's identity, an ephemeral public key, and the DEK wrapped to that recipient. |
abe |
Tier 2 | A scheme name and an ABE-encrypted key. The Enterprise implementation produces and consumes it; the default-off public client preserves it without interpretation. |
A reader simply tries to unwrap each recipient entry with their own X25519 secret. Authorization is the unwrap succeeding — the identity field is only a routing hint, never the access decision itself.
Forward compatibility — the ABE-forward envelope¶
Tier 2 (CP-ABE) does not change the envelope format. It uses the abe recipient kind, not a new
object shape. A default public reader that meets an abe recipient it cannot interpret
preserves it verbatim on sync and reports the object as unreadable-by-this-identity — distinct
from corruption. This is the ABE-forward guarantee that keeps the public format interoperable with the
separately released Enterprise implementation.
How encryption meets your working copy¶
Two design choices keep plaintext out of places it should never be.
Snapshot-time encryption. TOVIO has no staging area; it continuously snapshots your working copy. A path that matches a read policy is encrypted at snapshot time, before its content is hashed into a tracked tree. The result is that a clear copy of a protected file never enters the object store and therefore never syncs. On checkout, decryption happens in memory and is never written back as a clear tracked object.
Why .tovioignore is not the mechanism
Ignoring a path only stops it being tracked — it is a tracking filter, never access control. The
correct way to protect a secret is a read policy, which keeps the secret in the repository but
as ciphertext. Relying on .tovioignore to "protect" a secret is a mistake the docs call out
explicitly.
Large files: chunk, then encrypt. A protected large file is split into content-defined chunks on the plaintext (so deduplication boundaries stay stable), and each chunk is encrypted independently. One caveat follows from this: deduplication of encrypted chunks holds only within the same policy/key domain. Two identical chunks encrypted under different keys produce different ciphertext and do not deduplicate — accepted, because cross-policy dedup would leak plaintext equality across access domains.
Integrity and signatures¶
Confidentiality is only half the job; TOVIO also makes tampering detectable.
- Every object is re-hashed on read and on receipt. A payload that does not match its BLAKE3 address is rejected as corruption or tampering — never silently accepted. A different payload simply has a different address.
- Commits, audit entries, tokens, and refs are Ed25519-signed over a domain-separated payload. The domain tag means a signature valid for one object type can never be replayed as a signature for another. A signature that does not verify is rejected with object context, not a soft warning.
- Protected file objects are not themselves signed. Their integrity comes from the encryption's authentication tag and their content address; authorship is established by the commit that references them.
What the cryptography does not claim for v1
These pages and the spec are explicit that, in v1, read-revocation is forward-only (removing a reader means re-encrypting future content) and the Team-tier Key Authority is trusted not to mis-wrap. Neither is presented as cryptographically closed in v1. See the threat model for the full list and what Tier 2 closes.
Where to go next¶
- Looking after your keys — at-rest storage, recovery keys, rotation: key management.
- What the audit log records for compliance: compliance.
- The whole defensive picture and the residual risks: threat model.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure