docs: correct padding rule — bucketed throughout, not random above 256
Prior round misread Opus's recommendation: I wrote "rand(0..=256) above 256" for slots and "round up to nearest 256KB above 256KB" for body. Body was right; slots were wrong. Correct rule: bucketed throughout. Slot buckets: 8, 16, 32, 64, 128, 256 (power-of-2 sub-256), then 384, 512, 640, 768, ... (+128 steps above). Body buckets: 1KB, 2KB, ..., 256KB (power-of-2 sub-256KB), then 512KB, 768KB, 1024KB, ... (+256KB steps above; aligns with future chunk size). Stronger privacy than random: observer learns bucket, never position within it. Stable across posts; no min-over-many-posts floor attack. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3ee20736aa
commit
9040d70bf6
3 changed files with 20 additions and 17 deletions
|
|
@ -23,8 +23,8 @@ Builds on Layer 2's `pub_post` / `priv_post` / `wrap_slot` primitives — same s
|
|||
|
||||
- **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 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.
|
||||
- **Bucketed slot-count padding.** Deterministic bucket boundaries throughout — observers learn the bucket, not the position within it. Buckets: 8, 16, 32, 64, 128, 256 (power-of-2 up to 256), then 384, 512, 640, 768, … (linear +128 steps above 256). Author publishes `next_bucket(real_count)` slots with random dummies filling the gap. Power-of-2 sub-256 keeps small-author overhead bounded; +128 steps above 256 avoid the 2× waste of pure power-of-2 at scale. 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).
|
||||
- **Bucketed body-size padding.** Same shape applied to body ciphertext bytes: power-of-2 buckets up to 256KB (1KB, 2KB, 4KB, …, 256KB), then 256KB-step buckets above (512KB, 768KB, 1024KB, …). 256KB above is the future storage chunk size — once large enough to chunk, padding aligns to chunk boundaries naturally.
|
||||
- **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. 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).
|
||||
|
|
@ -130,7 +130,7 @@ Ciphertext `FoFClosed` posts ride the same CDN propagation as other encrypted po
|
|||
|
||||
## 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.
|
||||
- **Slot count padding**: bucketed throughout. Power-of-2 buckets to 256 (8, 16, 32, 64, 128, 256), then linear +128 buckets (384, 512, 640, …). Body-size padding follows the same shape with 256KB as the power-of-2 ceiling and 256KB linear steps above.
|
||||
- **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.
|
||||
|
|
@ -140,8 +140,8 @@ Ciphertext `FoFClosed` posts ride the same CDN propagation as other encrypted po
|
|||
## Ship criteria for Layer 3
|
||||
|
||||
- `PostVisibility::FoFClosed` exists end-to-end.
|
||||
- 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.
|
||||
- Author creation path generates per-post keypairs, wraps CEK+priv_x under each unique `V_x` (deduped), and pads to the next slot bucket: power-of-2 up to 256, then +128 steps above.
|
||||
- Body-size padded to next body bucket: power-of-2 up to 256KB, then 256KB steps above.
|
||||
- Reader decryption path iterates personas × keyring with prefilter tag.
|
||||
- `receive_post` accepts FoFClosed ciphertext without decrypting.
|
||||
- UI surface: post composer has three presets — Public / Friends-only / Friends-of-Friends. Custom subset is v2.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue