keyfork derive key: initial commit
This commit is contained in:
parent
c46f9e48b7
commit
503c6fa0b4
|
@ -1742,6 +1742,7 @@ dependencies = [
|
||||||
name = "keyfork"
|
name = "keyfork"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64",
|
||||||
"card-backend-pcsc",
|
"card-backend-pcsc",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
@ -1756,6 +1757,7 @@ dependencies = [
|
||||||
"keyfork-shard",
|
"keyfork-shard",
|
||||||
"keyforkd",
|
"keyforkd",
|
||||||
"keyforkd-client",
|
"keyforkd-client",
|
||||||
|
"keyforkd-models",
|
||||||
"openpgp-card",
|
"openpgp-card",
|
||||||
"openpgp-card-sequoia",
|
"openpgp-card-sequoia",
|
||||||
"sequoia-openpgp",
|
"sequoia-openpgp",
|
||||||
|
|
|
@ -74,6 +74,7 @@ image = { version = "0.25.2", default-features = false }
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
tokio = "1.35.1"
|
tokio = "1.35.1"
|
||||||
v4l = "0.14.0"
|
v4l = "0.14.0"
|
||||||
|
base64 = "0.22.1"
|
||||||
|
|
||||||
[profile.dev.package.keyfork-qrcode]
|
[profile.dev.package.keyfork-qrcode]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
|
@ -45,3 +45,5 @@ openpgp-card-sequoia = { workspace = true }
|
||||||
openpgp-card = { workspace = true }
|
openpgp-card = { workspace = true }
|
||||||
clap_complete = { version = "4.4.6", optional = true }
|
clap_complete = { version = "4.4.6", optional = true }
|
||||||
sequoia-openpgp = { workspace = true }
|
sequoia-openpgp = { workspace = true }
|
||||||
|
keyforkd-models.workspace = true
|
||||||
|
base64.workspace = true
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
openpgp::{
|
openpgp::{
|
||||||
|
@ -10,9 +10,13 @@ use keyfork_derive_openpgp::{
|
||||||
},
|
},
|
||||||
XPrvKey,
|
XPrvKey,
|
||||||
};
|
};
|
||||||
use keyfork_derive_util::DerivationIndex;
|
|
||||||
use keyfork_derive_path_data::paths;
|
use keyfork_derive_path_data::paths;
|
||||||
|
use keyfork_derive_util::{
|
||||||
|
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
||||||
|
DerivationIndex, DerivationPath, IndexError,
|
||||||
|
};
|
||||||
use keyforkd_client::Client;
|
use keyforkd_client::Client;
|
||||||
|
use keyforkd_models::Request;
|
||||||
|
|
||||||
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>;
|
||||||
|
|
||||||
|
@ -28,7 +32,10 @@ pub enum DeriveSubcommands {
|
||||||
/// It is recommended to use the default expiration of one day and to change the expiration
|
/// 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.
|
/// using an external utility, to ensure the Certify key is usable.
|
||||||
#[command(name = "openpgp")]
|
#[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)]
|
#[derive(Args, Clone, Debug)]
|
||||||
|
@ -37,10 +44,64 @@ pub struct OpenPGP {
|
||||||
user_id: String,
|
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 {
|
impl DeriveSubcommands {
|
||||||
fn handle(&self, account: DerivationIndex) -> Result<()> {
|
fn handle(&self, account: DerivationIndex) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
DeriveSubcommands::OpenPGP(opgp) => opgp.handle(account),
|
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)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
pub struct Derive {
|
pub struct Derive {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
|
Loading…
Reference in New Issue