use super::*;
use lair_keystore_api::dependencies::base64::Engine;
use std::sync::Mutex;
pub(crate) async fn exec(
config: LairServerConfig,
opt: OptImportSeed,
) -> LairResult<()> {
let mut server =
lair_keystore::server::StandaloneServer::new(config).await?;
let (store_pass, bundle_pass, deep_pass) = if opt.piped {
let mut multi = read_piped_passphrase().await?;
let multi_guard = multi.lock();
let multi =
std::str::from_utf8(&multi_guard).map_err(one_err::OneErr::new)?;
let mut pass_list = multi
.split('\n')
.filter_map(|s| {
let s = s.trim();
let s = s.as_bytes();
if s.is_empty() {
None
} else {
let mut n = sodoken::LockedArray::new(s.len())
.expect("failed to allocate locked BufWrite");
{
let mut n = n.lock();
n.copy_from_slice(s);
}
Some(n)
}
})
.collect::<Vec<_>>();
if opt.deep_lock {
if pass_list.len() != 3 {
return Err("expected 3 newline delimited passphrases".into());
}
(
pass_list.remove(0),
pass_list.remove(0),
Some(pass_list.remove(0)),
)
} else {
if pass_list.len() != 2 {
return Err("expected 2 newline delimited passphrases".into());
}
(pass_list.remove(0), pass_list.remove(0), None)
}
} else {
(
read_interactive_passphrase("\n# store passphrase> ").await?,
read_interactive_passphrase("\n# bundle passphrase> ").await?,
if opt.deep_lock {
Some(
read_interactive_passphrase("\n# deep-lock passphrase> ")
.await?,
)
} else {
None
},
)
};
server.run(Arc::new(Mutex::new(store_pass))).await?;
let bundle_bytes = base64::prelude::BASE64_URL_SAFE_NO_PAD
.decode(&opt.seed_bundle_base64)
.map_err(one_err::OneErr::new)?;
let cipher_list =
hc_seed_bundle::UnlockedSeedBundle::from_locked(&bundle_bytes).await?;
let bundle_pass = Arc::new(Mutex::new(bundle_pass));
let mut bundle = None;
for cipher in cipher_list {
use hc_seed_bundle::LockedSeedCipher::*;
match cipher {
PwHash(pw_hash) => {
match pw_hash.unlock(bundle_pass.clone()).await {
Ok(b) => {
bundle = Some(b);
break;
}
_ => continue,
}
}
_ => continue,
}
}
let bundle = bundle
.ok_or_else(|| one_err::OneErr::from("could not unlock seed bundle"))?;
let seed = bundle.get_seed();
let store = server.store().await?;
let seed_info = if let Some(mut deep_pass) = deep_pass {
let limits = hc_seed_bundle::PwHashLimits::Moderate;
let ops_limit = limits.as_ops_limit();
let mem_limit = limits.as_mem_limit();
let mut pw_hash = sodoken::SizedLockedArray::<64>::new()?;
sodoken::blake2b::blake2b_hash(
&mut *pw_hash.lock(),
&deep_pass.lock(),
None,
)?;
store
.insert_deep_locked_seed(
seed,
opt.tag.as_str().into(),
ops_limit,
mem_limit,
pw_hash,
opt.exportable,
)
.await?
} else {
store
.insert_seed(seed, opt.tag.as_str().into(), opt.exportable)
.await?
};
println!("# imported seed {} {:?}", opt.tag, seed_info);
Ok(())
}