use crate::lair_api::traits::*;
use crate::*;
use futures::future::{BoxFuture, FutureExt};
use futures::stream::StreamExt;
use parking_lot::RwLock;
use std::collections::HashMap;
use std::future::Future;
use std::sync::Arc;
pub mod traits {
use super::*;
pub trait AsLairClient: 'static + Send + Sync {
fn get_enc_ctx_key(&self) -> sodoken::BufReadSized<32>;
fn get_dec_ctx_key(&self) -> sodoken::BufReadSized<32>;
fn shutdown(&self) -> BoxFuture<'static, LairResult<()>>;
fn request(
&self,
request: LairApiEnum,
) -> BoxFuture<'static, LairResult<LairApiEnum>>;
}
}
use traits::*;
#[derive(Clone)]
pub struct LairClient(pub Arc<dyn AsLairClient>);
fn priv_lair_api_request<R: AsLairRequest>(
client: &dyn AsLairClient,
request: R,
) -> impl Future<Output = LairResult<R::Response>> + 'static + Send
where
one_err::OneErr: std::convert::From<
<<R as AsLairRequest>::Response as std::convert::TryFrom<
LairApiEnum,
>>::Error,
>,
{
let request = request.into_api_enum();
let fut = AsLairClient::request(client, request);
async move {
let res = fut.await?;
match res {
LairApiEnum::ResError(err) => Err(err.error),
res => {
let res: R::Response = std::convert::TryFrom::try_from(res)?;
Ok(res)
}
}
}
}
impl LairClient {
pub fn get_enc_ctx_key(&self) -> sodoken::BufReadSized<32> {
AsLairClient::get_enc_ctx_key(&*self.0)
}
pub fn get_dec_ctx_key(&self) -> sodoken::BufReadSized<32> {
AsLairClient::get_dec_ctx_key(&*self.0)
}
pub fn shutdown(
&self,
) -> impl Future<Output = LairResult<()>> + 'static + Send {
AsLairClient::shutdown(&*self.0)
}
pub fn request<R: AsLairRequest>(
&self,
request: R,
) -> impl Future<Output = LairResult<R::Response>> + 'static + Send
where
one_err::OneErr: std::convert::From<
<<R as AsLairRequest>::Response as std::convert::TryFrom<
LairApiEnum,
>>::Error,
>,
{
priv_lair_api_request(&*self.0, request)
}
pub fn hello(
&self,
expected_server_pub_key: BinDataSized<32>,
) -> impl Future<Output = LairResult<Arc<str>>> + 'static + Send {
let inner = self.0.clone();
async move {
let req = LairApiReqHello::new();
let res = priv_lair_api_request(&*inner, req).await?;
if res.server_pub_key != expected_server_pub_key {
return Err(one_err::OneErr::with_message(
"ServerPubKeyMismatch",
format!(
"expected {} != returned {}",
expected_server_pub_key, res.server_pub_key,
),
));
}
Ok(res.version)
}
}
pub fn unlock(
&self,
passphrase: sodoken::BufRead,
) -> impl Future<Output = LairResult<()>> + 'static + Send {
let inner = self.0.clone();
async move {
let pw_hash = sodoken::BufWriteSized::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), passphrase).await?;
let key = inner.get_enc_ctx_key();
let passphrase =
SecretDataSized::encrypt(key, pw_hash.to_read_sized()).await?;
let req = LairApiReqUnlock::new(passphrase);
let _res = priv_lair_api_request(&*inner, req).await?;
Ok(())
}
}
pub fn list_entries(
&self,
) -> impl Future<Output = LairResult<Vec<LairEntryInfo>>> + 'static + Send
{
let r_fut =
priv_lair_api_request(&*self.0, LairApiReqListEntries::new());
async move {
let r = r_fut.await?;
Ok(r.entry_list)
}
}
pub fn get_entry(
&self,
tag: Arc<str>,
) -> impl Future<Output = LairResult<LairEntryInfo>> + 'static + Send {
let inner = self.0.clone();
async move {
let req = LairApiReqGetEntry::new(tag);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.entry_info)
}
}
pub fn new_seed(
&self,
tag: Arc<str>,
deep_lock_passphrase: Option<sodoken::BufRead>,
exportable: bool,
) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
let limits = hc_seed_bundle::PwHashLimits::current();
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(DeepLockPassphrase {
ops_limit: limits.as_ops_limit(),
mem_limit: limits.as_mem_limit(),
passphrase: secret,
})
}
};
let req = LairApiReqNewSeed::new(tag, secret, exportable);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.seed_info)
}
}
pub fn export_seed_by_tag(
&self,
tag: Arc<str>,
sender_pub_key: X25519PubKey,
recipient_pub_key: X25519PubKey,
deep_lock_passphrase: Option<sodoken::BufRead>,
) -> impl Future<Output = LairResult<([u8; 24], Arc<[u8]>)>> + 'static + Send
{
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(secret)
}
};
let req = LairApiReqExportSeedByTag::new(
tag,
sender_pub_key,
recipient_pub_key,
secret,
);
let res = priv_lair_api_request(&*inner, req).await?;
Ok((res.nonce, res.cipher))
}
}
#[allow(clippy::too_many_arguments)]
pub fn import_seed(
&self,
sender_pub_key: X25519PubKey,
recipient_pub_key: X25519PubKey,
deep_lock_passphrase: Option<sodoken::BufRead>,
nonce: [u8; 24],
cipher: Arc<[u8]>,
tag: Arc<str>,
exportable: bool,
) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
let limits = hc_seed_bundle::PwHashLimits::current();
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(DeepLockPassphrase {
ops_limit: limits.as_ops_limit(),
mem_limit: limits.as_mem_limit(),
passphrase: secret,
})
}
};
let req = LairApiReqImportSeed::new(
sender_pub_key,
recipient_pub_key,
secret,
nonce,
cipher,
tag,
exportable,
);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.seed_info)
}
}
#[allow(clippy::boxed_local)]
pub fn derive_seed(
&self,
_src_tag: Arc<str>,
_src_deep_lock_passphrase: Option<sodoken::BufRead>,
_dst_tag: Arc<str>,
_dst_deep_lock_passphrase: Option<sodoken::BufRead>,
_derivation: Box<[u32]>,
) -> impl Future<Output = LairResult<SeedInfo>> + 'static + Send {
async move { unimplemented!() }
}
pub fn sign_by_pub_key(
&self,
pub_key: Ed25519PubKey,
deep_lock_passphrase: Option<sodoken::BufRead>,
data: Arc<[u8]>,
) -> impl Future<Output = LairResult<Ed25519Signature>> + 'static + Send
{
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(secret)
}
};
let req = LairApiReqSignByPubKey::new(pub_key, secret, data);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.signature)
}
}
pub fn crypto_box_xsalsa_by_pub_key(
&self,
sender_pub_key: X25519PubKey,
recipient_pub_key: X25519PubKey,
deep_lock_passphrase: Option<sodoken::BufRead>,
data: Arc<[u8]>,
) -> impl Future<Output = LairResult<([u8; 24], Arc<[u8]>)>> + 'static + Send
{
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(secret)
}
};
let req = LairApiReqCryptoBoxXSalsaByPubKey::new(
sender_pub_key,
recipient_pub_key,
secret,
data,
);
let res = priv_lair_api_request(&*inner, req).await?;
Ok((res.nonce, res.cipher))
}
}
pub fn crypto_box_xsalsa_open_by_pub_key(
&self,
sender_pub_key: X25519PubKey,
recipient_pub_key: X25519PubKey,
deep_lock_passphrase: Option<sodoken::BufRead>,
nonce: [u8; 24],
cipher: Arc<[u8]>,
) -> impl Future<Output = LairResult<Arc<[u8]>>> + 'static + Send {
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(secret)
}
};
let req = LairApiReqCryptoBoxXSalsaOpenByPubKey::new(
sender_pub_key,
recipient_pub_key,
secret,
nonce,
cipher,
);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.message)
}
}
pub fn new_wka_tls_cert(
&self,
tag: Arc<str>,
) -> impl Future<Output = LairResult<CertInfo>> + 'static + Send {
let inner = self.0.clone();
async move {
let req = LairApiReqNewWkaTlsCert::new(tag);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.cert_info)
}
}
pub fn get_wka_tls_cert_priv_key(
&self,
tag: Arc<str>,
) -> impl Future<Output = LairResult<sodoken::BufRead>> + 'static + Send
{
let inner = self.0.clone();
async move {
let req = LairApiReqGetWkaTlsCertPrivKey::new(tag);
let res = priv_lair_api_request(&*inner, req).await?;
let res = res.priv_key.decrypt(inner.get_dec_ctx_key()).await?;
Ok(res)
}
}
pub fn secretbox_xsalsa_by_tag(
&self,
tag: Arc<str>,
deep_lock_passphrase: Option<sodoken::BufRead>,
data: Arc<[u8]>,
) -> impl Future<Output = LairResult<([u8; 24], Arc<[u8]>)>> + 'static + Send
{
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(secret)
}
};
let req = LairApiReqSecretBoxXSalsaByTag::new(tag, secret, data);
let res = priv_lair_api_request(&*inner, req).await?;
Ok((res.nonce, res.cipher))
}
}
pub fn secretbox_xsalsa_open_by_tag(
&self,
tag: Arc<str>,
deep_lock_passphrase: Option<sodoken::BufRead>,
nonce: [u8; 24],
cipher: Arc<[u8]>,
) -> impl Future<Output = LairResult<Arc<[u8]>>> + 'static + Send {
let inner = self.0.clone();
async move {
let secret = match deep_lock_passphrase {
None => None,
Some(pass) => {
let pw_hash =
<sodoken::BufWriteSized<64>>::new_mem_locked()?;
sodoken::hash::blake2b::hash(pw_hash.clone(), pass).await?;
let key = inner.get_enc_ctx_key();
let secret =
SecretDataSized::encrypt(key, pw_hash.to_read_sized())
.await?;
Some(secret)
}
};
let req = LairApiReqSecretBoxXSalsaOpenByTag::new(
tag, secret, nonce, cipher,
);
let res = priv_lair_api_request(&*inner, req).await?;
Ok(res.message)
}
}
}
pub mod async_io;