Compare commits
No commits in common. "c868afedbf6ced096b36f026310566d1602a4cb8" and "142bea3b9fa30ecdab0c1fe6584a117fbd1ad269" have entirely different histories.
c868afedbf
...
142bea3b9f
|
@ -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!(path.len() >= 2, "Expected path of at least m/{index}/account_id'");
|
assert_eq!(2, path.len(), "Expected path of 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!(
|
||||||
|
|
|
@ -1,26 +1,20 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use std::{
|
use std::{collections::HashSet, fs::File, io::IsTerminal, path::PathBuf};
|
||||||
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};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
openpgp::{self, packet::UserID, types::KeyFlags, Cert, serialize::Marshal, armor::{Writer, Kind}},
|
openpgp::{self, packet::UserID, types::KeyFlags, Cert},
|
||||||
XPrv,
|
XPrv,
|
||||||
};
|
};
|
||||||
use keyfork_derive_util::{DerivationIndex, DerivationPath, VariableLengthSeed};
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
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};
|
||||||
|
|
||||||
|
@ -30,8 +24,6 @@ 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(),
|
||||||
|
@ -193,46 +185,6 @@ fn generate_shard_secret(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bottoms_up(key_discovery: &Path, threshold: u8, output_shardfile: &Path, output_cert: &Path, user_id: &str,) -> Result<()> {
|
|
||||||
let entropy = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
|
||||||
let mnemonic = Mnemonic::from_nonstandard_bytes(entropy);
|
|
||||||
// TODO: make this return const size, since is hash based
|
|
||||||
let seed = mnemonic.generate_seed(None);
|
|
||||||
|
|
||||||
// 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 certs = OpenPGP::<DefaultTerminal>::discover_certs(key_discovery)?;
|
|
||||||
|
|
||||||
let shardfile = File::create(output_shardfile)?;
|
|
||||||
opgp.shard_and_encrypt(threshold, certs.len() as u8, &entropy, &certs[..], shardfile)?;
|
|
||||||
|
|
||||||
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.
|
||||||
|
@ -257,32 +209,6 @@ 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_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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WizardSubcommands {
|
impl WizardSubcommands {
|
||||||
|
@ -294,13 +220,6 @@ 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_shardfile,
|
|
||||||
output_cert,
|
|
||||||
user_id,
|
|
||||||
} => bottoms_up(key_discovery, *threshold, output_shardfile, output_cert, user_id),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# pipe `cargo metadata --format-version=1` into this
|
|
||||||
|
|
||||||
priority_queue = []
|
priority_queue = []
|
||||||
packages = json.load(sys.stdin)["packages"]
|
packages = json.load(sys.stdin)["packages"]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue