keyforkd: split into enum based request

This commit is contained in:
Ryan Heywood 2023-11-05 22:51:40 -06:00
parent 48e4d7096c
commit ada6cf150b
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
11 changed files with 188 additions and 46 deletions

19
Cargo.lock generated
View File

@ -1103,6 +1103,7 @@ dependencies = [
"keyfork-frame",
"keyfork-mnemonic-util",
"keyfork-slip10-test-data",
"keyforkd-models",
"serde",
"thiserror",
"tokio",
@ -1121,11 +1122,21 @@ dependencies = [
"keyfork-frame",
"keyfork-slip10-test-data",
"keyforkd",
"keyforkd-models",
"tempdir",
"thiserror",
"tokio",
]
[[package]]
name = "keyforkd-models"
version = "0.1.0"
dependencies = [
"keyfork-derive-util",
"serde",
"thiserror",
]
[[package]]
name = "lalrpop"
version = "0.20.0"
@ -1944,18 +1955,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.188"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",

View File

@ -15,5 +15,6 @@ members = [
"keyfork-slip10-test-data",
"keyforkd",
"keyforkd-client",
"keyforkd-models",
"smex",
]

View File

@ -1,7 +1,7 @@
use std::{env, process::ExitCode, str::FromStr};
use keyfork_derive_util::{
request::{DerivationAlgorithm, DerivationError, DerivationRequest},
request::{DerivationAlgorithm, DerivationError, DerivationRequest, DerivationResponse},
DerivationPath,
};
use keyforkd_client::Client;
@ -38,8 +38,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let mut client = Client::discover_socket()?;
let request = DerivationRequest::new(algo, &path);
let response = client.request(&request)?;
println!("{}", smex::encode(&response.data));
let response = client.request(&request.into())?;
println!("{}", smex::encode(&DerivationResponse::try_from(response)?.data));
Ok(())
}

View File

@ -1,7 +1,7 @@
use std::{env, process::ExitCode, str::FromStr};
use keyfork_derive_util::{
request::{DerivationAlgorithm, DerivationRequest},
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
DerivationIndex, DerivationPath,
};
use keyforkd_client::Client;
@ -107,7 +107,9 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
};
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let derived_data = Client::discover_socket()?.request(&request)?;
let derived_data: DerivationResponse = Client::discover_socket()?
.request(&request.into())?
.try_into()?;
let subkeys = subkey_format
.iter()
.map(|kt| kt.inner().clone())

View File

@ -14,6 +14,7 @@ secp256k1 = ["keyfork-derive-util/secp256k1"]
bincode = "1.3.3"
keyfork-derive-util = { version = "0.1.0", path = "../keyfork-derive-util", default-features = false }
keyfork-frame = { version = "0.1.0", path = "../keyfork-frame" }
keyforkd-models = { version = "0.1.0", path = "../keyforkd-models" }
thiserror = "1.0.49"
[dev-dependencies]

View File

@ -1,7 +1,7 @@
use std::{collections::HashMap, os::unix::net::UnixStream, path::PathBuf};
use keyfork_derive_util::request::{DerivationRequest, DerivationResponse};
use keyfork_frame::{try_decode_from, try_encode_to, DecodeError, EncodeError};
use keyforkd_models::{Request, Response /* Error as KeyforkdError */};
#[cfg(test)]
mod tests;
@ -65,7 +65,7 @@ impl Client {
get_socket().map(|socket| Self { socket })
}
pub fn request(&mut self, req: &DerivationRequest) -> Result<DerivationResponse> {
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)?;
bincode::deserialize(&resp).map_err(From::from)

View File

@ -46,7 +46,8 @@ fn secp256k1() {
DerivationAlgorithm::Secp256k1,
&DerivationPath::from_str(test.chain).unwrap(),
);
let response = client.request(&req).unwrap();
let response =
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
assert_eq!(response.data, test.private_key);
}
@ -90,7 +91,8 @@ fn ed25519() {
DerivationAlgorithm::Ed25519,
&DerivationPath::from_str(test.chain).unwrap(),
);
let response = client.request(&req).unwrap();
let response =
DerivationResponse::try_from(client.request(&req.into()).unwrap()).unwrap();
assert_eq!(response.data, test.private_key);
}

View File

@ -0,0 +1,11 @@
[package]
name = "keyforkd-models"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
keyfork-derive-util = { version = "0.1.0", path = "../keyfork-derive-util", default-features = false }
serde = { version = "1.0.190", features = ["derive"] }
thiserror = "1.0.50"

View File

@ -0,0 +1,84 @@
//! All values that can be sent to and returned from Keyforkd.
//!
//! All Error values are stored as enum unit values.
use keyfork_derive_util::request::{DerivationRequest, DerivationResponse};
use serde::{Deserialize, Serialize};
/// A request made to Keyforkd.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum Request {
/// A derivation request.
Derivation(DerivationRequest),
/// A derivation request, with a TTY provided for pinentry if necessary.
DerivationWithTTY(DerivationRequest, String),
}
impl From<DerivationRequest> for Request {
fn from(value: DerivationRequest) -> Self {
Self::Derivation(value)
}
}
impl From<(DerivationRequest, String)> for Request {
fn from((request, tty): (DerivationRequest, String)) -> Self {
Self::DerivationWithTTY(request, tty)
}
}
#[derive(thiserror::Error, Clone, Debug, Serialize, Deserialize)]
pub enum DerivationError {
#[error("The provided TTY was not valid")]
InvalidTTY,
#[error("A TTY was required by the pinentry program but was not provided")]
NoTTY,
#[error("Invalid derivation length: Expected 2, actual: {0}")]
InvalidDerivationLength(usize),
#[error("Derivation error: {0}")]
Derivation(String),
}
#[derive(thiserror::Error, Clone, Debug, Serialize, Deserialize)]
pub enum Error {
#[error(transparent)]
Derivation(#[from] DerivationError),
}
/// Any response from a Keyforkd request.
///
/// Responses can be converted to their inner values without matching the Response type by
/// using a type annotation and [`TryInto::try_into`].
#[derive(Serialize, Deserialize, Clone, Debug)]
#[non_exhaustive]
pub enum Response {
/// A derivation response.
///
/// From:
/// * [`Request::Derivation`]
/// * [`Request::DerivationWithTTY`]
Derivation(DerivationResponse),
}
#[derive(thiserror::Error, Debug)]
#[error("Unable to downcast to {0}")]
pub struct Downcast(&'static str);
use std::any::type_name;
impl TryFrom<Response> for DerivationResponse {
type Error = Downcast;
fn try_from(value: Response) -> Result<Self, Self::Error> {
#[allow(irrefutable_let_patterns)]
if let Response::Derivation(response) = value {
Ok(response)
} else {
Err(Downcast(type_name::<Self>()))
}
}
}

View File

@ -29,6 +29,7 @@ tower = { version = "0.4.13", features = ["tokio", "util"] }
thiserror = "1.0.47"
serde = { version = "1.0.186", features = ["derive"] }
keyfork-derive-path-data = { version = "0.1.0", path = "../keyfork-derive-path-data" }
keyforkd-models = { version = "0.1.0", path = "../keyforkd-models" }
[dev-dependencies]
hex-literal = "0.4.1"

View File

@ -1,22 +1,14 @@
use std::{future::Future, pin::Pin, sync::Arc, task::Poll};
use keyfork_derive_path_data::guess_target;
use keyfork_derive_util::request::{DerivationError, DerivationRequest, DerivationResponse};
// use keyfork_derive_util::request::{DerivationError, DerivationRequest, DerivationResponse};
use keyforkd_models::{DerivationError, Error, Request, Response};
use tower::Service;
use tracing::info;
// NOTE: All values implemented in Keyforkd must implement Clone with low overhead, either by
// using an Arc or by having a small signature. This is because Service<T> takes &mut self.
#[derive(thiserror::Error, Debug)]
pub enum KeyforkdRequestError {
#[error("Invalid derivation length: Expected: 2, actual: {0}")]
InvalidDerivationLength(usize),
#[error("Derivation error: {0}")]
Derivation(#[from] DerivationError),
}
//
#[derive(Clone, Debug)]
pub struct Keyforkd {
seed: Arc<Vec<u8>>,
@ -30,10 +22,11 @@ impl Keyforkd {
}
}
impl Service<DerivationRequest> for Keyforkd {
type Response = DerivationResponse;
impl Service<Request> for Keyforkd {
type Response = Response;
type Error = KeyforkdRequestError;
// TODO: indicate serialize in BincodeLayer
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
@ -45,24 +38,28 @@ impl Service<DerivationRequest> for Keyforkd {
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
fn call(&mut self, req: DerivationRequest) -> Self::Future {
fn call(&mut self, req: Request) -> Self::Future {
let seed = self.seed.clone();
Box::pin(async move {
let len = req.path().len();
if len < 2 {
return Err(KeyforkdRequestError::InvalidDerivationLength(len));
}
match req {
Request::Derivation(req) => Box::pin(async move {
let len = req.path().len();
if len < 2 {
return Err(DerivationError::InvalidDerivationLength(len).into());
}
#[cfg(feature = "tracing")]
if let Some(target) = guess_target(req.path()) {
info!("Deriving path: {target}");
} else {
info!("Deriving path: {}", req.path());
}
#[cfg(feature = "tracing")]
if let Some(target) = guess_target(req.path()) {
info!("Deriving path: {target}");
} else {
info!("Deriving path: {}", req.path());
}
req.derive_with_master_seed((*seed).clone())
.map_err(KeyforkdRequestError::from)
})
req.derive_with_master_seed((*seed).clone())
.map(Response::Derivation)
.map_err(|e| DerivationError::Derivation(e.to_string()).into())
}),
Request::DerivationWithTTY(_, _) => todo!(),
}
}
}
@ -92,7 +89,15 @@ mod tests {
continue;
}
let req = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
let response = keyforkd.ready().await.unwrap().call(req).await.unwrap();
let response: DerivationResponse = keyforkd
.ready()
.await
.unwrap()
.call(Request::Derivation(req))
.await
.unwrap()
.try_into()
.unwrap();
assert_eq!(response.data, test.private_key);
assert_eq!(response.chain_code.as_slice(), test.chain_code);
}
@ -114,7 +119,15 @@ mod tests {
continue;
}
let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain);
let response = keyforkd.ready().await.unwrap().call(req).await.unwrap();
let response: DerivationResponse = keyforkd
.ready()
.await
.unwrap()
.call(Request::Derivation(req))
.await
.unwrap()
.try_into()
.unwrap();
assert_eq!(response.data, test.private_key);
assert_eq!(response.chain_code.as_slice(), test.chain_code);
}
@ -134,7 +147,15 @@ mod tests {
for (seed, path, _, private_key, _) in tests {
let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let mut keyforkd = Keyforkd::new(seed.to_vec());
let response = keyforkd.ready().await.unwrap().call(req).await.unwrap();
let response: DerivationResponse = keyforkd
.ready()
.await
.unwrap()
.call(Request::Derivation(req))
.await
.unwrap()
.try_into()
.unwrap();
assert_eq!(response.data, private_key)
}
}
@ -152,7 +173,15 @@ mod tests {
for (seed, path, _, private_key, _) in tests {
let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let mut keyforkd = Keyforkd::new(seed.to_vec());
let response = keyforkd.ready().await.unwrap().call(req).await.unwrap();
let response: DerivationResponse = keyforkd
.ready()
.await
.unwrap()
.call(Request::Derivation(req))
.await
.unwrap()
.try_into()
.unwrap();
assert_eq!(response.data, private_key)
}
}