cleanup pddb for gen2 targets

gate out modals entirely from gen2 pddb

passwords in gen2 are now entirely handled outside of the
respective service crates, cleaning up the APIs

some history - on gen1 (precursor) devices, the thesis was to
reduce the amount of distance a password has to travel from
the point of a user entering it to the point of use. I think this
is academically interesting, but it pulls in a lot of code
into each service to manage the passwords, conflating services
with UI layers.

it was a nice experiment.

in gen2 (and hopefully beyond) targets, we now commit to
the idea that something else is passing in the sensitive key
materials. This means that, in fact, keying material has to
transition between process spaces, which increases the attack
surface to recover this. However, it also makes the crates
more portable and maintainable.

This is mitigated partially by the fact that in gen2 targets
the entirety of the code base runs within a piece of silicon,
so "sniffing" (passive observation of data going to memory)
attacks are a good bit harder.
This commit is contained in:
bunnie 2025-11-08 03:52:39 +08:00
parent 792de177b0
commit fa8325c2ed
5 changed files with 258 additions and 121 deletions

View file

@ -62,19 +62,20 @@ utralib = { version = "0.1.26", optional = true, default-features = false }
perflib = { path = "../../libs/perflib", optional = true }
# Gen-2 dependencies
keystore = { path = "../keystore", optional = true } # contains all key handlers
xous-swapper = { path = "../xous-swapper", optional = true } # contains spinor
keystore = { path = "../keystore", optional = true } # contains all key handlers
xous-swapper = { path = "../xous-swapper", optional = true } # contains spinor
bao1x-hal-service = { path = "../bao1x-hal-service", optional = true }
bao1x-emu = { path = "../bao1x-emu", optional = true, default-features = false }
bao1x-hal = { path = "../../libs/bao1x-hal", optional = true }
hex = { version = "0.4.3", default-features = false, features = [
"alloc",
], optional = true }
[target.'cfg(any(windows,unix))'.dependencies]
rand = "0.8.5"
rand_chacha = "0.3.1"
hex = { version = "0.4.3", default-features = false, features = [
"alloc",
], optional = true }
[features]
gen1 = [
@ -89,7 +90,7 @@ gen1 = [
"modals",
"precursor-hal",
]
gen2 = ["keystore", "keystore-api/gen2"]
gen2 = ["keystore", "keystore-api/gen2", "hex"]
# gen 1 targets
precursor = ["utralib/precursor", "sha2/precursor", "gen1", "aes/precursor"]
vexii-test = ["precursor-hal/vexii-test", "aes/vexii-test"]
@ -102,6 +103,7 @@ board-baosec = [
"xous-swapper/board-baosec",
"bao1x-hal-service/board-baosec",
"bao1x-hal/board-baosec",
# modals is needed exclusively to know the type to make a None for
"modals/board-baosec",
"keystore/board-baosec",
"aes/bao1x",

View file

@ -311,6 +311,7 @@ pub struct PddbBasisRequest {
pub name: String,
pub code: PddbRequestCode,
pub policy: Option<BasisRetentionPolicy>,
pub basis_key: Option<[u8; 32]>,
}
#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone)]
pub struct PddbDictRequest {

View file

@ -8,15 +8,18 @@ use std::collections::HashMap;
#[cfg(any(all(feature = "pddbtest", feature = "autobasis"), not(feature = "deterministic")))]
use std::collections::HashSet;
use std::convert::TryInto;
#[allow(unused_imports)]
use std::io::{Error, ErrorKind, Result};
#[cfg(feature = "gen1")]
use aes::BLOCK_SIZE;
use aes::cipher::{BlockDecrypt, BlockEncrypt, generic_array::GenericArray};
use aes::{Aes256, BLOCK_SIZE, Block};
use aes::{Aes256, Block};
use aes_gcm_siv::aead::{Aead, Payload};
use aes_gcm_siv::{Aes256GcmSiv, Nonce, Tag};
use backend::bcrypt::*;
#[cfg(all(feature = "gen2"))]
use bao1x_hal::board::{BOOKEND_END, BOOKEND_START, SPINOR_BULK_ERASE_SIZE, SPINOR_ERASE_SIZE};
use bao1x_hal::board::{SPINOR_BULK_ERASE_SIZE, SPINOR_ERASE_SIZE};
#[cfg(feature = "gen1")]
use keystore_api::AesRootkeyType;
use keystore_api::{Checksums, KeywrapError};
@ -960,12 +963,8 @@ impl PddbOs {
self.cipher_ecb = None;
}
pub(crate) fn clear_password(&self) {
#[cfg(feature = "gen1")]
self.rootkeys.clear_password(AesRootkeyType::User0);
#[cfg(feature = "gen2")]
self.rootkeys.clear_password();
}
#[cfg(feature = "gen1")]
pub(crate) fn clear_password(&self) { self.rootkeys.clear_password(AesRootkeyType::User0); }
#[cfg(feature = "gen2")]
pub(crate) fn ensure_password(&mut self) -> PasswordState {
@ -977,7 +976,7 @@ impl PddbOs {
use aes::cipher::KeyInit;
if self.system_basis_key.is_none() || self.cipher_ecb.is_none() {
let scd = self.static_crypto_data_get();
if scd.version == 0xFFFF_FFFF {
if scd.version == 0xFFFF_FFFF || scd.version == 0x0000_0000 {
// system is in the blank state
return PasswordState::Uninit;
}
@ -1077,6 +1076,7 @@ impl PddbOs {
}
fn syskey_ensure(&mut self) {
#[cfg(feature = "gen1")]
while self.try_login() != PasswordState::Correct {
self.clear_password(); // clear the bad password entry
let xns = xous_names::XousNames::new().unwrap();
@ -2176,6 +2176,7 @@ impl PddbOs {
}
}
#[cfg(feature = "gen1")]
fn pw_check(&self, modals: &modals::Modals) -> Result<()> {
let mut success = false;
while !success {
@ -2208,6 +2209,7 @@ impl PddbOs {
}
/// this function attempts to change the PIN. returns Ok() if changed, error if not.
#[cfg(feature = "gen1")]
pub(crate) fn pddb_change_pin(&mut self, modals: &modals::Modals) -> Result<()> {
if let Some(system_keys) = &self.system_basis_key {
// get the new password
@ -2251,6 +2253,7 @@ impl PddbOs {
));
}
// step 0. If we have a modal, confirm that the password entered was correct with a double-entry.
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
self.pw_check(modals)?;
}
@ -2258,6 +2261,7 @@ impl PddbOs {
// step 1. Erase the entire PDDB region - leaves the state in all 1's
if !fast {
log::info!("Erasing the PDDB region");
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.start_progress(
@ -2307,6 +2311,7 @@ impl PddbOs {
}
}
}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(PDDB_A_LOC + PDDB_A_LEN as u32).expect("couldn't update progress bar");
modals.finish_progress().expect("couldn't dismiss progress bar");
@ -2316,6 +2321,7 @@ impl PddbOs {
}
// step 2. fill in the page table with junk, which marks it as cryptographically empty
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.start_progress(t!("pddb.initpt", locales::LANG), 0, size_of::<PageTableInFlash>() as u32, 0)
@ -2331,6 +2337,7 @@ impl PddbOs {
}
}
}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.update_progress(size_of::<PageTableInFlash>() as u32)
@ -2348,6 +2355,7 @@ impl PddbOs {
}
self.patch_pagetable_raw(&temp, remainder_start as u32);
}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.finish_progress().expect("couldn't dismiss progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2359,6 +2367,7 @@ impl PddbOs {
//if !self.rootkeys.ensure_aes_password() {
// return Err(Error::new(ErrorKind::PermissionDenied, "unlock password was incorrect"));
//}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.start_progress(t!("pddb.key", locales::LANG), 0, 100, 0)
@ -2385,6 +2394,7 @@ impl PddbOs {
self.system_basis_key = Some(BasisKeys { pt: system_basis_key_pt, data: system_basis_key }); // this causes system_basis_key to be owned by self and go out of scope
let mut crypto_keys = StaticCryptoData::default();
crypto_keys.version = SCD_VERSION; // should already be set by `default()` but let's be sure.
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(50).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2404,6 +2414,7 @@ impl PddbOs {
self.entropy.borrow_mut().get_slice(&mut crypto_keys.salt_base);
// commit keys
self.patch_keys(crypto_keys.deref(), 0);
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(100).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2425,6 +2436,7 @@ impl PddbOs {
// step 5. fscb handling
// pick a set of random pages from the free pool and assign it to the fscb
// pass the generator an empty cache - this causes it to treat the entire disk as free space
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.start_progress(t!("pddb.fastspace", locales::LANG), 0, 100, 0)
@ -2436,6 +2448,7 @@ impl PddbOs {
for (&src, dst) in free_pool.iter().zip(fast_space.free_pool.iter_mut()) {
*dst = src;
}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(50).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2457,6 +2470,7 @@ impl PddbOs {
// step 5. salt the free space with random numbers. this can take a while, we might need a "progress
// report" of some kind... this is coded using "direct disk" offsets...under the assumption
// that we only ever really want to do this here, and not re-use this routine elsewhere.
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.start_progress(
@ -2510,6 +2524,7 @@ impl PddbOs {
#[cfg(all(feature = "gen2", target_os = "xous"))]
self.patch(&temp, offset);
}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(PDDB_A_LEN as u32).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2520,6 +2535,7 @@ impl PddbOs {
}
// step 6. create the system basis root structure
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals
.start_progress(t!("pddb.structure", locales::LANG), 0, 100, 0)
@ -2537,6 +2553,7 @@ impl PddbOs {
// step 7. Create a hashmap for our reverse PTE, allocate sectors, and add it to the Pddb's cache
self.fast_space_read(); // we reconstitute our fspace map even though it was just generated, partially as a sanity check that everything is ok
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(33).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2573,6 +2590,7 @@ impl PddbOs {
let syskey = self.system_basis_key.take().unwrap(); // take the key out
self.data_encrypt_and_patch_page_with_commit(&syskey.data, &aad, &mut block, &pp);
self.system_basis_key = Some(syskey); // put the key back
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(66).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2585,6 +2603,7 @@ impl PddbOs {
// mark the entry as clean, as it has been sync'd to disk
phys.set_clean(true);
}
#[cfg(feature = "gen1")]
if let Some(modals) = progress {
modals.update_progress(100).expect("couldn't update progress bar");
#[cfg(feature = "ux-swap-delay")]
@ -2693,15 +2712,18 @@ impl PddbOs {
// 0. acquire all the keys
if let Some(all_keys) = self.pddb_get_all_keys(&cache) {
// build a modals for rekey progress
#[cfg(feature = "gen1")]
let xns = xous_names::XousNames::new().unwrap();
#[cfg(feature = "gen1")]
let modals = modals::Modals::new(&xns).unwrap();
#[cfg(feature = "gen1")]
modals.start_progress(t!("pddb.rekey.keys", locales::LANG), 0, all_keys.len() as u32, 0).ok();
// we need a map of page numbers to encryption keys. The keys are referenced by their basis name.
let mut pagemap = HashMap::<PhysAddr, &str>::new();
// transform the returned Vec into a HashMap that maps basis names into pre-keyed ciphers.
let mut keymap = HashMap::<&str, MigrationCiphers>::new();
for (index, (basis_keys, name)) in all_keys.iter().enumerate() {
for (_index, (basis_keys, name)) in all_keys.iter().enumerate() {
if let Some(map) = self.pt_scan_key(&basis_keys.pt, &basis_keys.data, &name) {
for pp in map.values() {
log::info!("page {} -> basis {}", pp.page_number(), name);
@ -2728,7 +2750,8 @@ impl PddbOs {
data_key: basis_keys.data.into(),
},
);
modals.update_progress(index as u32 + 1).ok();
#[cfg(feature = "gen1")]
modals.update_progress(_index as u32 + 1).ok();
}
// check that we have no pages in the FSCB that aren't already mapped in the
@ -2746,6 +2769,7 @@ impl PddbOs {
}
}
}
#[cfg(feature = "gen1")]
modals.finish_progress().ok();
if !clean {
log::warn!(
@ -2766,6 +2790,7 @@ impl PddbOs {
let pagetable: &[u8] = unsafe { &self.pddb_mr.as_slice()[..pddb_data_pages * size_of::<Pte>()] };
log::info!("Derived page table of len 0x{:x}", pagetable.len());
let entries_per_page = PAGE_SIZE / size_of::<Pte>();
#[cfg(feature = "gen1")]
modals
.start_progress(
t!("pddb.rekey.running", locales::LANG),
@ -2875,8 +2900,10 @@ impl PddbOs {
self.patch_pagetable_raw(&new_pt_page[..chunk_len], chunk_start_address as u32);
// this only updates once per page of PTEs, so, every 256 pages that get re-encrypted in the
// worst case.
#[cfg(feature = "gen1")]
modals.update_progress(chunk_start_address as u32).ok();
}
#[cfg(feature = "gen1")]
modals.finish_progress().ok();
self.dna_mode = DnaMode::Normal; // we're done with the legacy DNA!
@ -2890,6 +2917,7 @@ impl PddbOs {
};
if do_fscb {
log::info!("regenerating fast space...");
#[cfg(feature = "gen1")]
modals.dynamic_notification(Some(t!("pddb.rekey.fastspace", locales::LANG)), None).ok();
// convert our used page map into the structure needed by fast_space_generate()
let mut page_heap = BinaryHeap::new();
@ -2913,6 +2941,7 @@ impl PddbOs {
self.fast_space_write(&fast_space);
// this will ensure the data cache is fully in sync
self.fast_space_read();
#[cfg(feature = "gen1")]
modals.dynamic_notification_close().ok();
log::info!("fast space generation done.");
}
@ -2922,6 +2951,21 @@ impl PddbOs {
}
}
#[cfg(not(all(feature = "pddbtest", feature = "autobasis")))]
#[cfg(feature = "gen2")]
/// This is a bit unsafe in that it simply returns the list of all the known keys and doesn't
/// prompt the user to enter any hidden bases. However, for gen2-targets, we're simplifying
/// the basis logic and assuming that the UI is all handled outside of the PDDB.
pub(crate) fn pddb_get_all_keys(&self, cache: &Vec<BasisCacheEntry>) -> Option<Vec<(BasisKeys, String)>> {
// populate the "known" entries
let mut ret = Vec::<(BasisKeys, String)>::new();
for entry in cache {
ret.push((BasisKeys { pt: entry.pt_key.into(), data: entry.key.into() }, entry.name.to_string()));
}
log::info!("{} basis are open, with the following names:", cache.len());
Some(ret)
}
/// UX function that informs the user of the currently open Basis, and prompts the user to enter passwords
/// for other basis that may not currently be open. This function is also responsible for validating
/// that the password is correct by doing a quick scan for "any" PTEs that happen to decrypt to something
@ -2929,6 +2973,7 @@ impl PddbOs {
/// a Vec of keys & names, not a BasisCacheEntry -- so it means that the Basis still are "closed"
/// at the conclusion of the sweep, but their page use can be accounted for.
#[cfg(not(all(feature = "pddbtest", feature = "autobasis")))]
#[cfg(feature = "gen1")]
pub(crate) fn pddb_get_all_keys(&self, cache: &Vec<BasisCacheEntry>) -> Option<Vec<(BasisKeys, String)>> {
#[cfg(feature = "ux-swap-delay")]
const SWAP_DELAY_MS: usize = 300;
@ -3074,6 +3119,7 @@ impl PddbOs {
}
}
#[cfg(feature = "gen1")]
fn yes_no_approval(&self, modals: &modals::Modals, request: &str) -> bool {
modals
.add_list(vec![t!("pddb.yes", locales::LANG), t!("pddb.no", locales::LANG)])

View file

@ -318,7 +318,12 @@ impl Pddb {
/// returns the latest basis that is opened -- this is where all new values are being sent by default
/// if the PDDB is not mounted, returns None
pub fn latest_basis(&self) -> Option<String> {
let mgmt = PddbBasisRequest { name: String::new(), code: PddbRequestCode::Uninit, policy: None };
let mgmt = PddbBasisRequest {
name: String::new(),
code: PddbRequestCode::Uninit,
policy: None,
basis_key: None,
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::LatestBasis.to_u32().unwrap())
.expect("Couldn't execute ListBasis opcode");
@ -333,12 +338,17 @@ impl Pddb {
}
}
#[cfg(feature = "gen1")]
pub fn create_basis(&self, basis_name: &str) -> Result<()> {
if basis_name.len() > BASIS_NAME_LEN - 1 {
return Err(Error::new(ErrorKind::InvalidInput, "basis name too long"));
}
let mgmt =
PddbBasisRequest { name: String::from(basis_name), code: PddbRequestCode::Create, policy: None };
let mgmt = PddbBasisRequest {
name: String::from(basis_name),
code: PddbRequestCode::Create,
policy: None,
basis_key: None,
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::CreateBasis.to_u32().unwrap())
.expect("Couldn't execute CreateBasis opcode");
@ -361,11 +371,85 @@ impl Pddb {
}
}
#[cfg(feature = "gen2")]
pub fn create_basis(&self, basis_name: &str, basis_key: &[u8; 32]) -> Result<()> {
if basis_name.len() > BASIS_NAME_LEN - 1 {
return Err(Error::new(ErrorKind::InvalidInput, "basis name too long"));
}
let mgmt = PddbBasisRequest {
name: String::from(basis_name),
code: PddbRequestCode::Create,
policy: None,
basis_key: Some(*basis_key),
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::CreateBasis.to_u32().unwrap())
.expect("Couldn't execute CreateBasis opcode");
let ret = buf.to_original::<PddbBasisRequest, _>().expect("couldn't restore mgmt structure");
match ret.code {
PddbRequestCode::NoErr => Ok(()),
PddbRequestCode::NoFreeSpace => {
Err(Error::new(ErrorKind::OutOfMemory, "No free space to create basis"))
}
PddbRequestCode::InternalError => {
Err(Error::new(ErrorKind::Other, "Internal error creating basis"))
}
PddbRequestCode::DuplicateEntry => {
Err(Error::new(ErrorKind::AlreadyExists, "Basis already exists"))
}
_ => {
log::error!("Invalid return code");
panic!("Invalid return code");
}
}
}
#[cfg(feature = "gen1")]
pub fn unlock_basis(&self, basis_name: &str, policy: Option<BasisRetentionPolicy>) -> Result<()> {
if basis_name.len() > BASIS_NAME_LEN - 1 {
return Err(Error::new(ErrorKind::InvalidInput, "basis name too long"));
}
let mgmt = PddbBasisRequest { name: String::from(basis_name), code: PddbRequestCode::Open, policy };
let mgmt = PddbBasisRequest {
name: String::from(basis_name),
code: PddbRequestCode::Open,
policy,
basis_key: None,
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::OpenBasis.to_u32().unwrap())
.expect("Couldn't execute OpenBasis opcode");
let ret = buf.to_original::<PddbBasisRequest, _>().expect("couldn't restore mgmt structure");
match ret.code {
PddbRequestCode::NoErr => Ok(()),
PddbRequestCode::AccessDenied => {
Err(Error::new(ErrorKind::PermissionDenied, "Authentication error"))
}
PddbRequestCode::InternalError => {
Err(Error::new(ErrorKind::Other, "Internal error creating basis"))
}
_ => {
log::error!("Invalid return code");
panic!("Invalid return code");
}
}
}
#[cfg(feature = "gen2")]
pub fn unlock_basis(
&self,
basis_name: &str,
basis_key: &[u8; 32],
policy: Option<BasisRetentionPolicy>,
) -> Result<()> {
if basis_name.len() > BASIS_NAME_LEN - 1 {
return Err(Error::new(ErrorKind::InvalidInput, "basis name too long"));
}
let mgmt = PddbBasisRequest {
name: String::from(basis_name),
code: PddbRequestCode::Open,
policy,
basis_key: Some(*basis_key),
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::OpenBasis.to_u32().unwrap())
.expect("Couldn't execute OpenBasis opcode");
@ -389,8 +473,12 @@ impl Pddb {
if basis_name.len() > BASIS_NAME_LEN - 1 {
return Err(Error::new(ErrorKind::InvalidInput, "basis name too long"));
}
let mgmt =
PddbBasisRequest { name: String::from(basis_name), code: PddbRequestCode::Close, policy: None };
let mgmt = PddbBasisRequest {
name: String::from(basis_name),
code: PddbRequestCode::Close,
policy: None,
basis_key: None,
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::CloseBasis.to_u32().unwrap())
.expect("Couldn't execute CloseBasis opcode");
@ -412,8 +500,12 @@ impl Pddb {
if basis_name.len() > BASIS_NAME_LEN - 1 {
return Err(Error::new(ErrorKind::InvalidInput, "basis name too long"));
}
let mgmt =
PddbBasisRequest { name: String::from(basis_name), code: PddbRequestCode::Delete, policy: None };
let mgmt = PddbBasisRequest {
name: String::from(basis_name),
code: PddbRequestCode::Delete,
policy: None,
basis_key: None,
};
let mut buf = Buffer::into_buf(mgmt).expect("Couldn't convert to memory structure");
buf.lend_mut(self.conn, Opcode::DeleteBasis.to_u32().unwrap())
.expect("Couldn't execute DeleteBasis opcode");

View file

@ -570,6 +570,7 @@ fn wrapped_main() -> ! {
let entropy = Rc::new(RefCell::new(TrngPool::new()));
// for less-secured user prompts (everything but password entry)
#[cfg(feature = "gen1")]
let modals = modals::Modals::new(&xns).expect("can't connect to Modals server");
// our very own password modal. Password modals are precious and privately owned, to avoid
@ -699,8 +700,6 @@ fn wrapped_main() -> ! {
// it is the only server capable of doing this.
#[cfg(feature = "gen1")]
let time_resetter = xns.request_connection_blocking(crate::TIME_SERVER_PDDB).unwrap();
#[cfg(feature = "gen2")]
let time_resetter = 0; // dummy
// track processes that want a notification of a mount event
let mut mount_notifications = Vec::<xous::MessageSender>::new();
@ -800,67 +799,51 @@ fn wrapped_main() -> ! {
xous::msg_blocking_scalar_unpack!(msg, _, _, _, _, {
match pddb_os.ensure_password() {
PasswordState::Correct => {
log::info!("pw correct");
if try_mount_or_format(
&modals,
&mut pddb_os,
&mut basis_cache,
PasswordState::Correct,
time_resetter,
&mut basis_monitor_notifications,
) {
is_mounted.store(true, Ordering::SeqCst);
for requester in mount_notifications.drain(..) {
xous::return_scalar2(requester, 0, 0).expect("couldn't return scalar");
fn try_mount(
pddb_os: &mut PddbOs,
basis_cache: &mut BasisCache,
basis_monitor_notifications: &mut Vec<xous::MessageEnvelope>,
) -> bool {
if let Some(sys_basis) = pddb_os.pddb_mount() {
log::info!("PDDB mount operation finished successfully");
basis_cache.basis_add(sys_basis);
if basis_monitor_notifications.len() > 0 {
notify_basis_change(
basis_monitor_notifications,
basis_cache.basis_list(),
);
}
true
} else {
false
}
assert!(
mount_notifications.len() == 0,
"apparently I don't understand what drain() does"
);
log::info!("{}PDDB.MOUNTED,{}", BOOKEND_START, BOOKEND_END);
xous::return_scalar2(msg.sender, 0, 0).expect("couldn't return scalar");
} else {
xous::return_scalar2(msg.sender, 1, 0).expect("couldn't return scalar");
}
// The behavior in gen2 is mount-or-format: no user approval
// is requested in case of format. This is silghtly dangerous in case
// of data corruption but I think this is the "right" user experience
if !try_mount(&mut pddb_os, &mut basis_cache, &mut basis_monitor_notifications) {
log::warn!("PDDB not formatted - formatting the PDDB!");
pddb_os.pddb_format(false, None).expect("couldn't format PDDB");
if !try_mount(
&mut pddb_os,
&mut basis_cache,
&mut basis_monitor_notifications,
) {
panic!("Couldn't format & mount PDDB");
}
}
log::info!("{}PDDB.MOUNTED,{}", BOOKEND_START, BOOKEND_END);
xous::return_scalar2(msg.sender, 0, 0).expect("couldn't return scalar");
}
PasswordState::Uninit => {
log::info!("uninit");
if try_mount_or_format(
&modals,
&mut pddb_os,
&mut basis_cache,
PasswordState::Uninit,
time_resetter,
&mut basis_monitor_notifications,
) {
for requester in mount_notifications.drain(..) {
xous::return_scalar2(requester, 0, 0).expect("couldn't return scalar");
}
assert!(
mount_notifications.len() == 0,
"apparently I don't understand what drain() does"
);
log::info!("{}PDDB.MOUNTED,{}", BOOKEND_START, BOOKEND_END);
xous::return_scalar2(msg.sender, 0, 0).expect("couldn't return scalar");
is_mounted.store(true, Ordering::SeqCst);
} else {
xous::return_scalar2(msg.sender, 1, 0).expect("couldn't return scalar");
}
unimplemented!("Password is always available on gen2")
}
PasswordState::ForcedAbort(failcount) => {
log::info!("forced abort");
xous::return_scalar2(
msg.sender,
2,
// failcount is a u64, but on u32-archs, this gets truncated going to
// usize. clip to u32::MAX.
failcount.min(u32::MAX as u64) as usize,
)
.expect("couldn't return scalar");
PasswordState::ForcedAbort(_failcount) => {
unreachable!();
}
PasswordState::Incorrect(failcount) => {
xous::return_scalar2(msg.sender, 1, failcount.min(u32::MAX as u64) as usize)
.expect("couldn't return scalar")
PasswordState::Incorrect(_failcount) => {
unreachable!();
}
}
})
@ -1086,16 +1069,9 @@ fn wrapped_main() -> ! {
buf.to_original::<BasisRequestPassword, _>().unwrap().plaintext_pw
};
#[cfg(feature = "gen2")]
let ret = match modals
.alert_builder(
format!("{}'{}'", t!("pddb.password_for", locales::LANG), mgmt.name).as_str(),
)
.field(None, None)
.build()
{
Ok(text) => Some(text.content()[0].content.clone()),
_ => panic!("failure retrieving password"),
};
let pw_str = hex::encode(mgmt.basis_key.expect("No key provided"));
#[cfg(feature = "gen2")]
let ret = Some(&pw_str);
if let Some(pw) = ret {
match basis_cache.basis_create(&mut pddb_os, mgmt.name.as_str(), pw.as_str()) {
Ok(_) => {
@ -1144,17 +1120,9 @@ fn wrapped_main() -> ! {
buf.to_original::<BasisRequestPassword, _>().unwrap().plaintext_pw
};
#[cfg(feature = "gen2")]
let ret = match modals
.alert_builder(
format!("{}'{}'", t!("pddb.password_for", locales::LANG), &mgmt.name)
.as_str(),
)
.field(None, None)
.build()
{
Ok(text) => Some(text.content()[0].content.clone()),
_ => panic!("failure retrieving password"),
};
let pw_str = hex::encode(mgmt.basis_key.expect("No key provided"));
#[cfg(feature = "gen2")]
let ret = Some(&pw_str);
if let Some(pw) = ret {
if let Some(basis) = basis_cache.basis_unlock(
&mut pddb_os,
@ -1166,6 +1134,7 @@ fn wrapped_main() -> ! {
basis_cache
.basis_unmount(&mut pddb_os, &basis.name)
.expect("couldn't unmount previously mounted basis of same name");
#[cfg(feature = "gen1")]
modals
.show_notification(
t!("pddb.unmount_previous", locales::LANG),
@ -1195,27 +1164,35 @@ fn wrapped_main() -> ! {
mgmt.name.as_str(),
BOOKEND_END
);
modals
.add_list_item(t!("pddb.yes", locales::LANG))
.expect("couldn't build radio item list");
modals
.add_list_item(t!("pddb.no", locales::LANG))
.expect("couldn't build radio item list");
match modals.get_radiobutton(t!("pddb.badpass", locales::LANG)) {
Ok(response) => {
if response.as_str() == t!("pddb.yes", locales::LANG) {
finished = false;
// this will cause just another go-around
} else if response.as_str() == t!("pddb.no", locales::LANG) {
finished = true;
mgmt.code = PddbRequestCode::AccessDenied; // this will cause a return of AccessDenied
} else {
panic!("Got unexpected return from radiobutton");
#[cfg(feature = "gen1")]
{
modals
.add_list_item(t!("pddb.yes", locales::LANG))
.expect("couldn't build radio item list");
modals
.add_list_item(t!("pddb.no", locales::LANG))
.expect("couldn't build radio item list");
match modals.get_radiobutton(t!("pddb.badpass", locales::LANG)) {
Ok(response) => {
if response.as_str() == t!("pddb.yes", locales::LANG) {
finished = false;
// this will cause just another go-around
} else if response.as_str() == t!("pddb.no", locales::LANG) {
finished = true;
mgmt.code = PddbRequestCode::AccessDenied; // this will cause a return of AccessDenied
} else {
panic!("Got unexpected return from radiobutton");
}
}
_ => panic!("get_radiobutton failed"),
}
_ => panic!("get_radiobutton failed"),
xous::yield_slice(); // allow a redraw to happen before repeating the request
}
#[cfg(feature = "gen2")]
{
mgmt.code = PddbRequestCode::AccessDenied; // this will cause a return of AccessDenied
finished = true;
}
xous::yield_slice(); // allow a redraw to happen before repeating the request
}
} else {
finished = true;
@ -2045,6 +2022,7 @@ fn wrapped_main() -> ! {
) {
Ok(attr) => attr,
Err(e) => {
#[cfg(feature = "gen1")]
modals
.show_notification(
&format!(
@ -2061,6 +2039,14 @@ fn wrapped_main() -> ! {
None,
)
.ok();
#[cfg(feature = "gen2")]
log::error!(
"Error: key not found during bulk read:\n{:?}\n{:?}:{}:{}",
e,
if state.is_basis_specified { Some(&state.basis) } else { None },
&state.dict,
&key_name,
);
continue;
}
};
@ -2326,6 +2312,7 @@ fn wrapped_main() -> ! {
};
}),
#[cfg(feature = "gen1")]
Opcode::MenuListBasis => {
let bases = basis_cache.basis_list();
let mut note = String::from(t!("pddb.menu.listbasis_response", locales::LANG));
@ -2335,6 +2322,7 @@ fn wrapped_main() -> ! {
}
modals.show_notification(&note, None).expect("couldn't show basis list");
}
#[cfg(feature = "gen1")]
Opcode::MenuChangePin => {
if basis_cache.basis_count() == 0 {
modals
@ -2482,7 +2470,10 @@ fn wrapped_main() -> ! {
Opcode::ComputeBackupHashes => {
let mut buffer =
unsafe { Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) };
#[cfg(feature = "gen1")]
let result = pddb_os.checksums(Some(&modals));
#[cfg(feature = "gen2")]
let result = pddb_os.checksums(None);
buffer.replace(result).unwrap();
}
Opcode::Quit => {
@ -2503,6 +2494,10 @@ fn wrapped_main() -> ! {
Opcode::InvalidOpcode => {
log::error!("couldn't convert opcode: {:?}", msg);
}
#[cfg(feature = "gen2")]
Opcode::MenuListBasis | Opcode::MenuChangePin => {
unimplemented!("Not available in gen2 targets")
}
}
}
// clean up our program
@ -2576,6 +2571,7 @@ fn ensure_password(modals: &modals::Modals, pddb_os: &mut PddbOs, _pw_cid: xous:
}
}
#[allow(unused_variables)]
#[cfg(feature = "gen1")]
fn try_mount_or_format(
modals: &modals::Modals,
pddb_os: &mut PddbOs,