- No client-side encryption: plaintext content-addressed remote (SHA-256 names), no backup key, no cryptography dependency (server disk encryption is the trust model). - Filters back user data: images, archives, databases, PDFs accepted; 10 MiB cap; binary sniffing removed; temp names hardened (~$, #..#, .temp). - SQLite zero-error policy: backup-API snapshots + integrity_check, journal folding, locked/corrupt loud skips, verified restores. - Recovery: reindex from manifests, remote adopt, on-demand blob fetch, 5-day retention + thinning, GC, date-guarded remote purge, metrics. - Scheduler with WebDAV quota signal and 70% pressure backstop (floor kept). - 37 tests incl. live-monitor capture safety and DB safety.
20 KiB
Backup encryption security choices: key management
How is the master key generated and derived from a password (scrypt, Argon2, PBKDF2 parameters)?
Takeaway
All five systems use password-derived envelope encryption with a random master/data key, but KDFs and parameters differ: restic pins scrypt N=65536/r=8/p=1 (auto-calibrated on newer adds); Borg 1.x uses PBKDF2-HMAC-SHA256 while Borg 2.x defaults to Argon2id + ChaCha20-Poly1305; Kopia uses scrypt-65536-8-1 over UniqueID salt plus HKDF-SHA256 subkeys (PBKDF2/scrypt configurability added ~2025); rclone crypt uses scrypt N=16384/r=8/p=1; Tarsnap generates keys locally and only uses scrypt to passphrase-wrap the key file.
Cited Findings
- restic: all repo data encrypted with AES-256-CTR + Poly1305-AES MAC as
IV || CIPHERTEXT || MAC(32 bytes overhead, random 16-byte nonce per file); three keys needed (32-byte AES-256 key + 16-byte AES key + 16-byte Poly1305 key) — Source - restic: on open, password + per-key
N,r,p,saltfeed scrypt to derive 64 bytes; first 32 = AES-256 encryption key, last 32 split into 16-byte AES keyk+ 16-byte Poly1305 keyr(masked); these decrypt thedatafield to reveal the master key JSON — Source - restic: canonical key-file example pins
"kdf": "scrypt", "N": 65536, "r": 8, "p": 1; code rejects any KDF other than scrypt (only supported KDF is scrypt()) — Source - restic:
AddKeycalibrates KDF parameters (crypto.Calibrate(KDFTimeout, KDFMemory)) when adding keys, generates a random salt (crypto.NewSalt()) and either a fresh random master key (crypto.NewRandomKey()) or copies the existing master key template — Source - Borg 1.x/legacy: key file encrypted with PBKDF2-HMAC-SHA256 (32-byte salt,
PBKDF2_ITERATIONS), AES-CTR with zero IV plus HMAC-SHA256 encrypt-and-MAC construction — Source - Borg 2.x: 256-bit key-encryption key (KEK) derived from passphrase with Argon2 + random 256-bit salt, then Encrypt-then-MAC of packed key material with ChaCha20-Poly1305 AEAD and constant IV==0 (safe because salt makes KEK unique per encryption) — Source
- Borg: new key files support
argon2 chacha20-poly1305vs legacysha256(PBKDF2) algorithms; Argon2 path derives 32-byte KEK viaargon2.low_level.hash_secret_rawwith configurable time/memory/parallelism/type — Source - Borg:
--key-algorithm argon2is default (Argon2id);pbkdf2kept for old-client compatibility; docs note Argon2 path also fixes two issues at once (separate encrypt vs MAC keys, encrypt-then-MAC instead of encrypt-and-MAC) — Source - Borg: random Borg key itself consists of three random secrets (crypt key, id key, chunker seed); passphrase only locks/encrypts this key, chunking and IDs also derive from it — Source
- Kopia: envelope encryption; format blob carries
uniqueID(random 32 bytes),keyAlgo(e.g.scrypt-65536-8-1),encryption: AES256_GCM, andencryptedBlockFormatholding the real content-encryption secrets (HMACSecret,MasterKey) — Source - Kopia:
Km = PBKDF(passphrase, UniqueID, cost params)(32 bytes) →Ke = HKDF(SHA256, Km, UniqueID, "AES", 32)andAD = HKDF(SHA256, Km, UniqueID, "CHECKSUM", 32); format block encrypted with AES256-GCM using Ke, random IV prepended, AD authenticated — Source - Kopia: only scrypt N=65536/r=8/p=1 documented as supported ("at the moment"), field reserved for future algorithm/cost changes — Source
- Kopia: 2025 PR adds configurable KDF:
--pbkdf (pbkdf2|scrypt),--pbkdf-iter(default 600000 for PBKDF2),--pbkdf-memory(default 64 MB for scrypt), usable atrepo createandchange-password— Source - rclone crypt: file content uses NaCl SecretBox (XSalsa20 + Poly1305), 64 KiB chunks each with 16-byte authenticator, header = 8-byte magic
RCLONE\x00\x00+ 24-byte random nonce incremented per chunk — Source - rclone crypt: filename segments PKCS#7-padded to 16 bytes, encrypted deterministically with EME (ECB-Mix-ECB) AES-256, emitted as lowercase unpadded base32 — Source
- rclone crypt: 80 bytes of key material derived via scrypt
N=16384, r=8, p=1from password (+ optionalpassword2salt); without user salt a built-in internal salt is used — Source - Tarsnap: key files hold authentication keys (server access proofs) and encryption keys (archive encrypt/sign/verify/decrypt) separately, so server compromise does not disclose data — Source
- Tarsnap: no password-derived master key by default; keys generated by
tarsnap-keygen; passphrase protection is optional (--passphrased) and wraps the key file with keys from the scrypt KDF, with tunable--passphrase-mem/--passphrase-time— Source
Inferences
- restic/Kopia/rclone all standardize on scrypt but with 4x cost difference (16384 vs 65536), so rclone password guessing is cheaper; Borg's Argon2id move is the most modern KDF posture.
- Kopia's double-HKDF (separate Ke and AD from one Km) is the cleanest domain separation; restic's split-and-mask of 64 scrypt bytes is older Poly1305-AES style.
Gaps
- restic's current auto-calibration targets (
KDFTimeout,KDFMemory) were not retrieved; documented example still shows 65536/8/1 but newly added keys may use higher N. - Borg's exact default Argon2 time/memory/parallelism constants (
ARGON2_ARGS,ARGON2_SALT_BYTES) were not captured. - Tarsnap's default scrypt mem/time when
--passphrase-mem/--passphrase-timeare omitted was not found.
Where is key material stored (repo config, local key file, OS keyring, TPM)? What is the exact UX for export/backup of keys?
Takeaway
restic and Kopia keep the (password-locked) key material inside the repository; Borg offers repokey (in repo) vs keyfile (client ~/.config/borg/keys) as a first-class choice; Tarsnap and rclone keep keys strictly client-side (key file / rclone.conf), making client-side backup mandatory. None document TPM; OS-credential integration exists only for Kopia (server/connect password caching) and via external helpers for the rest.
Cited Findings
- restic:
keys/directory in repo holds JSON key files (hostname, username, created, kdf params, salt, encrypteddata);restic cat masterkeydecrypts and pretty-prints master encryption/MAC keys — Source - restic: passwords supplied via prompt,
--password-file, or--password-command($RESTIC_PASSWORD_FILE/$RESTIC_PASSWORD_COMMAND); empty passwords refused by default,--insecure-no-passwordrequired since 0.17.0 — Source - Borg 2.x: repokey modes store encrypted key in repo (
repo_dir/config); keyfile modes store in home dir (~/.config/borg/keys);--key-locationrepokey|keyfile chosen atrcreate, movable later viakey change-location— Source - Borg 1.x: identical split — repokey inside repo directory, keyfile in
~/.config/borg/keys(macOS:~/Library/Application Support/borg/keys); remote repos via ssh never see passphrase, plaintext key, or plaintext files — Source - Borg export UX:
borg key export [PATH],--paper(printable, per-line checksums for type-in),--qr-html(QR + paper copy); export stays passphrase-encrypted (no passphrase included);borg key import [--paper]restores; paper-key web tool atpaperkey.html— Source; Source - Borg: for keyfile repos the key must be backed up independently ("NOT sufficient" to keep copy on the backed-up system); for repokey a backup is "not strictly needed" but guards against corruption/loss of the in-repo key — Source
- Kopia: format blob (
kopia.repository/kopia.blobcfgobjects) lives in storage; localrepository-*.configholds connection parameters (not the password); quick-reconnect token viakopia repository status -t(opaque) and-sembeds password (trivially decodable, must be guarded) — Source - Kopia: repository password cached in OS-specific credential storage (Keychain on macOS, Credential Manager on Windows, Keyring on Linux) — Source
- rclone crypt: password + optional salt (
password2) stored inrclone.confin lightly obscured form (AES-CTR with static shared key, random IV prepended) — explicitly "not secure" without overall config-file encryption (rclone configpassword protection); env varsRCLONE_CRYPT_PASSWORD/RCLONE_CRYPT_PASSWORD2(must berclone obscured) — Source - rclone crypt: recovery = remember password or keep
rclone.conf; same passwords re-entered on another machine reproduce access (obscured strings differ due to salt) — Source - Tarsnap: single key file from
tarsnap-keygen --keyfile <file> --user <email> --machine <name>; everytarsnapinvocation needs--keyfile; passphrase via--passphrase method:arg(dev:tty-stdindefault,env:VAR,file:FILENAMEflagged as risky) — Source; Source - Tarsnap:
tarsnap-keymgmt --outkeyfile <new> [-r] [-w] [-d] [--nuke]mints restricted sub-keys (read-only, write-only, delete, nuke-only);-dimplies-r— Source
Inferences
- Only Borg gives a deliberate two-factor-style choice (possession of keyfile + knowledge of passphrase); restic/Kopia/rclone-default are passphrase-only if the repo/config is exfiltrated.
- No system in scope documents TPM / hardware-backed key storage as of 2026; OS keyring appears only as a convenience cache (Kopia), not as a KDF or sealing mechanism.
Gaps
- No reliable source found on native TPM2 / Secure Enclave / Windows Hello integration for any of the four; community wrappers (pass, keyring scripts, systemd-creds) exist but were not verified as documented behavior.
- restic's recommended master-key backup workflow beyond
cat masterkey(e.g. official paper-key guidance) was not found.
Do any support key rotation, multiple keys/passphrases, or Shamir/shared recovery? How?
Takeaway
restic and Borg (2.x/master) support multiple concurrent keys/passphrases sharing one master secret; Kopia and rclone crypt support only password change (re-wrapping the same secrets), with rclone requiring full re-upload; Tarsnap supports restricted sub-keys but not multi-passphrase unlock. No system documents Shamir secret sharing natively.
Cited Findings
- restic:
keycommand withlist,add,remove,passwdsubcommands manages multiple access keys per repo;key addprompts for current password then new password and saves a new key wrapping the same master key; list marks current key with*— Source - restic:
key passwdcreates a new key ID and removes the old one;key remove <ID>refuses to remove the currently-used key — Source; Source - restic:
AddKeywith non-niltemplatecopies master keys from the old key instead of generating new ones, confirming rotation re-wraps rather than re-encrypts data — Source - Borg:
key change-passphraseonly re-locks the same secrets ("does not protect future nor past backups" if key+passphrase were compromised) — Source - Borg:
key change-algorithm argon2|pbkdf2upgrades/downgrades the KDF wrapping without changing secrets — Source - Borg (master/2.x): multiple borg keys per repo — each key holds the same secret material under an independent passphrase and label (first key labeled
admin, protected from removal);key list/add/remove/export --label|--keyselectors; passphrase tried against every key — Source - Borg:
rcreate --other-repo SRC --copy-crypt-keycan reuse crypt key across related repos (default: fresh random crypt key, shared chunker/ID keys for dedup) — Source - Kopia:
kopia repository change-password(CLI only, not GUI) re-encrypts the format block with a new password; must already be connected (so a still-connected client can reset a forgotten password, a disconnected one cannot) — Source; Source - rclone crypt: no in-place password change — changing the configured password orphans existing content; must re-upload everything (in place via second crypt remote +
rclone copydecrypting/re-encrypting on the fly, at 2x bandwidth/quota cost) — Source - Tarsnap: no multi-passphrase unlock or rotation of archive encryption keys documented; capability separation via
tarsnap-keymgmtrestricted keys is the only delegation mechanism — Source - Shamir/shared recovery: no hits in official docs for restic, Borg, Kopia, rclone crypt, or Tarsnap; only community workarounds (splitting password/paper key with external SSS tools) exist — no primary-source citation available.
Inferences
- True cryptographic rotation (new data key, re-encryption) is offered by none of the five for existing snapshots; all "rotation" is re-wrapping or passphrase change.
- Borg's multi-key design is closest to shared/admin recovery (per-user passphrases + protected admin key), but all keys still share one secret — compromise of the secret compromises all slots.
Gaps
- Whether
borg key add(multi-key) is released in stable 2.0 vs master-only could not be pinned down; primary evidence is the PR plus masterusage/key.html. - No documented Shamir/threshold scheme in any official docs; confirm as absent-by-design vs merely undocumented.
What happens on key loss — is data unrecoverable by design? Any documented footguns?
Takeaway
All five declare password/key loss unrecoverable by design. The sharpest footguns: restic key remove of the sole key and deleted/corrupt keys/ files; Borg keyfile loss without export and repokey-with-empty-passphrase on exposed storage; Kopia disconnected-password loss; rclone salt (password2) loss and obscured-config confusion; Tarsnap key-file loss (including billing trap).
Cited Findings
- restic docs repeat on every backend page: "knowledge of your password is required… Losing your password means that your data is irrecoverably lost" — Source
- restic forum (maintainer fd0): if key-file
dataorsaltis missing/corrupt there is "no way to decrypt the data again. Even if you know the password"; intactdata+salt(+N/r/p, brute-forceable) is required — Source - restic footgun case: user generated a random key file, ran
key removeon the old key, then lost the random file (only copy was inside the backup) — recovery required disk forensics for the deleted key file;key removeis just a file deletion inkeys/— Source - restic: brute force only viable if much of the password structure is known; otherwise "consider it lost" — Source
- Borg: "always need both the Borg key and passphrase"; keyfile loss = repo loss ("if you lose the key, you lose access"); mandated offsite export ("leaving your keys inside your car" warning); empty passphrase with repokey = anyone reading the repo can unlock (as good as no encryption); keyfile+empty passphrase acceptable only if client disk is encrypted — Source
- Borg: exported key stays encrypted — recovery needs both export file AND original passphrase, stored in separate safe places — Source
- Kopia: "you cannot restore your files if you forget your password, there is no way to recover a forgotten password because only you know it"; FAQ: "There is no way to recover it or the files and folders within that repository. Store your repository password in a safe place, such as a password manager" — Source; Source
- Kopia footgun nuance: password CAN be reset while still connected (
change-password), which rescues forgotten-but-connected GUI users via CLI, but a disconnected client with a forgotten password is unrecoverable — Source - Kopia reconnect token (
status -t -s) trivially decodes to the password — storing it insecurely leaks the repo — Source - rclone crypt: custom salt is "effectively a second password that must be memorized" and is NOT stored with the data; losing
password2loses data even with correctpassword— Source - rclone footguns: obscured passwords look encrypted but use a static shared AES-CTR key (cursory-inspection only); 1.49.0–1.53.2 random-password generator bug produced insecure passwords (fixed 1.53.3, must rotate by re-upload) — Source
- Tarsnap FAQ: "You can't. Your key file contains the only copy of the cryptographic keys… if you lose them there is no way to get your data back" (same for forgotten key-file passphrase); separate FAQ covers being unable to stop billing after losing all keys — Source
Inferences
- The envelope designs make server-side recovery cryptographically impossible, so every vendor pushes the same operational answer: paper/physical export + password manager + offsite separation of key-export and passphrase.
- rclone's model is the most fragile operationally (two secrets + weak-by-default config obscuring + no re-wrap), Tarsnap second (single local file, no passphrase fallback).
Gaps
- Corrupt-repo recovery (bitrot, backend errors) vs key loss is covered by Kopia consistency docs and restic
rebuild-index/recover, but systematic per-tool data on partial recovery without keys was out of scope and not collected.