[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
This commit is contained in:
Yeastplume 2018-12-20 13:10:36 +00:00 committed by GitHub
parent a42250445d
commit 7812a9e892
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 6 deletions

View file

@ -99,6 +99,7 @@ where
/// use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// ///
/// let mut wallet_config = WalletConfig::default(); /// 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 /// // A NodeClient must first be created to handle communication between
/// // the wallet and the node. /// // the wallet and the node.
@ -148,6 +149,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(
@ -203,6 +205,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(
@ -256,6 +259,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(
@ -313,6 +317,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(
@ -389,6 +394,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(
@ -462,6 +468,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(
@ -575,6 +582,7 @@ where
/// # use wallet::libwallet::api::APIOwner; /// # use wallet::libwallet::api::APIOwner;
/// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig}; /// # use wallet::{LMDBBackend, HTTPNodeClient, WalletBackend, WalletConfig};
/// # let mut wallet_config = WalletConfig::default(); /// # 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 node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, None);
/// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> = /// # let mut wallet:Arc<Mutex<WalletBackend<HTTPNodeClient, ExtKeychain>>> =
/// # Arc::new(Mutex::new( /// # Arc::new(Mutex::new(

View file

@ -26,6 +26,8 @@ use serde_json;
use failure::ResultExt; use failure::ResultExt;
use uuid::Uuid; use uuid::Uuid;
use crate::blake2::blake2b::Blake2b;
use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain}; use crate::keychain::{ChildNumber, ExtKeychain, Identifier, Keychain};
use crate::store::{self, option_to_not_found, to_key, to_key_u64}; 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::libwallet::{internal, Error, ErrorKind};
use crate::types::{WalletConfig, WalletSeed}; use crate::types::{WalletConfig, WalletSeed};
use crate::util; use crate::util;
use crate::util::secp::constants::SECRET_KEY_SIZE;
use crate::util::secp::pedersen; use crate::util::secp::pedersen;
pub const DB_DIR: &'static str = "db"; pub const DB_DIR: &'static str = "db";
@ -62,6 +65,39 @@ pub fn wallet_db_exists(config: WalletConfig) -> bool {
db_path.exists() 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<K>(
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<C, K> { pub struct LMDBBackend<C, K> {
db: store::Store, db: store::Store,
config: WalletConfig, config: WalletConfig,
@ -232,11 +268,19 @@ where
fn get_private_context(&mut self, slate_id: &[u8]) -> Result<Context, Error> { fn get_private_context(&mut self, slate_id: &[u8]) -> Result<Context, Error> {
let ctx_key = to_key(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec()); 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), self.db.get_ser(&ctx_key),
&format!("Slate id: {:x?}", slate_id.to_vec()), &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<dyn Iterator<Item = AcctPathMapping> + 'a> { fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a> {
@ -501,11 +545,21 @@ where
self.save(out.clone()) 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> { 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()); 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(()) Ok(())
} }