Two security findings from Sigil-cc0's audit of the sigilOS rootfs encryption layer are now closed. Finding #1 replaces a trivial hash KDF with a memory-hard ROMix KDF — a brute-force attack now costs 1 MB of RAM per guess, not just CPU cycles. Finding #2 adds a mount-before-write structural guard that eliminates the class of bugs where a write reaches the filesystem before it is mounted. Together they close the last 0.5.0 encryption gate. (FS 424c516, confirmed DSPM ok + UMR ok)
Finding #1: Memory-hard key derivation
Before: the rootfs AEAD keyslot derived encryption keys from a passphrase with a trivial hash. CPU is cheap; an offline brute-force attack costs roughly one hash per guess, which is fast on commodity hardware.
Fix: kdf_w=131072 — ROMix with a 1 MB work factor (217 = 131072 blocks of 1 KB each). The KDF now requires allocating and reading 1 MB of memory for every single guess. Parallelism doesn't help: each attempt needs its own 1 MB working set. This is the same principle as scrypt and Argon2i — memory bandwidth is the bottleneck, not compute.
Verification: DSPM ok — Sigil-cc0 confirmed the derivation cost is memory-bound at the specified work factor.
-- before: trivial hash
key = hash(passphrase ++ salt)
-- after: memory-hard ROMix
key = romix(passphrase, salt, W=131072)
-- W × 1 KB = 131 MB working set per guess
-- offline attacker needs 1 MB RAM per attempt
Finding #2: Write-before-mount (UMR class)
Before: a write call could reach the filesystem code before fs_mount had completed initialization. The write would proceed against uninitialized state — either corrupting data or crashing, depending on what the uninitialized cells happened to contain.
Fix: fs_mount now sets a mounted flag in the FS control block before returning. Every write entry point checks the flag and returns an error if it's not set. fs_mount must succeed before any write path can proceed — structurally, not by convention.
Verification: UMR ok — Sigil-cc0 confirmed the guard fires correctly and eliminates the write-before-mount class.
The guard is a single boolean check at the top of each write dispatcher — cheap at runtime, but it eliminates an entire class of bugs: any code path that calls a write before mount (whether by race, by ordering error, or by future refactor) hits the check and fails loudly rather than silently corrupting state.
The 0.5.0 encryption gate
These were the last two items blocking the 0.5.0 encryption closure. The full encryption stack in 0.5.0 is now:
- AEAD cipher (ChaCha20-Poly1305 or equivalent) over the rootfs block device
- Memory-hard ROMix KDF for key derivation (1 MB work factor)
- Mount-before-write structural guard (no write reaches uninitialized FS)
- Capability gate:
Cap<Storage>required to reach the mount path; the decryption key never appears in EL0
With both cc0 findings closed, the encryption layer is ready for the 1.0 security audit pass. The remaining encryption work (multi-keyslot, hardware key storage via TPM/SE) is 1.x scope, not 1.0-blocking.