#48 Add keyring rotation history to preserve member access to pre-rotation documents

closed medium · sable · 2026-03-02 23:37 · closed 2026-03-02 23:37 · feature · Phase 3: Keyrings

Comments — 1

sable note 2026-03-02 23:37

Problem

When a keyring member is removed, remove_member.rs discards the old wrapped group keys entirely: 1. keyring.members.retain() removes the departing member's wrapped key 2. A brand new group key is generated via crypto::create_group_key() 3. keyring.members is replaced wholesale with new wrapped keys for remaining members 4. rotation counter increments

The old group key is gone from the record. Documents uploaded under rotation N have their content key wrapped under the rotation-N group key. If a remaining member's local group key cache is lost (new device, cache cleared, etc.), they cannot unwrap the old group key because there's no wrapped copy of it anymore.

Current behavior

  • remove_member: old wrapped keys discarded, new group key generated, record updated atomically
  • keyring record only stores current-rotation wrapped keys
  • members who had the old group key cached locally can still decrypt old documents (cache-dependent)
  • members who lose their cache are permanently locked out of pre-rotation documents

Identified in

BLACKBOX-REPORT.md finding 2.3 (Historical Access Loss / Rotation Footgun), tested by Lux.

Proposed fix

Add a key_history field to the keyring record: an array of { rotation: integer, members: wrappedKey[] } entries. On rotation, the current members+wrapped keys are archived into key_history before being replaced. Remaining members can look up old rotation entries to unwrap old group keys for old documents.

Lexicon change needed

app.opake.cloud.keyring.json needs a new optional field (backward-compatible):

  • key_history: array of objects with rotation (integer) and members (array of wrappedKey)

Files affected

  • lexicons/app.opake.cloud.keyring.json (add key_history field)
  • crates/opake-core/src/records.rs (Keyring struct)
  • crates/opake-core/src/keyrings/remove_member.rs (archive before replacing)
  • crates/opake-core/src/keyrings/download.rs (fall back to key_history for old rotations)
  • crates/opake-cli/src/commands/download.rs (pass rotation info through)
Generated 2026-03-03 02:53 UTC