Compare commits
No commits in common. "8756c3d233ebe9ef4349eec1e690dd5240e15bda" and "19fbb51d128d307a7ea005cf820e9db337a9d0d3" have entirely different histories.
8756c3d233
...
19fbb51d12
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{stdin, stdout, Read, Write},
|
io::{stdin, stdout, Read, Write},
|
||||||
rc::Rc,
|
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use aes_gcm::{
|
use aes_gcm::{
|
||||||
|
@ -140,7 +140,7 @@ pub trait Format {
|
||||||
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
||||||
) -> Result<(Vec<Share>, u8), Self::Error>;
|
) -> Result<(Vec<Share>, u8), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt a single share and associated metadata from a readable input. For the current
|
/// Decrypt a single share and associated metadata from a reaable input. For the current
|
||||||
/// version of Keyfork, the only associated metadata is a u8 representing the threshold to
|
/// version of Keyfork, the only associated metadata is a u8 representing the threshold to
|
||||||
/// combine secrets.
|
/// combine secrets.
|
||||||
///
|
///
|
||||||
|
@ -154,40 +154,6 @@ pub trait Format {
|
||||||
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
||||||
) -> Result<(Share, u8), Self::Error>;
|
) -> Result<(Share, u8), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt the public keys and metadata from encrypted data.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// The method may return an error if hte shardfile couldn't be read from or if the metadata
|
|
||||||
/// could neither be encrypted nor parsed.
|
|
||||||
fn decrypt_metadata(
|
|
||||||
&self,
|
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
|
||||||
encrypted_data: &[Self::EncryptedData],
|
|
||||||
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
|
||||||
) -> std::result::Result<(u8, Vec<Self::PublicKey>), Self::Error>;
|
|
||||||
|
|
||||||
/// Decrypt the public keys and metadata from a Shardfile.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// The method may return an error if hte shardfile couldn't be read from or if the metadata
|
|
||||||
/// could neither be encrypted nor parsed.
|
|
||||||
fn decrypt_metadata_from_file(
|
|
||||||
&self,
|
|
||||||
private_key_discovery: Option<impl KeyDiscovery<Self>>,
|
|
||||||
reader: impl Read + Send + Sync,
|
|
||||||
prompt: Box<dyn PromptHandler>,
|
|
||||||
) -> Result<(u8, Vec<Self::PublicKey>), Self::Error> {
|
|
||||||
let private_keys = private_key_discovery
|
|
||||||
.map(|p| p.discover_private_keys())
|
|
||||||
.transpose()?;
|
|
||||||
let encrypted_messages = self.parse_shard_file(reader)?;
|
|
||||||
self.decrypt_metadata(
|
|
||||||
private_keys,
|
|
||||||
&encrypted_messages,
|
|
||||||
Rc::new(Mutex::new(prompt)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decrypt multiple shares and combine them to recreate a secret.
|
/// Decrypt multiple shares and combine them to recreate a secret.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -549,26 +549,6 @@ impl Format for OpenPGP {
|
||||||
|
|
||||||
panic!("unable to decrypt shard");
|
panic!("unable to decrypt shard");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_metadata(
|
|
||||||
&self,
|
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
|
||||||
encrypted_data: &[Self::EncryptedData],
|
|
||||||
prompt: Rc<Mutex<Box<dyn PromptHandler>>>,
|
|
||||||
) -> std::result::Result<(u8, Vec<Self::PublicKey>), Self::Error> {
|
|
||||||
let policy = NullPolicy::new();
|
|
||||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default(), prompt.clone())?;
|
|
||||||
let mut manager = SmartcardManager::new(prompt.clone())?;
|
|
||||||
let mut encrypted_messages = encrypted_data.iter();
|
|
||||||
|
|
||||||
let metadata = encrypted_messages
|
|
||||||
.next()
|
|
||||||
.expect(bug!(METADATA_MESSAGE_MISSING));
|
|
||||||
let metadata_content = decrypt_metadata(metadata, &policy, &mut keyring, &mut manager)?;
|
|
||||||
|
|
||||||
let (threshold, _root_cert, certs) = decode_metadata_v1(&metadata_content)?;
|
|
||||||
Ok((threshold, certs))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyDiscovery<OpenPGP> for &Path {
|
impl KeyDiscovery<OpenPGP> for &Path {
|
||||||
|
|
|
@ -50,14 +50,6 @@ trait ShardExec {
|
||||||
key_discovery: Option<&Path>,
|
key_discovery: Option<&Path>,
|
||||||
input: impl Read + Send + Sync,
|
input: impl Read + Send + Sync,
|
||||||
) -> Result<(), Box<dyn std::error::Error>>;
|
) -> Result<(), Box<dyn std::error::Error>>;
|
||||||
|
|
||||||
fn metadata(
|
|
||||||
&self,
|
|
||||||
key_discovery: Option<&Path>,
|
|
||||||
input: impl Read + Send + Sync,
|
|
||||||
output_pubkeys: &mut impl Write,
|
|
||||||
output: &mut impl Write,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -100,31 +92,6 @@ impl ShardExec for OpenPGP {
|
||||||
openpgp.decrypt_one_shard_for_transport(key_discovery, input, prompt)?;
|
openpgp.decrypt_one_shard_for_transport(key_discovery, input, prompt)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metadata(
|
|
||||||
&self,
|
|
||||||
key_discovery: Option<&Path>,
|
|
||||||
input: impl Read + Send + Sync,
|
|
||||||
output_pubkeys: &mut impl Write,
|
|
||||||
output: &mut impl Write,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
use keyfork_derive_openpgp::openpgp::{
|
|
||||||
serialize::Marshal,
|
|
||||||
armor::{Writer, Kind},
|
|
||||||
};
|
|
||||||
|
|
||||||
let openpgp = keyfork_shard::openpgp::OpenPGP;
|
|
||||||
let prompt = default_handler()?;
|
|
||||||
|
|
||||||
let (threshold, certs) = openpgp.decrypt_metadata_from_file(key_discovery, input, prompt)?;
|
|
||||||
let mut writer = Writer::new(output_pubkeys, Kind::PublicKey)?;
|
|
||||||
for cert in certs {
|
|
||||||
cert.serialize(&mut writer)?;
|
|
||||||
}
|
|
||||||
writer.finalize()?;
|
|
||||||
writeln!(output, "Threshold: {threshold}")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -174,20 +141,6 @@ pub enum ShardSubcommands {
|
||||||
/// The path to discover private keys from.
|
/// The path to discover private keys from.
|
||||||
key_discovery: Option<PathBuf>,
|
key_discovery: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Decrypt metadata for a shardfile, including the threshold and the public keys. Public keys
|
|
||||||
/// are serialized to a file.
|
|
||||||
Metadata {
|
|
||||||
/// The path to load the Shardfile from.
|
|
||||||
shardfile: PathBuf,
|
|
||||||
|
|
||||||
/// The path to write public keys to.
|
|
||||||
#[arg(long)]
|
|
||||||
output_pubkeys: PathBuf,
|
|
||||||
|
|
||||||
/// The path to discover private keys from.
|
|
||||||
key_discovery: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShardSubcommands {
|
impl ShardSubcommands {
|
||||||
|
@ -256,27 +209,6 @@ impl ShardSubcommands {
|
||||||
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
|
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ShardSubcommands::Metadata { shardfile, output_pubkeys, key_discovery } => {
|
|
||||||
let shard_content = std::fs::read_to_string(shardfile)?;
|
|
||||||
if shard_content.contains("BEGIN PGP MESSAGE") {
|
|
||||||
let _ = format.insert(Format::OpenPGP(OpenPGP));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut output_pubkeys_file = std::fs::File::create(output_pubkeys)?;
|
|
||||||
|
|
||||||
match format {
|
|
||||||
Some(Format::OpenPGP(o)) => o.metadata(
|
|
||||||
key_discovery.as_deref(),
|
|
||||||
shard_content.as_bytes(),
|
|
||||||
&mut output_pubkeys_file,
|
|
||||||
&mut stdout,
|
|
||||||
),
|
|
||||||
Some(Format::P256(_p)) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,9 @@ use keyfork_derive_openpgp::{
|
||||||
openpgp::{
|
openpgp::{
|
||||||
self,
|
self,
|
||||||
armor::{Kind, Writer},
|
armor::{Kind, Writer},
|
||||||
packet::{UserID, signature::SignatureBuilder},
|
packet::UserID,
|
||||||
serialize::Marshal,
|
serialize::Marshal,
|
||||||
types::{SignatureType, KeyFlags},
|
types::KeyFlags,
|
||||||
policy::StandardPolicy,
|
|
||||||
Cert,
|
Cert,
|
||||||
},
|
},
|
||||||
XPrv,
|
XPrv,
|
||||||
|
@ -21,7 +20,8 @@ use keyfork_derive_path_data::paths;
|
||||||
use keyfork_derive_util::DerivationIndex;
|
use keyfork_derive_util::DerivationIndex;
|
||||||
use keyfork_mnemonic::Mnemonic;
|
use keyfork_mnemonic::Mnemonic;
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
default_handler, prompt_validated_passphrase,
|
default_handler,
|
||||||
|
prompt_validated_passphrase,
|
||||||
validators::{SecurePinValidator, Validator},
|
validators::{SecurePinValidator, Validator},
|
||||||
Message,
|
Message,
|
||||||
};
|
};
|
||||||
|
@ -133,10 +133,6 @@ pub struct GenerateShardSecret {
|
||||||
/// 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: Option<PathBuf>,
|
||||||
|
|
||||||
/// The file to write generated certificates to.
|
|
||||||
#[arg(long)]
|
|
||||||
cert_output: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates,
|
/// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates,
|
||||||
|
@ -176,41 +172,6 @@ impl WizardSubcommands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cross_sign_certs(certs: &mut [Cert]) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let policy = StandardPolicy::new();
|
|
||||||
for signing_cert in certs.to_vec() {
|
|
||||||
let mut certify_key = signing_cert
|
|
||||||
.with_policy(&policy, None)?
|
|
||||||
.keys()
|
|
||||||
.unencrypted_secret()
|
|
||||||
.for_certification()
|
|
||||||
.next()
|
|
||||||
.expect("certify key unusable/not found")
|
|
||||||
.key()
|
|
||||||
.clone()
|
|
||||||
.into_keypair()?;
|
|
||||||
for signable_cert in certs.iter_mut() {
|
|
||||||
let sb = SignatureBuilder::new(SignatureType::GenericCertification);
|
|
||||||
let userid = signable_cert
|
|
||||||
.userids()
|
|
||||||
.next()
|
|
||||||
.expect("a signable user ID is necessary to create web of trust");
|
|
||||||
let signature = sb.sign_userid_binding(
|
|
||||||
&mut certify_key,
|
|
||||||
signable_cert.primary_key().key(),
|
|
||||||
&*userid,
|
|
||||||
)?;
|
|
||||||
let changed;
|
|
||||||
(*signable_cert, changed) = signable_cert.clone().insert_packets2(signature)?;
|
|
||||||
assert!(
|
|
||||||
changed,
|
|
||||||
"OpenPGP certificate was unchanged after inserting packets"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenerateShardSecret {
|
impl GenerateShardSecret {
|
||||||
fn handle(&self) -> Result<()> {
|
fn handle(&self) -> Result<()> {
|
||||||
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
|
||||||
|
@ -276,8 +237,6 @@ impl GenerateShardSecret {
|
||||||
certs.push(cert);
|
certs.push(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
cross_sign_certs(&mut certs)?;
|
|
||||||
|
|
||||||
let opgp = OpenPGP;
|
let opgp = OpenPGP;
|
||||||
|
|
||||||
if let Some(output_file) = self.output.as_ref() {
|
if let Some(output_file) = self.output.as_ref() {
|
||||||
|
@ -292,16 +251,6 @@ impl GenerateShardSecret {
|
||||||
std::io::stdout(),
|
std::io::stdout(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(cert_output_file) = self.cert_output.as_ref() {
|
|
||||||
let output = File::create(cert_output_file)?;
|
|
||||||
let mut writer = Writer::new(output, Kind::PublicKey)?;
|
|
||||||
for cert in certs {
|
|
||||||
cert.serialize(&mut writer)?;
|
|
||||||
}
|
|
||||||
writer.finalize()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue