diff --git a/services/pddb/Cargo.toml b/services/pddb/Cargo.toml index 4cd5da9ca..f48f84222 100644 --- a/services/pddb/Cargo.toml +++ b/services/pddb/Cargo.toml @@ -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", diff --git a/services/pddb/src/api.rs b/services/pddb/src/api.rs index 6f6294d15..d7652991d 100644 --- a/services/pddb/src/api.rs +++ b/services/pddb/src/api.rs @@ -311,6 +311,7 @@ pub struct PddbBasisRequest { pub name: String, pub code: PddbRequestCode, pub policy: Option, + pub basis_key: Option<[u8; 32]>, } #[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Clone)] pub struct PddbDictRequest { diff --git a/services/pddb/src/backend/hw.rs b/services/pddb/src/backend/hw.rs index 766e79ed2..ab3eb2318 100644 --- a/services/pddb/src/backend/hw.rs +++ b/services/pddb/src/backend/hw.rs @@ -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::() as u32, 0) @@ -2331,6 +2337,7 @@ impl PddbOs { } } } + #[cfg(feature = "gen1")] if let Some(modals) = progress { modals .update_progress(size_of::() 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::::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::()] }; log::info!("Derived page table of len 0x{:x}", pagetable.len()); let entries_per_page = PAGE_SIZE / size_of::(); + #[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) -> Option> { + // 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) -> Option> { #[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)]) diff --git a/services/pddb/src/lib.rs b/services/pddb/src/lib.rs index 5f824276e..7b873ede2 100644 --- a/services/pddb/src/lib.rs +++ b/services/pddb/src/lib.rs @@ -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 { - 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::().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) -> 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::().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, + ) -> 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"); diff --git a/services/pddb/src/main.rs b/services/pddb/src/main.rs index 86efe21c4..cf1bcdd7b 100644 --- a/services/pddb/src/main.rs +++ b/services/pddb/src/main.rs @@ -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::::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, + ) -> 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::().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::().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(¬e, 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,