From f157a8c954de8601d926e4f3aafcdd26e591cc9c Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 28 Dec 2023 17:54:38 -0500 Subject: [PATCH] keyfork-prompt: split on word boundaries for textual prompts --- keyfork-prompt/src/bin/test-basic-prompt.rs | 2 +- keyfork-prompt/src/lib.rs | 52 ++++++++++++++----- .../src/bin/keyfork-shard-decrypt-openpgp.rs | 6 +-- keyfork-shard/src/openpgp/smartcard.rs | 6 +-- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/keyfork-prompt/src/bin/test-basic-prompt.rs b/keyfork-prompt/src/bin/test-basic-prompt.rs index c14f671..26ce910 100644 --- a/keyfork-prompt/src/bin/test-basic-prompt.rs +++ b/keyfork-prompt/src/bin/test-basic-prompt.rs @@ -9,6 +9,6 @@ pub fn main() -> Result<()> { let string = mgr.prompt_wordlist("Mnemonic: ", &Default::default())?; let mnemonic = Mnemonic::from_str(&string).unwrap(); let entropy = mnemonic.entropy(); - mgr.prompt_message(&format!("Your entropy is: {entropy:X?}"))?; + mgr.prompt_message(Message::Text(format!("Your entropy is: {entropy:X?}")))?; Ok(()) } diff --git a/keyfork-prompt/src/lib.rs b/keyfork-prompt/src/lib.rs index 726c26a..b9ea5a5 100644 --- a/keyfork-prompt/src/lib.rs +++ b/keyfork-prompt/src/lib.rs @@ -31,6 +31,11 @@ pub enum Error { pub type Result = std::result::Result; +pub enum Message { + Text(String), + Data(String), +} + pub struct PromptManager { read: BufReader, write: W, @@ -243,40 +248,59 @@ where Ok(passphrase) } - pub fn prompt_message(&mut self, prompt: &str) -> Result<()> { + pub fn prompt_message(&mut self, prompt: Message) -> Result<()> { let mut terminal = AlternateScreen::new(&mut self.write)?; let mut terminal = RawMode::new(&mut terminal)?; loop { - // TODO: split on word boundaries + let (cols, _) = terminal::size()?; + terminal .queue(terminal::Clear(terminal::ClearType::All))? .queue(cursor::MoveTo(0, 0))?; - let mut lines = prompt.lines().peekable(); - while let Some(line) = lines.next() { - terminal.queue(Print(line))?; - if lines.peek().is_some() { - terminal - .queue(cursor::MoveDown(1))? - .queue(cursor::MoveToColumn(0))?; + + use Message::*; + match &prompt { + Text(text) => { + for line in text.lines() { + let mut written_chars = 0; + for word in line.split_whitespace() { + let len = word.len() as u16; + written_chars += len + 1; + if written_chars > cols { + terminal + .queue(cursor::MoveDown(1))? + .queue(cursor::MoveToColumn(0))?; + written_chars = len + 1; + } + terminal.queue(Print(word))?.queue(Print(" "))?; + } + terminal + .queue(cursor::MoveDown(1))? + .queue(cursor::MoveToColumn(0))?; + } + } + Data(data) => { + for line in data.lines() { + terminal + .queue(Print(line))? + .queue(cursor::MoveDown(1))? + .queue(cursor::MoveToColumn(0))?; + } } } terminal .queue(cursor::DisableBlinking)? - .queue(cursor::MoveDown(1))? - .queue(cursor::MoveToColumn(0))? .queue(PrintStyledContent(" OK ".negative()))? .flush()?; match read()? { Event::Key(k) => match k.code { - KeyCode::Enter | KeyCode::Char(' ') => break, + KeyCode::Enter | KeyCode::Char(' ') | KeyCode::Char('q') => break, _ => (), }, _ => (), } - - } terminal.queue(cursor::EnableBlinking)?.flush()?; Ok(()) diff --git a/keyfork-shard/src/bin/keyfork-shard-decrypt-openpgp.rs b/keyfork-shard/src/bin/keyfork-shard-decrypt-openpgp.rs index d3b1b1e..9211186 100644 --- a/keyfork-shard/src/bin/keyfork-shard-decrypt-openpgp.rs +++ b/keyfork-shard/src/bin/keyfork-shard-decrypt-openpgp.rs @@ -11,7 +11,7 @@ use aes_gcm::{aead::Aead, aes::cipher::consts::U12, Aes256Gcm, KeyInit, Nonce}; use x25519_dalek::{EphemeralSecret, PublicKey}; use keyfork_mnemonic_util::{Mnemonic, Wordlist}; -use keyfork_prompt::PromptManager; +use keyfork_prompt::{PromptManager, Message}; use keyfork_shard::openpgp::{decrypt_one, discover_certs, openpgp::Cert, parse_messages}; type Result> = std::result::Result; @@ -85,7 +85,7 @@ fn run() -> Result<()> { let our_mnemonic = Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?; // TODO: Encode using `qrencode -t ansiutf8 -m 2` - pm.prompt_message(&format!("Our words: {our_mnemonic}"))?; + pm.prompt_message(Message::Text(format!("Our words: {our_mnemonic}")))?; let shared_secret = our_key .diffie_hellman(&PublicKey::from(their_key)) @@ -109,7 +109,7 @@ fn run() -> Result<()> { // safety: size of out_bytes is constant and always % 4 == 0 let mnemonic = unsafe { Mnemonic::from_raw_entropy(&out_bytes, Default::default()) }; - pm.prompt_message(&format!("Our payload: {mnemonic}"))?; + pm.prompt_message(Message::Text(format!("Our payload: {mnemonic}")))?; Ok(()) } diff --git a/keyfork-shard/src/openpgp/smartcard.rs b/keyfork-shard/src/openpgp/smartcard.rs index de37f38..b8fb690 100644 --- a/keyfork-shard/src/openpgp/smartcard.rs +++ b/keyfork-shard/src/openpgp/smartcard.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use keyfork_prompt::{default_prompt_manager, DefaultPromptManager, Error as PromptError}; +use keyfork_prompt::{default_prompt_manager, DefaultPromptManager, Error as PromptError, Message}; use super::openpgp::{ self, @@ -146,7 +146,7 @@ impl SmartcardManager { } #[rustfmt::skip] - self.pm.prompt_message("Please plug in a smart card and press enter")?; + self.pm.prompt_message(Message::Text("Please plug in a smart card and press enter".to_string()))?; } Ok(None) @@ -246,7 +246,7 @@ impl DecryptionHelper for &mut SmartcardManager { pin.replace(temp_pin); } Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => { - self.pm.prompt_message("Invalid PIN length entered.")?; + self.pm.prompt_message(Message::Text("Invalid PIN length entered.".to_string()))?; } Err(_) => {} }