2024-01-16 02:44:48 +00:00
|
|
|
//!
|
|
|
|
|
2023-11-05 06:29:10 +00:00
|
|
|
use std::{env, process::ExitCode, str::FromStr};
|
2023-09-30 07:19:37 +00:00
|
|
|
|
|
|
|
use keyfork_derive_util::{
|
2023-11-06 04:51:40 +00:00
|
|
|
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
2023-10-03 02:56:15 +00:00
|
|
|
DerivationIndex, DerivationPath,
|
2023-09-30 07:19:37 +00:00
|
|
|
};
|
|
|
|
use keyforkd_client::Client;
|
2024-01-07 04:19:28 +00:00
|
|
|
use sequoia_openpgp::{packet::UserID, types::KeyFlags, armor::{Kind, Writer}, serialize::Marshal};
|
2023-09-30 07:19:37 +00:00
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
enum Error {
|
|
|
|
#[error("Bad character: {0}")]
|
|
|
|
BadChar(char),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct KeyType {
|
2024-01-07 04:19:28 +00:00
|
|
|
inner: KeyFlags,
|
2023-09-30 07:19:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for KeyType {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2024-01-07 04:19:28 +00:00
|
|
|
inner: KeyFlags::empty(),
|
2023-09-30 07:19:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KeyType {
|
|
|
|
fn certify(mut self) -> Self {
|
2024-01-07 04:19:28 +00:00
|
|
|
self.inner = self.inner.set_certification();
|
2023-09-30 07:19:37 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sign(mut self) -> Self {
|
2024-01-07 04:19:28 +00:00
|
|
|
self.inner = self.inner.set_signing();
|
2023-09-30 07:19:37 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn encrypt(mut self) -> Self {
|
2024-01-07 04:19:28 +00:00
|
|
|
self.inner = self.inner.set_transport_encryption();
|
|
|
|
self.inner = self.inner.set_storage_encryption();
|
2023-09-30 07:19:37 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn authenticate(mut self) -> Self {
|
2024-01-07 04:19:28 +00:00
|
|
|
self.inner = self.inner.set_authentication();
|
2023-09-30 07:19:37 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn inner(&self) -> &KeyFlags {
|
2024-01-07 04:19:28 +00:00
|
|
|
&self.inner
|
2023-09-30 07:19:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for KeyType {
|
|
|
|
type Err = Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
s.chars().try_fold(Self::default(), |s, ch| match ch {
|
|
|
|
'C' | 'c' => Ok(s.certify()),
|
|
|
|
'S' | 's' => Ok(s.sign()),
|
|
|
|
'E' | 'e' => Ok(s.encrypt()),
|
|
|
|
'A' | 'a' => Ok(s.authenticate()),
|
|
|
|
ch => Err(Error::BadChar(ch)),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn validate(
|
|
|
|
path: &str,
|
|
|
|
subkey_format: &str,
|
|
|
|
default_userid: &str,
|
|
|
|
) -> Result<(DerivationPath, Vec<KeyType>, UserID), Box<dyn std::error::Error>> {
|
|
|
|
let mut pgp_u32 = [0u8; 4];
|
|
|
|
pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::<Vec<u8>>());
|
|
|
|
let index = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
|
|
|
|
|
|
|
|
let path = DerivationPath::from_str(path)?;
|
|
|
|
assert_eq!(2, path.len(), "Expected path of m/{index}/account_id'");
|
|
|
|
|
|
|
|
let given_index = path.iter().next().expect("checked .len() above");
|
|
|
|
assert_eq!(
|
|
|
|
&index, given_index,
|
|
|
|
"Expected derivation path starting with m/{index}, got: {given_index}",
|
|
|
|
);
|
|
|
|
|
|
|
|
let subkey_format = subkey_format
|
|
|
|
.split(',')
|
|
|
|
.map(KeyType::from_str)
|
|
|
|
.collect::<Result<Vec<_>, Error>>()?;
|
|
|
|
assert!(
|
|
|
|
subkey_format[0].inner().for_certification(),
|
|
|
|
"First key must be able to certify"
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok((path, subkey_format, UserID::from(default_userid)))
|
|
|
|
}
|
|
|
|
|
2023-11-02 02:28:06 +00:00
|
|
|
fn run() -> Result<(), Box<dyn std::error::Error>> {
|
2023-09-30 07:19:37 +00:00
|
|
|
let mut args = env::args();
|
|
|
|
let program_name = args.next().expect("program name");
|
|
|
|
let args = args.collect::<Vec<_>>();
|
|
|
|
let (path, subkey_format, default_userid) = match args.as_slice() {
|
|
|
|
[path, subkey_format, default_userid] => validate(path, subkey_format, default_userid)?,
|
|
|
|
_ => panic!("Usage: {program_name} path subkey_format default_userid"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
2023-11-06 04:51:40 +00:00
|
|
|
let derived_data: DerivationResponse = Client::discover_socket()?
|
|
|
|
.request(&request.into())?
|
|
|
|
.try_into()?;
|
2023-10-03 02:56:15 +00:00
|
|
|
let subkeys = subkey_format
|
|
|
|
.iter()
|
|
|
|
.map(|kt| kt.inner().clone())
|
|
|
|
.collect::<Vec<_>>();
|
2023-09-30 07:19:37 +00:00
|
|
|
|
2024-01-07 04:19:28 +00:00
|
|
|
let cert = keyfork_derive_openpgp::derive(derived_data, subkeys.as_slice(), &default_userid)?;
|
2023-09-30 07:19:37 +00:00
|
|
|
|
2023-11-02 02:26:46 +00:00
|
|
|
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;
|
2023-09-30 07:19:37 +00:00
|
|
|
|
|
|
|
for packet in cert.into_packets() {
|
|
|
|
packet.serialize(&mut w)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
w.finalize()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2023-11-02 02:28:06 +00:00
|
|
|
|
|
|
|
fn main() -> ExitCode {
|
|
|
|
if let Err(e) = run() {
|
|
|
|
eprintln!("Error: {e}");
|
|
|
|
ExitCode::FAILURE
|
|
|
|
} else {
|
|
|
|
ExitCode::SUCCESS
|
|
|
|
}
|
|
|
|
}
|