keyfork derive key: initial commit
This commit is contained in:
parent
c46f9e48b7
commit
503c6fa0b4
|
@ -1742,6 +1742,7 @@ dependencies = [
|
|||
name = "keyfork"
|
||||
version = "0.2.4"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"card-backend-pcsc",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
|
@ -1756,6 +1757,7 @@ dependencies = [
|
|||
"keyfork-shard",
|
||||
"keyforkd",
|
||||
"keyforkd-client",
|
||||
"keyforkd-models",
|
||||
"openpgp-card",
|
||||
"openpgp-card-sequoia",
|
||||
"sequoia-openpgp",
|
||||
|
|
|
@ -74,6 +74,7 @@ image = { version = "0.25.2", default-features = false }
|
|||
thiserror = "1.0.56"
|
||||
tokio = "1.35.1"
|
||||
v4l = "0.14.0"
|
||||
base64 = "0.22.1"
|
||||
|
||||
[profile.dev.package.keyfork-qrcode]
|
||||
opt-level = 3
|
||||
|
|
|
@ -45,3 +45,5 @@ openpgp-card-sequoia = { workspace = true }
|
|||
openpgp-card = { workspace = true }
|
||||
clap_complete = { version = "4.4.6", optional = true }
|
||||
sequoia-openpgp = { workspace = true }
|
||||
keyforkd-models.workspace = true
|
||||
base64.workspace = true
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::Keyfork;
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
|
||||
use keyfork_derive_openpgp::{
|
||||
openpgp::{
|
||||
|
@ -10,9 +10,13 @@ use keyfork_derive_openpgp::{
|
|||
},
|
||||
XPrvKey,
|
||||
};
|
||||
use keyfork_derive_util::DerivationIndex;
|
||||
use keyfork_derive_path_data::paths;
|
||||
use keyfork_derive_util::{
|
||||
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
||||
DerivationIndex, DerivationPath, IndexError,
|
||||
};
|
||||
use keyforkd_client::Client;
|
||||
use keyforkd_models::Request;
|
||||
|
||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||
|
||||
|
@ -28,7 +32,10 @@ pub enum DeriveSubcommands {
|
|||
/// It is recommended to use the default expiration of one day and to change the expiration
|
||||
/// using an external utility, to ensure the Certify key is usable.
|
||||
#[command(name = "openpgp")]
|
||||
OpenPGP(OpenPGP)
|
||||
OpenPGP(OpenPGP),
|
||||
|
||||
/// Derive a bare key for a specific algorithm, in a given format.
|
||||
Key(Key),
|
||||
}
|
||||
|
||||
#[derive(Args, Clone, Debug)]
|
||||
|
@ -37,10 +44,64 @@ pub struct OpenPGP {
|
|||
user_id: String,
|
||||
}
|
||||
|
||||
/// A format for exporting a key.
|
||||
#[derive(ValueEnum, Clone, Debug)]
|
||||
pub enum KeyFormat {
|
||||
Hex,
|
||||
Base64,
|
||||
}
|
||||
|
||||
/// An invalid slug was provided.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum InvalidSlug {
|
||||
/// The value provided was longer than four bytes.
|
||||
#[error("The value provided was longer than four bytes: {0}")]
|
||||
InvalidSize(usize),
|
||||
|
||||
/// The value provided was higher than the maximum derivation index.
|
||||
#[error("The value provided was higher than the maximum derivation index: {0}")]
|
||||
InvalidValue(#[from] IndexError),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Slug(DerivationIndex);
|
||||
|
||||
impl std::str::FromStr for Slug {
|
||||
type Err = InvalidSlug;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let bytes = s.as_bytes();
|
||||
let mut parseable_bytes = [0u8; 4];
|
||||
if bytes.len() <= 4 && !bytes.is_empty() {
|
||||
parseable_bytes[(4 - bytes.len())..4].copy_from_slice(bytes);
|
||||
} else {
|
||||
return Err(InvalidSlug::InvalidSize(bytes.len()));
|
||||
}
|
||||
let slug = u32::from_be_bytes(parseable_bytes);
|
||||
let index = DerivationIndex::new(slug, true)?;
|
||||
Ok(Slug(index))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Clone, Debug)]
|
||||
pub struct Key {
|
||||
/// The derivation algorithm to derive a key for.
|
||||
derivation_algorithm: DerivationAlgorithm,
|
||||
|
||||
/// The output format.
|
||||
#[arg(value_enum)]
|
||||
format: KeyFormat,
|
||||
|
||||
/// A maximum of four bytes, used for creating the derivation path.
|
||||
#[arg(value_parser = clap::value_parser!(Slug))]
|
||||
slug: Slug,
|
||||
}
|
||||
|
||||
impl DeriveSubcommands {
|
||||
fn handle(&self, account: DerivationIndex) -> Result<()> {
|
||||
match self {
|
||||
DeriveSubcommands::OpenPGP(opgp) => opgp.handle(account),
|
||||
DeriveSubcommands::Key(key) => key.handle(account),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +133,29 @@ impl OpenPGP {
|
|||
}
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn handle(&self, account: DerivationIndex) -> Result<()> {
|
||||
let mut client = keyforkd_client::Client::discover_socket()?;
|
||||
let path = DerivationPath::default()
|
||||
.chain_push(self.slug.0.clone())
|
||||
.chain_push(account);
|
||||
let request = DerivationRequest::new(self.derivation_algorithm.clone(), &path);
|
||||
let request = Request::Derivation(request);
|
||||
let derived_key: DerivationResponse = client.request(&request)?.try_into()?;
|
||||
|
||||
let formatted = match self.format {
|
||||
KeyFormat::Hex => smex::encode(derived_key.data),
|
||||
KeyFormat::Base64 => {
|
||||
use base64::prelude::*;
|
||||
BASE64_STANDARD.encode(derived_key.data)
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!("{formatted}");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct Derive {
|
||||
#[command(subcommand)]
|
||||
|
|
Loading…
Reference in New Issue