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: encrypted shards of a generated seed:
```sh ```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 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. secret.
* `max`: The maximum amount of shardholders. * `max`: The maximum amount of shardholders.
* `keys-per-shard`: The amount of smart cardsz to provision per-shardholder. * `keys-per-shard`: The amount of smart cardsz to provision per-shardholder.
* `output`: The file to write the generated shard to.
### Prompts ### Prompts

View File

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