Encryption crypto core: age content decorator (v0.3, probe PR1) #11

Merged
Cordy merged 3 commits from feat/encryption into main 2026-07-22 23:15:15 +00:00
Owner

First of two PRs building Manuel's per-user / keys-in-IdP encryption model (see ENCRYPTION-COMPARISON.md). This is the crypto core only — no IdP, no wiring yet, so it's inert until PR2 mounts it.

  • internal/storage/encrypt: an age encrypting decorator around storage.Driver. Write encrypts to the request's recipients; Open decrypts with the request's identities; everything else passes through (content-only — names/sizes/tree not encrypted).
  • KeyProvider interface resolves keys per request from the user in context — so per-user ↔ per-deployment ↔ KMS custody is a provider swap, not a decorator rewrite (the cheap-pivot insurance from the decision record).
  • Multi-recipient: content is encrypted to the owner + an optional recovery recipient, so key-loss ≠ data-loss and admin/legal recovery works (the Nextcloud master-key lesson).
  • Open decrypts to a temp file (removed on Close) to satisfy http.ServeContent — the honest "full-object read in encrypted mode" trade; the seekable-STREAM reader is a documented follow-up.
  • Adds filippo.io/age v1.3.1 (pure-Go, audited — the §1.4 pedigree).
  • Tests (in-memory key provider): round-trip, stored bytes are an age file not plaintext, wrong-identity can't decrypt, recovery recipient can decrypt, no-recipients refuses to write plaintext.

Honest boundaries (in the decision record, restated): server-side transparent encryption, not client-side E2E; age is not post-quantum.

First of two PRs building Manuel's per-user / keys-in-IdP encryption model (see `ENCRYPTION-COMPARISON.md`). This is the **crypto core only** — no IdP, no wiring yet, so it's inert until PR2 mounts it. - `internal/storage/encrypt`: an **age** encrypting decorator around `storage.Driver`. `Write` encrypts to the request's recipients; `Open` decrypts with the request's identities; everything else passes through (content-only — names/sizes/tree not encrypted). - **`KeyProvider` interface** resolves keys **per request** from the user in `context` — so per-user ↔ per-deployment ↔ KMS custody is a provider swap, not a decorator rewrite (the cheap-pivot insurance from the decision record). - **Multi-recipient**: content is encrypted to the owner **+ an optional recovery recipient**, so key-loss ≠ data-loss and admin/legal recovery works (the Nextcloud master-key lesson). - `Open` decrypts to a temp file (removed on Close) to satisfy `http.ServeContent` — the honest "full-object read in encrypted mode" trade; the seekable-STREAM reader is a documented follow-up. - Adds `filippo.io/age v1.3.1` (pure-Go, audited — the §1.4 pedigree). - Tests (in-memory key provider): round-trip, stored bytes are an age file not plaintext, wrong-identity can't decrypt, recovery recipient can decrypt, no-recipients refuses to write plaintext. Honest boundaries (in the decision record, restated): server-side transparent encryption, **not** client-side E2E; age is **not** post-quantum.
Cordy merged commit cbab2eb16c into main 2026-07-22 23:15:15 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#11
No description provided.