# Encryption Design ## Overview All backup data (chunks, metadata, snapshots) is encrypted at rest using AES-256-GCM. Encryption is transparent at the object store layer — the backup engine does not need to be aware of it. ## Threat Model - **At-rest protection**: if B2 or PostgreSQL storage is compromised, data is unreadable without the encryption key. - **Tenant isolation**: even if database RLS is bypassed, each tenant's data is encrypted with a unique key. - **Key loss prevention (SaaS)**: the platform always holds a recovery path via the platform key slot. ## Ciphertext Format Every encrypted value follows the same binary layout: ``` version (1 byte) || nonce (12 bytes) || ciphertext || GCM tag (16 bytes) ``` - **Version `0x01`**: AES-256-GCM, 12-byte random nonce - Overhead: 29 bytes per object (negligible for chunks at 512 KB–8 MB, small for metadata objects) On read, if the first byte is not a recognised version, the data is returned as-is (plaintext). This allows gradual migration from unencrypted to encrypted storage. Existing unencrypted data is safe because it starts with either gzip magic bytes (`0x1f 0x8b`) or JSON (`0x7b`), neither of which collides with valid version bytes. ## Key Hierarchy ``` Platform Key (env var / KMS) └─ wraps → Platform Slot ─── unwraps → Tenant Master Key (256-bit) │ User Password (optional) │ └─ Argon2id derive + wrap → Password Slot ──┘ │ Recovery Key (optional, BIP39 mnemonic) │ └─ wraps → Recovery Slot ───────────────────┘ │ HKDF-SHA256(master, info="cloudstic-backup-v1") │ Encryption Key (256-bit AES) ├──────────── EncryptedStore │ HKDF-SHA256(enc_key, info="cloudstic-dedup-mac-v1") │ Dedup HMAC Key (256-bit) │ Chunker (HMAC-SHA256 refs) ``` ### Master key Each tenant has a 256-bit random master key generated from `crypto/rand` at tenant creation. The master key is never stored in plaintext. ### Key slots A key slot stores the master key encrypted ("wrapped") by a wrapping key. Multiple slots can coexist for the same tenant, each using a different wrapping key. | Slot type | Wrapping key source | Purpose | |----------------|--------------------------------------|------------------------------------------| | `platform` | `PLATFORM_ENCRYPTION_KEY` env var | Legacy platform recovery (plaintext key) | | `kms-platform` | AWS KMS CMK (envelope encryption) | HSM-backed platform recovery | | `password` | Argon2id(user password) | Zero-knowledge; user controls access | | `recovery` | Random 256-bit key (BIP39 mnemonic) | Offline backup; printed / stored safely | ### Key slot storage Key slots are stored in two locations: 1. **PostgreSQL** (`app.encryption_key_slots`): primary source for the web application, fast access during backup/restore setup. 2. **B2** (`keys/-