keyfork{,-shard}: make all shard-accepting commandsz take it as an argument, not stdin

This commit is contained in:
Ryan Heywood 2024-01-10 23:55:22 -05:00
parent dd3ffe74b3
commit ceb0ac2455
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 67 additions and 52 deletions

View File

@ -1,7 +1,7 @@
use std::{
env,
io::stdin,
path::PathBuf,
fs::File,
path::{Path, PathBuf},
process::ExitCode,
};
@ -9,10 +9,11 @@ use keyfork_shard::openpgp::{combine, discover_certs, openpgp::Cert, parse_messa
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
fn validate<'a>(
key_discovery: impl Into<Option<&'a str>>,
) -> Result<Vec<Cert>> {
let key_discovery = key_discovery.into().map(PathBuf::from);
fn validate(
shard: impl AsRef<Path>,
key_discovery: Option<&str>,
) -> Result<(File, Vec<Cert>)> {
let key_discovery = key_discovery.map(PathBuf::from);
key_discovery.as_ref().map(std::fs::metadata).transpose()?;
// Load certs from path
@ -21,20 +22,20 @@ fn validate<'a>(
.transpose()?
.unwrap_or(vec![]);
Ok(certs)
Ok((File::open(shard)?, certs))
}
fn run() -> Result<()> {
let mut args = env::args();
let program_name = args.next().expect("program name");
let args = args.collect::<Vec<_>>();
let cert_list = match args.as_slice() {
[key_discovery] => validate(key_discovery.as_str())?,
[] => validate(None)?,
_ => panic!("Usage: {program_name} threshold [key_discovery]"),
let (messages_file, cert_list) = match args.as_slice() {
[shard, key_discovery] => validate(shard, Some(key_discovery))?,
[shard] => validate(shard, None)?,
_ => panic!("Usage: {program_name} <shard> [key_discovery]"),
};
let mut encrypted_messages = parse_messages(stdin())?;
let mut encrypted_messages = parse_messages(messages_file)?;
let encrypted_metadata = encrypted_messages
.pop_front()

View File

@ -4,8 +4,9 @@ Combine `threshold` shares into a previously [`split`] secret.
## Arguments
`keyfork-shard-combine-openpgp [key_discovery]`
`keyfork-shard-combine-openpgp <shard> [key_discovery]`
* `shard`: The shard file to read from.
* `key_discovery`: A file or directory containing OpenPGP keys.
If the number of keys found is less than `threshold`, an OpenPGP Card
fallback will be used to decrypt the rest of the messages.
@ -17,10 +18,6 @@ The terminal may be overridden if the default pinentry command is
used if an OpenPGP key file has an encrypted secret key or to prompt for the
PIN for an OpenPGP smart card.
## Input
OpenPGP messages from [`split`].
## Output
Hex-encoded secret.
@ -29,10 +26,10 @@ Hex-encoded secret.
```sh
# Decrypt using only smartcards
keyfork-shard-combine-openpgp < shard.pgp
keyfork-shard-combine-openpgp shard.pgp
# Decrypt using on-disk private keys
keyfork-shard-combine-openpgp key_discovery.pgp < shard.pgp
keyfork-shard-combine-openpgp key_discovery.pgp shard.pgp
```
[`split`]: ./split.md

View File

@ -68,8 +68,9 @@ Combine `threshold` shares into a secret.
### Arguments
`keyfork shard combine [key_discovery]`
`keyfork shard combine <shard> [key_discovery]`
* `shard`: A file containing the encrypted shards.
* `key_discovery`: Either a file or a directory containing public keys.
If a file, load all private keys from a file.
If a directory, for every file in the directory (non-recursively), load
@ -77,11 +78,6 @@ Combine `threshold` shares into a secret.
If the amount of keys found is less than `threshold`, it is up to the format
to determine how to discover the keys.
### Input
The input of the command is dependent on the format, but should be the exact
same as the output from the `split` command previously used.
### Output
Hex-encoded secret.
@ -106,8 +102,9 @@ by a remote recovery operator.
### Arguments
`keyfork shard transport [key_discovery]`
`keyfork shard transport <shard> [key_discovery]`
* `shard`: A file containing encrypted shards.
* `key_discovery`: Either a file or a directory containing public keys.
If a file, load all private keys from a file.
If a directory, for every file in the directory (non-recursively), load
@ -115,11 +112,6 @@ by a remote recovery operator.
If the amount of keys found is less than `threshold`, it is up to the format
to determine how to discover the keys.
### Input
The input of the command is dependent on the format, but should be the exact
same as the output from the `split` command previously used.
### Prompts
The command will prompt for 33 words from the remote shard recovery operator,
@ -131,10 +123,10 @@ operator.
```sh
# Transport using a smart card
keyfork shard transport < shard.pgp
keyfork shard transport shard.pgp
# Transport using on-disk private keys
keyfork shard transport key_discovery.pgp < shard.pgp
keyfork shard transport key_discovery.pgp shard.pgp
```
[`keyfork recover remote-shard`]: ../recover/index.md#keyfork-recover-remote-shard

View File

@ -164,6 +164,9 @@ pub enum ShardSubcommands {
/// 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 load the shard from.
shard: PathBuf,
/// The path to discover private keys from.
key_discovery: Option<PathBuf>,
},
@ -175,6 +178,9 @@ pub enum ShardSubcommands {
/// hardware metadata discovery, any hardware key used to split may be used to decrypt metadata
/// used to combine.
Combine {
/// The path to load the shards from.
shard: PathBuf,
/// The path to discover private keys from.
key_discovery: Option<PathBuf>,
},
@ -186,21 +192,16 @@ impl ShardSubcommands {
shard: &Shard,
_keyfork: &Keyfork,
) -> Result<(), Box<dyn std::error::Error>> {
let mut stdin = stdin();
let stdin = stdin();
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 {
ShardSubcommands::Split {
threshold,
max,
key_discovery,
} => {
let input = std::io::read_to_string(stdin)?;
assert!(threshold <= max, "threshold {threshold} <= max {max}");
let secret = smex::decode(input.trim())?;
match format {
@ -213,20 +214,44 @@ impl ShardSubcommands {
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
}
}
ShardSubcommands::Transport { key_discovery } => match format {
Some(Format::OpenPGP(o)) => o.decrypt(key_discovery.as_ref(), input.as_bytes()),
ShardSubcommands::Transport {
shard,
key_discovery,
} => {
let shard_content = std::fs::read_to_string(shard)?;
if shard_content.contains("BEGIN PGP MESSAGE") {
let _ = format.insert(Format::OpenPGP(OpenPGP));
}
match format {
Some(Format::OpenPGP(o)) => {
o.decrypt(key_discovery.as_ref(), shard_content.as_bytes())
}
Some(Format::P256(_p)) => todo!(),
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
},
ShardSubcommands::Combine { key_discovery } => match format {
Some(Format::OpenPGP(o)) => {
o.combine(key_discovery.as_ref(), input.as_bytes(), &mut stdout)
}
}
ShardSubcommands::Combine {
shard,
key_discovery,
} => {
let shard_content = std::fs::read_to_string(shard)?;
if shard_content.contains("BEGIN PGP MESSAGE") {
let _ = format.insert(Format::OpenPGP(OpenPGP));
}
match format {
Some(Format::OpenPGP(o)) => o.combine(
key_discovery.as_ref(),
shard_content.as_bytes(),
&mut stdout,
),
Some(Format::P256(_p)) => {
todo!()
}
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
},
}
}
}
}
}