keyfork-prompt: traitify
This commit is contained in:
parent
50cc58469d
commit
b5320cabf3
|
@ -2,7 +2,7 @@ use std::io::{stdin, stdout};
|
|||
|
||||
use keyfork_prompt::{
|
||||
validators::{mnemonic, Validator},
|
||||
Terminal,
|
||||
Terminal, PromptHandler,
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
io::{stderr, stdin, BufRead, BufReader, Read, Stderr, Stdin, Write},
|
||||
os::fd::AsRawFd,
|
||||
os::fd::AsRawFd, borrow::Borrow,
|
||||
};
|
||||
|
||||
#[cfg(feature = "mnemonic")]
|
||||
|
@ -10,9 +10,9 @@ use keyfork_crossterm::{
|
|||
cursor,
|
||||
event::{read, DisableBracketedPaste, EnableBracketedPaste, Event, KeyCode, KeyModifiers},
|
||||
style::{Print, PrintStyledContent, Stylize},
|
||||
terminal::{self, TerminalIoctl, FdTerminal, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
terminal::{self, EnterAlternateScreen, FdTerminal, LeaveAlternateScreen, TerminalIoctl},
|
||||
tty::IsTty,
|
||||
QueueableCommand, ExecutableCommand
|
||||
ExecutableCommand, QueueableCommand,
|
||||
};
|
||||
|
||||
pub mod validators;
|
||||
|
@ -39,18 +39,57 @@ pub enum Message {
|
|||
Data(String),
|
||||
}
|
||||
|
||||
struct TerminalGuard<'a, R, W> where W: Write + AsRawFd {
|
||||
pub trait PromptHandler {
|
||||
fn prompt_input(&mut self, prompt: &str) -> Result<String>;
|
||||
|
||||
fn prompt_wordlist(&mut self, prompt: &str, wordlist: &Wordlist) -> Result<String>;
|
||||
|
||||
#[cfg(feature = "mnemonic")]
|
||||
fn prompt_validated_wordlist<V, F, E>(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
wordlist: &Wordlist,
|
||||
retries: u8,
|
||||
validator_fn: F,
|
||||
) -> Result<V, Error>
|
||||
where
|
||||
F: Fn(String) -> Result<V, E>,
|
||||
E: std::error::Error;
|
||||
|
||||
fn prompt_passphrase(&mut self, prompt: &str) -> Result<String>;
|
||||
|
||||
fn prompt_validated_passphrase<V, F, E>(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
retries: u8,
|
||||
validator_fn: F,
|
||||
) -> Result<V, Error>
|
||||
where
|
||||
F: Fn(String) -> Result<V, E>,
|
||||
E: std::error::Error;
|
||||
|
||||
fn prompt_message(&mut self, prompt: impl Borrow<Message>) -> Result<()>;
|
||||
}
|
||||
|
||||
struct TerminalGuard<'a, R, W>
|
||||
where
|
||||
W: Write + AsRawFd,
|
||||
{
|
||||
read: &'a mut BufReader<R>,
|
||||
write: &'a mut W,
|
||||
terminal: &'a mut FdTerminal,
|
||||
}
|
||||
|
||||
impl<'a, R, W> TerminalGuard<'a, R, W> where W: Write + AsRawFd, R: Read {
|
||||
impl<'a, R, W> TerminalGuard<'a, R, W>
|
||||
where
|
||||
W: Write + AsRawFd,
|
||||
R: Read,
|
||||
{
|
||||
fn new(read: &'a mut BufReader<R>, write: &'a mut W, terminal: &'a mut FdTerminal) -> Self {
|
||||
Self {
|
||||
read,
|
||||
write,
|
||||
terminal
|
||||
terminal,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +109,11 @@ impl<'a, R, W> TerminalGuard<'a, R, W> where W: Write + AsRawFd, R: Read {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, W> TerminalIoctl for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd {
|
||||
impl<R, W> TerminalIoctl for TerminalGuard<'_, R, W>
|
||||
where
|
||||
R: Read,
|
||||
W: Write + AsRawFd,
|
||||
{
|
||||
fn enable_raw_mode(&mut self) -> std::io::Result<()> {
|
||||
self.terminal.enable_raw_mode()
|
||||
}
|
||||
|
@ -88,13 +131,21 @@ impl<R, W> TerminalIoctl for TerminalGuard<'_, R, W> where R: Read, W: Write + A
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, W> Read for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd {
|
||||
impl<R, W> Read for TerminalGuard<'_, R, W>
|
||||
where
|
||||
R: Read,
|
||||
W: Write + AsRawFd,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.read.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, W> BufRead for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd {
|
||||
impl<R, W> BufRead for TerminalGuard<'_, R, W>
|
||||
where
|
||||
R: Read,
|
||||
W: Write + AsRawFd,
|
||||
{
|
||||
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
|
||||
self.read.fill_buf()
|
||||
}
|
||||
|
@ -104,7 +155,10 @@ impl<R, W> BufRead for TerminalGuard<'_, R, W> where R: Read, W: Write + AsRawFd
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, W> Write for TerminalGuard<'_, R, W> where W: Write + AsRawFd {
|
||||
impl<R, W> Write for TerminalGuard<'_, R, W>
|
||||
where
|
||||
W: Write + AsRawFd,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write.write(buf)
|
||||
}
|
||||
|
@ -114,7 +168,10 @@ impl<R, W> Write for TerminalGuard<'_, R, W> where W: Write + AsRawFd {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, W> Drop for TerminalGuard<'_, R, W> where W: Write + AsRawFd {
|
||||
impl<R, W> Drop for TerminalGuard<'_, R, W>
|
||||
where
|
||||
W: Write + AsRawFd,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.write.execute(DisableBracketedPaste).unwrap();
|
||||
self.write.execute(LeaveAlternateScreen).unwrap();
|
||||
|
@ -148,7 +205,11 @@ where
|
|||
TerminalGuard::new(&mut self.read, &mut self.write, &mut self.terminal)
|
||||
}
|
||||
|
||||
pub fn prompt_input(&mut self, prompt: &str) -> Result<String> {
|
||||
}
|
||||
|
||||
impl<R, W> PromptHandler for Terminal<R, W> where R: Read + Sized, W: Write + AsRawFd + Sized {
|
||||
|
||||
fn prompt_input(&mut self, prompt: &str) -> Result<String> {
|
||||
let mut terminal = self.lock().alternate_screen()?;
|
||||
terminal
|
||||
.queue(terminal::Clear(terminal::ClearType::All))?
|
||||
|
@ -170,7 +231,7 @@ where
|
|||
}
|
||||
|
||||
#[cfg(feature = "mnemonic")]
|
||||
pub fn prompt_validated_wordlist<V, F, E>(
|
||||
fn prompt_validated_wordlist<V, F, E>(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
wordlist: &Wordlist,
|
||||
|
@ -202,8 +263,12 @@ where
|
|||
|
||||
#[cfg(feature = "mnemonic")]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn prompt_wordlist(&mut self, prompt: &str, wordlist: &Wordlist) -> Result<String> {
|
||||
let mut terminal = self.lock().alternate_screen()?.raw_mode()?.bracketed_paste()?;
|
||||
fn prompt_wordlist(&mut self, prompt: &str, wordlist: &Wordlist) -> Result<String> {
|
||||
let mut terminal = self
|
||||
.lock()
|
||||
.alternate_screen()?
|
||||
.raw_mode()?
|
||||
.bracketed_paste()?;
|
||||
|
||||
terminal
|
||||
.queue(terminal::Clear(terminal::ClearType::All))?
|
||||
|
@ -318,8 +383,7 @@ where
|
|||
Ok(input)
|
||||
}
|
||||
|
||||
#[cfg(feature = "mnemonic")]
|
||||
pub fn prompt_validated_passphrase<V, F, E>(
|
||||
fn prompt_validated_passphrase<V, F, E>(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
retries: u8,
|
||||
|
@ -335,7 +399,9 @@ where
|
|||
match validator_fn(s) {
|
||||
Ok(v) => return Ok(v),
|
||||
Err(e) => {
|
||||
self.prompt_message(&Message::Text(format!("Error validating passphrase: {e}")))?;
|
||||
self.prompt_message(&Message::Text(format!(
|
||||
"Error validating passphrase: {e}"
|
||||
)))?;
|
||||
let _ = last_error.insert(e);
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +415,7 @@ where
|
|||
}
|
||||
|
||||
// TODO: return secrecy::Secret<String>
|
||||
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
|
||||
fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
|
||||
let mut terminal = self.lock().alternate_screen()?.raw_mode()?;
|
||||
|
||||
terminal
|
||||
|
@ -407,7 +473,7 @@ where
|
|||
Ok(passphrase)
|
||||
}
|
||||
|
||||
pub fn prompt_message(&mut self, prompt: &Message) -> Result<()> {
|
||||
fn prompt_message(&mut self, prompt: impl Borrow<Message>) -> Result<()> {
|
||||
let mut terminal = self.lock().alternate_screen()?.raw_mode()?;
|
||||
|
||||
loop {
|
||||
|
@ -417,7 +483,7 @@ where
|
|||
.queue(terminal::Clear(terminal::ClearType::All))?
|
||||
.queue(cursor::MoveTo(0, 0))?;
|
||||
|
||||
match &prompt {
|
||||
match prompt.borrow() {
|
||||
Message::Text(text) => {
|
||||
for line in text.lines() {
|
||||
let mut written_chars = 0;
|
||||
|
|
|
@ -9,7 +9,7 @@ use keyfork_mnemonic_util::{Mnemonic, Wordlist};
|
|||
use keyfork_prompt::{
|
||||
qrencode,
|
||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||
Message as PromptMessage, Terminal,
|
||||
Message as PromptMessage, Terminal, PromptHandler
|
||||
};
|
||||
use sha2::Sha256;
|
||||
use sharks::{Share, Sharks};
|
||||
|
@ -59,12 +59,12 @@ pub fn remote_decrypt(w: &mut impl Write) -> Result<(), Box<dyn std::error::Erro
|
|||
let key_mnemonic =
|
||||
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
|
||||
let combined_mnemonic = format!("{nonce_mnemonic} {key_mnemonic}");
|
||||
pm.prompt_message(&PromptMessage::Text(format!(
|
||||
pm.prompt_message(PromptMessage::Text(format!(
|
||||
"Our words: {combined_mnemonic}"
|
||||
)))?;
|
||||
|
||||
if let Ok(qrcode) = qrencode::qrencode(&combined_mnemonic) {
|
||||
pm.prompt_message(&PromptMessage::Data(qrcode))?;
|
||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||
}
|
||||
|
||||
let validator = MnemonicSetValidator {
|
||||
|
|
|
@ -19,7 +19,7 @@ use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationEr
|
|||
use keyfork_prompt::{
|
||||
qrencode,
|
||||
validators::{mnemonic::MnemonicSetValidator, Validator},
|
||||
Error as PromptError, Message as PromptMessage, Terminal,
|
||||
Error as PromptError, Message as PromptMessage, Terminal, PromptHandler,
|
||||
};
|
||||
use openpgp::{
|
||||
armor::{Kind, Writer},
|
||||
|
@ -476,12 +476,12 @@ pub fn decrypt(
|
|||
let mnemonic = unsafe { Mnemonic::from_raw_entropy(&out_bytes, Default::default()) };
|
||||
let combined_mnemonic = format!("{our_mnemonic} {mnemonic}");
|
||||
|
||||
pm.prompt_message(&PromptMessage::Text(format!(
|
||||
pm.prompt_message(PromptMessage::Text(format!(
|
||||
"Our words: {combined_mnemonic}"
|
||||
)))?;
|
||||
|
||||
if let Ok(qrcode) = qrencode::qrencode(&combined_mnemonic) {
|
||||
pm.prompt_message(&PromptMessage::Data(qrcode))?;
|
||||
pm.prompt_message(PromptMessage::Data(qrcode))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use keyfork_prompt::{Error as PromptError, DefaultTerminal, default_terminal};
|
||||
use keyfork_prompt::{Error as PromptError, DefaultTerminal, default_terminal, PromptHandler};
|
||||
|
||||
use super::openpgp::{
|
||||
self,
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
|
|||
use keyfork_prompt::{
|
||||
default_terminal,
|
||||
validators::{PinValidator, Validator},
|
||||
DefaultTerminal, Error as PromptError, Message,
|
||||
DefaultTerminal, Error as PromptError, Message, PromptHandler
|
||||
};
|
||||
|
||||
use super::openpgp::{
|
||||
|
@ -93,14 +93,14 @@ impl SmartcardManager {
|
|||
/// Load any backend.
|
||||
pub fn load_any_card(&mut self) -> Result<Fingerprint> {
|
||||
let card_backend = loop {
|
||||
self.pm.prompt_message(&Message::Text(
|
||||
self.pm.prompt_message(Message::Text(
|
||||
"Please plug in a smart card and press enter".to_string(),
|
||||
))?;
|
||||
if let Some(c) = PcscBackend::cards(None)?.next().transpose()? {
|
||||
break c;
|
||||
}
|
||||
self.pm
|
||||
.prompt_message(&Message::Text("No smart card was found".to_string()))?;
|
||||
.prompt_message(Message::Text("No smart card was found".to_string()))?;
|
||||
};
|
||||
let mut card = Card::<Open>::new(card_backend).map_err(Error::OpenSmartCard)?;
|
||||
let transaction = card.transaction().map_err(Error::Transaction)?;
|
||||
|
@ -154,7 +154,7 @@ impl SmartcardManager {
|
|||
}
|
||||
}
|
||||
|
||||
self.pm.prompt_message(&Message::Text(
|
||||
self.pm.prompt_message(Message::Text(
|
||||
"Please plug in a smart card and press enter".to_string(),
|
||||
))?;
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ impl DecryptionHelper for &mut SmartcardManager {
|
|||
}
|
||||
// NOTE: This should not be hit, because of the above validator.
|
||||
Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => {
|
||||
self.pm.prompt_message(&Message::Text(
|
||||
self.pm.prompt_message(Message::Text(
|
||||
"Invalid PIN length entered.".to_string(),
|
||||
))?;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use keyfork_derive_util::{
|
|||
};
|
||||
use keyfork_prompt::{
|
||||
validators::{PinValidator, Validator},
|
||||
Message, Terminal,
|
||||
Message, Terminal, PromptHandler,
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -125,7 +125,7 @@ fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8) -> Result<(
|
|||
for index in 0..max {
|
||||
let cert = derive_key(&seed, index)?;
|
||||
for i in 0..keys_per_shard {
|
||||
pm.prompt_message(&Message::Text(format!(
|
||||
pm.prompt_message(Message::Text(format!(
|
||||
"Please insert key #{} for user #{}",
|
||||
i + 1,
|
||||
index + 1,
|
||||
|
|
Loading…
Reference in New Issue