//! The gatekeeper (see the vision's Gatekeeper note): authenticates the //! owner before anything that touches user data starts, and hands the //! master key over. `flow` is the logic; `run` wires the devices. #![no_std] #![forbid(unsafe_code)] extern crate alloc; pub mod devices; pub mod flow; #[cfg(test)] #[path = "tests/mod.rs"] mod tests; use alloc::vec; use keystore::{Block, KdfParams}; use libkos::syscall::{self, Received}; use types::bounded::CapSlot; use types::gatekeeper::{KEY, NAME, REMOVED, SEED, UNLOCK, reply, unlocked}; /// Where init's manifest put them: its own endpoint (devices arrive on /// it, then init's calls), then the terminal. const EP: CapSlot = CapSlot::const_new(0); const TERMINAL: CapSlot = CapSlot::const_new(1); /// Answers init's next call, if it is `op`, with `words`. fn answer(op: usize, words: [usize; 4]) -> bool { loop { match syscall::receive_msg(EP) { Ok(Received::Call { badge: 0, words: [w, ..], .. }) if w == op => { return syscall::reply(words, None).is_ok(); } // Anyone else, or anything else: refused. Ok(Received::Call { .. }) => { let _ = syscall::reply([reply::FAILED, 0, 0, 0], None); } Ok(_) => {} Err(_) => return false, } } } pub fn run() -> ! { // Init hands over the keystore disk, then the randomness device. let keystore = libkos::device::receive_device(EP).mmio.and_then(driver_block_virtio::VirtioBlockDevice::new); let random = libkos::device::receive_device(EP).mmio.and_then(driver_rng_virtio::VirtioRng::new); let (Some(keystore), Some(random)) = (keystore, random) else { libkos::println!("gatekeeper: no keystore or no randomness, nothing to unlock with"); let _ = answer(UNLOCK, [reply::FAILED, 0, 0, 0]); syscall::exit(); }; let mut memory = vec![Block::default(); KdfParams::DEFAULT.memory_kib as usize]; libkos::println!("gatekeeper: waiting for the owner"); let mut random = devices::Hardware(random); let unlocked = flow::run( &mut devices::Disk(keystore), &mut random, &mut devices::Terminal(TERMINAL), KdfParams::DEFAULT, &mut memory, ); // The guesses' working memory goes before anything else happens. for b in memory.iter_mut() { *b = Block::default(); } drop(memory); match unlocked { Ok(flow::Unlocked { user, key, fresh, removed }) => { libkos::println!("gatekeeper: unlocked"); libkos::terminal::clear(TERMINAL); let words = |b: &[u8; 32]| core::array::from_fn::(|i| usize::from_le_bytes(b[i * 8..i * 8 + 8].try_into().unwrap_or([0; 8]))); let mut seed = [0u8; 32]; if !keystore::Random::fill(&mut random, &mut seed) { let _ = answer(UNLOCK, [reply::FAILED, 0, 0, 0]); syscall::exit(); } let flags = if fresh { unlocked::FRESH } else { 0 } | if user.is_none() { unlocked::GUEST } else { 0 }; let id = user.map_or(0, |u| usize::from_le_bytes(u.id.0)); let role = match user.map(|u| u.role) { Some(keystore::Role::Owner) => unlocked::OWNER, Some(keystore::Role::Standard) => unlocked::STANDARD, None => 0, }; let mut name_buf = [0u8; 16]; let len = match &user { Some(u) => { let n = u.name.as_bytes(); name_buf[..n.len()].copy_from_slice(n); n.len() } None => { name_buf[..5].copy_from_slice(b"guest"); 5 } }; let name = &name_buf[..len]; let handed = answer(UNLOCK, [reply::OK, flags, id, role]) && answer(KEY, words(&key.0)) && answer(SEED, words(&seed)) && answer(NAME, types::log::text_words(name)); seed.fill(0); if handed { for gone in removed.iter().flatten() { if !answer(REMOVED, [reply::OK, usize::from_le_bytes(gone.0), 0, 0]) { break; } } let _ = answer(REMOVED, [reply::DONE, 0, 0, 0]); } } Err(why) => { libkos::println!("gatekeeper: cannot go on: {:?}", why); let _ = answer(UNLOCK, [reply::FAILED, 0, 0, 0]); } } // Its capabilities go with it. syscall::exit() }