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"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"keyfork-mnemonic-util",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,11 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["mnemonic"]
|
||||||
|
mnemonic = ["keyfork-mnemonic-util"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossterm = { version = "0.27.0", default-features = false, features = ["use-dev-tty", "events"] }
|
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"
|
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_prompt::*;
|
||||||
|
use keyfork_mnemonic_util::Mnemonic;
|
||||||
|
|
||||||
pub fn main() -> Result<()> {
|
pub fn main() -> Result<()> {
|
||||||
let mut mgr = PromptManager::new(stdin(), stdout())?;
|
let mut mgr = PromptManager::new(stdin(), stdout())?;
|
||||||
mgr.prompt_input("Mnemonic: ")?;
|
|
||||||
mgr.prompt_message("Please press enter.")?;
|
|
||||||
mgr.prompt_passphrase("Passphrase: ")?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ use std::{
|
||||||
os::fd::AsRawFd,
|
os::fd::AsRawFd,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "mnemonic")]
|
||||||
|
use keyfork_mnemonic_util::Wordlist;
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor,
|
cursor,
|
||||||
event::{read, Event, KeyCode},
|
event::{read, Event, KeyCode},
|
||||||
|
@ -69,8 +72,8 @@ where
|
||||||
Ok(line)
|
Ok(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: return secrecy::Secret<String>
|
#[cfg(feature = "mnemonic")]
|
||||||
pub fn prompt_passphrase(&mut self, prompt: &str) -> Result<String> {
|
pub fn prompt_wordlist(&mut self, prompt: &str, wordlist: &Wordlist) -> Result<String> {
|
||||||
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)?;
|
||||||
|
|
||||||
|
@ -78,7 +81,9 @@ where
|
||||||
.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();
|
let mut lines = prompt.lines().peekable();
|
||||||
|
let mut prefix_length = 0;
|
||||||
while let Some(line) = lines.next() {
|
while let Some(line) = lines.next() {
|
||||||
|
prefix_length = line.len();
|
||||||
terminal.queue(Print(line))?;
|
terminal.queue(Print(line))?;
|
||||||
if lines.peek().is_some() {
|
if lines.peek().is_some() {
|
||||||
terminal
|
terminal
|
||||||
|
@ -88,16 +93,123 @@ where
|
||||||
}
|
}
|
||||||
terminal.flush()?;
|
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();
|
let mut passphrase = String::new();
|
||||||
loop {
|
loop {
|
||||||
match read()? {
|
match read()? {
|
||||||
|
Event::Resize(new_cols, new_rows) => {
|
||||||
|
cols = new_cols;
|
||||||
|
_rows = new_rows;
|
||||||
|
}
|
||||||
Event::Key(k) => match k.code {
|
Event::Key(k) => match k.code {
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
passphrase.push('\n');
|
passphrase.push('\n');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => {
|
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
|
terminal
|
||||||
.queue(cursor::MoveLeft(1))?
|
.queue(cursor::MoveLeft(1))?
|
||||||
.queue(Print(" "))?
|
.queue(Print(" "))?
|
||||||
|
@ -106,7 +218,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
|
if prefix_length + passphrase.len() < (cols - 1) as usize {
|
||||||
terminal.queue(Print("*"))?.flush()?;
|
terminal.queue(Print("*"))?.flush()?;
|
||||||
|
}
|
||||||
passphrase.push(c);
|
passphrase.push(c);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
Loading…
Reference in New Issue