feat(boot): gatekeeper gates boot (init→UI→gatekeeper→data); keystore disk + virtio-rng discovery; terminal READ_SECRET; storage SET_KEY; PageCount ≤8192, alloc cap gone; device window 0x01D0_0000; test-qemu types passcode over 2 boots

This commit is contained in:
2026-10-27 23:43:47 +01:00
parent b7ef67ffd8
commit 3b7d004285
36 changed files with 455 additions and 74 deletions

View File

@@ -25,6 +25,8 @@ pub enum StorageRequest {
Mkdir { path_len: usize },
/// Carries the endpoint to print to.
SetLog,
/// Half (`half`: 0 or 1) of the master key.
SetKey { half: usize, words: [usize; 2] },
/// A capability to the path (`path_len` bytes of the buffer), inside
/// the caller's scope.
Grant {
@@ -74,6 +76,7 @@ impl Encode for StorageRequest {
Delete { path_len } => [op::DELETE, path_len, 0, 0],
Mkdir { path_len } => [op::MKDIR, path_len, 0, 0],
SetLog => [op::SET_LOG, 0, 0, 0],
SetKey { half, words } => [op::SET_KEY, half, words[0], words[1]],
Grant { path_len, write } => [op::GRANT, path_len, usize::from(write), 0],
Release => [op::RELEASE, 0, 0, 0],
}
@@ -95,6 +98,7 @@ impl Decode for StorageRequest {
op::DELETE => Delete { path_len: w[1] },
op::MKDIR => Mkdir { path_len: w[1] },
op::SET_LOG => SetLog,
op::SET_KEY => SetKey { half: w[1], words: [w[2], w[3]] },
op::GRANT => Grant {
path_len: w[1],
write: w[2] != 0,

View File

@@ -50,6 +50,9 @@ pub struct StorageServer<F: FileSystem, H: Host> {
clients: [Option<Client>; MAX_CLIENTS],
owners: [Option<Owner>; MAX_HANDLES],
log: Option<CapSlot>,
/// The master key's halves, from the gatekeeper through init: what the
/// disk's encryption will be keyed with.
key: [Option<[usize; 2]>; 2],
}
impl<F: FileSystem, H: Host> StorageServer<F, H> {
@@ -61,6 +64,7 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
clients: [const { None }; MAX_CLIENTS],
owners: [None; MAX_HANDLES],
log: None,
key: [None; 2],
}
}
@@ -83,6 +87,23 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
}
}
/// Only the unbadged holder (init), each half only once: nobody can
/// replace the key later.
fn set_key(&mut self, badge: usize, half: usize, words: [usize; 2]) -> StorageResponse {
match (badge, self.key.get_mut(half)) {
(0, Some(slot @ None)) => {
*slot = Some(words);
StorageResponse::Ok
}
_ => StorageResponse::Error,
}
}
/// Whether the whole master key has arrived.
pub fn unlocked(&self) -> bool {
self.key.iter().all(Option::is_some)
}
pub fn filesystem(&self) -> &F {
&self.fs
}
@@ -265,7 +286,7 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
None => R::NotFound,
};
let response = match request {
Attach | SetLog | Release => R::Error,
Attach | SetLog | SetKey { .. } | Release => R::Error,
Grant { path_len, write } => {
let narrower = scope.narrow(&buf[sent(path_len)], write);
return match narrower.and_then(|s| grants.add(badge, s, |b| host.mint(b))) {
@@ -368,6 +389,7 @@ impl<F: FileSystem, H: Host> Server for StorageServer<F, H> {
) -> ([usize; 4], Option<Give>) {
match StorageRequest::decode(words) {
Some(StorageRequest::SetLog) => (self.set_log(badge, cap).encode(), None),
Some(StorageRequest::SetKey { half, words }) => (self.set_key(badge, half, words).encode(), None),
// A badge storage never issued, or one it has taken back.
Some(request) => match self.grants.scope(badge) {
None => (StorageResponse::Denied.encode(), None),

View File

@@ -25,6 +25,7 @@ fn every_request_roundtrips() {
write: false,
},
StorageRequest::Release,
StorageRequest::SetKey { half: 1, words: [7, 9] },
] {
assert_eq!(StorageRequest::decode(req.encode()), Some(req));
}

View File

@@ -472,3 +472,16 @@ fn the_unbadged_holder_cannot_be_released() {
StorageResponse::Error
);
}
#[test]
fn only_init_sets_the_key_and_only_once() {
let mut s = server();
let set = |s: &mut Srv, badge, half| call(s, badge, StorageRequest::SetKey { half, words: [1, 2] }).0;
assert_eq!(set(&mut s, 5, 0), StorageResponse::Error, "a client cannot");
assert_eq!(set(&mut s, 0, 0), StorageResponse::Ok);
assert!(!s.unlocked());
assert_eq!(set(&mut s, 0, 0), StorageResponse::Error, "nor can it be replaced");
assert_eq!(set(&mut s, 0, 2), StorageResponse::Error, "two halves only");
assert_eq!(set(&mut s, 0, 1), StorageResponse::Ok);
assert!(s.unlocked());
}