docs: Layer 3 round 1 + unified hybrid padding rule
Hybrid padding rule (slots + body, same shape): - <=256 real units: pad to next power of 2 (8, 16, ..., 256) - >256 real units: pad to real + rand(0..=256) / nearest 256KB Replaces Layer 2 round 2's rand(32..=128). Small authors/posts get strong bucket-grouping; large authors/posts get probabilistic noise without 2x bandwidth waste of pure power-of-2 at scale. Layer 3 resolutions: - Custom mode deferred; v1 ships Public / Friends-only / FoF only - Slot dedup at V_x byte level (one slot per unique key) - Body-length padding adopted Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a79cab049f
commit
3ee20736aa
3 changed files with 51 additions and 13 deletions
|
|
@ -22,11 +22,12 @@ Builds on Layer 2's `pub_post` / `priv_post` / `wrap_slot` primitives — same s
|
|||
## Lead decisions
|
||||
|
||||
- **New variant, not extended Encrypted.** `PostVisibility::FoFClosed` is its own variant. Existing `Encrypted{recipients}` wraps per-recipient NodeIds — visible on the wire. FoF wraps anonymously under symmetric keys — no NodeIds.
|
||||
- **One wrap slot per `V_x` in the author's keyring.** For a Friends-only post, one slot under `V_me`. For FoF, N+1 slots (one per `V_x` the author holds + own `V_me`). For Custom, subset chosen by author.
|
||||
- **Slot count padded to power-of-2.** Prevents observers from counting vouchers the author has. TBD — OPUS: confirm padding up to next power of 2 with random dummy slots (non-decryptable ciphertext indistinguishable from real slots).
|
||||
- **Each slot carries both CEK and priv_post.** Wrapped together as a single plaintext. One successful unwrap gives reader everything they need to read body + sign comments.
|
||||
- **One wrap slot per unique `V_x`.** Dedup at the `V_x` byte level — if multiple personas hold the same `V_x`, include one slot. Friends-only post: one slot under `V_me`. FoF post: own `V_me` + every distinct `V_x` the author holds. Custom: subset chosen by author (deferred to v2 power-user UI; v1 ships three presets only: Public / Friends-only / Friends-of-Friends).
|
||||
- **Hybrid slot-count padding.** Up to 256 real slots: pad to next power of 2 (smallest authors get strong bucket-grouping). Above 256: pad to `real_count + rand(0..=256)` (large authors get probabilistic noise; power-of-2 jumps would waste up to 50% bandwidth). Dummy slots are byte-identical to real ones, AEAD-fails on any `V_x`. Dummy entries also added to `pub_post_set` 1:1 (see Layer 2).
|
||||
- **Hybrid body-size padding.** Same shape: up to 256KB of body ciphertext, pad to next power of 2 (1KB, 2KB, 4KB, …, 256KB). Above 256KB, round up to nearest 256KB. Aligns large posts with the storage chunking-block size; small posts get strong bucket-grouping against length-based classification.
|
||||
- **Each slot carries both CEK and priv_x (Layer 2 dual-derivation).** Layer 2's `WrapSlot` dual-derivation (read → CEK, sign → priv_x) is the canonical form. Mode 1 simply also uses the CEK to encrypt the body, where Mode 2 leaves the body plaintext.
|
||||
- **Prefilter tag is `HMAC(V_x, post_id)[:2B]`.** Readers precompute a 2-byte tag for each key in their keyring and skip slots that don't match. Cuts trial-decrypt cost by ~2^16 on average.
|
||||
- **Order of slots is randomized.** No positional leak about which slot corresponds to which voucher.
|
||||
- **Order of slots is randomized.** No positional leak about which slot corresponds to which voucher. Re-shuffled on every header revision (including access-grant appends from Layer 2 — TBD whether append-only ordering is acceptable, or whether the entire set is re-shuffled at each grant).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -121,21 +122,28 @@ Ciphertext `FoFClosed` posts ride the same CDN propagation as other encrypted po
|
|||
|
||||
## Open questions
|
||||
|
||||
- **Slot size uniformity.** Real slots and dummy padding slots must be byte-identical-sized. Confirmed. TBD — OPUS: should we also pad the body length to a bucket to avoid length-based classification?
|
||||
- **Prefilter false-positive cost.** 1/65536 false positive per slot. With 500 slots × reader iterating 500 keys, expected ~3.8 false-positive AEAD attempts per post. Acceptable.
|
||||
- **Prefilter collision on legitimate hits.** Two different `V_x` could produce the same `prefilter_tag` for the same `post_id`. Reader just tries both. No correctness issue.
|
||||
- **Slot-reuse across posts.** If the same `V_x` is used across many posts, an attacker can observe prefilter tags recur. Since `post_id` is in the HMAC input, tags differ per post. No leak.
|
||||
- **Custom mode slot selection.** Does the author UI let them pick specific vouchers, specific groups of vouchers, or only "all held + own" vs "own only"? Lead leaning: initial UI = only the three preset levels (Friends-only / FoF / Public); custom ships as power-user option later.
|
||||
- **Deduplication of `V_x` across personas.** If multiple personas hold the same `V_x`, do we include one slot or one-per-persona? Lead leaning: dedup at the `V_x` bytes level; one slot per unique key.
|
||||
- **Access-grant re-shuffle vs append-only.** When the author publishes a Layer 2 access-grant comment, do we re-shuffle the entire `wrap_slots` + `pub_post_set` (preserves the random-order property for the full set but invalidates `pub_x_index` values in already-propagated comments), or append-only (`pub_x_index` is stable across the post's lifetime, but the newest entries are always at the tail — small positional leak that grants are recent)? Lead leaning: **append-only**; `pub_x_index` stability is load-bearing for revocation and comment verification on already-stored comments.
|
||||
- **Padding floor for small authors.** Power-of-2 padding on 1 real slot → 1, 2, or 4? Power-of-2-of-1 is 1, but that's no padding. Probably enforce a minimum bucket of 4 or 8 so that a brand-new persona with one vouch doesn't publish a singleton. Lead leaning: minimum 8.
|
||||
|
||||
## Resolved (2026-04-24)
|
||||
|
||||
- **Slot count padding**: hybrid scheme — up to 256, next power of 2; above 256, `real_count + rand(0..=256)`. Body-size padding follows the same shape with 256KB as the inflection point.
|
||||
- **Custom mode UI**: deferred. v1 ships only the three presets (Public / Friends-only / FoF). Power-user custom-subset UI is v2.
|
||||
- **Slot deduplication**: dedup at the `V_x` byte level. One slot per unique key.
|
||||
- **Body length padding**: yes — pad to next power of 2 up to 256KB, then 256KB chunks above.
|
||||
|
||||
---
|
||||
|
||||
## Ship criteria for Layer 3
|
||||
|
||||
- `PostVisibility::FoFClosed` exists end-to-end.
|
||||
- Author creation path generates ephemeral keypair, wraps CEK+priv_post under each eligible `V_x`, pads to power-of-2.
|
||||
- Author creation path generates per-post keypairs, wraps CEK+priv_x under each unique `V_x` (deduped), and pads per the hybrid rule: power-of-2 up to 256 real slots, then `real_count + rand(0..=256)` above.
|
||||
- Body-size padded: power-of-2 up to 256KB, then nearest 256KB above.
|
||||
- Reader decryption path iterates personas × keyring with prefilter tag.
|
||||
- `receive_post` accepts FoFClosed ciphertext without decrypting.
|
||||
- UI surface: post composer has Public / Friends-only / FoF / Custom picker.
|
||||
- UI surface: post composer has three presets — Public / Friends-only / Friends-of-Friends. Custom subset is v2.
|
||||
- Integration test: A posts FoFClosed. B (direct vouchee) reads. C (FoF via B) reads. D (unrelated) gets ciphertext, cannot decrypt.
|
||||
- Performance: decryption completes within budget at 500-key keyring × 500-slot posts (see Layer 5 for the optimization work that makes this budget feasible).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue