keyfork-prompt: split on word boundaries for textual prompts

This commit is contained in:
Ryan Heywood 2023-12-28 17:54:38 -05:00
parent df7be182e4
commit f157a8c954
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 45 additions and 21 deletions

View File

@ -9,6 +9,6 @@ pub fn main() -> Result<()> {
let string = mgr.prompt_wordlist("Mnemonic: ", &Default::default())?; let string = mgr.prompt_wordlist("Mnemonic: ", &Default::default())?;
let mnemonic = Mnemonic::from_str(&string).unwrap(); let mnemonic = Mnemonic::from_str(&string).unwrap();
let entropy = mnemonic.entropy(); 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(()) Ok(())
} }

View File

@ -31,6 +31,11 @@ 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 enum Message {
Text(String),
Data(String),
}
pub struct PromptManager<R, W> { pub struct PromptManager<R, W> {
read: BufReader<R>, read: BufReader<R>,
write: W, write: W,
@ -243,40 +248,59 @@ where
Ok(passphrase) 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 = AlternateScreen::new(&mut self.write)?;
let mut terminal = RawMode::new(&mut terminal)?; let mut terminal = RawMode::new(&mut terminal)?;
loop { loop {
// TODO: split on word boundaries let (cols, _) = terminal::size()?;
terminal terminal
.queue(terminal::Clear(terminal::ClearType::All))? .queue(terminal::Clear(terminal::ClearType::All))?
.queue(cursor::MoveTo(0, 0))?; .queue(cursor::MoveTo(0, 0))?;
let mut lines = prompt.lines().peekable();
while let Some(line) = lines.next() { use Message::*;
terminal.queue(Print(line))?; match &prompt {
if lines.peek().is_some() { 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 terminal
.queue(cursor::MoveDown(1))? .queue(cursor::MoveDown(1))?
.queue(cursor::MoveToColumn(0))?; .queue(cursor::MoveToColumn(0))?;
} }
} }
Data(data) => {
for line in data.lines() {
terminal
.queue(Print(line))?
.queue(cursor::MoveDown(1))?
.queue(cursor::MoveToColumn(0))?;
}
}
}
terminal terminal
.queue(cursor::DisableBlinking)? .queue(cursor::DisableBlinking)?
.queue(cursor::MoveDown(1))?
.queue(cursor::MoveToColumn(0))?
.queue(PrintStyledContent(" OK ".negative()))? .queue(PrintStyledContent(" OK ".negative()))?
.flush()?; .flush()?;
match read()? { match read()? {
Event::Key(k) => match k.code { Event::Key(k) => match k.code {
KeyCode::Enter | KeyCode::Char(' ') => break, KeyCode::Enter | KeyCode::Char(' ') | KeyCode::Char('q') => break,
_ => (), _ => (),
}, },
_ => (), _ => (),
} }
} }
terminal.queue(cursor::EnableBlinking)?.flush()?; terminal.queue(cursor::EnableBlinking)?.flush()?;
Ok(()) Ok(())

View File

@ -11,7 +11,7 @@ use aes_gcm::{aead::Aead, aes::cipher::consts::U12, Aes256Gcm, KeyInit, Nonce};
use x25519_dalek::{EphemeralSecret, PublicKey}; use x25519_dalek::{EphemeralSecret, PublicKey};
use keyfork_mnemonic_util::{Mnemonic, Wordlist}; 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}; use keyfork_shard::openpgp::{decrypt_one, discover_certs, openpgp::Cert, parse_messages};
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>;
@ -85,7 +85,7 @@ fn run() -> Result<()> {
let our_mnemonic = let our_mnemonic =
Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?; Mnemonic::from_entropy(PublicKey::from(&our_key).as_bytes(), Default::default())?;
// TODO: Encode using `qrencode -t ansiutf8 -m 2` // 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 let shared_secret = our_key
.diffie_hellman(&PublicKey::from(their_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 // safety: size of out_bytes is constant and always % 4 == 0
let mnemonic = unsafe { Mnemonic::from_raw_entropy(&out_bytes, Default::default()) }; 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(()) Ok(())
} }

View File

@ -1,6 +1,6 @@
use std::collections::{HashMap, HashSet}; 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::{ use super::openpgp::{
self, self,
@ -146,7 +146,7 @@ impl SmartcardManager {
} }
#[rustfmt::skip] #[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) Ok(None)
@ -246,7 +246,7 @@ impl DecryptionHelper for &mut SmartcardManager {
pin.replace(temp_pin); pin.replace(temp_pin);
} }
Err(CardError::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => { 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(_) => {} Err(_) => {}
} }