keyfork wizard generate-shard-secret: write to given output file

This commit is contained in:
Ryan Heywood 2024-01-14 23:52:20 -05:00
parent 4782a15af9
commit 33aeae1177
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
3 changed files with 23 additions and 10 deletions

View File

@ -145,7 +145,7 @@ On an airgapped system, run the following command to generate a file containing
encrypted shards of a generated seed:
```sh
keyfork wizard generate-shard-secret --threshold $N --max $M --keys-per-shard $I > shards.pgp
keyfork wizard generate-shard-secret --threshold $N --max $M --keys-per-shard $I --output shards.pgp
```
Once generated, the shards file can be safely stored in any location, as the

View File

@ -20,6 +20,7 @@ It is recommended to use smart cards dedicated to the purpose of seed recovery.
secret.
* `max`: The maximum amount of shardholders.
* `keys-per-shard`: The amount of smart cardsz to provision per-shardholder.
* `output`: The file to write the generated shard to.
### Prompts

View File

@ -1,6 +1,6 @@
use super::Keyfork;
use clap::{Parser, Subcommand};
use std::{collections::HashSet, io::IsTerminal};
use std::{collections::HashSet, io::IsTerminal, path::PathBuf, fs::OpenOptions};
use card_backend_pcsc::PcscBackend;
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
@ -12,7 +12,7 @@ use keyfork_derive_util::{
};
use keyfork_prompt::{
validators::{PinValidator, Validator},
Message, Terminal, PromptHandler,
Message, PromptHandler, Terminal,
};
#[derive(thiserror::Error, Debug)]
@ -100,16 +100,18 @@ fn factory_reset_current_card(
Ok(())
}
fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8) -> Result<()> {
fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8, output_file: &Option<PathBuf>) -> Result<()> {
let seed = keyfork_entropy::generate_entropy_of_size(256 / 8)?;
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
let mut certs = vec![];
let mut seen_cards: HashSet<String> = HashSet::new();
let stdout = std::io::stdout();
assert!(
!stdout.is_terminal(),
"not printing shard to terminal, redirect output"
);
if output_file.is_none() {
assert!(
!stdout.is_terminal(),
"not printing shard to terminal, redirect output"
);
}
let user_pin_validator = PinValidator {
min_length: Some(6),
@ -145,7 +147,12 @@ fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8) -> Result<(
certs.push(cert);
}
keyfork_shard::openpgp::split(threshold, certs, &seed, std::io::stdout())?;
if let Some(output_file) = output_file {
let output = OpenOptions::new().write(true).open(output_file)?;
keyfork_shard::openpgp::split(threshold, certs, &seed, output)?;
} else {
keyfork_shard::openpgp::split(threshold, certs, &seed, std::io::stdout())?;
}
Ok(())
}
@ -168,6 +175,10 @@ pub enum WizardSubcommands {
/// The amount of smart cards to provision per-shard.
#[arg(long, default_value = "1")]
keys_per_shard: u8,
/// The file to write the generated shard file to.
#[arg(long)]
output: Option<PathBuf>,
},
}
@ -178,7 +189,8 @@ impl WizardSubcommands {
threshold,
max,
keys_per_shard,
} => generate_shard_secret(*threshold, *max, *keys_per_shard),
output,
} => generate_shard_secret(*threshold, *max, *keys_per_shard, output),
}
}
}