Compare commits

..

2 Commits

1 changed files with 50 additions and 26 deletions

View File

@ -11,15 +11,16 @@ use card_backend_pcsc::PcscBackend;
use openpgp_card_sequoia::{state::Open, types::KeyType, Card}; use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
use keyfork_derive_openpgp::{ use keyfork_derive_openpgp::{
openpgp::{self, packet::UserID, types::KeyFlags, Cert}, openpgp::{self, packet::UserID, types::KeyFlags, Cert, serialize::Marshal, armor::{Writer, Kind}},
XPrv, XPrv,
}; };
use keyfork_derive_util::{DerivationIndex, DerivationPath}; use keyfork_derive_util::{DerivationIndex, DerivationPath, VariableLengthSeed};
use keyfork_prompt::{ use keyfork_prompt::{
default_terminal, default_terminal,
validators::{SecurePinValidator, Validator}, validators::{SecurePinValidator, Validator},
DefaultTerminal, Message, PromptHandler, DefaultTerminal, Message, PromptHandler,
}; };
use keyfork_mnemonic_util::Mnemonic;
use keyfork_shard::{openpgp::OpenPGP, Format}; use keyfork_shard::{openpgp::OpenPGP, Format};
@ -29,6 +30,8 @@ pub struct PinLength(usize);
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>;
// TODO: refactor to use mnemonic derived seed instead of 256 bit entropy to allow for possible
// recovery in the future.
fn derive_key(seed: [u8; 32], index: u8) -> Result<Cert> { fn derive_key(seed: [u8; 32], index: u8) -> Result<Cert> {
let subkeys = vec![ let subkeys = vec![
KeyFlags::empty().set_certification(), KeyFlags::empty().set_certification(),
@ -190,31 +193,42 @@ fn generate_shard_secret(
Ok(()) Ok(())
} }
fn bottoms_up(key_discovery: &Path, threshold: u8, output: &Option<PathBuf>) -> Result<()> { fn bottoms_up(key_discovery: &Path, threshold: u8, output_shardfile: &Path, output_cert: &Path, user_id: &str,) -> Result<()> {
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?; let entropy = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
let stdout = std::io::stdout(); let mnemonic = Mnemonic::from_nonstandard_bytes(entropy);
if output.is_none() { // TODO: make this return const size, since is hash based
assert!( let seed = mnemonic.generate_seed(None);
!stdout.is_terminal(),
"not printing shard to terminal, redirect output" // TODO: should this allow for customizing the account index from 0? Potential for key reuse
); // errors.
} let path = DerivationPath::default()
.chain_push(DerivationIndex::new(u32::from_be_bytes(*b"\x00pgp"), true)?)
.chain_push(DerivationIndex::new(u32::from_be_bytes(*b"\x00\x00dr"), true)?)
.chain_push(DerivationIndex::new(0, true)?);
let subkeys = [
KeyFlags::empty().set_certification(),
KeyFlags::empty().set_signing(),
KeyFlags::empty()
.set_transport_encryption()
.set_storage_encryption(),
KeyFlags::empty().set_authentication(),
];
let xprv = XPrv::new(VariableLengthSeed::new(&seed))
.expect("could not construct master key from seed")
.derive_path(&path)?;
let userid = UserID::from(user_id);
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
let certfile = File::create(output_cert)?;
let mut w = Writer::new(certfile, Kind::PublicKey)?;
cert.serialize(&mut w)?;
w.finalize()?;
let opgp = OpenPGP::<DefaultTerminal>::new(); let opgp = OpenPGP::<DefaultTerminal>::new();
let certs = OpenPGP::<DefaultTerminal>::discover_certs(key_discovery)?; let certs = OpenPGP::<DefaultTerminal>::discover_certs(key_discovery)?;
if let Some(output_file) = output { let shardfile = File::create(output_shardfile)?;
let output = File::create(output_file)?; opgp.shard_and_encrypt(threshold, certs.len() as u8, &entropy, &certs[..], shardfile)?;
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(()) Ok(())
} }
@ -259,7 +273,15 @@ pub enum WizardSubcommands {
/// The file to write the generated shard file to. /// The file to write the generated shard file to.
#[arg(long)] #[arg(long)]
output: Option<PathBuf>, output_shardfile: PathBuf,
/// The file to write the generated OpenPGP certificate to.
#[arg(long)]
output_cert: PathBuf,
/// The User ID for the generated OpenPGP certificate.
#[arg(long, default_value = "Disaster Recovery")]
user_id: String,
}, },
} }
@ -275,8 +297,10 @@ impl WizardSubcommands {
WizardSubcommands::BottomsUp { WizardSubcommands::BottomsUp {
key_discovery, key_discovery,
threshold, threshold,
output, output_shardfile,
} => bottoms_up(key_discovery, *threshold, output), output_cert,
user_id,
} => bottoms_up(key_discovery, *threshold, output_shardfile, output_cert, user_id),
} }
} }
} }