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

View File

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

View File

@ -68,8 +68,9 @@ Combine `threshold` shares into a secret.
### Arguments ### 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. * `key_discovery`: Either a file or a directory containing public keys.
If a file, load all private keys from a file. If a file, load all private keys from a file.
If a directory, for every file in the directory (non-recursively), load 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 If the amount of keys found is less than `threshold`, it is up to the format
to determine how to discover the keys. 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 ### Output
Hex-encoded secret. Hex-encoded secret.
@ -106,8 +102,9 @@ by a remote recovery operator.
### Arguments ### 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. * `key_discovery`: Either a file or a directory containing public keys.
If a file, load all private keys from a file. If a file, load all private keys from a file.
If a directory, for every file in the directory (non-recursively), load 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 If the amount of keys found is less than `threshold`, it is up to the format
to determine how to discover the keys. 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 ### Prompts
The command will prompt for 33 words from the remote shard recovery operator, The command will prompt for 33 words from the remote shard recovery operator,
@ -131,10 +123,10 @@ operator.
```sh ```sh
# Transport using a smart card # Transport using a smart card
keyfork shard transport < shard.pgp keyfork shard transport shard.pgp
# Transport using on-disk private keys # 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 [`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 /// 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. /// prompts. The mnemonics can be sent over insecure channels.
Transport { Transport {
/// The path to load the shard from.
shard: PathBuf,
/// The path to discover private keys from. /// The path to discover private keys from.
key_discovery: Option<PathBuf>, 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 /// hardware metadata discovery, any hardware key used to split may be used to decrypt metadata
/// used to combine. /// used to combine.
Combine { Combine {
/// The path to load the shards from.
shard: PathBuf,
/// The path to discover private keys from. /// The path to discover private keys from.
key_discovery: Option<PathBuf>, key_discovery: Option<PathBuf>,
}, },
@ -186,21 +192,16 @@ impl ShardSubcommands {
shard: &Shard, shard: &Shard,
_keyfork: &Keyfork, _keyfork: &Keyfork,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let mut stdin = stdin(); let 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(); 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,
max, max,
key_discovery, key_discovery,
} => { } => {
let input = std::io::read_to_string(stdin)?;
assert!(threshold <= max, "threshold {threshold} <= max {max}"); assert!(threshold <= max, "threshold {threshold} <= max {max}");
let secret = smex::decode(input.trim())?; let secret = smex::decode(input.trim())?;
match format { match format {
@ -213,20 +214,44 @@ impl ShardSubcommands {
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"), None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
} }
} }
ShardSubcommands::Transport { key_discovery } => match format { ShardSubcommands::Transport {
Some(Format::OpenPGP(o)) => o.decrypt(key_discovery.as_ref(), input.as_bytes()), 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!(), Some(Format::P256(_p)) => todo!(),
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"), 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)) => { Some(Format::P256(_p)) => {
todo!() todo!()
} }
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"), None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
}, }
}
} }
} }
} }