keyfork: impl basic `recover mnemonic`
This commit is contained in:
parent
d2965745e8
commit
c5d1a6d62c
|
@ -21,9 +21,14 @@ pub enum RecoverSubcommands {
|
||||||
/// Combine remotely decrypted shards. The shards should be sent using the command `keyfork
|
/// Combine remotely decrypted shards. The shards should be sent using the command `keyfork
|
||||||
/// shard transport`.
|
/// shard transport`.
|
||||||
RemoteShard {},
|
RemoteShard {},
|
||||||
|
|
||||||
|
/// Read a mnemonic from input.
|
||||||
|
Mnemonic {},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecoverSubcommands {
|
impl RecoverSubcommands {
|
||||||
|
/// Return the 128-bit or 256-bit entropy for a bip39 mnemonic. This is _not_ the same as the
|
||||||
|
/// 512-bit seed used by bip32.
|
||||||
fn handle(&self) -> Result<Vec<u8>> {
|
fn handle(&self) -> Result<Vec<u8>> {
|
||||||
match self {
|
match self {
|
||||||
RecoverSubcommands::Shard {
|
RecoverSubcommands::Shard {
|
||||||
|
@ -51,6 +56,27 @@ impl RecoverSubcommands {
|
||||||
remote_decrypt(&mut seed)?;
|
remote_decrypt(&mut seed)?;
|
||||||
Ok(seed)
|
Ok(seed)
|
||||||
}
|
}
|
||||||
|
RecoverSubcommands::Mnemonic {} => {
|
||||||
|
use keyfork_prompt::{
|
||||||
|
default_terminal,
|
||||||
|
validators::{
|
||||||
|
mnemonic::{MnemonicChoiceValidator, WordLength},
|
||||||
|
Validator,
|
||||||
|
},
|
||||||
|
PromptHandler,
|
||||||
|
};
|
||||||
|
let mut term = default_terminal()?;
|
||||||
|
let validator = MnemonicChoiceValidator {
|
||||||
|
word_lengths: [WordLength::Count(12), WordLength::Count(24)],
|
||||||
|
};
|
||||||
|
let mnemonic = term.prompt_validated_wordlist(
|
||||||
|
"Mnemonic: ",
|
||||||
|
&Default::default(),
|
||||||
|
3,
|
||||||
|
validator.to_fn(),
|
||||||
|
)?;
|
||||||
|
Ok(mnemonic.entropy())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,47 @@ pub mod mnemonic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mnemonic of a given choice of lengths. For example, a 128-bit or 256-bit bip32 seed.
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum MnemonicChoiceValidationError {
|
||||||
|
/// The provided mnemonic did not match any of the valid ranges.
|
||||||
|
#[error("Invalid word length: {0} was not in any {1:?}")]
|
||||||
|
InvalidLength(usize, Vec<WordLength>),
|
||||||
|
|
||||||
|
/// A mnemonic could not be parsed from the provided mnemonic.
|
||||||
|
#[error("{0}")]
|
||||||
|
MnemonicFromStrError(#[from] MnemonicFromStrError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a single mnemonic against a set of possible word lengths.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MnemonicChoiceValidator<const N: usize> {
|
||||||
|
/// The accepted [`WordLength`] of the mnemonic.
|
||||||
|
pub word_lengths: [WordLength; N],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Validator for MnemonicChoiceValidator<N> {
|
||||||
|
type Output = Mnemonic;
|
||||||
|
type Error = MnemonicChoiceValidationError;
|
||||||
|
|
||||||
|
fn to_fn(&self) -> Box<dyn Fn(String) -> Result<Self::Output, Self::Error>> {
|
||||||
|
let word_lengths = self.word_lengths.clone();
|
||||||
|
Box::new(move |s: String| {
|
||||||
|
let count = s.split_whitespace().count();
|
||||||
|
for word_length in &word_lengths {
|
||||||
|
if word_length.matches(count) {
|
||||||
|
let m = Mnemonic::from_str(&s)?;
|
||||||
|
return Ok(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(MnemonicChoiceValidationError::InvalidLength(
|
||||||
|
count,
|
||||||
|
word_lengths.to_vec(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A mnemonic in the set of mnemonics could not be validated from the given inputs.
|
/// A mnemonic in the set of mnemonics could not be validated from the given inputs.
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum MnemonicSetValidationError {
|
pub enum MnemonicSetValidationError {
|
||||||
|
|
Loading…
Reference in New Issue