use anyhow::{bail, format_err, Error};
use base64::Engine;
use clap::builder::PossibleValue;
use msecret::Result;
use std::fmt::{Display, Formatter};
use std::io::{stdout, Write};
use std::str::FromStr;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum EccFormat {
BinFormat(BinFormat),
Ssh,
Pkcs8,
}
impl clap::ValueEnum for EccFormat {
fn value_variants<'a>() -> &'a [Self] {
&[
EccFormat::BinFormat(BinFormat::Hex),
EccFormat::BinFormat(BinFormat::Base64),
EccFormat::BinFormat(BinFormat::Base58),
EccFormat::BinFormat(BinFormat::Mnemonic),
EccFormat::BinFormat(BinFormat::Raw),
EccFormat::Ssh,
EccFormat::Pkcs8,
]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
match self {
EccFormat::BinFormat(x) => x.to_possible_value(),
EccFormat::Ssh => Some(PossibleValue::new("ssh").help("SSH key format")),
EccFormat::Pkcs8 => {
Some(PossibleValue::new("pkcs8").help("Standardized key export format"))
}
}
}
}
#[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, clap::ValueEnum)]
pub enum RsaFormat {
#[default]
Pem,
Der,
Debug,
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, clap::ValueEnum)]
pub enum BinFormat {
Raw,
#[value(alias("b16"))]
Hex,
#[value(alias("b10"), alias("base10"))]
Dec,
#[value(alias("b64"))]
Base64,
#[value(alias("b58"))]
Base58,
#[value(name = "words", alias("mnemonic"))]
Mnemonic,
}
impl Display for BinFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
BinFormat::Hex => "hex",
BinFormat::Dec => "dec",
BinFormat::Raw => "raw",
BinFormat::Base64 => "base64",
BinFormat::Base58 => "base58",
BinFormat::Mnemonic => "mnemonic",
})
}
}
impl FromStr for BinFormat {
type Err = Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"hex" | "b16" | "base16" => Ok(BinFormat::Hex),
"raw" => Ok(BinFormat::Raw),
"base64" | "b64" => Ok(BinFormat::Base64),
"base58" | "b58" | "bs58" => Ok(BinFormat::Base58),
"dec" | "b10" | "base10" => Ok(BinFormat::Dec),
"words" | "mnemonic" | "mnemonic-en" => Ok(BinFormat::Mnemonic),
_ => Err(format_err!("Unknown binary format {:?}", s)),
}
}
}
impl BinFormat {
pub fn try_from_str_len<T: AsRef<str>>(encoded: T, len: usize) -> Result<Vec<u8>, Error> {
let encoded = encoded.as_ref();
let mut ret = vec![];
if mnemonic::decode(encoded, &mut ret).is_ok() && ret.len() == len {
return Ok(ret);
}
if let Ok(ret) = bs58::decode(encoded).into_vec() {
if ret.len() == len {
return Ok(ret);
}
}
if let Ok(ret) = hex::decode(encoded) {
if ret.len() == len {
return Ok(ret);
}
}
if let Ok(ret) = base64::engine::general_purpose::STANDARD.decode(encoded) {
if ret.len() == len {
return Ok(ret);
}
}
bail!("Unable to decode");
}
pub fn write<W: Write>(&self, out: &mut W, bytes: &[u8]) -> Result {
match self {
BinFormat::Raw => out.write_all(bytes)?,
_ => write!(out, "{}", self.to_string(bytes)?)?,
}
Ok(())
}
pub fn print_out(&self, bytes: &[u8]) -> Result<(), Error> {
use std::io::IsTerminal;
match self {
BinFormat::Raw if stdout().is_terminal() => {
bail!("Refusing to write raw data to terminal")
}
_ => self.write(&mut stdout(), bytes)?,
}
Ok(())
}
#[allow(clippy::wrong_self_convention)]
pub fn to_string(&self, bytes: &[u8]) -> Result<String, Error> {
Ok(match self {
BinFormat::Hex => hex::encode(bytes),
BinFormat::Raw => bail!("Cannot write raw data to a string."),
BinFormat::Base64 => base64::engine::general_purpose::STANDARD.encode(bytes),
BinFormat::Base58 => bs58::encode(bytes).into_string(),
BinFormat::Dec => num_bigint::BigUint::from_bytes_be(bytes).to_string(),
BinFormat::Mnemonic => mnemonic::to_string(bytes),
})
}
#[allow(clippy::wrong_self_convention)]
pub fn to_bytes(&self, bytes: &[u8]) -> Result<Vec<u8>, Error> {
Ok(match self {
BinFormat::Raw => bytes.to_vec(),
_ => self.to_string(bytes)?.into_bytes(),
})
}
pub fn try_from_str<T: AsRef<str>>(&self, encoded: T) -> Result<Vec<u8>, Error> {
let encoded = encoded.as_ref();
Ok(match self {
BinFormat::Hex => hex::decode(encoded)?,
BinFormat::Raw => encoded.bytes().collect(),
BinFormat::Base64 => base64::engine::general_purpose::STANDARD.decode(encoded)?,
BinFormat::Base58 => bs58::decode(encoded).into_vec()?,
BinFormat::Dec => num_bigint::BigUint::from_str(encoded)?.to_bytes_be(),
BinFormat::Mnemonic => {
let mut ret = vec![];
mnemonic::decode(encoded, &mut ret)?;
ret
}
})
}
}