keyfork/keyfork/src/cli/mnemonic.rs

156 lines
4.3 KiB
Rust

use super::Keyfork;
use clap::{Parser, Subcommand, ValueEnum, builder::PossibleValue};
use std::fmt::Display;
#[derive(Clone, Debug, Default)]
pub enum SeedSize {
Bits128,
#[default]
Bits256,
}
// custom impl to override names in ValueEnum
impl ValueEnum for SeedSize {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Bits128, Self::Bits256]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(match self {
SeedSize::Bits128 => "128",
SeedSize::Bits256 => "256",
}))
}
}
#[derive(thiserror::Error, Debug, Clone)]
pub enum SeedSizeError {
#[error("Expected one of 128, 256")]
InvalidChoice,
}
impl Display for SeedSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SeedSize::Bits128 => write!(f, "128"),
SeedSize::Bits256 => write!(f, "256"),
}
}
}
impl std::str::FromStr for SeedSize {
type Err = SeedSizeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"128" => SeedSize::Bits128,
"256" => SeedSize::Bits256,
_ => return Err(SeedSizeError::InvalidChoice),
})
}
}
impl From<&SeedSize> for usize {
fn from(value: &SeedSize) -> Self {
match value {
SeedSize::Bits128 => 128,
SeedSize::Bits256 => 256,
}
}
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum MnemonicSeedSourceParseError {
#[error("Expected one of system, playing, tarot, dice")]
InvalidChoice,
}
#[derive(Clone, Debug, Default, ValueEnum)]
pub enum MnemonicSeedSource {
/// System entropy
#[default]
System,
/// Playing cards
Playing,
/// Tarot cards
Tarot,
/// Dice
Dice,
}
impl std::str::FromStr for MnemonicSeedSource {
type Err = MnemonicSeedSourceParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"system" => Self::System,
"playing" => Self::Playing,
"tarot" => Self::Tarot,
"dice" => Self::Dice,
_ => return Err(Self::Err::InvalidChoice),
})
}
}
impl MnemonicSeedSource {
pub fn handle(&self, size: &SeedSize) -> Result<String, Box<dyn std::error::Error>> {
let size = match size {
SeedSize::Bits128 => 128,
SeedSize::Bits256 => 256,
};
let seed = match self {
MnemonicSeedSource::System => {
keyfork_plumbing::ensure_safe();
keyfork_plumbing::generate_entropy_of_size(size / 8)?
}
MnemonicSeedSource::Playing => todo!(),
MnemonicSeedSource::Tarot => todo!(),
MnemonicSeedSource::Dice => todo!(),
};
let mnemonic = keyfork_mnemonic_util::Mnemonic::from_entropy(&seed, Default::default())?;
Ok(mnemonic.to_string())
}
}
#[derive(Subcommand, Clone, Debug)]
pub enum MnemonicSubcommands {
/// Generate a mnemonic using a given entropy source.
///
/// Mnemonics are a form of encoding a given form of entropy and are used to create a master
/// seed for BIP-0032 hierarchial derivation. The mnemonic is like the "password" for all of
/// Keyfork's derivations, and should be treated securely. This command provides a secure
/// method of generating a seed using system entropy, as well as various forms of loading
/// physicalized entropy into a mnemonic. The mnemonic should be stored in a safe location
/// (such as a Trezor "recovery seed card") and never persisted digitally.
Generate {
/// The source from where a seed is created.
#[arg(long, value_enum, default_value_t = Default::default())]
source: MnemonicSeedSource,
/// The size of the mnemonic, in bits.
#[arg(long, default_value_t = Default::default())]
size: SeedSize,
},
}
impl MnemonicSubcommands {
pub fn handle(
&self,
_m: &Mnemonic,
_keyfork: &Keyfork,
) -> Result<String, Box<dyn std::error::Error>> {
match self {
MnemonicSubcommands::Generate { source, size } => source.handle(size),
}
}
}
#[derive(Parser, Debug, Clone)]
pub struct Mnemonic {
#[command(subcommand)]
pub command: MnemonicSubcommands,
}