use crate::{
actors::{InternalMsg, InternalResults, SMsg},
line_error,
state::client::{Client, ClientMsg},
utils::{ResultMessage, StatusMessage},
Location,
};
use stronghold_utils::GuardDebug;
use crypto::keys::slip10::{Chain, ChainCode};
use engine::{
snapshot,
vault::{ClientId, RecordHint, RecordId},
};
use serde::{Deserialize, Serialize};
use riker::actors::*;
use core::{
array::TryFromSliceError,
convert::{TryFrom, TryInto},
};
use std::{path::PathBuf, time::Duration};
#[cfg(feature = "communication")]
use communication::actor::{PermissionValue, RequestPermissions, ToPermissionVariants, VariantPermission};
#[derive(GuardDebug, Clone, Serialize, Deserialize)]
pub enum SLIP10DeriveInput {
Seed(Location),
Key(Location),
}
#[allow(dead_code)]
#[derive(GuardDebug, Clone, Serialize, Deserialize)]
pub enum Procedure {
SLIP10Generate {
output: Location,
hint: RecordHint,
size_bytes: Option<usize>,
},
SLIP10Derive {
chain: Chain,
input: SLIP10DeriveInput,
output: Location,
hint: RecordHint,
},
BIP39Recover {
mnemonic: String,
passphrase: Option<String>,
output: Location,
hint: RecordHint,
},
BIP39Generate {
passphrase: Option<String>,
output: Location,
hint: RecordHint,
},
BIP39MnemonicSentence { seed: Location },
Ed25519PublicKey { private_key: Location },
Ed25519Sign { private_key: Location, msg: Vec<u8> },
}
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "SerdeProcResult")]
#[serde(into = "SerdeProcResult")]
pub enum ProcResult {
SLIP10Generate(StatusMessage),
SLIP10Derive(ResultMessage<ChainCode>),
BIP39Recover(StatusMessage),
BIP39Generate(StatusMessage),
BIP39MnemonicSentence(ResultMessage<String>),
Ed25519PublicKey(ResultMessage<[u8; crypto::signatures::ed25519::COMPRESSED_PUBLIC_KEY_LENGTH]>),
Ed25519Sign(ResultMessage<[u8; crypto::signatures::ed25519::SIGNATURE_LENGTH]>),
Error(String),
}
impl TryFrom<SerdeProcResult> for ProcResult {
type Error = TryFromSliceError;
fn try_from(serde_proc_result: SerdeProcResult) -> Result<Self, TryFromSliceError> {
match serde_proc_result {
SerdeProcResult::SLIP10Generate(msg) => Ok(ProcResult::SLIP10Generate(msg)),
SerdeProcResult::SLIP10Derive(msg) => Ok(ProcResult::SLIP10Derive(msg)),
SerdeProcResult::BIP39Recover(msg) => Ok(ProcResult::BIP39Recover(msg)),
SerdeProcResult::BIP39Generate(msg) => Ok(ProcResult::BIP39Generate(msg)),
SerdeProcResult::BIP39MnemonicSentence(msg) => Ok(ProcResult::BIP39MnemonicSentence(msg)),
SerdeProcResult::Ed25519PublicKey(msg) => {
let msg: ResultMessage<[u8; crypto::signatures::ed25519::COMPRESSED_PUBLIC_KEY_LENGTH]> = match msg {
ResultMessage::Ok(v) => ResultMessage::Ok(v.as_slice().try_into()?),
ResultMessage::Error(e) => ResultMessage::Error(e),
};
Ok(ProcResult::Ed25519PublicKey(msg))
}
SerdeProcResult::Ed25519Sign(msg) => {
let msg: ResultMessage<[u8; crypto::signatures::ed25519::SIGNATURE_LENGTH]> = match msg {
ResultMessage::Ok(v) => ResultMessage::Ok(v.as_slice().try_into()?),
ResultMessage::Error(e) => ResultMessage::Error(e),
};
Ok(ProcResult::Ed25519Sign(msg))
}
SerdeProcResult::Error(err) => Ok(ProcResult::Error(err)),
}
}
}
#[derive(Clone, Serialize, Deserialize)]
enum SerdeProcResult {
SLIP10Generate(StatusMessage),
SLIP10Derive(ResultMessage<ChainCode>),
BIP39Recover(StatusMessage),
BIP39Generate(StatusMessage),
BIP39MnemonicSentence(ResultMessage<String>),
Ed25519PublicKey(ResultMessage<Vec<u8>>),
Ed25519Sign(ResultMessage<Vec<u8>>),
Error(String),
}
impl From<ProcResult> for SerdeProcResult {
fn from(proc_result: ProcResult) -> Self {
match proc_result {
ProcResult::SLIP10Generate(msg) => SerdeProcResult::SLIP10Generate(msg),
ProcResult::SLIP10Derive(msg) => SerdeProcResult::SLIP10Derive(msg),
ProcResult::BIP39Recover(msg) => SerdeProcResult::BIP39Recover(msg),
ProcResult::BIP39Generate(msg) => SerdeProcResult::BIP39Generate(msg),
ProcResult::BIP39MnemonicSentence(msg) => SerdeProcResult::BIP39MnemonicSentence(msg),
ProcResult::Ed25519PublicKey(msg) => {
let msg = match msg {
ResultMessage::Ok(slice) => ResultMessage::Ok(slice.to_vec()),
ResultMessage::Error(error) => ResultMessage::Error(error),
};
SerdeProcResult::Ed25519PublicKey(msg)
}
ProcResult::Ed25519Sign(msg) => {
let msg = match msg {
ResultMessage::Ok(slice) => ResultMessage::Ok(slice.to_vec()),
ResultMessage::Error(error) => ResultMessage::Error(error),
};
SerdeProcResult::Ed25519Sign(msg)
}
ProcResult::Error(err) => SerdeProcResult::Error(err),
}
}
}
#[allow(dead_code)]
#[derive(Clone, GuardDebug, Serialize, Deserialize)]
#[cfg_attr(feature = "communication", derive(RequestPermissions))]
pub enum SHRequest {
CheckVault(Vec<u8>),
CheckRecord {
location: Location,
},
WriteToStore {
location: Location,
payload: Vec<u8>,
lifetime: Option<Duration>,
},
ReadFromStore {
location: Location,
},
DeleteFromStore(Location),
CreateNewVault(Location),
WriteToVault {
location: Location,
payload: Vec<u8>,
hint: RecordHint,
},
#[cfg(test)]
ReadFromVault {
location: Location,
},
RevokeData {
location: Location,
},
GarbageCollect(Vec<u8>),
ListIds(Vec<u8>),
ReadSnapshot {
key: snapshot::Key,
filename: Option<String>,
path: Option<PathBuf>,
cid: ClientId,
former_cid: Option<ClientId>,
},
WriteSnapshot {
key: snapshot::Key,
filename: Option<String>,
path: Option<PathBuf>,
},
FillSnapshot,
ClearCache {
kill: bool,
},
ControlRequest(Procedure),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum SHResults {
ReturnWriteStore(StatusMessage),
ReturnReadStore(Vec<u8>, StatusMessage),
ReturnDeleteStore(StatusMessage),
ReturnCreateVault(StatusMessage),
ReturnWriteVault(StatusMessage),
ReturnReadVault(Vec<u8>, StatusMessage),
ReturnRevoke(StatusMessage),
ReturnGarbage(StatusMessage),
ReturnList(Vec<(RecordId, RecordHint)>, StatusMessage),
ReturnFillSnap(StatusMessage),
ReturnWriteSnap(StatusMessage),
ReturnReadSnap(StatusMessage),
ReturnClearCache(StatusMessage),
ReturnControlRequest(ProcResult),
ReturnExistsVault(bool),
ReturnExistsRecord(bool),
}
impl ActorFactoryArgs<ClientId> for Client {
fn create_args(client_id: ClientId) -> Self {
Client::new(client_id)
}
}
impl Actor for Client {
type Msg = ClientMsg;
fn recv(&mut self, ctx: &Context<Self::Msg>, msg: Self::Msg, sender: Sender) {
self.receive(ctx, msg, sender);
}
}
impl Receive<SHResults> for Client {
type Msg = ClientMsg;
fn receive(&mut self, _ctx: &Context<Self::Msg>, _msg: SHResults, _sender: Sender) {}
}
impl Receive<SHRequest> for Client {
type Msg = ClientMsg;
fn receive(&mut self, ctx: &Context<Self::Msg>, msg: SHRequest, sender: Sender) {
macro_rules! ensure_vault_exists {
( $x:expr, $V:tt, $k:expr ) => {
if self.vault_exist($x).is_none() {
sender
.as_ref()
.expect(line_error!())
.try_tell(
SHResults::ReturnControlRequest(ProcResult::$V(ResultMessage::Error(format!(
"Failed to find {} vault. Please generate one",
$k
)))),
None,
)
.expect(line_error!());
return;
}
};
}
match msg {
SHRequest::CheckVault(vpath) => {
let vid = self.derive_vault_id(vpath);
let res = matches!(self.vault_exist(vid), Some(_));
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnExistsVault(res), None)
.expect(line_error!());
}
SHRequest::CheckRecord { location } => {
let client_str = self.get_client_str();
let (vid, rid) = self.resolve_location(location);
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::CheckRecord(vid, rid), sender);
}
SHRequest::CreateNewVault(location) => {
let (vid, rid) = self.resolve_location(location);
let client_str = self.get_client_str();
self.add_new_vault(vid);
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::CreateVault(vid, rid), sender);
}
SHRequest::WriteToVault {
location,
payload,
hint,
} => {
let (vid, rid) = self.resolve_location(location);
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::WriteToVault(vid, rid, payload, hint), sender);
}
#[cfg(test)]
SHRequest::ReadFromVault { location } => {
let (vid, rid) = self.resolve_location(location);
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::ReadFromVault(vid, rid), sender);
}
SHRequest::RevokeData { location } => {
let (vid, rid) = self.resolve_location(location);
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::RevokeData(vid, rid), sender);
}
SHRequest::GarbageCollect(vpath) => {
let vid = self.derive_vault_id(vpath);
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::GarbageCollect(vid), sender);
}
SHRequest::ListIds(vpath) => {
let vid = self.derive_vault_id(vpath);
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::ListIds(vid), sender);
}
SHRequest::ReadSnapshot {
key,
filename,
path,
cid,
former_cid,
} => {
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::ReadSnapshot(key, filename, path, cid, former_cid), sender);
}
SHRequest::ClearCache { kill } => {
self.clear_cache();
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
if kill {
internal.try_tell(InternalMsg::KillInternal, None);
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnClearCache(ResultMessage::Ok(())), None)
.expect(line_error!());
ctx.stop(ctx.myself());
} else {
internal.try_tell(InternalMsg::ClearCache, sender);
}
}
SHRequest::FillSnapshot => {
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
internal.try_tell(InternalMsg::FillSnapshot { client: self.clone() }, sender)
}
SHRequest::WriteSnapshot { key, filename, path } => {
let snapshot = ctx.select("/user/snapshot/").expect(line_error!());
snapshot.try_tell(SMsg::WriteSnapshot { key, filename, path }, sender);
}
SHRequest::DeleteFromStore(loc) => {
let (vid, _) = self.resolve_location(loc);
self.store_delete_item(vid.into());
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnDeleteStore(StatusMessage::Ok(())), None)
.expect(line_error!());
}
SHRequest::WriteToStore {
location,
payload,
lifetime,
} => {
let (vid, _) = self.resolve_location(location);
self.write_to_store(vid.into(), payload, lifetime);
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnWriteStore(StatusMessage::Ok(())), None)
.expect(line_error!());
}
SHRequest::ReadFromStore { location } => {
let (vid, _) = self.resolve_location(location);
let payload = self.read_from_store(vid.into());
if let Some(payload) = payload {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnReadStore(payload, StatusMessage::Ok(())), None)
.expect(line_error!());
} else {
sender
.as_ref()
.expect(line_error!())
.try_tell(
SHResults::ReturnReadStore(
vec![],
StatusMessage::Error("Unable to read from store".into()),
),
None,
)
.expect(line_error!());
}
}
SHRequest::ControlRequest(procedure) => {
let client_str = self.get_client_str();
let internal = ctx
.select(&format!("/user/internal-{}/", client_str))
.expect(line_error!());
match procedure {
Procedure::SLIP10Generate {
output,
hint,
size_bytes,
} => {
let (vid, rid) = self.resolve_location(output);
if self.vault_exist(vid).is_none() {
self.add_new_vault(vid);
}
internal.try_tell(
InternalMsg::SLIP10Generate {
vault_id: vid,
record_id: rid,
hint,
size_bytes: size_bytes.unwrap_or(64),
},
sender,
)
}
Procedure::SLIP10Derive {
chain,
input: SLIP10DeriveInput::Seed(seed),
output,
hint,
} => {
let (seed_vault_id, seed_record_id) = self.resolve_location(seed);
ensure_vault_exists!(seed_vault_id, SLIP10Derive, "seed");
let (key_vault_id, key_record_id) = self.resolve_location(output);
if self.vault_exist(key_vault_id).is_none() {
self.add_new_vault(key_vault_id);
}
internal.try_tell(
InternalMsg::SLIP10DeriveFromSeed {
chain,
seed_vault_id,
seed_record_id,
key_vault_id,
key_record_id,
hint,
},
sender,
)
}
Procedure::SLIP10Derive {
chain,
input: SLIP10DeriveInput::Key(parent),
output,
hint,
} => {
let (parent_vault_id, parent_record_id) = self.resolve_location(parent);
ensure_vault_exists!(parent_vault_id, SLIP10Derive, "parent key");
let (child_vault_id, child_record_id) = self.resolve_location(output);
if self.vault_exist(child_vault_id).is_none() {
self.add_new_vault(child_vault_id);
}
internal.try_tell(
InternalMsg::SLIP10DeriveFromKey {
chain,
parent_vault_id,
parent_record_id,
child_vault_id,
child_record_id,
hint,
},
sender,
)
}
Procedure::BIP39Generate {
passphrase,
output,
hint,
} => {
let (vault_id, record_id) = self.resolve_location(output);
if self.vault_exist(vault_id).is_none() {
self.add_new_vault(vault_id);
}
internal.try_tell(
InternalMsg::BIP39Generate {
passphrase: passphrase.unwrap_or_else(|| "".into()),
vault_id,
record_id,
hint,
},
sender,
)
}
Procedure::BIP39Recover {
mnemonic,
passphrase,
output,
hint,
} => {
let (vault_id, record_id) = self.resolve_location(output);
if self.vault_exist(vault_id).is_none() {
self.add_new_vault(vault_id);
}
internal.try_tell(
InternalMsg::BIP39Recover {
mnemonic,
passphrase: passphrase.unwrap_or_else(|| "".into()),
vault_id,
record_id,
hint,
},
sender,
)
}
Procedure::BIP39MnemonicSentence { .. } => unimplemented!(),
Procedure::Ed25519PublicKey { private_key } => {
let (vault_id, record_id) = self.resolve_location(private_key);
internal.try_tell(InternalMsg::Ed25519PublicKey { vault_id, record_id }, sender)
}
Procedure::Ed25519Sign { private_key, msg } => {
let (vault_id, record_id) = self.resolve_location(private_key);
internal.try_tell(
InternalMsg::Ed25519Sign {
vault_id,
record_id,
msg,
},
sender,
)
}
}
}
}
}
}
impl Receive<InternalResults> for Client {
type Msg = ClientMsg;
fn receive(&mut self, _ctx: &Context<Self::Msg>, msg: InternalResults, sender: Sender) {
match msg {
InternalResults::ReturnCreateVault(status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnCreateVault(status), None)
.expect(line_error!());
}
InternalResults::ReturnCheckRecord(res) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnExistsRecord(res), None)
.expect(line_error!());
}
InternalResults::ReturnReadVault(payload, status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnReadVault(payload, status), None)
.expect(line_error!());
}
InternalResults::ReturnList(list, status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnList(list, status), None)
.expect(line_error!());
}
InternalResults::RebuildCache {
id,
vaults,
store,
status,
} => {
self.clear_cache();
self.rebuild_cache(id, vaults, store);
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnReadSnap(status), None)
.expect(line_error!());
}
InternalResults::ReturnWriteVault(status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnWriteVault(status), None)
.expect(line_error!());
}
InternalResults::ReturnRevoke(status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnRevoke(status), None)
.expect(line_error!());
}
InternalResults::ReturnGarbage(status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnGarbage(status), None)
.expect(line_error!());
}
InternalResults::ReturnWriteSnap(status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnWriteSnap(status), None)
.expect(line_error!());
}
InternalResults::ReturnControlRequest(result) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnControlRequest(result), None)
.expect(line_error!());
}
InternalResults::ReturnClearCache(status) => {
sender
.as_ref()
.expect(line_error!())
.try_tell(SHResults::ReturnClearCache(status), None)
.expect(line_error!());
}
}
}
}