keyfork: impl `shard transport`

This commit is contained in:
Ryan Heywood 2024-01-07 02:41:26 -05:00
parent d548276bc3
commit 6fc2c47391
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
1 changed files with 63 additions and 13 deletions

View File

@ -1,10 +1,12 @@
use super::Keyfork; use super::Keyfork;
use clap::{builder::PossibleValue, Parser, Subcommand, ValueEnum}; use clap::{builder::PossibleValue, Parser, Subcommand, ValueEnum};
use std::{ use std::{
io::{stdin, stdout, BufRead, BufReader, Read, Write}, io::{stdin, stdout, Read, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
const COULD_NOT_DETERMINE_FORMAT: &str = "could not determine format, try including --format";
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Format { enum Format {
OpenPGP(OpenPGP), OpenPGP(OpenPGP),
@ -42,6 +44,14 @@ trait ShardExec {
) -> Result<(), Box<dyn std::error::Error>> ) -> Result<(), Box<dyn std::error::Error>>
where where
T: AsRef<Path>; T: AsRef<Path>;
fn decrypt<T>(
&self,
key_discovery: Option<T>,
input: impl Read + Send + Sync,
) -> Result<(), Box<dyn std::error::Error>>
where
T: AsRef<Path>;
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -99,6 +109,32 @@ impl ShardExec for OpenPGP {
Ok(()) Ok(())
} }
fn decrypt<T>(
&self,
key_discovery: Option<T>,
input: impl Read + Send + Sync,
) -> Result<(), Box<dyn std::error::Error>>
where
T: AsRef<Path>,
{
let certs = key_discovery
.map(|kd| keyfork_shard::openpgp::discover_certs(kd.as_ref()))
.transpose()?
.unwrap_or(vec![]);
let mut encrypted_messages = keyfork_shard::openpgp::parse_messages(input)?;
let encrypted_metadata = encrypted_messages
.pop_front()
.expect("any pgp encrypted message");
keyfork_shard::openpgp::decrypt(
&certs,
&encrypted_metadata,
encrypted_messages.make_contiguous(),
)?;
Ok(())
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -125,6 +161,13 @@ pub enum ShardSubcommands {
key_discovery: PathBuf, key_discovery: PathBuf,
}, },
/// Decrypt a single share and re-encrypt it to an ephemeral symmetric key using mnemonic-based
/// prompts. The mnemonics can be sent over insecure channels.
Transport {
/// The path to discover private keys from.
key_discovery: Option<PathBuf>,
},
/// Combine multiple encrypted shares into a hex-encoded secret, printed to stdout. /// Combine multiple encrypted shares into a hex-encoded secret, printed to stdout.
/// ///
/// This command only accepts input from `keyfork shard split`, and is dependent on the format /// This command only accepts input from `keyfork shard split`, and is dependent on the format
@ -143,8 +186,15 @@ impl ShardSubcommands {
shard: &Shard, shard: &Shard,
_keyfork: &Keyfork, _keyfork: &Keyfork,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let stdin = stdin(); let mut stdin = stdin();
let mut stdout = stdout(); let mut stdout = stdout();
let mut input = String::new();
stdin.read_to_string(&mut input)?;
let mut format = shard.format.clone();
// bang sandwich macro fun
if input.contains("BEGIN PGP MESSAGE") && !matches!(self, ShardSubcommands::Split { .. }) {
let _ = format.insert(Format::OpenPGP(OpenPGP));
}
match self { match self {
ShardSubcommands::Split { ShardSubcommands::Split {
threshold, threshold,
@ -152,30 +202,30 @@ impl ShardSubcommands {
key_discovery, key_discovery,
} => { } => {
assert!(threshold <= max, "threshold {threshold} <= max {max}"); assert!(threshold <= max, "threshold {threshold} <= max {max}");
let mut input = BufReader::new(stdin); let secret = smex::decode(input.trim())?;
let mut hex_line = String::new(); match format {
input.read_line(&mut hex_line)?;
let secret = smex::decode(hex_line.trim())?;
match &shard.format {
Some(Format::OpenPGP(o)) => { Some(Format::OpenPGP(o)) => {
o.split(*threshold, *max, key_discovery, &secret, &mut stdout) o.split(*threshold, *max, key_discovery, &secret, &mut stdout)
} }
Some(Format::P256(_p)) => { Some(Format::P256(_p)) => {
todo!() todo!()
} }
None => panic!("--format was not given"), None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
} }
} }
ShardSubcommands::Combine { ShardSubcommands::Transport { key_discovery } => match format {
key_discovery, Some(Format::OpenPGP(o)) => o.decrypt(key_discovery.as_ref(), input.as_bytes()),
} => match &shard.format { Some(Format::P256(_p)) => todo!(),
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
},
ShardSubcommands::Combine { key_discovery } => match format {
Some(Format::OpenPGP(o)) => { Some(Format::OpenPGP(o)) => {
o.combine(key_discovery.as_ref(), stdin, &mut stdout) o.combine(key_discovery.as_ref(), input.as_bytes(), &mut stdout)
} }
Some(Format::P256(_p)) => { Some(Format::P256(_p)) => {
todo!() todo!()
} }
None => panic!("--format was not given"), None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
}, },
} }
} }