BIP39 + extra word

This commit is contained in:
Hanh 2023-03-06 20:52:00 +10:00
parent 3205a4fe59
commit 6bbe39c236
5 changed files with 34 additions and 126 deletions

View File

@ -1,115 +0,0 @@
//! DEAD CODE - Candidate for deletion
//!
use bech32::{ToBase32, Variant};
use bip39::{Language, Mnemonic, Seed};
use rand::rngs::OsRng;
use rand::RngCore;
use zcash_client_backend::address::RecipientAddress;
use zcash_client_backend::encoding::{
decode_extended_full_viewing_key, decode_extended_spending_key,
encode_extended_full_viewing_key, encode_extended_spending_key, encode_payment_address,
};
use zcash_params::coin::{get_coin_chain, CoinChain, CoinType};
use zcash_primitives::consensus::Parameters;
use zcash_primitives::zip32::{ChildIndex, ExtendedFullViewingKey, ExtendedSpendingKey};
pub struct KeyHelpers {
coin_type: CoinType,
}
impl KeyHelpers {
pub fn new(coin_type: CoinType) -> Self {
KeyHelpers { coin_type }
}
fn chain(&self) -> &dyn CoinChain {
get_coin_chain(self.coin_type)
}
pub fn decode_key(
&self,
key: &str,
index: u32,
) -> anyhow::Result<(Option<String>, Option<String>, String, String)> {
let network = self.chain().network();
let res = if let Ok(mnemonic) = Mnemonic::from_phrase(key, Language::English) {
let (sk, ivk, pa) = self.derive_secret_key(&mnemonic, index)?;
Ok((Some(key.to_string()), Some(sk), ivk, pa))
} else if let Ok(sk) =
decode_extended_spending_key(network.hrp_sapling_extended_spending_key(), key)
{
let (ivk, pa) = self.derive_viewing_key(&sk)?;
Ok((None, Some(key.to_string()), ivk, pa))
} else if let Ok(fvk) =
decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), key)
{
let pa = self.derive_address(&fvk)?;
Ok((None, None, key.to_string(), pa))
} else {
Err(anyhow::anyhow!("Not a valid key"))
};
res
}
pub fn is_valid_key(&self, key: &str) -> i8 {
let network = self.chain().network();
if Mnemonic::from_phrase(key, Language::English).is_ok() {
return 0;
}
if decode_extended_spending_key(network.hrp_sapling_extended_spending_key(), key).is_ok()
{
return 1;
}
if decode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), key).is_ok()
{
return 2;
}
-1
}
pub fn derive_secret_key(
&self,
mnemonic: &Mnemonic,
index: u32,
) -> anyhow::Result<(String, String, String)> {
let network = self.chain().network();
let seed = Seed::new(mnemonic, "");
let master = ExtendedSpendingKey::master(seed.as_bytes());
let path = [
ChildIndex::Hardened(32),
ChildIndex::Hardened(network.coin_type()),
ChildIndex::Hardened(index),
];
let extsk = ExtendedSpendingKey::from_path(&master, &path);
let sk = encode_extended_spending_key(network.hrp_sapling_extended_spending_key(), &extsk);
let (fvk, pa) = self.derive_viewing_key(&extsk)?;
Ok((sk, fvk, pa))
}
pub fn derive_viewing_key(
&self,
extsk: &ExtendedSpendingKey,
) -> anyhow::Result<(String, String)> {
let network = self.chain().network();
let fvk = ExtendedFullViewingKey::from(extsk);
let pa = self.derive_address(&fvk)?;
let fvk =
encode_extended_full_viewing_key(network.hrp_sapling_extended_full_viewing_key(), &fvk);
Ok((fvk, pa))
}
pub fn derive_address(&self, fvk: &ExtendedFullViewingKey) -> anyhow::Result<String> {
let network = self.chain().network();
let (_, payment_address) = fvk.default_address();
let address =
encode_payment_address(network.hrp_sapling_payment_address(), &payment_address);
Ok(address)
}
pub fn valid_address(&self, address: &str) -> bool {
let recipient = RecipientAddress::decode(self.chain().network(), address);
recipient.is_some()
}
}

View File

@ -10,6 +10,20 @@ use zcash_client_backend::keys::UnifiedFullViewingKey;
use zcash_primitives::consensus::{Network, Parameters};
use zcash_primitives::zip32::{ChildIndex, ExtendedFullViewingKey, ExtendedSpendingKey};
pub fn split_key(key: &str) -> (String, String) {
let words: Vec<_> = key.split_whitespace().collect();
let len = words.len();
let (phrase, password) = if len % 3 == 1 {
// extra word
let phrase = words[0..len - 1].join(" ");
let password = words[len - 1].to_string();
(phrase, password)
} else {
(key.to_string(), String::new())
};
(phrase, password)
}
pub fn decode_key(
coin: u8,
key: &str,
@ -23,8 +37,9 @@ pub fn decode_key(
)> {
let c = CoinConfig::get(coin);
let network = c.chain.network();
let res = if let Ok(mnemonic) = Mnemonic::from_phrase(key, Language::English) {
let (sk, ivk, pa) = derive_secret_key(network, &mnemonic, index)?;
let (phrase, password) = split_key(key);
let res = if let Ok(mnemonic) = Mnemonic::from_phrase(&phrase, Language::English) {
let (sk, ivk, pa) = derive_secret_key(network, &mnemonic, &password, index)?;
Ok((Some(key.to_string()), Some(sk), ivk, pa, None))
} else if let Ok(sk) =
decode_extended_spending_key(network.hrp_sapling_extended_spending_key(), key)
@ -59,7 +74,8 @@ pub fn decode_key(
pub fn is_valid_key(coin: u8, key: &str) -> i8 {
let c = CoinConfig::get(coin);
let network = c.chain.network();
if Mnemonic::from_phrase(key, Language::English).is_ok() {
let (phrase, _password) = split_key(key);
if Mnemonic::from_phrase(&phrase, Language::English).is_ok() {
return 0;
}
if decode_extended_spending_key(network.hrp_sapling_extended_spending_key(), key).is_ok() {
@ -80,15 +96,16 @@ pub fn is_valid_key(coin: u8, key: &str) -> i8 {
pub fn decode_address(coin: u8, address: &str) -> Option<RecipientAddress> {
let c = CoinConfig::get(coin);
let network = c.chain.network();
zcash_client_backend::address::RecipientAddress::decode(network, address)
RecipientAddress::decode(network, address)
}
fn derive_secret_key(
network: &Network,
mnemonic: &Mnemonic,
password: &str,
index: u32,
) -> anyhow::Result<(String, String, String)> {
let seed = Seed::new(mnemonic, "");
let seed = Seed::new(mnemonic, password);
let master = ExtendedSpendingKey::master(seed.as_bytes());
let path = [
ChildIndex::Hardened(32),

View File

@ -1,3 +1,4 @@
use crate::key2::split_key;
use bip39::{Language, Mnemonic, Seed};
use orchard::keys::{FullViewingKey, Scope, SpendingKey};
use orchard::Address;
@ -16,8 +17,9 @@ impl OrchardKeyBytes {
}
pub fn derive_orchard_keys(coin_type: u32, seed: &str, account_index: u32) -> OrchardKeyBytes {
let mnemonic = Mnemonic::from_phrase(seed, Language::English).unwrap();
let seed = Seed::new(&mnemonic, "");
let (phrase, password) = split_key(seed);
let mnemonic = Mnemonic::from_phrase(&phrase, Language::English).unwrap();
let seed = Seed::new(&mnemonic, &password);
let sk = SpendingKey::from_zip32_seed(seed.as_bytes(), coin_type, account_index).unwrap();
let fvk = FullViewingKey::from(&sk);
OrchardKeyBytes {

View File

@ -3,6 +3,7 @@ use crate::api::recipient::RecipientMemo;
use crate::chain::{get_checkpoint_height, EXPIRY_HEIGHT_OFFSET};
use crate::coinconfig::CoinConfig;
use crate::db::AccountData;
use crate::key2::split_key;
use crate::note_selection::{SecretKeys, Source, UTXO};
use crate::unified::orchard_as_unified;
use crate::{
@ -214,8 +215,9 @@ pub fn derive_tkeys(
phrase: &str,
path: &str,
) -> anyhow::Result<(String, String)> {
let mnemonic = Mnemonic::from_phrase(phrase, Language::English)?;
let seed = Seed::new(&mnemonic, "");
let (phrase, password) = split_key(phrase);
let mnemonic = Mnemonic::from_phrase(&phrase, Language::English)?;
let seed = Seed::new(&mnemonic, &password);
let ext = ExtendedPrivKey::derive(seed.as_bytes(), path)
.map_err(|_| anyhow!("Invalid derivation path"))?;
let secret_key = SecretKey::from_slice(&ext.secret())?;

View File

@ -1,3 +1,4 @@
use crate::key2::split_key;
use anyhow::anyhow;
use base58check::ToBase58Check;
use bip39::{Language, Mnemonic, Seed};
@ -28,8 +29,9 @@ pub fn derive_zip32(
external: u32,
address_index: Option<u32>,
) -> anyhow::Result<KeyPack> {
let mnemonic = Mnemonic::from_phrase(phrase, Language::English)?;
let seed = Seed::new(&mnemonic, "");
let (phrase, password) = split_key(phrase);
let mnemonic = Mnemonic::from_phrase(&phrase, Language::English)?;
let seed = Seed::new(&mnemonic, &password);
let master = ExtendedSpendingKey::master(seed.as_bytes());
let mut z_path = vec![
ChildIndex::Hardened(32),