keyfork-prompt: fixup passphrase handling, add prompt_wordlist
This commit is contained in:
parent
d452eba133
commit
6c25cb8f31
|
@ -1078,6 +1078,7 @@ name = "keyfork-prompt"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossterm",
|
||||
"keyfork-mnemonic-util",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
|
|
@ -5,6 +5,11 @@ edition = "2021"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["mnemonic"]
|
||||
mnemonic = ["keyfork-mnemonic-util"]
|
||||
|
||||
[dependencies]
|
||||
crossterm = { version = "0.27.0", default-features = false, features = ["use-dev-tty", "events"] }
|
||||
keyfork-mnemonic-util = { version = "0.1.0", path = "../keyfork-mnemonic-util", optional = true }
|
||||
thiserror = "1.0.51"
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
use std::io::{stdin, stdout};
|
||||
use std::{io::{stdin, stdout}, str::FromStr};
|
||||
|
||||
use keyfork_prompt::*;
|
||||
use keyfork_mnemonic_util::Mnemonic;
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
let mut mgr = PromptManager::new(stdin(), stdout())?;
|
||||
mgr.prompt_input("Mnemonic: ")?;
|
||||
mgr.prompt_message("Please press enter.")?;
|
||||
mgr.prompt_passphrase("Passphrase: ")?;
|
||||
mgr.prompt_message("Please press space bar.")?;
|
||||
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?}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ use std::{
|
|||
os::fd::AsRawFd,
|
||||
};
|
||||
|
||||
#[cfg(feature = "mnemonic")]
|
||||
use keyfork_mnemonic_util::Wordlist;
|
||||
|
||||
use crossterm::{
|
||||
cursor,
|
||||
event::{read, Event, KeyCode},
|
||||
|
@ -69,8 +72,8 @@ where
|
|||
Ok(line)
|
||||
}
|
||||
|
||||
// TODO: return secrecy::Secret<String>
|
||||
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
|
||||
#[cfg(feature = "mnemonic")]
|
||||
pub fn prompt_wordlist(&mut self, prompt: &str, wordlist: &Wordlist) -> Result<String> {
|
||||
let mut terminal = AlternateScreen::new(&mut self.write)?;
|
||||
let mut terminal = RawMode::new(&mut terminal)?;
|
||||
|
||||
|
@ -78,7 +81,9 @@ where
|
|||
.queue(terminal::Clear(terminal::ClearType::All))?
|
||||
.queue(cursor::MoveTo(0, 0))?;
|
||||
let mut lines = prompt.lines().peekable();
|
||||
let mut prefix_length = 0;
|
||||
while let Some(line) = lines.next() {
|
||||
prefix_length = line.len();
|
||||
terminal.queue(Print(line))?;
|
||||
if lines.peek().is_some() {
|
||||
terminal
|
||||
|
@ -88,16 +93,123 @@ where
|
|||
}
|
||||
terminal.flush()?;
|
||||
|
||||
let (mut cols, mut _rows) = terminal::size()?;
|
||||
let mut input = String::new();
|
||||
|
||||
loop {
|
||||
match read()? {
|
||||
Event::Resize(new_cols, new_rows) => {
|
||||
cols = new_cols;
|
||||
_rows = new_rows;
|
||||
}
|
||||
Event::Key(k) => match k.code {
|
||||
KeyCode::Enter => {
|
||||
input.push('\n');
|
||||
break;
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
input.pop();
|
||||
}
|
||||
KeyCode::Char(' ') => {
|
||||
if !input.chars().rev().next().is_some_and(char::is_whitespace) {
|
||||
input.push(' ');
|
||||
}
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
input.push(c);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let usable_space = cols as usize - prefix_length - 1;
|
||||
|
||||
terminal
|
||||
.queue(cursor::MoveToColumn(prefix_length as u16))?
|
||||
.queue(terminal::Clear(terminal::ClearType::UntilNewLine))?
|
||||
.flush()?;
|
||||
|
||||
let printable_input_start = if input.len() > usable_space {
|
||||
let start_index = input.len() - usable_space;
|
||||
input
|
||||
.chars()
|
||||
.enumerate()
|
||||
.skip(start_index)
|
||||
.skip_while(|(_, ch)| !ch.is_whitespace())
|
||||
.next()
|
||||
.expect("any printable character")
|
||||
.0
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let printable_input = &input[printable_input_start..];
|
||||
let mut iter = printable_input.split_whitespace().peekable();
|
||||
|
||||
while let Some(word) = iter.next() {
|
||||
if wordlist.contains(word) {
|
||||
terminal.queue(PrintStyledContent(word.green()))?;
|
||||
} else {
|
||||
terminal.queue(PrintStyledContent(word.red()))?;
|
||||
}
|
||||
if iter.peek().is_some()
|
||||
|| printable_input
|
||||
.chars()
|
||||
.rev()
|
||||
.next()
|
||||
.is_some_and(char::is_whitespace)
|
||||
{
|
||||
terminal.queue(Print(" "))?;
|
||||
}
|
||||
}
|
||||
|
||||
terminal.flush()?;
|
||||
}
|
||||
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
// TODO: return secrecy::Secret<String>
|
||||
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
|
||||
let mut terminal = AlternateScreen::new(&mut self.write)?;
|
||||
let mut terminal = RawMode::new(&mut terminal)?;
|
||||
|
||||
terminal
|
||||
.queue(terminal::Clear(terminal::ClearType::All))?
|
||||
.queue(cursor::MoveTo(0, 0))?;
|
||||
let mut lines = prompt.lines().peekable();
|
||||
let mut prefix_length = 0;
|
||||
while let Some(line) = lines.next() {
|
||||
prefix_length = line.len();
|
||||
terminal.queue(Print(line))?;
|
||||
if lines.peek().is_some() {
|
||||
terminal
|
||||
.queue(cursor::MoveDown(1))?
|
||||
.queue(cursor::MoveToColumn(0))?;
|
||||
}
|
||||
}
|
||||
terminal.flush()?;
|
||||
|
||||
let (mut cols, mut _rows) = terminal::size()?;
|
||||
|
||||
let mut passphrase = String::new();
|
||||
loop {
|
||||
match read()? {
|
||||
Event::Resize(new_cols, new_rows) => {
|
||||
cols = new_cols;
|
||||
_rows = new_rows;
|
||||
}
|
||||
Event::Key(k) => match k.code {
|
||||
KeyCode::Enter => {
|
||||
passphrase.push('\n');
|
||||
break;
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
if passphrase.pop().is_some() {
|
||||
let passphrase_len = passphrase.len();
|
||||
if passphrase.pop().is_some()
|
||||
&& prefix_length + passphrase_len < cols as usize
|
||||
{
|
||||
terminal
|
||||
.queue(cursor::MoveLeft(1))?
|
||||
.queue(Print(" "))?
|
||||
|
@ -106,7 +218,9 @@ where
|
|||
}
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
terminal.queue(Print("*"))?.flush()?;
|
||||
if prefix_length + passphrase.len() < (cols - 1) as usize {
|
||||
terminal.queue(Print("*"))?.flush()?;
|
||||
}
|
||||
passphrase.push(c);
|
||||
}
|
||||
_ => (),
|
||||
|
|
Loading…
Reference in New Issue