mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
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:
parent
792de177b0
commit
fa8325c2ed
5 changed files with 258 additions and 121 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)])
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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(¬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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue