Select Git revision
-
Hugo Trentesaux authoredHugo Trentesaux authored
keys.rs 5.69 KiB
use crate::*;
use clap::builder::OsStr;
use sr25519::Pair as Sr25519Pair;
use std::str::FromStr;
pub const SUBSTRATE_MNEMONIC: &str =
"bottom drive obey lake curtain smoke basket hold race lonely fit walk";
/// secret format
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub enum SecretFormat {
/// Raw 32B seed
Seed,
/// Substrate secret key or BIP39 mnemonic (optionally followed by derivation path)
#[default]
Substrate,
/// Predefined (Alice, Bob, ...)
Predefined,
/// Cesium (scrypt + nacl)
Cesium,
}
impl FromStr for SecretFormat {
type Err = std::io::Error;
fn from_str(s: &str) -> std::io::Result<Self> {
match s {
"seed" => Ok(SecretFormat::Seed),
"substrate" => Ok(SecretFormat::Substrate),
"predefined" => Ok(SecretFormat::Predefined),
"cesium" => Ok(SecretFormat::Cesium),
_ => Err(std::io::Error::from(std::io::ErrorKind::InvalidInput)),
}
}
}
impl From<SecretFormat> for &'static str {
fn from(val: SecretFormat) -> &'static str {
match val {
SecretFormat::Seed => "seed",
SecretFormat::Substrate => "substrate",
SecretFormat::Predefined => "predefined",
SecretFormat::Cesium => "cesium",
}
}
}
impl From<SecretFormat> for OsStr {
fn from(val: SecretFormat) -> OsStr {
OsStr::from(Into::<&str>::into(val))
}
}
/// wrapper type for keys + signature
pub enum KeyPair {
Sr25519(Sr25519Pair),
Nacl(nacl::sign::Keypair),
}
impl KeyPair {
pub fn address(&self) -> AccountId {
match self {
KeyPair::Sr25519(keypair) => keypair.public().into(),
KeyPair::Nacl(keypair) => keypair.pkey.into(),
}
}
}
// can not derive clone because nacl does not implement it
impl Clone for KeyPair {
fn clone(&self) -> Self {
match self {
KeyPair::Sr25519(keypair) => KeyPair::Sr25519(keypair.clone()),
KeyPair::Nacl(keypair) => KeyPair::Nacl(nacl::sign::Keypair {
skey: keypair.skey,
pkey: keypair.pkey,
}),
}
}
}
impl From<Sr25519Pair> for KeyPair {
fn from(pair: Sr25519Pair) -> KeyPair {
KeyPair::Sr25519(pair)
}
}
impl From<nacl::sign::Keypair> for KeyPair {
fn from(pair: nacl::sign::Keypair) -> KeyPair {
KeyPair::Nacl(pair)
}
}
pub enum Signature {
Sr25519(sr25519::Signature),
Nacl(Vec<u8>),
}
/// get keypair in any possible way
/// at this point, if secret is predefined, it's not replaced yet
pub fn get_keypair(
secret_format: SecretFormat,
secret: Option<&str>,
) -> Result<KeyPair, GcliError> {
match (secret_format, secret) {
(SecretFormat::Cesium, None) => Ok(prompt_secret(SecretFormat::Cesium)),
(SecretFormat::Predefined, Some(deriv)) => pair_from_predefined(deriv).map(|v| v.into()),
(_, Some(secret)) => Ok(pair_from_secret(secret_format, secret)?.into()),
_ => Err(GcliError::Logic(
"can not get keypair from available options".to_string(),
)),
}
}
/// get keypair from given secret
/// if secret is predefined, secret should contain the predefined value
pub fn pair_from_secret(
secret_format: SecretFormat,
secret: &str,
) -> Result<Sr25519Pair, GcliError> {
match secret_format {
SecretFormat::Substrate => pair_from_str(secret),
SecretFormat::Predefined => pair_from_str(secret), /* if predefined, secret arg is replaced in config */
SecretFormat::Seed => pair_from_seed(secret),
SecretFormat::Cesium => Err(GcliError::Logic(
"cesium format incompatible with single secret".to_string(),
)),
}
}
/// get keypair from given string secret
pub fn pair_from_str(secret: &str) -> Result<Sr25519Pair, GcliError> {
Sr25519Pair::from_string(secret, None)
.map_err(|_| GcliError::Input("Invalid secret".to_string()))
}
/// get keypair from given seed
pub fn pair_from_seed(secret: &str) -> Result<Sr25519Pair, GcliError> {
let mut seed = [0; 32];
hex::decode_to_slice(secret, &mut seed)
.map_err(|_| GcliError::Input("Invalid secret".to_string()))?;
let pair = Sr25519Pair::from_seed(&seed);
Ok(pair)
}
/// get mnemonic from predefined derivation path
pub fn predefined_mnemonic(deriv: &str) -> String {
format!("{SUBSTRATE_MNEMONIC}//{deriv}")
}
/// get keypair from predefined secret
pub fn pair_from_predefined(deriv: &str) -> Result<Sr25519Pair, GcliError> {
pair_from_str(&predefined_mnemonic(deriv))
}
/// get keypair from Cesium id/pwd
pub fn pair_from_cesium(id: String, pwd: String) -> nacl::sign::Keypair {
let params = scrypt::Params::new(12u8, 16u32, 1u32, 32).unwrap();
let seed = &mut [0u8; 32];
scrypt::scrypt(&pwd.into_bytes(), &id.into_bytes(), ¶ms, seed).unwrap();
nacl::sign::generate_keypair(seed)
}
/// ask user to input a secret
pub fn prompt_secret_substrate() -> Sr25519Pair {
loop {
let mnemonic = &rpassword::prompt_password("Mnemonic: ").unwrap();
match pair_from_str(mnemonic) {
Ok(pair) => return pair,
Err(_) => println!("Invalid secret"),
}
}
}
/// ask user pass (Cesium format)
pub fn prompt_secret_cesium() -> nacl::sign::Keypair {
let id = rpassword::prompt_password("Cesium id: ").unwrap();
let pwd = rpassword::prompt_password("Cesium password: ").unwrap();
pair_from_cesium(id, pwd)
}
/// ask user to input a seed
pub fn prompt_seed() -> Sr25519Pair {
loop {
let seed = &rpassword::prompt_password("Seed: ").unwrap();
match pair_from_seed(seed) {
Ok(pair) => return pair,
Err(_) => println!("Invalid seed"),
}
}
}
/// ask user pass (Cesium format)
pub fn prompt_predefined() -> Sr25519Pair {
let deriv = rpassword::prompt_password("Enter derivation path: ").unwrap();
pair_from_predefined(&deriv).expect("invalid secret")
}
/// ask user secret in relevant format
pub fn prompt_secret(secret_format: SecretFormat) -> KeyPair {
match secret_format {
SecretFormat::Substrate => prompt_secret_substrate().into(),
SecretFormat::Cesium => prompt_secret_cesium().into(),
SecretFormat::Seed => prompt_seed().into(),
SecretFormat::Predefined => prompt_predefined().into(),
}
}