feat(users): per-user sealed folders users/<id>/{home,apps,data,permissions}; storage SET_USER/LOCK/PURGE; init sign-in/sign-out loop (purge fresh/removed/guest, seed from factory, session start/stop); shell logout; pm user+system app shelves; terminal SIGNED_OUT; per-program stack (gatekeeper 12p); 128K load scratch

This commit is contained in:
2026-10-28 01:08:50 +01:00
parent 29a25bd8c2
commit 91f7d28834
38 changed files with 1156 additions and 378 deletions

View File

@@ -94,16 +94,21 @@ pub struct Unlocked {
pub removed: [Option<UserId>; SLOTS],
}
// Each step keeps its records and buffers (KiBs) to itself: inlined into
// `run` together they would overflow the 16 KiB stack init gives a program.
/// The keystore's newest record, and where the next write goes.
#[inline(never)]
fn load(ks: &mut impl Keystore) -> Result<(Option<Record>, usize), Failure> {
let (mut a, mut b) = ([0u8; RECORD_SIZE], [0u8; RECORD_SIZE]);
let (mut a, mut b) = (alloc::boxed::Box::new([0u8; RECORD_SIZE]), alloc::boxed::Box::new([0u8; RECORD_SIZE]));
if !ks.read(0, &mut a) || !ks.read(1, &mut b) {
return Err(Failure::Device);
}
let copies = Copies { copies: [&a, &b] };
let copies = Copies { copies: [&a[..], &b[..]] };
Ok((copies.newest().map(|(r, _)| r), copies.next_copy()))
}
#[inline(never)]
fn store(ks: &mut impl Keystore, copy: usize, r: &Record) -> Result<(), Failure> {
if ks.write(copy, &r.encode()) { Ok(()) } else { Err(Failure::Device) }
}
@@ -117,6 +122,7 @@ fn save(ks: &mut impl Keystore, r: &Record) -> Result<(), Failure> {
/// Destroys every slot and the directory: noise over both copies, twice,
/// then blank (the device then looks never set up). Marked as wiping
/// first, so a power cut part way is finished on the next boot.
#[inline(never)]
fn wipe(ks: &mut impl Keystore, record: &Record, next: usize, rng: &mut impl Random) -> Result<(), Failure> {
let first = record.wiping(rng).ok_or(Failure::Random)?;
let second = first.wiping(rng).ok_or(Failure::Random)?;
@@ -175,6 +181,7 @@ fn ask_passcodes(prompt: &mut impl Prompt, real: &mut Secret, duress: &mut Secre
}
/// A device never set up (or wiped): its first user, an owner.
#[inline(never)]
fn setup(ks: &mut impl Keystore, rng: &mut impl Random, prompt: &mut impl Prompt, params: KdfParams, memory: &mut [Block]) -> Result<Unlocked, Failure> {
// After a wipe nothing earlier may show: it must look like a new device.
prompt.clear();
@@ -190,6 +197,7 @@ fn setup(ks: &mut impl Keystore, rng: &mut impl Random, prompt: &mut impl Prompt
}
/// What the owner may change at the lock screen, after signing in.
#[inline(never)]
fn manage(ks: &mut impl Keystore, rng: &mut impl Random, prompt: &mut impl Prompt, memory: &mut [Block], session: &Session, removed: &mut [Option<UserId>; SLOTS]) -> Result<(), Failure> {
loop {
let (Some(record), _) = load(ks)? else { return Err(Failure::Device) };