keyfork/crates/daemon/keyforkd-client/src/lib.rs

153 lines
5.5 KiB
Rust
Raw Normal View History

2024-01-16 02:44:48 +00:00
//! A client for Keyforkd.
use std::{collections::HashMap, os::unix::net::UnixStream, path::PathBuf};
use keyfork_derive_util::{
request::{AsAlgorithm, DerivationRequest},
DerivationPath, ExtendedPrivateKey, PrivateKey,
};
use keyfork_frame::{try_decode_from, try_encode_to, DecodeError, EncodeError};
use keyforkd_models::{Error as KeyforkdError, Request, Response};
#[cfg(test)]
mod tests;
2024-01-16 02:44:48 +00:00
/// An error occurred while interacting with Keyforkd.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// The response from the server did not match the request.
#[error("The response from the server did not match the request")]
InvalidResponse,
2024-01-16 02:44:48 +00:00
/// The environment variables used for determining a Keyforkd socket path were not set.
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
EnvVarsNotFound,
2024-01-16 02:44:48 +00:00
/// The Keyforkd client was unable to connect to the soocket.
2024-01-08 19:06:03 +00:00
#[error("Socket was unable to connect to {1}: {0} (make sure keyforkd is running)")]
Connect(std::io::Error, PathBuf),
2024-01-16 02:44:48 +00:00
/// Data could not be written to, or read from, the socket.
#[error("Could not write to or from the socket: {0}")]
Io(#[from] std::io::Error),
2024-01-16 02:44:48 +00:00
/// Attempting to serialize or deserialize a type to or from bincode encountered an error.
#[error("Could not perform bincode transformation: {0}")]
Bincode(#[from] Box<bincode::ErrorKind>),
2024-01-16 02:44:48 +00:00
/// A frame could not be encoded from the given data.
#[error("Could not perform frame transformation: {0}")]
FrameEnc(#[from] EncodeError),
2024-01-16 02:44:48 +00:00
/// A frame could not be decoded from the given data.
#[error("Could not perform frame transformation: {0}")]
FrameDec(#[from] DecodeError),
2024-01-16 02:44:48 +00:00
/// An error encountered in Keyforkd.
#[error("Error in Keyforkd: {0}")]
Keyforkd(#[from] KeyforkdError),
}
2024-01-16 02:44:48 +00:00
#[allow(missing_docs)]
pub type Result<T, E = Error> = std::result::Result<T, E>;
2024-01-16 02:44:48 +00:00
/// Create a [`UnixStream`] from the common Keyforkd socket paths.
///
/// # Errors
/// An error may be returned if the required environment variables were not set or if the socket
/// could not be connected to.
pub fn get_socket() -> Result<UnixStream, Error> {
let socket_vars = std::env::vars()
.filter(|(key, _)| ["XDG_RUNTIME_DIR", "KEYFORKD_SOCKET_PATH"].contains(&key.as_str()))
.collect::<HashMap<String, String>>();
let mut socket_path: PathBuf;
#[allow(clippy::single_match_else)]
match socket_vars.get("KEYFORKD_SOCKET_PATH") {
Some(occupied) => {
socket_path = PathBuf::from(occupied);
}
None => {
socket_path = PathBuf::from(
socket_vars
.get("XDG_RUNTIME_DIR")
.ok_or(Error::EnvVarsNotFound)?,
);
socket_path.extend(["keyforkd", "keyforkd.sock"]);
}
}
UnixStream::connect(&socket_path).map_err(|e| Error::Connect(e, socket_path))
}
/// A client to interact with Keyforkd.
///
/// Upon creation, a socket is opened, and is kept open for the duration of the object's lifetime.
/// Currently, Keyforkd does not support the reuse of sockets. Attempting to reuse the socket after
/// previously using it will likely result in an error.
#[derive(Debug)]
pub struct Client {
socket: UnixStream,
}
impl Client {
/// Create a new client from a given already-connected [`UnixStream`].
pub fn new(socket: UnixStream) -> Self {
Self { socket }
}
/// Create a new client using well-known socket locations.
2024-01-16 02:44:48 +00:00
///
/// # Errors
/// An error may be returned if the required environment variables were not set or if the
/// socket could not be connected to.
pub fn discover_socket() -> Result<Self> {
get_socket().map(|socket| Self { socket })
}
/// Request an [`ExtendedPrivateKey`] for a given [`DerivationPath`].
///
/// # Errors
/// An error may be returned if:
/// * Reading or writing from or to the socket encountered an error.
/// * Bincode could not serialize the request or deserialize the response.
/// * An error occurred in Keyforkd.
/// * Keyforkd returned invalid data.
pub fn request_xprv<K>(&mut self, path: &DerivationPath) -> Result<ExtendedPrivateKey<K>>
where
K: PrivateKey + Clone + AsAlgorithm,
{
let algo = K::as_algorithm();
let request = Request::Derivation(DerivationRequest::new(algo.clone(), path));
let response = self.request(&request)?;
match response {
Response::Derivation(d) => {
if d.algorithm != algo {
return Err(Error::InvalidResponse);
}
let depth = path.len() as u8;
Ok(ExtendedPrivateKey::new_from_parts(
&d.data,
depth,
d.chain_code,
))
}
_ => Err(Error::InvalidResponse),
}
}
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
2024-01-16 02:44:48 +00:00
///
/// # Errors
/// An error may be returned if:
/// * Reading or writing from or to the socket encountered an error.
/// * Bincode could not serialize the request or deserialize the response.
/// * An error occurred in Keyforkd.
pub fn request(&mut self, req: &Request) -> Result<Response> {
try_encode_to(&bincode::serialize(&req)?, &mut self.socket)?;
let resp = try_decode_from(&mut self.socket)?;
let resp: Result<Response, KeyforkdError> = bincode::deserialize(&resp)?;
resp.map_err(From::from)
}
}