keyfork-derive-util, keyforkd-client: support fearless conversions
This commit is contained in:
parent
4e2c4487e9
commit
8108f5e61a
|
@ -2,8 +2,13 @@
|
||||||
|
|
||||||
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::{AsAlgorithm, DerivationRequest},
|
||||||
|
DerivationPath, ExtendedPrivateKey, PrivateKey,
|
||||||
|
};
|
||||||
|
|
||||||
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};
|
use keyforkd_models::{Error as KeyforkdError, Request, Response};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -11,6 +16,10 @@ mod tests;
|
||||||
/// An error occurred while interacting with Keyforkd.
|
/// An error occurred while interacting with Keyforkd.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum 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,
|
||||||
|
|
||||||
/// The environment variables used for determining a Keyforkd socket path were not set.
|
/// The environment variables used for determining a Keyforkd socket path were not set.
|
||||||
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
|
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
|
||||||
EnvVarsNotFound,
|
EnvVarsNotFound,
|
||||||
|
@ -37,7 +46,7 @@ pub enum Error {
|
||||||
|
|
||||||
/// An error encountered in Keyforkd.
|
/// An error encountered in Keyforkd.
|
||||||
#[error("Error in Keyforkd: {0}")]
|
#[error("Error in Keyforkd: {0}")]
|
||||||
Keyforkd(#[from] KeyforkdError)
|
Keyforkd(#[from] KeyforkdError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -95,6 +104,38 @@ impl Client {
|
||||||
get_socket().map(|socket| Self { socket })
|
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>`].
|
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -2,13 +2,10 @@
|
||||||
|
|
||||||
use std::time::{Duration, SystemTime, SystemTimeError};
|
use std::time::{Duration, SystemTime, SystemTimeError};
|
||||||
|
|
||||||
use derive_util::{
|
use derive_util::{DerivationIndex, ExtendedPrivateKey, IndexError, PrivateKey};
|
||||||
request::{DerivationResponse, TryFromDerivationResponseError},
|
|
||||||
DerivationIndex, ExtendedPrivateKey, PrivateKey,
|
|
||||||
IndexError,
|
|
||||||
};
|
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
pub use keyfork_derive_util as derive_util;
|
pub use keyfork_derive_util as derive_util;
|
||||||
|
pub use sequoia_openpgp as openpgp;
|
||||||
use sequoia_openpgp::{
|
use sequoia_openpgp::{
|
||||||
packet::{
|
packet::{
|
||||||
key::{Key4, PrimaryRole, SubordinateRole},
|
key::{Key4, PrimaryRole, SubordinateRole},
|
||||||
|
@ -18,7 +15,9 @@ use sequoia_openpgp::{
|
||||||
types::{KeyFlags, SignatureType},
|
types::{KeyFlags, SignatureType},
|
||||||
Cert, Packet,
|
Cert, Packet,
|
||||||
};
|
};
|
||||||
pub use sequoia_openpgp as openpgp;
|
|
||||||
|
pub type XPrvKey = SigningKey;
|
||||||
|
pub type XPrv = ExtendedPrivateKey<SigningKey>;
|
||||||
|
|
||||||
/// An error occurred while creating an OpenPGP key.
|
/// An error occurred while creating an OpenPGP key.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
@ -32,10 +31,6 @@ pub enum Error {
|
||||||
#[error("Key configured with both encryption and non-encryption key flags: {0:?}")]
|
#[error("Key configured with both encryption and non-encryption key flags: {0:?}")]
|
||||||
InvalidKeyFlags(KeyFlags),
|
InvalidKeyFlags(KeyFlags),
|
||||||
|
|
||||||
/// The derivation response contained incorrect data.
|
|
||||||
#[error("Incorrect derived data: {0}")]
|
|
||||||
IncorrectDerivedData(#[from] TryFromDerivationResponseError),
|
|
||||||
|
|
||||||
/// A derivation index could not be created from the given index.
|
/// A derivation index could not be created from the given index.
|
||||||
#[error("Could not create derivation index: {0}")]
|
#[error("Could not create derivation index: {0}")]
|
||||||
Index(#[from] IndexError),
|
Index(#[from] IndexError),
|
||||||
|
@ -66,7 +61,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The function may error for any condition mentioned in [`Error`].
|
/// The function may error for any condition mentioned in [`Error`].
|
||||||
pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
|
pub fn derive(xprv: XPrv, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
|
||||||
let primary_key_flags = match keys.get(0) {
|
let primary_key_flags = match keys.get(0) {
|
||||||
Some(kf) if kf.for_certification() => kf,
|
Some(kf) if kf.for_certification() => kf,
|
||||||
_ => return Err(Error::NotCert),
|
_ => return Err(Error::NotCert),
|
||||||
|
@ -76,7 +71,6 @@ pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: &UserID) -> R
|
||||||
let one_day = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
|
let one_day = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
|
||||||
|
|
||||||
// Create certificate with initial key and signature
|
// Create certificate with initial key and signature
|
||||||
let xprv = ExtendedPrivateKey::<SigningKey>::try_from(data)?;
|
|
||||||
let derived_primary_key = xprv.derive_child(&DerivationIndex::new(0, true)?)?;
|
let derived_primary_key = xprv.derive_child(&DerivationIndex::new(0, true)?)?;
|
||||||
let primary_key = Key::from(Key4::<_, PrimaryRole>::import_secret_ed25519(
|
let primary_key = Key::from(Key4::<_, PrimaryRole>::import_secret_ed25519(
|
||||||
&PrivateKey::to_bytes(derived_primary_key.private_key()),
|
&PrivateKey::to_bytes(derived_primary_key.private_key()),
|
||||||
|
@ -118,21 +112,14 @@ pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: &UserID) -> R
|
||||||
bytes[0] &= 0b1111_1000;
|
bytes[0] &= 0b1111_1000;
|
||||||
bytes[31] &= !0b1000_0000;
|
bytes[31] &= !0b1000_0000;
|
||||||
bytes[31] |= 0b0100_0000;
|
bytes[31] |= 0b0100_0000;
|
||||||
Key::from(
|
Key::from(Key4::<_, SubordinateRole>::import_secret_cv25519(
|
||||||
Key4::<_, SubordinateRole>::import_secret_cv25519(
|
&bytes, None, None, epoch,
|
||||||
&bytes,
|
)?)
|
||||||
None,
|
|
||||||
None,
|
|
||||||
epoch,
|
|
||||||
)?
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Key::from(
|
Key::from(Key4::<_, SubordinateRole>::import_secret_ed25519(
|
||||||
Key4::<_, SubordinateRole>::import_secret_ed25519(
|
|
||||||
&PrivateKey::to_bytes(derived_key.private_key()),
|
&PrivateKey::to_bytes(derived_key.private_key()),
|
||||||
epoch,
|
epoch,
|
||||||
)?
|
)?)
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// As per OpenPGP spec, signing keys must backsig the primary key
|
// As per OpenPGP spec, signing keys must backsig the primary key
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
use std::{env, process::ExitCode, str::FromStr};
|
use std::{env, process::ExitCode, str::FromStr};
|
||||||
|
|
||||||
use keyfork_derive_util::{
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
|
||||||
DerivationIndex, DerivationPath,
|
|
||||||
};
|
|
||||||
use keyforkd_client::Client;
|
use keyforkd_client::Client;
|
||||||
use sequoia_openpgp::{packet::UserID, types::KeyFlags, armor::{Kind, Writer}, serialize::Marshal};
|
|
||||||
|
use ed25519_dalek::SigningKey;
|
||||||
|
use sequoia_openpgp::{
|
||||||
|
armor::{Kind, Writer},
|
||||||
|
packet::UserID,
|
||||||
|
serialize::Marshal,
|
||||||
|
types::KeyFlags,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
enum Error {
|
enum Error {
|
||||||
|
@ -108,16 +112,13 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
_ => panic!("Usage: {program_name} path subkey_format default_userid"),
|
_ => panic!("Usage: {program_name} path subkey_format default_userid"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let derived_xprv = Client::discover_socket()?.request_xprv::<SigningKey>(&path)?;
|
||||||
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())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let cert = keyfork_derive_openpgp::derive(derived_data, subkeys.as_slice(), &default_userid)?;
|
let cert = keyfork_derive_openpgp::derive(derived_xprv, subkeys.as_slice(), &default_userid)?;
|
||||||
|
|
||||||
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,36 @@ type HmacSha512 = Hmac<Sha512>;
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
|
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
|
||||||
/// The internal private key data.
|
/// The internal private key data.
|
||||||
|
#[serde(with = "serde_with")]
|
||||||
private_key: K,
|
private_key: K,
|
||||||
depth: u8,
|
depth: u8,
|
||||||
chain_code: ChainCode,
|
chain_code: ChainCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod serde_with {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub(crate) fn serialize<S, K>(value: &K, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
K: PrivateKey + Clone,
|
||||||
|
{
|
||||||
|
serializer.serialize_bytes(&value.to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize<'de, D, K>(deserializer: D) -> Result<K, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
K: PrivateKey + Clone,
|
||||||
|
{
|
||||||
|
let variable_len_bytes = <&[u8]>::deserialize(deserializer)?;
|
||||||
|
let bytes: [u8; 32] = variable_len_bytes
|
||||||
|
.try_into()
|
||||||
|
.expect("unable to parse serialized private key; no support for static len");
|
||||||
|
Ok(K::from_bytes(&bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
|
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("ExtendedPrivateKey")
|
f.debug_struct("ExtendedPrivateKey")
|
||||||
|
@ -105,12 +130,14 @@ where
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
|
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
|
||||||
|
|
||||||
Self::new_from_parts(
|
Ok(Self::new_from_parts(
|
||||||
private_key,
|
private_key
|
||||||
|
.try_into()
|
||||||
|
.expect("KEY_SIZE / 8 did not give a 32 byte slice"),
|
||||||
0,
|
0,
|
||||||
// Checked: chain_code is always the same length, hash is static size
|
// Checked: chain_code is always the same length, hash is static size
|
||||||
chain_code.try_into().expect("Invalid chain code length"),
|
chain_code.try_into().expect("Invalid chain code length"),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an [`ExtendedPrivateKey`] from a given `seed`, `depth`, and `chain_code`.
|
/// Create an [`ExtendedPrivateKey`] from a given `seed`, `depth`, and `chain_code`.
|
||||||
|
@ -125,21 +152,18 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let key: &[u8; 32] = //
|
/// let key: &[u8; 32] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let chain_code: &[u8; 32] = //
|
/// let chain_code: &[u8; 32] = //
|
||||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
|
pub fn new_from_parts(key: &[u8; 32], depth: u8, chain_code: [u8; 32]) -> Self {
|
||||||
Ok(Self {
|
Self {
|
||||||
private_key: K::from_bytes(seed.try_into()?),
|
private_key: K::from_bytes(&key),
|
||||||
depth,
|
depth,
|
||||||
chain_code,
|
chain_code,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the [`PrivateKey`].
|
/// Returns a reference to the [`PrivateKey`].
|
||||||
|
@ -152,15 +176,12 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let key: &[u8; 32] = //
|
/// let key: &[u8; 32] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let chain_code: &[u8; 32] = //
|
/// let chain_code: &[u8; 32] = //
|
||||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
|
||||||
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
|
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn private_key(&self) -> &K {
|
pub fn private_key(&self) -> &K {
|
||||||
&self.private_key
|
&self.private_key
|
||||||
|
|
|
@ -110,6 +110,18 @@ impl std::str::FromStr for DerivationAlgorithm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Acquire the associated [`DerivationAlgorithm`] for a [`PrivateKey`].
|
||||||
|
pub trait AsAlgorithm: PrivateKey {
|
||||||
|
/// Return the appropriate [`DerivationAlgorithm`].
|
||||||
|
fn as_algorithm() -> DerivationAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsAlgorithm for TestPrivateKey {
|
||||||
|
fn as_algorithm() -> DerivationAlgorithm {
|
||||||
|
DerivationAlgorithm::Internal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A derivation request.
|
/// A derivation request.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct DerivationRequest {
|
pub struct DerivationRequest {
|
||||||
|
@ -234,7 +246,7 @@ pub struct DerivationResponse {
|
||||||
pub algorithm: DerivationAlgorithm,
|
pub algorithm: DerivationAlgorithm,
|
||||||
|
|
||||||
/// The derived private key.
|
/// The derived private key.
|
||||||
pub data: Vec<u8>,
|
pub data: [u8; 32],
|
||||||
|
|
||||||
/// The chain code, used for further derivation.
|
/// The chain code, used for further derivation.
|
||||||
pub chain_code: [u8; 32],
|
pub chain_code: [u8; 32],
|
||||||
|
@ -251,7 +263,7 @@ impl DerivationResponse {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
algorithm,
|
algorithm,
|
||||||
data: PrivateKey::to_bytes(xprv.private_key()).to_vec(),
|
data: PrivateKey::to_bytes(xprv.private_key()),
|
||||||
chain_code: xprv.chain_code(),
|
chain_code: xprv.chain_code(),
|
||||||
depth: xprv.depth(),
|
depth: xprv.depth(),
|
||||||
}
|
}
|
||||||
|
@ -272,47 +284,71 @@ pub enum TryFromDerivationResponseError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
|
mod secp256k1 {
|
||||||
|
use super::*;
|
||||||
|
use k256::SecretKey;
|
||||||
|
|
||||||
|
impl AsAlgorithm for SecretKey {
|
||||||
|
fn as_algorithm() -> DerivationAlgorithm {
|
||||||
|
DerivationAlgorithm::Secp256k1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SecretKey> {
|
||||||
type Error = TryFromDerivationResponseError;
|
type Error = TryFromDerivationResponseError;
|
||||||
|
|
||||||
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
|
||||||
match value.algorithm {
|
match value.algorithm {
|
||||||
DerivationAlgorithm::Secp256k1 => {
|
DerivationAlgorithm::Secp256k1 => Ok(Self::new_from_parts(
|
||||||
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
|
&value.data,
|
||||||
}
|
value.depth,
|
||||||
|
value.chain_code,
|
||||||
|
)),
|
||||||
_ => Err(Self::Error::Algorithm),
|
_ => Err(Self::Error::Algorithm),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "secp256k1")]
|
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<SecretKey> {
|
||||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
|
|
||||||
type Error = TryFromDerivationResponseError;
|
type Error = TryFromDerivationResponseError;
|
||||||
|
|
||||||
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
fn try_from(value: DerivationResponse) -> Result<Self, Self::Error> {
|
||||||
ExtendedPrivateKey::<k256::SecretKey>::try_from(&value)
|
ExtendedPrivateKey::<SecretKey>::try_from(&value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
|
mod ed25519 {
|
||||||
|
use super::*;
|
||||||
|
use ed25519_dalek::SigningKey;
|
||||||
|
|
||||||
|
impl AsAlgorithm for SigningKey {
|
||||||
|
fn as_algorithm() -> DerivationAlgorithm {
|
||||||
|
DerivationAlgorithm::Ed25519
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SigningKey> {
|
||||||
type Error = TryFromDerivationResponseError;
|
type Error = TryFromDerivationResponseError;
|
||||||
|
|
||||||
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
|
||||||
match value.algorithm {
|
match value.algorithm {
|
||||||
DerivationAlgorithm::Ed25519 => {
|
DerivationAlgorithm::Ed25519 => Ok(Self::new_from_parts(
|
||||||
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
|
&value.data,
|
||||||
}
|
value.depth,
|
||||||
|
value.chain_code,
|
||||||
|
)),
|
||||||
_ => Err(Self::Error::Algorithm),
|
_ => Err(Self::Error::Algorithm),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ed25519")]
|
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<SigningKey> {
|
||||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
|
|
||||||
type Error = TryFromDerivationResponseError;
|
type Error = TryFromDerivationResponseError;
|
||||||
|
|
||||||
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
fn try_from(value: DerivationResponse) -> Result<Self, Self::Error> {
|
||||||
ExtendedPrivateKey::<ed25519_dalek::SigningKey>::try_from(&value)
|
ExtendedPrivateKey::<SigningKey>::try_from(&value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ use aes_gcm::{
|
||||||
Aes256Gcm, Error as AesError, KeyInit, Nonce,
|
Aes256Gcm, Error as AesError, KeyInit, Nonce,
|
||||||
};
|
};
|
||||||
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
||||||
use keyfork_derive_openpgp::derive_util::{
|
use keyfork_derive_openpgp::{
|
||||||
request::{DerivationAlgorithm, DerivationRequest},
|
derive_util::{DerivationPath, PathError},
|
||||||
DerivationPath, PathError,
|
XPrv,
|
||||||
};
|
};
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
|
@ -123,6 +123,10 @@ pub enum Error {
|
||||||
#[error("IO error: {0}")]
|
#[error("IO error: {0}")]
|
||||||
Io(#[source] std::io::Error),
|
Io(#[source] std::io::Error),
|
||||||
|
|
||||||
|
/// An error occurred while deriving data.
|
||||||
|
#[error("Derivation: {0}")]
|
||||||
|
Derivation(#[from] keyfork_derive_openpgp::derive_util::extended_key::private_key::Error),
|
||||||
|
|
||||||
/// An error occurred while parsing a derivation path.
|
/// An error occurred while parsing a derivation path.
|
||||||
#[error("Derivation path: {0}")]
|
#[error("Derivation path: {0}")]
|
||||||
DerivationPath(#[from] PathError),
|
DerivationPath(#[from] PathError),
|
||||||
|
@ -643,13 +647,10 @@ pub fn combine(
|
||||||
|
|
||||||
// TODO: extract as function
|
// TODO: extract as function
|
||||||
let userid = UserID::from("keyfork-sss");
|
let userid = UserID::from("keyfork-sss");
|
||||||
let kdr = DerivationRequest::new(
|
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
||||||
DerivationAlgorithm::Ed25519,
|
let xprv = XPrv::new(&secret)?.derive_path(&path)?;
|
||||||
&DerivationPath::from_str("m/7366512'/0'")?,
|
|
||||||
)
|
|
||||||
.derive_with_master_seed(secret.clone())?;
|
|
||||||
let derived_cert = keyfork_derive_openpgp::derive(
|
let derived_cert = keyfork_derive_openpgp::derive(
|
||||||
kdr,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
&userid,
|
&userid,
|
||||||
)?;
|
)?;
|
||||||
|
@ -680,13 +681,10 @@ pub fn combine(
|
||||||
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
||||||
// build cert to sign encrypted shares
|
// build cert to sign encrypted shares
|
||||||
let userid = UserID::from("keyfork-sss");
|
let userid = UserID::from("keyfork-sss");
|
||||||
let kdr = DerivationRequest::new(
|
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
||||||
DerivationAlgorithm::Ed25519,
|
let xprv = XPrv::new(&secret)?.derive_path(&path)?;
|
||||||
&DerivationPath::from_str("m/7366512'/0'")?,
|
|
||||||
)
|
|
||||||
.derive_with_master_seed(secret.to_vec())?;
|
|
||||||
let derived_cert = keyfork_derive_openpgp::derive(
|
let derived_cert = keyfork_derive_openpgp::derive(
|
||||||
kdr,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
&userid,
|
&userid,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
|
openpgp::{
|
||||||
armor::{Kind, Writer},
|
armor::{Kind, Writer},
|
||||||
packet::UserID,
|
packet::UserID,
|
||||||
serialize::Marshal,
|
serialize::Marshal,
|
||||||
types::KeyFlags,
|
types::KeyFlags,
|
||||||
|
},
|
||||||
|
XPrvKey,
|
||||||
};
|
};
|
||||||
use keyfork_derive_util::{
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
|
||||||
DerivationIndex, DerivationPath,
|
|
||||||
};
|
|
||||||
use keyforkd_client::Client;
|
use keyforkd_client::Client;
|
||||||
|
|
||||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
@ -48,12 +48,9 @@ impl DeriveSubcommands {
|
||||||
.set_storage_encryption(),
|
.set_storage_encryption(),
|
||||||
KeyFlags::empty().set_authentication(),
|
KeyFlags::empty().set_authentication(),
|
||||||
];
|
];
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let xprv = Client::discover_socket()?.request_xprv::<XPrvKey>(&path)?;
|
||||||
let derived_data: DerivationResponse = Client::discover_socket()?
|
|
||||||
.request(&request.into())?
|
|
||||||
.try_into()?;
|
|
||||||
let default_userid = UserID::from(user_id.as_str());
|
let default_userid = UserID::from(user_id.as_str());
|
||||||
let cert = keyfork_derive_openpgp::derive(derived_data, &subkeys, &default_userid)?;
|
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &default_userid)?;
|
||||||
|
|
||||||
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,11 @@ use std::{collections::HashSet, fs::File, io::IsTerminal, path::PathBuf};
|
||||||
use card_backend_pcsc::PcscBackend;
|
use card_backend_pcsc::PcscBackend;
|
||||||
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::openpgp::{self, packet::UserID, types::KeyFlags, Cert};
|
use keyfork_derive_openpgp::{
|
||||||
use keyfork_derive_util::{
|
openpgp::{self, packet::UserID, types::KeyFlags, Cert},
|
||||||
request::{DerivationAlgorithm, DerivationRequest},
|
XPrv,
|
||||||
DerivationIndex, DerivationPath,
|
|
||||||
};
|
};
|
||||||
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{PinValidator, Validator},
|
validators::{PinValidator, Validator},
|
||||||
Message, PromptHandler, Terminal,
|
Message, PromptHandler, Terminal,
|
||||||
|
@ -42,10 +42,9 @@ fn derive_key(seed: &[u8], index: u8) -> Result<Cert> {
|
||||||
.chain_push(chain)
|
.chain_push(chain)
|
||||||
.chain_push(account)
|
.chain_push(account)
|
||||||
.chain_push(subkey);
|
.chain_push(subkey);
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let xprv = XPrv::new(seed)?.derive_path(&path)?;
|
||||||
let response = request.derive_with_master_seed(seed.to_vec())?;
|
|
||||||
let userid = UserID::from(format!("Keyfork Shard {index}"));
|
let userid = UserID::from(format!("Keyfork Shard {index}"));
|
||||||
let cert = keyfork_derive_openpgp::derive(response, &subkeys, &userid)?;
|
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
|
||||||
Ok(cert)
|
Ok(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue