keyfork-shard: first pass of reusable prompthandler

This commit is contained in:
Ryan Heywood 2024-02-20 18:33:54 -05:00
parent b15d088905
commit 354eae5a6a
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
10 changed files with 149 additions and 77 deletions

View File

@ -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(())

View File

@ -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(())
} }

View File

@ -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(())

View File

@ -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}"
)))?; )))?;

View File

@ -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()?;

View File

@ -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

View File

@ -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,8 +98,12 @@ 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)?;
@ -152,7 +158,10 @@ impl SmartcardManager {
} }
} }
self.pm.prompt_message(Message::Text( self.pm
.lock()
.unwrap()
.prompt_message(Message::Text(
"Please plug in a smart card and press enter".to_string(), "Please plug in a smart card and press enter".to_string(),
))?; ))?;
} }
@ -161,7 +170,7 @@ impl SmartcardManager {
} }
} }
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(_) => {}

View File

@ -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,

View File

@ -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(())
} }
} }

View File

@ -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)?;