use std::{
sync::{Arc, RwLock},
time::Duration,
};
use btlib::{
bterr,
crypto::{AsymKeyPub, ConcreteCreds, CredStore, CredStoreMut, Creds, Encrypt, Error},
error::DisplayErr,
Epoch, Result,
};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
pub const TEST_STORE_ROOT_PASSWORD: &str = "shubawumba";
lazy_static! {
pub static ref TEST_STORE: InMemCredStore = {
let store = InMemCredStore::new().unwrap();
let expires = Epoch::now() + Duration::from_secs(7200);
let root_creds = store.provision_root(TEST_STORE_ROOT_PASSWORD, expires).unwrap();
let node_principal = store.provision_node_start().unwrap();
let writecap = root_creds
.issue_writecap(node_principal, &mut std::iter::empty(), expires)
.unwrap();
store.provision_node_finish(writecap).unwrap();
store
};
}
pub struct InMemCredStore {
node_creds: RwLock<Arc<ConcreteCreds>>,
root_creds: RwLock<Option<RootInfo>>,
storage_key: AsymKeyPub<Encrypt>,
}
impl InMemCredStore {
pub fn new() -> Result<Self> {
let node_creds = ConcreteCreds::generate()?;
let root_creds = RwLock::new(None);
let storage_key = node_creds.encrypt_pair().public().clone();
let node_creds = RwLock::new(Arc::new(node_creds));
Ok(Self {
node_creds,
root_creds,
storage_key,
})
}
}
impl CredStore for InMemCredStore {
type CredHandle = Arc<ConcreteCreds>;
type ExportedCreds = ExportedCreds;
fn node_creds(&self) -> btlib::Result<Self::CredHandle> {
let guard = self.node_creds.read().display_err()?;
Ok(guard.clone())
}
fn root_creds(&self, password: &str) -> btlib::Result<Self::CredHandle> {
let guard = self.root_creds.read().display_err()?;
if let Some(info) = guard.as_ref() {
if info.password == password {
Ok(info.creds.clone())
} else {
Err(bterr!(Error::WrongRootPassword))
}
} else {
Err(bterr!("root credentials have not been generated"))
}
}
fn storage_key(&self) -> Result<AsymKeyPub<Encrypt>> {
Ok(self.storage_key.clone())
}
fn export_root_creds(
&self,
root_creds: &Self::CredHandle,
_password: &str,
_new_parent: &AsymKeyPub<Encrypt>,
) -> Result<Self::ExportedCreds> {
Ok(ExportedCreds {
password: _password.to_string(),
creds: root_creds.as_ref().clone(),
})
}
}
impl CredStoreMut for InMemCredStore {
fn gen_root_creds(&self, password: &str) -> Result<Self::CredHandle> {
{
let guard = self.root_creds.read().display_err()?;
if guard.is_some() {
return Err(bterr!("root creds have already been generated"));
}
}
let mut guard = self.root_creds.write().display_err()?;
let creds = Arc::new(ConcreteCreds::generate()?);
*guard = Some(RootInfo {
password: password.to_owned(),
creds: creds.clone(),
});
Ok(creds)
}
fn import_root_creds(
&self,
password: &str,
exported: Self::ExportedCreds,
) -> Result<Self::CredHandle> {
if exported.password != password {
return Err(Error::WrongRootPassword.into());
}
let creds = Arc::new(exported.creds);
let mut guard = self.root_creds.write().display_err()?;
*guard = Some(RootInfo {
password: password.to_owned(),
creds: creds.clone(),
});
Ok(creds)
}
fn assign_node_writecap(
&self,
handle: &mut Self::CredHandle,
writecap: btlib::Writecap,
) -> Result<()> {
let creds = Arc::make_mut(handle);
creds.set_writecap(writecap)?;
let mut guard = self.node_creds.write().display_err()?;
*guard = handle.clone();
Ok(())
}
fn assign_root_writecap(
&self,
handle: &mut Self::CredHandle,
writecap: btlib::Writecap,
) -> Result<()> {
let creds = Arc::make_mut(handle);
creds.set_writecap(writecap)?;
let mut guard = self.root_creds.write().display_err()?;
if let Some(info) = guard.as_mut() {
info.creds = handle.clone();
Ok(())
} else {
Err(bterr!("no root creds have been generated"))
}
}
}
struct RootInfo {
password: String,
creds: Arc<ConcreteCreds>,
}
#[derive(Serialize, Deserialize)]
pub struct ExportedCreds {
password: String,
creds: ConcreteCreds,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cred_store_test_cases;
cred_store_test_cases!(InMemCredStore::new().unwrap());
}