feat(storage): per-app/owner/system encryption live: gatekeeper hands seed + fresh flag, storage unlocks layer, init seeds areas from factory/ (empties on new key); rmdir op; test-qemu checks sealed home + reboot persistence

This commit is contained in:
2026-10-28 00:07:41 +01:00
parent e8191be160
commit 9dc751c466
20 changed files with 344 additions and 72 deletions

View File

@@ -31,11 +31,13 @@ declares `display`. Nothing gets anything "just in case".
## Disk layout
```
/home/ the owner's files; the shell's `/`
/home/ the owner's files; the shell's `/` (encrypted)
/apps/<id>/ an installed app: manifest.toml and its binary
/data/<id>/ the app's private folder (its `storage` capability)
/system/permissions/ the owner's decisions, one file per app id
/data/<id>/ the app's private folder (its `storage`) (encrypted, its own key)
/system/permissions/ the owner's decisions, one file per app id (encrypted)
/bin/ system servers and the shell (init starts these)
/factory/ plaintext starter content the encrypted areas are
seeded from with a new master key
```
Only the permission manager reaches `/apps`, `/data` and `/system`. The
@@ -89,8 +91,9 @@ granted = ["display"]
```
Written by the permission manager when the owner decides; apps on the
system image ship with their decisions already recorded (pre-approved by
whoever built the image). Changing one later is the owner's call, through
system image ship with their decisions already recorded under
`factory/system/` (pre-approved by whoever built the image), and init
seeds them into the encrypted `system/` on a fresh setup. Changing one later is the owner's call, through
the permission manager.
## Permission manager

View File

@@ -97,8 +97,14 @@ gatekeeper (`types::gatekeeper`), handing it only the terminal, the keystore
(a small disk of its own, told from the data disk by its size) and the
virtio-rng device. Init calls `UNLOCK`, which returns once the owner is in,
then `KEY`, whose reply is the master key; init passes it to storage
(`SET_KEY`, from the unbadged holder only, each half once) and only then
starts the permission manager and the desktop. If the gatekeeper cannot
(`SET_KEY`, from the unbadged holder only, each half once), and a fresh
32-byte seed (`SEED` → `SET_SEED`) for storage's randomness this boot.
Storage's encrypted areas (`home/`, `system/`, each app's `data/<id>/`;
crate `filecrypt`, layer `storage_server::encrypted`) open once both are
in. With a new key (a fresh device, or one just wiped: `UNLOCK`'s word 1)
init empties those areas and seeds them from the plaintext `factory/`;
an area that is missing is seeded too. Only then does init start the
permission manager and the desktop. If the gatekeeper cannot
unlock, init stops there. The gatekeeper exits, and its capabilities go
with it.