Skip to content

Commit

Permalink
Merge pull request #53 from Electric-Coin-Company/encrypted-mnemonic
Browse files Browse the repository at this point in the history
Encrypt the mnemonic phrase with age
  • Loading branch information
str4d authored Dec 8, 2024
2 parents 08c5e89 + 5530cc0 commit 8a51272
Show file tree
Hide file tree
Showing 17 changed files with 715 additions and 184 deletions.
424 changes: 412 additions & 12 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ zcash_proofs = "0.20"
zcash_protocol = "0.4"
zip321 = "0.2"

# Seed encryption
age = { version = "0.11", features = ["armor", "plugin"] }

# Currency conversion
iso_currency = { version = "0.5", features = ["with-serde"] }
rust_decimal = "1"
Expand Down
5 changes: 1 addition & 4 deletions src/commands/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ use zcash_client_sqlite::WalletDb;
use zcash_protocol::value::{Zatoshis, COIN};

use crate::{
data::{get_db_paths, get_wallet_network},
error,
remote::tor_client,
ui::format_zec,
config::get_wallet_network, data::get_db_paths, error, remote::tor_client, ui::format_zec,
MIN_CONFIRMATIONS,
};

Expand Down
3 changes: 2 additions & 1 deletion src/commands/enhance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use zcash_primitives::transaction::{Transaction, TxId};
use zcash_protocol::consensus::{BlockHeight, BranchId, Network};

use crate::{
data::{get_db_paths, get_wallet_network},
config::get_wallet_network,
data::get_db_paths,
remote::{tor_client, Servers},
};

Expand Down
13 changes: 10 additions & 3 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ use zcash_primitives::consensus::{self, Parameters};
use zcash_protocol::consensus::BlockHeight;

use crate::{
data::{init_dbs, init_wallet_config, Network},
config::WalletConfig,
data::{init_dbs, Network},
error,
remote::{tor_client, Servers},
};

// Options accepted for the `init` command
#[derive(Debug, Options)]
pub(crate) struct Command {
#[options(help = "age identity file to encrypt the mnemonic phrase to")]
identity: String,

#[options(help = "mnemonic phrase to initialise the wallet with (default is new phrase)")]
phrase: Option<String>,

Expand Down Expand Up @@ -66,6 +70,8 @@ impl Command {
.try_into()
.expect("block heights must fit into u32");

let recipients = age::IdentityFile::from_file(opts.identity)?.to_recipients()?;

// Parse or create the wallet's mnemonic phrase.
let (mnemonic, recover_until) = if let Some(phrase) = opts.phrase {
(
Expand All @@ -86,9 +92,10 @@ impl Command {
.await?;

// Save the wallet keys to disk.
init_wallet_config(
WalletConfig::init_with_mnemonic(
wallet_dir.as_ref(),
Some(&mnemonic),
recipients.iter().map(|r| r.as_ref() as _),
&mnemonic,
birthday.height(),
opts.network.into(),
)?;
Expand Down
7 changes: 4 additions & 3 deletions src/commands/init_fvk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use zcash_primitives::consensus::NetworkType;
use zcash_protocol::consensus;

use crate::{
data::{init_dbs, init_wallet_config},
config::WalletConfig,
data::init_dbs,
remote::{tor_client, Servers},
};

Expand Down Expand Up @@ -122,8 +123,8 @@ impl Command {
)
.await?;

// Save the wallet keys to disk.
init_wallet_config(wallet_dir.as_ref(), None, birthday.height().into(), network)?;
// Save the wallet config to disk.
WalletConfig::init_without_mnemonic(wallet_dir.as_ref(), birthday.height(), network)?;

let mut wallet_db = init_dbs(network, wallet_dir.as_ref())?;
wallet_db.import_account_ufvk(&ufvk, &birthday, opts.purpose.into())?;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/list_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use gumdrop::Options;
use zcash_client_backend::data_api::{Account, WalletRead};
use zcash_client_sqlite::WalletDb;

use crate::data::{get_db_paths, get_wallet_network};
use crate::{config::get_wallet_network, data::get_db_paths};

// Options accepted for the `list-accounts` command
#[derive(Debug, Options)]
Expand Down
2 changes: 1 addition & 1 deletion src/commands/list_addresses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use zcash_client_backend::data_api::{Account, WalletRead};
use zcash_client_sqlite::{AccountId, WalletDb};
use zcash_keys::keys::UnifiedAddressRequest;

use crate::data::{get_db_paths, get_wallet_network};
use crate::{config::get_wallet_network, data::get_db_paths};

// Options accepted for the `list-accounts` command
#[derive(Debug, Options)]
Expand Down
6 changes: 1 addition & 5 deletions src/commands/list_unspent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ use zcash_client_backend::{
use zcash_client_sqlite::WalletDb;
use zcash_protocol::value::{Zatoshis, MAX_MONEY};

use crate::{
data::{get_db_paths, get_wallet_network},
error,
ui::format_zec,
};
use crate::{config::get_wallet_network, data::get_db_paths, error, ui::format_zec};

// Options accepted for the `balance` command
#[derive(Debug, Options)]
Expand Down
5 changes: 1 addition & 4 deletions src/commands/propose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ use zcash_client_sqlite::WalletDb;
use zcash_protocol::value::Zatoshis;
use zip321::{Payment, TransactionRequest};

use crate::{
data::{get_db_paths, get_wallet_network},
error, MIN_CONFIRMATIONS,
};
use crate::{config::get_wallet_network, data::get_db_paths, error, MIN_CONFIRMATIONS};
// Options accepted for the `propose` command
#[derive(Debug, Options)]
pub(crate) struct Command {
Expand Down
19 changes: 14 additions & 5 deletions src/commands/reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ use gumdrop::Options;
use zcash_client_backend::proto::service;

use crate::{
data::{erase_wallet_state, read_config},
config::WalletConfig,
data::erase_wallet_state,
remote::{tor_client, Servers},
};

// Options accepted for the `reset` command
#[derive(Debug, Options)]
pub(crate) struct Command {
#[options(help = "age identity file to decrypt the mnemonic phrase with")]
identity: String,

#[options(help = "the number of accounts to re-initialise the wallet with (default is 1)")]
accounts: Option<usize>,

Expand All @@ -28,8 +32,8 @@ pub(crate) struct Command {
impl Command {
pub(crate) async fn run(self, wallet_dir: Option<String>) -> Result<(), anyhow::Error> {
// Load the wallet network, seed, and birthday from disk.
let keys = read_config(wallet_dir.as_ref())?;
let params = keys.network();
let mut config = WalletConfig::read(wallet_dir.as_ref())?;
let params = config.network();

// Connect to the client (for re-initializing the wallet).
let server = self.server.pick(params)?;
Expand All @@ -49,17 +53,22 @@ impl Command {
.expect("block heights must fit into u32");

let birthday =
super::init::Command::get_wallet_birthday(client, keys.birthday(), Some(chain_tip))
super::init::Command::get_wallet_birthday(client, config.birthday(), Some(chain_tip))
.await?;

// Erase the wallet state (excluding key material).
erase_wallet_state(wallet_dir.as_ref()).await;

// Decrypt the mnemonic to access the seed.
let identities = age::IdentityFile::from_file(self.identity)?.into_identities()?;
config.decrypt(identities.iter().map(|i| i.as_ref() as _))?;

// Re-initialize the wallet state.
super::init::Command::init_dbs(
params,
wallet_dir.as_ref(),
keys.seed()
config
.seed()
.ok_or(anyhow!("Seed is required for database reset"))?,
birthday,
self.accounts.unwrap_or(1),
Expand Down
17 changes: 13 additions & 4 deletions src/commands/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use zcash_protocol::value::Zatoshis;
use zip321::{Payment, TransactionRequest};

use crate::{
data::{get_db_paths, read_config},
config::WalletConfig,
data::get_db_paths,
error,
remote::{tor_client, Servers},
MIN_CONFIRMATIONS,
Expand All @@ -34,6 +35,9 @@ use crate::{
// Options accepted for the `send` command
#[derive(Debug, Options)]
pub(crate) struct Command {
#[options(help = "age identity file to decrypt the mnemonic phrase with")]
identity: String,

#[options(
required,
help = "the recipient's Unified, Sapling or transparent address"
Expand Down Expand Up @@ -68,8 +72,8 @@ pub(crate) struct Command {

impl Command {
pub(crate) async fn run(self, wallet_dir: Option<String>) -> Result<(), anyhow::Error> {
let keys = read_config(wallet_dir.as_ref())?;
let params = keys.network();
let mut config = WalletConfig::read(wallet_dir.as_ref())?;
let params = config.network();

let (_, db_data) = get_db_paths(wallet_dir.as_ref());
let mut db_data = WalletDb::for_path(db_data, params)?;
Expand All @@ -87,9 +91,14 @@ impl Command {
}
};

// Decrypt the mnemonic to access the seed.
let identities = age::IdentityFile::from_file(self.identity)?.into_identities()?;
config.decrypt(identities.iter().map(|i| i.as_ref() as _))?;

let usk = UnifiedSpendingKey::from_seed(
&params,
keys.seed()
config
.seed()
.ok_or(anyhow!("Seed must be present to enable sending"))?
.expose_secret(),
account_index,
Expand Down
3 changes: 2 additions & 1 deletion src/commands/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use zcash_primitives::merkle_tree::HashSer;
use zcash_protocol::consensus::{BlockHeight, Parameters};

use crate::{
data::{get_block_path, get_db_paths, get_wallet_network},
config::get_wallet_network,
data::{get_block_path, get_db_paths},
error,
remote::Servers,
ShutdownListener,
Expand Down
20 changes: 17 additions & 3 deletions src/commands/upgrade.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::anyhow;
use gumdrop::Options;

use zcash_client_sqlite::{
Expand All @@ -7,13 +8,17 @@ use zcash_client_sqlite::{
};

use crate::{
data::{get_db_paths, get_wallet_network, get_wallet_seed},
config::{get_wallet_network, get_wallet_seed},
data::get_db_paths,
error,
};

// Options accepted for the `upgrade` command
#[derive(Debug, Options)]
pub(crate) struct Command {}
pub(crate) struct Command {
#[options(help = "age identity file to decrypt the mnemonic phrase with")]
identity: Option<String>,
}

impl Command {
pub(crate) fn run(self, wallet_dir: Option<String>) -> Result<(), anyhow::Error> {
Expand All @@ -30,7 +35,16 @@ impl Command {
error, ..
} if matches!(error, WalletMigrationError::SeedRequired))
{
init_wallet_db(&mut db_data, get_wallet_seed(wallet_dir)?)?;
let identities = age::IdentityFile::from_file(
self.identity
.ok_or(anyhow!("Identity file required to decrypt mnemonic phrase"))?,
)?
.into_identities()?;

init_wallet_db(
&mut db_data,
get_wallet_seed(wallet_dir, identities.iter().map(|i| i.as_ref() as _))?,
)?;
} else {
return Err(e.into());
}
Expand Down
Loading

0 comments on commit 8a51272

Please sign in to comment.