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)
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
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):
Files affected