keyfork-shard: first pass of reusable prompthandler
This commit is contained in:
parent
b15d088905
commit
354eae5a6a
|
@ -7,6 +7,7 @@ use std::{
|
||||||
process::ExitCode,
|
process::ExitCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use keyfork_prompt::{DefaultTerminal, default_terminal};
|
||||||
use keyfork_shard::{openpgp::OpenPGP, Format};
|
use keyfork_shard::{openpgp::OpenPGP, Format};
|
||||||
|
|
||||||
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>;
|
||||||
|
@ -31,8 +32,10 @@ fn run() -> Result<()> {
|
||||||
_ => panic!("Usage: {program_name} <shard> [key_discovery]"),
|
_ => panic!("Usage: {program_name} <shard> [key_discovery]"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let openpgp = OpenPGP;
|
let openpgp = OpenPGP::<DefaultTerminal>::new();
|
||||||
let bytes = openpgp.decrypt_all_shards_to_secret(key_discovery.as_deref(), messages_file)?;
|
let prompt_handler = default_terminal()?;
|
||||||
|
|
||||||
|
let bytes = openpgp.decrypt_all_shards_to_secret(key_discovery.as_deref(), messages_file, prompt_handler)?;
|
||||||
print!("{}", smex::encode(bytes));
|
print!("{}", smex::encode(bytes));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
||||||
process::ExitCode,
|
process::ExitCode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use keyfork_prompt::{DefaultTerminal, default_terminal};
|
||||||
use keyfork_shard::{Format, openpgp::OpenPGP};
|
use keyfork_shard::{Format, openpgp::OpenPGP};
|
||||||
|
|
||||||
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>;
|
||||||
|
@ -31,9 +32,10 @@ fn run() -> Result<()> {
|
||||||
_ => panic!("Usage: {program_name} <shard> [key_discovery]"),
|
_ => panic!("Usage: {program_name} <shard> [key_discovery]"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let openpgp = OpenPGP;
|
let openpgp = OpenPGP::<DefaultTerminal>::new();
|
||||||
|
let prompt_handler = default_terminal()?;
|
||||||
|
|
||||||
openpgp.decrypt_one_shard_for_transport(key_discovery.as_deref(), messages_file)?;
|
openpgp.decrypt_one_shard_for_transport(key_discovery.as_deref(), messages_file, prompt_handler)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::{env, path::PathBuf, process::ExitCode, str::FromStr};
|
use std::{env, path::PathBuf, process::ExitCode, str::FromStr};
|
||||||
|
|
||||||
|
use keyfork_prompt::terminal::DefaultTerminal;
|
||||||
use keyfork_shard::{Format, openpgp::OpenPGP};
|
use keyfork_shard::{Format, openpgp::OpenPGP};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -50,7 +51,7 @@ fn run() -> Result<()> {
|
||||||
smex::decode(line?)?
|
smex::decode(line?)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let openpgp = OpenPGP;
|
let openpgp = OpenPGP::<DefaultTerminal>::new();
|
||||||
|
|
||||||
openpgp.shard_and_encrypt(threshold, max, &input, key_discovery.as_path(), std::io::stdout())?;
|
openpgp.shard_and_encrypt(threshold, max, &input, key_discovery.as_path(), std::io::stdout())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
use std::io::{stdin, stdout, Read, Write};
|
use std::{
|
||||||
|
io::{stdin, stdout, Read, Write},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use aes_gcm::{
|
use aes_gcm::{
|
||||||
aead::{consts::U12, Aead, AeadCore, OsRng},
|
aead::{consts::U12, Aead, AeadCore, OsRng},
|
||||||
|
@ -122,6 +125,7 @@ pub trait Format {
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
encrypted_messages: &[Self::EncryptedData],
|
encrypted_messages: &[Self::EncryptedData],
|
||||||
|
prompt: Arc<Mutex<impl PromptHandler>>,
|
||||||
) -> Result<(Vec<Share>, u8), Self::Error>;
|
) -> Result<(Vec<Share>, u8), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt a single share and associated metadata from a reaable input. For the current
|
/// Decrypt a single share and associated metadata from a reaable input. For the current
|
||||||
|
@ -135,6 +139,7 @@ pub trait Format {
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
encrypted_data: &[Self::EncryptedData],
|
encrypted_data: &[Self::EncryptedData],
|
||||||
|
prompt: Arc<Mutex<impl PromptHandler>>,
|
||||||
) -> Result<(Share, u8), Self::Error>;
|
) -> Result<(Share, u8), Self::Error>;
|
||||||
|
|
||||||
/// Decrypt multiple shares and combine them to recreate a secret.
|
/// Decrypt multiple shares and combine them to recreate a secret.
|
||||||
|
@ -146,12 +151,17 @@ pub trait Format {
|
||||||
&self,
|
&self,
|
||||||
private_key_discovery: Option<impl KeyDiscovery<Self>>,
|
private_key_discovery: Option<impl KeyDiscovery<Self>>,
|
||||||
reader: impl Read + Send + Sync,
|
reader: impl Read + Send + Sync,
|
||||||
|
prompt: impl PromptHandler,
|
||||||
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||||
let private_keys = private_key_discovery
|
let private_keys = private_key_discovery
|
||||||
.map(|p| p.discover_private_keys())
|
.map(|p| p.discover_private_keys())
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let encrypted_messages = self.parse_shard_file(reader)?;
|
let encrypted_messages = self.parse_shard_file(reader)?;
|
||||||
let (shares, threshold) = self.decrypt_all_shards(private_keys, &encrypted_messages)?;
|
let (shares, threshold) = self.decrypt_all_shards(
|
||||||
|
private_keys,
|
||||||
|
&encrypted_messages,
|
||||||
|
Arc::new(Mutex::new(prompt)),
|
||||||
|
)?;
|
||||||
|
|
||||||
let secret = Sharks(threshold)
|
let secret = Sharks(threshold)
|
||||||
.recover(&shares)
|
.recover(&shares)
|
||||||
|
@ -171,8 +181,9 @@ pub trait Format {
|
||||||
&self,
|
&self,
|
||||||
private_key_discovery: Option<impl KeyDiscovery<Self>>,
|
private_key_discovery: Option<impl KeyDiscovery<Self>>,
|
||||||
reader: impl Read + Send + Sync,
|
reader: impl Read + Send + Sync,
|
||||||
|
prompt: impl PromptHandler,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut pm = Terminal::new(stdin(), stdout())?;
|
let prompt = Arc::new(Mutex::new(prompt));
|
||||||
|
|
||||||
// parse input
|
// parse input
|
||||||
let private_keys = private_key_discovery
|
let private_keys = private_key_discovery
|
||||||
|
@ -187,7 +198,10 @@ pub trait Format {
|
||||||
// receive remote data via scanning QR code from camera
|
// receive remote data via scanning QR code from camera
|
||||||
#[cfg(feature = "qrcode")]
|
#[cfg(feature = "qrcode")]
|
||||||
{
|
{
|
||||||
pm.prompt_message(PromptMessage::Text(QRCODE_PROMPT.to_string()))?;
|
prompt
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.prompt_message(PromptMessage::Text(QRCODE_PROMPT.to_string()))?;
|
||||||
if let Ok(Some(hex)) =
|
if let Ok(Some(hex)) =
|
||||||
keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0)
|
keyfork_qrcode::scan_camera(std::time::Duration::from_secs(30), 0)
|
||||||
{
|
{
|
||||||
|
@ -195,7 +209,10 @@ pub trait Format {
|
||||||
nonce_data = Some(decoded_data[..12].try_into().map_err(|_| InvalidData)?);
|
nonce_data = Some(decoded_data[..12].try_into().map_err(|_| InvalidData)?);
|
||||||
pubkey_data = Some(decoded_data[12..].try_into().map_err(|_| InvalidData)?)
|
pubkey_data = Some(decoded_data[12..].try_into().map_err(|_| InvalidData)?)
|
||||||
} else {
|
} else {
|
||||||
pm.prompt_message(PromptMessage::Text(QRCODE_ERROR.to_string()))?;
|
prompt
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.prompt_message(PromptMessage::Text(QRCODE_ERROR.to_string()))?;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +223,9 @@ pub trait Format {
|
||||||
let validator = MnemonicSetValidator {
|
let validator = MnemonicSetValidator {
|
||||||
word_lengths: [9, 24],
|
word_lengths: [9, 24],
|
||||||
};
|
};
|
||||||
let [nonce_mnemonic, pubkey_mnemonic] = pm
|
let [nonce_mnemonic, pubkey_mnemonic] = prompt
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.prompt_validated_wordlist::<English, _>(
|
.prompt_validated_wordlist::<English, _>(
|
||||||
QRCODE_COULDNT_READ,
|
QRCODE_COULDNT_READ,
|
||||||
3,
|
3,
|
||||||
|
@ -237,7 +256,8 @@ pub trait Format {
|
||||||
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
let shared_key = Aes256Gcm::new_from_slice(&hkdf_output)?;
|
||||||
|
|
||||||
// decrypt a single shard and create the payload
|
// decrypt a single shard and create the payload
|
||||||
let (share, threshold) = self.decrypt_one_shard(private_keys, &encrypted_messages)?;
|
let (share, threshold) =
|
||||||
|
self.decrypt_one_shard(private_keys, &encrypted_messages, prompt.clone())?;
|
||||||
let mut payload = Vec::from(&share);
|
let mut payload = Vec::from(&share);
|
||||||
payload.insert(0, HUNK_VERSION);
|
payload.insert(0, HUNK_VERSION);
|
||||||
payload.insert(1, threshold);
|
payload.insert(1, threshold);
|
||||||
|
@ -285,7 +305,7 @@ pub trait Format {
|
||||||
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
let mut qrcode_data = our_pubkey_mnemonic.to_bytes();
|
||||||
qrcode_data.extend(payload_mnemonic.as_bytes());
|
qrcode_data.extend(payload_mnemonic.as_bytes());
|
||||||
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
if let Ok(qrcode) = qrencode(&smex::encode(&qrcode_data), ErrorCorrection::Highest) {
|
||||||
pm.prompt_message(PromptMessage::Text(
|
prompt.lock().unwrap().prompt_message(PromptMessage::Text(
|
||||||
concat!(
|
concat!(
|
||||||
"A QR code will be displayed after this prompt. ",
|
"A QR code will be displayed after this prompt. ",
|
||||||
"Send the QR code back to the operator combining the shards. ",
|
"Send the QR code back to the operator combining the shards. ",
|
||||||
|
@ -293,11 +313,17 @@ pub trait Format {
|
||||||
)
|
)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
))?;
|
))?;
|
||||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
prompt
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.prompt_message(PromptMessage::Data(qrcode))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.prompt_message(PromptMessage::Text(format!(
|
prompt
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.prompt_message(PromptMessage::Text(format!(
|
||||||
"Upon request, these words should be sent: {our_pubkey_mnemonic} {payload_mnemonic}"
|
"Upon request, these words should be sent: {our_pubkey_mnemonic} {payload_mnemonic}"
|
||||||
)))?;
|
)))?;
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,15 @@ use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
path::Path,
|
path::Path,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
derive_util::{DerivationPath, VariableLengthSeed},
|
derive_util::{DerivationPath, VariableLengthSeed},
|
||||||
XPrv,
|
XPrv,
|
||||||
};
|
};
|
||||||
|
use keyfork_prompt::PromptHandler;
|
||||||
use openpgp::{
|
use openpgp::{
|
||||||
armor::{Kind, Writer},
|
armor::{Kind, Writer},
|
||||||
cert::{Cert, CertParser, ValidCert},
|
cert::{Cert, CertParser, ValidCert},
|
||||||
|
@ -176,9 +179,20 @@ impl EncryptedMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
pub struct OpenPGP;
|
pub struct OpenPGP<P: PromptHandler> {
|
||||||
|
p: PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
impl OpenPGP {
|
impl<P: PromptHandler> OpenPGP<P> {
|
||||||
|
#[allow(clippy::new_without_default, missing_docs)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
p: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: PromptHandler> OpenPGP<P> {
|
||||||
/// Read all OpenPGP certificates in a path and return a [`Vec`] of them. Certificates are read
|
/// Read all OpenPGP certificates in a path and return a [`Vec`] of them. Certificates are read
|
||||||
/// from a file, or from files one level deep in a directory.
|
/// from a file, or from files one level deep in a directory.
|
||||||
///
|
///
|
||||||
|
@ -209,7 +223,7 @@ impl OpenPGP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format for OpenPGP {
|
impl<P: PromptHandler> Format for OpenPGP<P> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type PublicKey = Cert;
|
type PublicKey = Cert;
|
||||||
type PrivateKeyData = Vec<Cert>;
|
type PrivateKeyData = Vec<Cert>;
|
||||||
|
@ -400,12 +414,14 @@ impl Format for OpenPGP {
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
encrypted_data: &[Self::EncryptedData],
|
encrypted_data: &[Self::EncryptedData],
|
||||||
|
prompt: Arc<Mutex<impl PromptHandler>>,
|
||||||
) -> std::result::Result<(Vec<Share>, u8), Self::Error> {
|
) -> std::result::Result<(Vec<Share>, u8), Self::Error> {
|
||||||
// Be as liberal as possible when decrypting.
|
// Be as liberal as possible when decrypting.
|
||||||
// We don't want to invalidate someone's keys just because the old sig expired.
|
// We don't want to invalidate someone's keys just because the old sig expired.
|
||||||
let policy = NullPolicy::new();
|
let policy = NullPolicy::new();
|
||||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default())?;
|
|
||||||
let mut manager = SmartcardManager::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 mut encrypted_messages = encrypted_data.iter();
|
||||||
|
|
||||||
|
@ -457,10 +473,12 @@ impl Format for OpenPGP {
|
||||||
&self,
|
&self,
|
||||||
private_keys: Option<Self::PrivateKeyData>,
|
private_keys: Option<Self::PrivateKeyData>,
|
||||||
encrypted_data: &[Self::EncryptedData],
|
encrypted_data: &[Self::EncryptedData],
|
||||||
|
prompt: Arc<Mutex<impl PromptHandler>>,
|
||||||
) -> std::result::Result<(Share, u8), Self::Error> {
|
) -> std::result::Result<(Share, u8), Self::Error> {
|
||||||
let policy = NullPolicy::new();
|
let policy = NullPolicy::new();
|
||||||
let mut keyring = Keyring::new(private_keys.unwrap_or_default())?;
|
|
||||||
let mut manager = SmartcardManager::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 mut encrypted_messages = encrypted_data.iter();
|
||||||
|
|
||||||
|
@ -499,22 +517,22 @@ impl Format for OpenPGP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyDiscovery<OpenPGP> for &Path {
|
impl<P: PromptHandler> KeyDiscovery<OpenPGP<P>> for &Path {
|
||||||
fn discover_public_keys(&self) -> Result<Vec<<OpenPGP as Format>::PublicKey>> {
|
fn discover_public_keys(&self) -> Result<Vec<<OpenPGP<P> as Format>::PublicKey>> {
|
||||||
OpenPGP::discover_certs(self)
|
OpenPGP::<P>::discover_certs(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discover_private_keys(&self) -> Result<<OpenPGP as Format>::PrivateKeyData> {
|
fn discover_private_keys(&self) -> Result<<OpenPGP<P> as Format>::PrivateKeyData> {
|
||||||
todo!()
|
OpenPGP::<P>::discover_certs(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyDiscovery<OpenPGP> for &[Cert] {
|
impl<P: PromptHandler> KeyDiscovery<OpenPGP<P>> for &[Cert] {
|
||||||
fn discover_public_keys(&self) -> Result<Vec<<OpenPGP as Format>::PublicKey>> {
|
fn discover_public_keys(&self) -> Result<Vec<<OpenPGP<P> as Format>::PublicKey>> {
|
||||||
Ok(self.to_vec())
|
Ok(self.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discover_private_keys(&self) -> Result<<OpenPGP as Format>::PrivateKeyData> {
|
fn discover_private_keys(&self) -> Result<<OpenPGP<P> as Format>::PrivateKeyData> {
|
||||||
Ok(self.to_vec())
|
Ok(self.to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -575,12 +593,12 @@ fn decode_metadata_v1(buf: &[u8]) -> Result<(u8, Cert, Vec<Cert>)> {
|
||||||
|
|
||||||
// NOTE: When using single-decryptor mechanism, use this method with `threshold = 1` to return a
|
// NOTE: When using single-decryptor mechanism, use this method with `threshold = 1` to return a
|
||||||
// single message.
|
// single message.
|
||||||
fn decrypt_with_manager(
|
fn decrypt_with_manager<P: PromptHandler>(
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
||||||
certs: &[Cert],
|
certs: &[Cert],
|
||||||
policy: &dyn Policy,
|
policy: &dyn Policy,
|
||||||
manager: &mut SmartcardManager,
|
manager: &mut SmartcardManager<P>,
|
||||||
) -> Result<HashMap<KeyID, Vec<u8>>> {
|
) -> Result<HashMap<KeyID, Vec<u8>>> {
|
||||||
let mut decrypted_messages = HashMap::new();
|
let mut decrypted_messages = HashMap::new();
|
||||||
|
|
||||||
|
@ -620,11 +638,11 @@ fn decrypt_with_manager(
|
||||||
|
|
||||||
// NOTE: When using single-decryptor mechanism, only a single key should be provided in Keyring to
|
// NOTE: When using single-decryptor mechanism, only a single key should be provided in Keyring to
|
||||||
// decrypt messages with.
|
// decrypt messages with.
|
||||||
fn decrypt_with_keyring(
|
fn decrypt_with_keyring<P: PromptHandler>(
|
||||||
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
messages: &mut HashMap<KeyID, EncryptedMessage>,
|
||||||
certs: &[Cert],
|
certs: &[Cert],
|
||||||
policy: &NullPolicy,
|
policy: &NullPolicy,
|
||||||
keyring: &mut Keyring,
|
keyring: &mut Keyring<P>,
|
||||||
) -> Result<HashMap<KeyID, Vec<u8>>, Error> {
|
) -> Result<HashMap<KeyID, Vec<u8>>, Error> {
|
||||||
let mut decrypted_messages = HashMap::new();
|
let mut decrypted_messages = HashMap::new();
|
||||||
|
|
||||||
|
@ -654,11 +672,11 @@ fn decrypt_with_keyring(
|
||||||
Ok(decrypted_messages)
|
Ok(decrypted_messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_metadata(
|
fn decrypt_metadata<P: PromptHandler>(
|
||||||
message: &EncryptedMessage,
|
message: &EncryptedMessage,
|
||||||
policy: &NullPolicy,
|
policy: &NullPolicy,
|
||||||
keyring: &mut Keyring,
|
keyring: &mut Keyring<P>,
|
||||||
manager: &mut SmartcardManager,
|
manager: &mut SmartcardManager<P>,
|
||||||
) -> Result<Vec<u8>> {
|
) -> Result<Vec<u8>> {
|
||||||
Ok(if keyring.is_empty() {
|
Ok(if keyring.is_empty() {
|
||||||
manager.load_any_card()?;
|
manager.load_any_card()?;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use keyfork_prompt::{Error as PromptError, DefaultTerminal, default_terminal, PromptHandler};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use keyfork_prompt::{Error as PromptError, PromptHandler};
|
||||||
|
|
||||||
use super::openpgp::{
|
use super::openpgp::{
|
||||||
self,
|
self,
|
||||||
|
@ -22,18 +24,18 @@ pub enum Error {
|
||||||
|
|
||||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
pub struct Keyring {
|
pub struct Keyring<P: PromptHandler> {
|
||||||
full_certs: Vec<Cert>,
|
full_certs: Vec<Cert>,
|
||||||
root: Option<Cert>,
|
root: Option<Cert>,
|
||||||
pm: DefaultTerminal,
|
pm: Arc<Mutex<P>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keyring {
|
impl<P: PromptHandler> Keyring<P> {
|
||||||
pub fn new(certs: impl AsRef<[Cert]>) -> Result<Self> {
|
pub fn new(certs: impl AsRef<[Cert]>, p: Arc<Mutex<P>>) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
full_certs: certs.as_ref().to_vec(),
|
full_certs: certs.as_ref().to_vec(),
|
||||||
root: Default::default(),
|
root: Default::default(),
|
||||||
pm: default_terminal()?,
|
pm: p,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +59,7 @@ impl Keyring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationHelper for &mut Keyring {
|
impl<P: PromptHandler> VerificationHelper for &mut Keyring<P> {
|
||||||
fn get_certs(&mut self, ids: &[KeyHandle]) -> openpgp::Result<Vec<Cert>> {
|
fn get_certs(&mut self, ids: &[KeyHandle]) -> openpgp::Result<Vec<Cert>> {
|
||||||
Ok(ids
|
Ok(ids
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -93,7 +95,7 @@ impl VerificationHelper for &mut Keyring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecryptionHelper for &mut Keyring {
|
impl<P: PromptHandler> DecryptionHelper for &mut Keyring<P> {
|
||||||
fn decrypt<D>(
|
fn decrypt<D>(
|
||||||
&mut self,
|
&mut self,
|
||||||
pkesks: &[PKESK],
|
pkesks: &[PKESK],
|
||||||
|
@ -137,6 +139,8 @@ impl DecryptionHelper for &mut Keyring {
|
||||||
};
|
};
|
||||||
let passphrase = self
|
let passphrase = self
|
||||||
.pm
|
.pm
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.prompt_passphrase(&message)
|
.prompt_passphrase(&message)
|
||||||
.context("Decryption passphrase")?;
|
.context("Decryption passphrase")?;
|
||||||
secret_key
|
secret_key
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
default_terminal,
|
|
||||||
validators::{PinValidator, Validator},
|
validators::{PinValidator, Validator},
|
||||||
DefaultTerminal, Error as PromptError, Message, PromptHandler,
|
Error as PromptError, Message, PromptHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::openpgp::{
|
use super::openpgp::{
|
||||||
|
@ -66,19 +68,19 @@ fn format_name(input: impl AsRef<str>) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
pub struct SmartcardManager {
|
pub struct SmartcardManager<P: PromptHandler> {
|
||||||
current_card: Option<Card<Open>>,
|
current_card: Option<Card<Open>>,
|
||||||
root: Option<Cert>,
|
root: Option<Cert>,
|
||||||
pm: DefaultTerminal,
|
pm: Arc<Mutex<P>>,
|
||||||
pin_cache: HashMap<Fingerprint, String>,
|
pin_cache: HashMap<Fingerprint, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SmartcardManager {
|
impl<P: PromptHandler> SmartcardManager<P> {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new(p: Arc<Mutex<P>>) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
current_card: None,
|
current_card: None,
|
||||||
root: None,
|
root: None,
|
||||||
pm: default_terminal()?,
|
pm: p,
|
||||||
pin_cache: Default::default(),
|
pin_cache: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -96,9 +98,13 @@ impl SmartcardManager {
|
||||||
if let Some(c) = PcscBackend::cards(None)?.next().transpose()? {
|
if let Some(c) = PcscBackend::cards(None)?.next().transpose()? {
|
||||||
break c;
|
break c;
|
||||||
}
|
}
|
||||||
self.pm.prompt_message(Message::Text(
|
self.pm
|
||||||
"No smart card was found. Please plug in a smart card and press enter".to_string(),
|
.lock()
|
||||||
))?;
|
.unwrap()
|
||||||
|
.prompt_message(Message::Text(
|
||||||
|
"No smart card was found. Please plug in a smart card and press enter"
|
||||||
|
.to_string(),
|
||||||
|
))?;
|
||||||
};
|
};
|
||||||
let mut card = Card::<Open>::new(card_backend).map_err(Error::OpenSmartCard)?;
|
let mut card = Card::<Open>::new(card_backend).map_err(Error::OpenSmartCard)?;
|
||||||
let transaction = card.transaction().map_err(Error::Transaction)?;
|
let transaction = card.transaction().map_err(Error::Transaction)?;
|
||||||
|
@ -152,16 +158,19 @@ impl SmartcardManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pm.prompt_message(Message::Text(
|
self.pm
|
||||||
"Please plug in a smart card and press enter".to_string(),
|
.lock()
|
||||||
))?;
|
.unwrap()
|
||||||
|
.prompt_message(Message::Text(
|
||||||
|
"Please plug in a smart card and press enter".to_string(),
|
||||||
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerificationHelper for &mut SmartcardManager {
|
impl<P: PromptHandler> VerificationHelper for &mut SmartcardManager<P> {
|
||||||
fn get_certs(&mut self, ids: &[openpgp::KeyHandle]) -> openpgp::Result<Vec<Cert>> {
|
fn get_certs(&mut self, ids: &[openpgp::KeyHandle]) -> openpgp::Result<Vec<Cert>> {
|
||||||
#[allow(clippy::flat_map_option)]
|
#[allow(clippy::flat_map_option)]
|
||||||
Ok(ids
|
Ok(ids
|
||||||
|
@ -194,7 +203,7 @@ impl VerificationHelper for &mut SmartcardManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DecryptionHelper for &mut SmartcardManager {
|
impl<P: PromptHandler> DecryptionHelper for &mut SmartcardManager<P> {
|
||||||
fn decrypt<D>(
|
fn decrypt<D>(
|
||||||
&mut self,
|
&mut self,
|
||||||
pkesks: &[PKESK],
|
pkesks: &[PKESK],
|
||||||
|
@ -254,6 +263,8 @@ impl DecryptionHelper for &mut SmartcardManager {
|
||||||
};
|
};
|
||||||
let temp_pin = self
|
let temp_pin = self
|
||||||
.pm
|
.pm
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.prompt_validated_passphrase(&message, 3, &pin_validator)?;
|
.prompt_validated_passphrase(&message, 3, &pin_validator)?;
|
||||||
let verification_status = transaction.verify_user_pin(temp_pin.as_str().trim());
|
let verification_status = transaction.verify_user_pin(temp_pin.as_str().trim());
|
||||||
match verification_status {
|
match verification_status {
|
||||||
|
@ -265,6 +276,8 @@ impl DecryptionHelper for &mut SmartcardManager {
|
||||||
// NOTE: This should not be hit, because of the above validator.
|
// NOTE: This should not be hit, because of the above validator.
|
||||||
Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => {
|
Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => {
|
||||||
self.pm
|
self.pm
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.prompt_message(Message::Text("Invalid PIN length entered.".to_string()))?;
|
.prompt_message(Message::Text("Invalid PIN length entered.".to_string()))?;
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use clap::{Parser, Subcommand};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use keyfork_mnemonic_util::{English, Mnemonic};
|
use keyfork_mnemonic_util::{English, Mnemonic};
|
||||||
|
use keyfork_prompt::{default_terminal, DefaultTerminal};
|
||||||
use keyfork_shard::{remote_decrypt, Format};
|
use keyfork_shard::{remote_decrypt, Format};
|
||||||
|
|
||||||
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>;
|
||||||
|
@ -34,10 +35,14 @@ impl RecoverSubcommands {
|
||||||
} => {
|
} => {
|
||||||
let content = std::fs::read_to_string(shard_file)?;
|
let content = std::fs::read_to_string(shard_file)?;
|
||||||
if content.contains("BEGIN PGP MESSAGE") {
|
if content.contains("BEGIN PGP MESSAGE") {
|
||||||
let openpgp = keyfork_shard::openpgp::OpenPGP;
|
let openpgp = keyfork_shard::openpgp::OpenPGP::<DefaultTerminal>::new();
|
||||||
|
let prompt_handler = default_terminal()?;
|
||||||
// TODO: remove .clone() by making handle() consume self
|
// TODO: remove .clone() by making handle() consume self
|
||||||
let seed = openpgp
|
let seed = openpgp.decrypt_all_shards_to_secret(
|
||||||
.decrypt_all_shards_to_secret(key_discovery.as_deref(), content.as_bytes())?;
|
key_discovery.as_deref(),
|
||||||
|
content.as_bytes(),
|
||||||
|
prompt_handler,
|
||||||
|
)?;
|
||||||
Ok(seed)
|
Ok(seed)
|
||||||
} else {
|
} else {
|
||||||
panic!("unknown format of shard file");
|
panic!("unknown format of shard file");
|
||||||
|
@ -50,7 +55,6 @@ impl RecoverSubcommands {
|
||||||
}
|
}
|
||||||
RecoverSubcommands::Mnemonic {} => {
|
RecoverSubcommands::Mnemonic {} => {
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
default_terminal,
|
|
||||||
validators::{
|
validators::{
|
||||||
mnemonic::{MnemonicChoiceValidator, WordLength},
|
mnemonic::{MnemonicChoiceValidator, WordLength},
|
||||||
Validator,
|
Validator,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{builder::PossibleValue, Parser, Subcommand, ValueEnum};
|
use clap::{builder::PossibleValue, Parser, Subcommand, ValueEnum};
|
||||||
|
use keyfork_prompt::{default_terminal, DefaultTerminal};
|
||||||
use keyfork_shard::Format as _;
|
use keyfork_shard::Format as _;
|
||||||
use std::{
|
use std::{
|
||||||
io::{stdin, stdout, Read, Write},
|
io::{stdin, stdout, Read, Write},
|
||||||
|
@ -63,7 +64,7 @@ impl ShardExec for OpenPGP {
|
||||||
secret: &[u8],
|
secret: &[u8],
|
||||||
output: &mut (impl Write + Send + Sync),
|
output: &mut (impl Write + Send + Sync),
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let opgp = keyfork_shard::openpgp::OpenPGP;
|
let opgp = keyfork_shard::openpgp::OpenPGP::<DefaultTerminal>::new();
|
||||||
opgp.shard_and_encrypt(threshold, max, secret, key_discovery, output)
|
opgp.shard_and_encrypt(threshold, max, secret, key_discovery, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,10 +73,10 @@ impl ShardExec for OpenPGP {
|
||||||
key_discovery: Option<&Path>,
|
key_discovery: Option<&Path>,
|
||||||
input: impl Read + Send + Sync,
|
input: impl Read + Send + Sync,
|
||||||
output: &mut impl Write,
|
output: &mut impl Write,
|
||||||
) -> Result<(), Box<dyn std::error::Error>>
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
{
|
let openpgp = keyfork_shard::openpgp::OpenPGP::<DefaultTerminal>::new();
|
||||||
let openpgp = keyfork_shard::openpgp::OpenPGP;
|
let prompt = default_terminal()?;
|
||||||
let bytes = openpgp.decrypt_all_shards_to_secret(key_discovery, input)?;
|
let bytes = openpgp.decrypt_all_shards_to_secret(key_discovery, input, prompt)?;
|
||||||
write!(output, "{}", smex::encode(bytes))?;
|
write!(output, "{}", smex::encode(bytes))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -85,10 +86,10 @@ impl ShardExec for OpenPGP {
|
||||||
&self,
|
&self,
|
||||||
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>> {
|
||||||
{
|
let openpgp = keyfork_shard::openpgp::OpenPGP::<DefaultTerminal>::new();
|
||||||
let openpgp = keyfork_shard::openpgp::OpenPGP;
|
let prompt = default_terminal()?;
|
||||||
openpgp.decrypt_one_shard_for_transport(key_discovery, input)?;
|
openpgp.decrypt_one_shard_for_transport(key_discovery, input, prompt)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use keyfork_derive_openpgp::{
|
||||||
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{PinValidator, Validator},
|
validators::{PinValidator, Validator},
|
||||||
Message, PromptHandler, Terminal,
|
Message, PromptHandler, DefaultTerminal, default_terminal
|
||||||
};
|
};
|
||||||
|
|
||||||
use keyfork_shard::{Format, openpgp::OpenPGP};
|
use keyfork_shard::{Format, openpgp::OpenPGP};
|
||||||
|
@ -105,7 +105,7 @@ fn generate_shard_secret(
|
||||||
output_file: &Option<PathBuf>,
|
output_file: &Option<PathBuf>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let seed = keyfork_entropy::generate_entropy_of_const_size::<{256 / 8}>()?;
|
let seed = keyfork_entropy::generate_entropy_of_const_size::<{256 / 8}>()?;
|
||||||
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
|
let mut pm = default_terminal()?;
|
||||||
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();
|
||||||
|
@ -165,7 +165,7 @@ fn generate_shard_secret(
|
||||||
certs.push(cert);
|
certs.push(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
let opgp = OpenPGP;
|
let opgp = OpenPGP::<DefaultTerminal>::new();
|
||||||
|
|
||||||
if let Some(output_file) = output_file {
|
if let Some(output_file) = output_file {
|
||||||
let output = File::create(output_file)?;
|
let output = File::create(output_file)?;
|
||||||
|
|
Loading…
Reference in New Issue