From 7812a9e8925ef8a5fb86c2a24fb5a89669a238e7 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Thu, 20 Dec 2018 13:10:36 +0000 Subject: [PATCH] [Floonet] Encrypt private slate data upon storage in DB (#2189) * xor encrypt stored nonce and blind sum in transaction data * rustfmt * stop doc tests splatting wallet files throughout --- wallet/src/libwallet/api.rs | 8 +++++ wallet/src/lmdb_wallet.rs | 66 +++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/wallet/src/libwallet/api.rs b/wallet/src/libwallet/api.rs index 2dd9f88d1..a253a1de3 100644 --- a/wallet/src/libwallet/api.rs +++ b/wallet/src/libwallet/api.rs @@ -99,6 +99,7 @@ where /// use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// /// let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// /// // A NodeClient must first be created to handle communication between /// // the wallet and the node. @@ -148,6 +149,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( @@ -203,6 +205,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( @@ -256,6 +259,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( @@ -313,6 +317,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( @@ -389,6 +394,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( @@ -462,6 +468,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( @@ -575,6 +582,7 @@ where /// # use wallet::libwallet::api::APIOwner; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # let mut wallet_config = WalletConfig::default(); + /// # wallet_config.data_file_dir = "test_output/doc/wallet1".to_owned(); /// # let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None); /// # let mut wallet:Arc>> = /// # Arc::new(Mutex::new( diff --git a/wallet/src/lmdb_wallet.rs b/wallet/src/lmdb_wallet.rs index 49c015f5c..564bb4dd8 100644 --- a/wallet/src/lmdb_wallet.rs +++ b/wallet/src/lmdb_wallet.rs @@ -26,6 +26,8 @@ use serde_json; use failure::ResultExt; use uuid::Uuid; +use crate::blake2::blake2b::Blake2b; + use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain}; use crate::store::{self, option_to_not_found, to_key, to_key_u64}; @@ -35,6 +37,7 @@ use crate::libwallet::types::*; use crate::libwallet::{internal, Error, ErrorKind}; use crate::types::{WalletConfig, WalletSeed}; use crate::util; +use crate::util::secp::constants::SECRET_KEY_SIZE; use crate::util::secp::pedersen; pub const DB_DIR: &'static str = "db"; @@ -62,6 +65,39 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool { db_path.exists() } +/// Helper to derive XOR keys for storing private transaction keys in the DB +/// (blind_xor_key, nonce_xor_key) +fn private_ctx_xor_keys( + keychain: &K, + slate_id: &[u8], +) -> Result<([u8; SECRET_KEY_SIZE], [u8; SECRET_KEY_SIZE]), Error> +where + K: Keychain, +{ + let root_key = keychain.derive_key(0, &K::root_key_id())?; + + // derive XOR values for storing secret values in DB + // h(root_key|slate_id|"blind") + let mut hasher = Blake2b::new(SECRET_KEY_SIZE); + hasher.update(&root_key.0[..]); + hasher.update(&slate_id[..]); + hasher.update(&"blind".as_bytes()[..]); + let blind_xor_key = hasher.finalize(); + let mut ret_blind = [0; SECRET_KEY_SIZE]; + ret_blind.copy_from_slice(&blind_xor_key.as_bytes()[0..SECRET_KEY_SIZE]); + + // h(root_key|slate_id|"nonce") + let mut hasher = Blake2b::new(SECRET_KEY_SIZE); + hasher.update(&root_key.0[..]); + hasher.update(&slate_id[..]); + hasher.update(&"nonce".as_bytes()[..]); + let nonce_xor_key = hasher.finalize(); + let mut ret_nonce = [0; SECRET_KEY_SIZE]; + ret_nonce.copy_from_slice(&nonce_xor_key.as_bytes()[0..SECRET_KEY_SIZE]); + + Ok((ret_blind, ret_nonce)) +} + pub struct LMDBBackend { db: store::Store, config: WalletConfig, @@ -232,11 +268,19 @@ where fn get_private_context(&mut self, slate_id: &[u8]) -> Result { let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec()); - option_to_not_found( + let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?; + + let mut ctx: Context = option_to_not_found( self.db.get_ser(&ctx_key), &format!("Slate id: {:x?}", slate_id.to_vec()), - ) - .map_err(|e| e.into()) + )?; + + for i in 0..SECRET_KEY_SIZE { + ctx.sec_key.0[i] = ctx.sec_key.0[i] ^ blind_xor_key[i]; + ctx.sec_nonce.0[i] = ctx.sec_nonce.0[i] ^ nonce_xor_key[i]; + } + + Ok(ctx) } fn acct_path_iter<'a>(&'a self) -> Box + 'a> { @@ -501,11 +545,21 @@ where self.save(out.clone()) } - //TODO: Keys stored unencrypted in DB.. not good - // should store keys as derivation paths instead fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> { let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec()); - self.db.borrow().as_ref().unwrap().put_ser(&ctx_key, &ctx)?; + let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?; + + let mut s_ctx = ctx.clone(); + for i in 0..SECRET_KEY_SIZE { + s_ctx.sec_key.0[i] = s_ctx.sec_key.0[i] ^ blind_xor_key[i]; + s_ctx.sec_nonce.0[i] = s_ctx.sec_nonce.0[i] ^ nonce_xor_key[i]; + } + + self.db + .borrow() + .as_ref() + .unwrap() + .put_ser(&ctx_key, &s_ctx)?; Ok(()) }