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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -46,7 +46,8 @@ fn secp256k1() {
DerivationAlgorithm::Secp256k1, DerivationAlgorithm::Secp256k1,
&DerivationPath::from_str(test.chain).unwrap(), &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); assert_eq!(response.data, test.private_key);
} }
@ -90,7 +91,8 @@ fn ed25519() {
DerivationAlgorithm::Ed25519, DerivationAlgorithm::Ed25519,
&DerivationPath::from_str(test.chain).unwrap(), &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); 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" thiserror = "1.0.47"
serde = { version = "1.0.186", features = ["derive"] } serde = { version = "1.0.186", features = ["derive"] }
keyfork-derive-path-data = { version = "0.1.0", path = "../keyfork-derive-path-data" } keyfork-derive-path-data = { version = "0.1.0", path = "../keyfork-derive-path-data" }
keyforkd-models = { version = "0.1.0", path = "../keyforkd-models" }
[dev-dependencies] [dev-dependencies]
hex-literal = "0.4.1" hex-literal = "0.4.1"

View File

@ -1,22 +1,14 @@
use std::{future::Future, pin::Pin, sync::Arc, task::Poll}; use std::{future::Future, pin::Pin, sync::Arc, task::Poll};
use keyfork_derive_path_data::guess_target; 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 tower::Service;
use tracing::info; use tracing::info;
// NOTE: All values implemented in Keyforkd must implement Clone with low overhead, either by // 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. // 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)] #[derive(Clone, Debug)]
pub struct Keyforkd { pub struct Keyforkd {
seed: Arc<Vec<u8>>, seed: Arc<Vec<u8>>,
@ -30,10 +22,11 @@ impl Keyforkd {
} }
} }
impl Service<DerivationRequest> for Keyforkd { impl Service<Request> for Keyforkd {
type Response = DerivationResponse; 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>>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
@ -45,12 +38,13 @@ impl Service<DerivationRequest> for Keyforkd {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))] #[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(); let seed = self.seed.clone();
Box::pin(async move { match req {
Request::Derivation(req) => Box::pin(async move {
let len = req.path().len(); let len = req.path().len();
if len < 2 { if len < 2 {
return Err(KeyforkdRequestError::InvalidDerivationLength(len)); return Err(DerivationError::InvalidDerivationLength(len).into());
} }
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@ -61,8 +55,11 @@ impl Service<DerivationRequest> for Keyforkd {
} }
req.derive_with_master_seed((*seed).clone()) req.derive_with_master_seed((*seed).clone())
.map_err(KeyforkdRequestError::from) .map(Response::Derivation)
}) .map_err(|e| DerivationError::Derivation(e.to_string()).into())
}),
Request::DerivationWithTTY(_, _) => todo!(),
}
} }
} }
@ -92,7 +89,15 @@ mod tests {
continue; continue;
} }
let req = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain); 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.data, test.private_key);
assert_eq!(response.chain_code.as_slice(), test.chain_code); assert_eq!(response.chain_code.as_slice(), test.chain_code);
} }
@ -114,7 +119,15 @@ mod tests {
continue; continue;
} }
let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain); 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.data, test.private_key);
assert_eq!(response.chain_code.as_slice(), test.chain_code); assert_eq!(response.chain_code.as_slice(), test.chain_code);
} }
@ -134,7 +147,15 @@ mod tests {
for (seed, path, _, private_key, _) in tests { for (seed, path, _, private_key, _) in tests {
let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path); let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let mut keyforkd = Keyforkd::new(seed.to_vec()); 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) assert_eq!(response.data, private_key)
} }
} }
@ -152,7 +173,15 @@ mod tests {
for (seed, path, _, private_key, _) in tests { for (seed, path, _, private_key, _) in tests {
let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path); let req = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let mut keyforkd = Keyforkd::new(seed.to_vec()); 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) assert_eq!(response.data, private_key)
} }
} }