Skip to content

Commit

Permalink
Merge branch 'development' into dan-layer-state-store
Browse files Browse the repository at this point in the history
* development:
  fix: wallet database encryption does not bind to field keys tari-project#4137 (tari-project#4340)
  fix: use SafePassword struct instead of String for passwords (tari-project#4320)
  feat(dan): template macro handles component state (tari-project#4380)
  fix(dht)!: add message padding for message decryption, to reduce message length leaks (fixes tari-project#4140) (tari-project#4362)
  fix(wallet): update seed words for output manager tests (tari-project#4379)
  • Loading branch information
sdbondi committed Aug 4, 2022
2 parents f00742c + 32184b5 commit 214fc14
Show file tree
Hide file tree
Showing 33 changed files with 1,189 additions and 365 deletions.
7 changes: 5 additions & 2 deletions applications/tari_console_wallet/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ use clap::{Args, Parser, Subcommand};
use tari_app_utilities::{common_cli_args::CommonCliArgs, utilities::UniPublicKey};
use tari_comms::multiaddr::Multiaddr;
use tari_core::transactions::{tari_amount, tari_amount::MicroTari};
use tari_utilities::hex::{Hex, HexError};
use tari_utilities::{
hex::{Hex, HexError},
SafePassword,
};

const DEFAULT_NETWORK: &str = "dibbler";

Expand All @@ -45,7 +48,7 @@ pub(crate) struct Cli {
/// command line, since it's visible using `ps ax` from anywhere on the system, so always use the env var where
/// possible.
#[clap(long, env = "TARI_WALLET_PASSWORD", hide_env_values = true)]
pub password: Option<String>,
pub password: Option<SafePassword>,
/// Change the password for the console wallet
#[clap(long, alias = "update-password")]
pub change_password: bool,
Expand Down
24 changes: 12 additions & 12 deletions applications/tari_console_wallet/src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use tari_crypto::keys::PublicKey;
use tari_key_manager::{cipher_seed::CipherSeed, mnemonic::MnemonicLanguage};
use tari_p2p::{initialization::CommsInitializationError, peer_seeds::SeedPeer, TransportType};
use tari_shutdown::ShutdownSignal;
use tari_utilities::SafePassword;
use tari_wallet::{
error::{WalletError, WalletStorageError},
output_manager_service::storage::database::OutputManagerDatabase,
Expand Down Expand Up @@ -72,20 +73,19 @@ pub enum WalletBoot {
/// Gets the password provided by command line argument or environment variable if available.
/// Otherwise prompts for the password to be typed in.
pub fn get_or_prompt_password(
arg_password: Option<String>,
config_password: Option<String>,
) -> Result<Option<String>, ExitError> {
arg_password: Option<SafePassword>,
config_password: Option<SafePassword>,
) -> Result<Option<SafePassword>, ExitError> {
if arg_password.is_some() {
return Ok(arg_password);
}

let env = std::env::var_os(TARI_WALLET_PASSWORD);
if let Some(p) = env {
let env_password = Some(
p.into_string()
.map_err(|_| ExitError::new(ExitCode::IOError, "Failed to convert OsString into String"))?,
);
return Ok(env_password);
let env_password = p
.into_string()
.map_err(|_| ExitError::new(ExitCode::IOError, "Failed to convert OsString into String"))?;
return Ok(Some(env_password.into()));
}

if config_password.is_some() {
Expand All @@ -97,7 +97,7 @@ pub fn get_or_prompt_password(
Ok(Some(password))
}

fn prompt_password(prompt: &str) -> Result<String, ExitError> {
fn prompt_password(prompt: &str) -> Result<SafePassword, ExitError> {
let password = loop {
let pass = prompt_password_stdout(prompt).map_err(|e| ExitError::new(ExitCode::IOError, e))?;
if pass.is_empty() {
Expand All @@ -108,13 +108,13 @@ fn prompt_password(prompt: &str) -> Result<String, ExitError> {
}
};

Ok(password)
Ok(SafePassword::from(password))
}

/// Allows the user to change the password of the wallet.
pub async fn change_password(
config: &ApplicationConfig,
arg_password: Option<String>,
arg_password: Option<SafePassword>,
shutdown_signal: ShutdownSignal,
) -> Result<(), ExitError> {
let mut wallet = init_wallet(config, arg_password, None, None, shutdown_signal).await?;
Expand Down Expand Up @@ -221,7 +221,7 @@ pub(crate) fn wallet_mode(cli: &Cli, boot_mode: WalletBoot) -> WalletMode {
#[allow(clippy::too_many_lines)]
pub async fn init_wallet(
config: &ApplicationConfig,
arg_password: Option<String>,
arg_password: Option<SafePassword>,
seed_words_file_name: Option<PathBuf>,
recovery_seed: Option<CipherSeed>,
shutdown_signal: ShutdownSignal,
Expand Down
3 changes: 2 additions & 1 deletion base_layer/wallet/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use tari_common::{
};
use tari_comms::multiaddr::Multiaddr;
use tari_p2p::P2pConfig;
use tari_utilities::SafePassword;

use crate::{
base_node_service::config::BaseNodeServiceConfig,
Expand Down Expand Up @@ -72,7 +73,7 @@ pub struct WalletConfig {
/// The main wallet db sqlite database backend connection pool size for concurrent reads
pub db_connection_pool_size: usize,
/// The main wallet password
pub password: Option<String>, // TODO: Make clear on drop
pub password: Option<SafePassword>,
/// The auto ping interval to use for contacts liveness data
#[serde(with = "serializers::seconds")]
pub contacts_auto_ping_interval: Duration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,24 +151,38 @@ pub struct KeyManagerStateUpdateSql {
}

impl Encryptable<Aes256Gcm> for KeyManagerStateSql {
fn domain(&self, field_name: &'static str) -> Vec<u8> {
[Self::KEY_MANAGER, self.branch_seed.as_bytes(), field_name.as_bytes()]
.concat()
.to_vec()
}

fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
let encrypted_index = encrypt_bytes_integral_nonce(cipher, self.primary_key_index.clone())?;
self.primary_key_index = encrypted_index;
self.primary_key_index =
encrypt_bytes_integral_nonce(cipher, self.domain("primary_key_index"), self.primary_key_index.clone())?;

Ok(())
}

fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
let decrypted_index = decrypt_bytes_integral_nonce(cipher, self.primary_key_index.clone())?;
self.primary_key_index = decrypted_index;
self.primary_key_index =
decrypt_bytes_integral_nonce(cipher, self.domain("primary_key_index"), self.primary_key_index.clone())?;

Ok(())
}
}

impl Encryptable<Aes256Gcm> for NewKeyManagerStateSql {
fn domain(&self, field_name: &'static str) -> Vec<u8> {
[Self::KEY_MANAGER, self.branch_seed.as_bytes(), field_name.as_bytes()]
.concat()
.to_vec()
}

fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
let encrypted_index = encrypt_bytes_integral_nonce(cipher, self.primary_key_index.clone())?;
self.primary_key_index = encrypted_index;
self.primary_key_index =
encrypt_bytes_integral_nonce(cipher, self.domain("primary_key_index"), self.primary_key_index.clone())?;

Ok(())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1456,13 +1456,23 @@ impl From<KnownOneSidedPaymentScript> for KnownOneSidedPaymentScriptSql {
}

impl Encryptable<Aes256Gcm> for KnownOneSidedPaymentScriptSql {
fn domain(&self, field_name: &'static str) -> Vec<u8> {
[
Self::KNOWN_ONESIDED_PAYMENT_SCRIPT,
self.script_hash.as_slice(),
field_name.as_bytes(),
]
.concat()
.to_vec()
}

fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
self.private_key = encrypt_bytes_integral_nonce(cipher, self.private_key.clone())?;
self.private_key = encrypt_bytes_integral_nonce(cipher, self.domain("private_key"), self.private_key.clone())?;
Ok(())
}

fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
self.private_key = decrypt_bytes_integral_nonce(cipher, self.private_key.clone())?;
self.private_key = decrypt_bytes_integral_nonce(cipher, self.domain("private_key"), self.private_key.clone())?;
Ok(())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,36 @@ impl NewOutputSql {
}

impl Encryptable<Aes256Gcm> for NewOutputSql {
fn domain(&self, field_name: &'static str) -> Vec<u8> {
// WARNING: using `OUTPUT` for both NewOutputSql and OutputSql due to later transition without re-encryption
[Self::OUTPUT, self.script.as_slice(), field_name.as_bytes()]
.concat()
.to_vec()
}

fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
self.spending_key = encrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?;
self.script_private_key = encrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?;
self.spending_key =
encrypt_bytes_integral_nonce(cipher, self.domain("spending_key"), self.spending_key.clone())?;

self.script_private_key = encrypt_bytes_integral_nonce(
cipher,
self.domain("script_private_key"),
self.script_private_key.clone(),
)?;

Ok(())
}

fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
self.spending_key = decrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?;
self.script_private_key = decrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?;
self.spending_key =
decrypt_bytes_integral_nonce(cipher, self.domain("spending_key"), self.spending_key.clone())?;

self.script_private_key = decrypt_bytes_integral_nonce(
cipher,
self.domain("script_private_key"),
self.script_private_key.clone(),
)?;

Ok(())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,15 +746,36 @@ impl TryFrom<OutputSql> for DbUnblindedOutput {
}

impl Encryptable<Aes256Gcm> for OutputSql {
fn domain(&self, field_name: &'static str) -> Vec<u8> {
// WARNING: using `OUTPUT` for both NewOutputSql and OutputSql due to later transition without re-encryption
[Self::OUTPUT, self.script.as_slice(), field_name.as_bytes()]
.concat()
.to_vec()
}

fn encrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
self.spending_key = encrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?;
self.script_private_key = encrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?;
self.spending_key =
encrypt_bytes_integral_nonce(cipher, self.domain("spending_key"), self.spending_key.clone())?;

self.script_private_key = encrypt_bytes_integral_nonce(
cipher,
self.domain("script_private_key"),
self.script_private_key.clone(),
)?;

Ok(())
}

fn decrypt(&mut self, cipher: &Aes256Gcm) -> Result<(), String> {
self.spending_key = decrypt_bytes_integral_nonce(cipher, self.spending_key.clone())?;
self.script_private_key = decrypt_bytes_integral_nonce(cipher, self.script_private_key.clone())?;
self.spending_key =
decrypt_bytes_integral_nonce(cipher, self.domain("spending_key"), self.spending_key.clone())?;

self.script_private_key = decrypt_bytes_integral_nonce(
cipher,
self.domain("script_private_key"),
self.script_private_key.clone(),
)?;

Ok(())
}
}
Expand Down
5 changes: 3 additions & 2 deletions base_layer/wallet/src/storage/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use tari_comms::{
tor::TorIdentity,
};
use tari_key_manager::cipher_seed::CipherSeed;
use tari_utilities::SafePassword;

use crate::{error::WalletStorageError, utxo_scanner_service::service::ScannedBlock};

Expand All @@ -46,7 +47,7 @@ pub trait WalletBackend: Send + Sync + Clone {
/// Modify the state the of the backend with a write operation
fn write(&self, op: WriteOperation) -> Result<Option<DbValue>, WalletStorageError>;
/// Apply encryption to the backend.
fn apply_encryption(&self, passphrase: String) -> Result<Aes256Gcm, WalletStorageError>;
fn apply_encryption(&self, passphrase: SafePassword) -> Result<Aes256Gcm, WalletStorageError>;
/// Remove encryption from the backend.
fn remove_encryption(&self) -> Result<(), WalletStorageError>;

Expand Down Expand Up @@ -276,7 +277,7 @@ where T: WalletBackend + 'static
Ok(())
}

pub async fn apply_encryption(&self, passphrase: String) -> Result<Aes256Gcm, WalletStorageError> {
pub async fn apply_encryption(&self, passphrase: SafePassword) -> Result<Aes256Gcm, WalletStorageError> {
let db_clone = self.db.clone();
tokio::task::spawn_blocking(move || db_clone.apply_encryption(passphrase))
.await
Expand Down

0 comments on commit 214fc14

Please sign in to comment.