From 6bbe39c236a36bd7d7c399af283fe689e6a83b61 Mon Sep 17 00:00:00 2001 From: Hanh Date: Mon, 6 Mar 2023 20:52:00 +1000 Subject: [PATCH] BIP39 + extra word --- src/key.rs | 115 --------------------------------------------- src/key2.rs | 27 +++++++++-- src/orchard/key.rs | 6 ++- src/taddr.rs | 6 ++- src/zip32.rs | 6 ++- 5 files changed, 34 insertions(+), 126 deletions(-) delete mode 100644 src/key.rs diff --git a/src/key.rs b/src/key.rs deleted file mode 100644 index 738b249..0000000 --- a/src/key.rs +++ /dev/null @@ -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, Option, 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 { - 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() - } -} diff --git a/src/key2.rs b/src/key2.rs index 60435c6..b10dbf3 100644 --- a/src/key2.rs +++ b/src/key2.rs @@ -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 { 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), diff --git a/src/orchard/key.rs b/src/orchard/key.rs index 5081fa8..d7bf576 100644 --- a/src/orchard/key.rs +++ b/src/orchard/key.rs @@ -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 { diff --git a/src/taddr.rs b/src/taddr.rs index 80ac884..edaff7c 100644 --- a/src/taddr.rs +++ b/src/taddr.rs @@ -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())?; diff --git a/src/zip32.rs b/src/zip32.rs index 421c631..f160aae 100644 --- a/src/zip32.rs +++ b/src/zip32.rs @@ -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, ) -> anyhow::Result { - 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),