keyfork: add `bottoms-up` wizard

This commit is contained in:
Ryan Heywood 2024-07-29 00:48:10 -04:00
parent 142bea3b9f
commit d5c3587343
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
2 changed files with 59 additions and 2 deletions

View File

@ -83,7 +83,7 @@ fn validate(
let index = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?; let index = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?;
let path = DerivationPath::from_str(path)?; let path = DerivationPath::from_str(path)?;
assert_eq!(2, path.len(), "Expected path of m/{index}/account_id'"); assert!(path.len() >= 2, "Expected path of at least m/{index}/account_id'");
let given_index = path.iter().next().expect("checked .len() above"); let given_index = path.iter().next().expect("checked .len() above");
assert_eq!( assert_eq!(

View File

@ -1,6 +1,11 @@
use super::Keyfork; use super::Keyfork;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::{collections::HashSet, fs::File, io::IsTerminal, path::PathBuf}; use std::{
collections::HashSet,
fs::File,
io::IsTerminal,
path::{Path, PathBuf},
};
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};
@ -185,6 +190,35 @@ fn generate_shard_secret(
Ok(()) Ok(())
} }
fn bottoms_up(key_discovery: &Path, threshold: u8, output: &Option<PathBuf>) -> Result<()> {
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
let stdout = std::io::stdout();
if output.is_none() {
assert!(
!stdout.is_terminal(),
"not printing shard to terminal, redirect output"
);
}
let opgp = OpenPGP::<DefaultTerminal>::new();
let certs = OpenPGP::<DefaultTerminal>::discover_certs(key_discovery)?;
if let Some(output_file) = output {
let output = File::create(output_file)?;
opgp.shard_and_encrypt(threshold, certs.len() as u8, &seed, &certs[..], output)?;
} else {
opgp.shard_and_encrypt(
threshold,
certs.len() as u8,
&seed,
&certs[..],
std::io::stdout(),
)?;
}
Ok(())
}
#[derive(Subcommand, Clone, Debug)] #[derive(Subcommand, Clone, Debug)]
pub enum WizardSubcommands { pub enum WizardSubcommands {
/// Create a 256 bit secret and shard the secret to smart cards. /// Create a 256 bit secret and shard the secret to smart cards.
@ -209,6 +243,24 @@ pub enum WizardSubcommands {
#[arg(long)] #[arg(long)]
output: Option<PathBuf>, output: Option<PathBuf>,
}, },
/// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates,
/// deriving the default OpenPGP certificate for the secret.
///
/// This command was purpose-built for DEFCON and is not intended to be used normally, as it
/// implies keys used for sharding have been generated by a custom source.
BottomsUp {
/// The location of OpenPGP certificates to use when sharding.
key_discovery: PathBuf,
/// The minimum amount of keys required to decrypt the secret.
#[arg(long)]
threshold: u8,
/// The file to write the generated shard file to.
#[arg(long)]
output: Option<PathBuf>,
},
} }
impl WizardSubcommands { impl WizardSubcommands {
@ -220,6 +272,11 @@ impl WizardSubcommands {
keys_per_shard, keys_per_shard,
output, output,
} => generate_shard_secret(*threshold, *max, *keys_per_shard, output), } => generate_shard_secret(*threshold, *max, *keys_per_shard, output),
WizardSubcommands::BottomsUp {
key_discovery,
threshold,
output,
} => bottoms_up(key_discovery, *threshold, output),
} }
} }
} }