wallet: additional tidy

This commit is contained in:
Jon Häggblad
2022-05-12 14:42:40 +02:00
parent 9e4904ff37
commit d2d99ca5c9
2 changed files with 89 additions and 116 deletions
@@ -70,7 +70,6 @@ impl StoredWallet {
.accounts
.iter_mut()
.find(|account| &account.id == id)
//.map(|account| &mut account.account)
.ok_or(BackendError::NoSuchIdInWallet)
}
@@ -132,6 +131,19 @@ pub(crate) struct EncryptedLogin {
pub account: EncryptedData<StoredLogin>,
}
impl EncryptedLogin {
pub(crate) fn encrypt(
id: LoginId,
login: &StoredLogin,
password: &UserPassword,
) -> Result<Self, BackendError> {
Ok(EncryptedLogin {
id,
account: super::encryption::encrypt_struct(login, password)?,
})
}
}
/// A stored login is either a account, such as a mnemonic, or a list of multiple accounts where
/// each has an inner id. Future proofed for having private key backed accounts.
#[derive(Serialize, Deserialize, Debug, Zeroize)]
@@ -144,17 +156,6 @@ pub(crate) enum StoredLogin {
}
impl StoredLogin {
pub(crate) fn new_mnemonic_backed_account(
mnemonic: bip39::Mnemonic,
hd_path: DerivationPath,
) -> Self {
Self::Mnemonic(MnemonicAccount { mnemonic, hd_path })
}
pub(crate) fn new_multiple_login() -> Self {
Self::Multiple(MultipleAccounts::empty())
}
#[cfg(test)]
pub(crate) fn as_mnemonic_account(&self) -> Option<&MnemonicAccount> {
match self {
@@ -171,64 +172,19 @@ impl StoredLogin {
}
}
// Return the login as multiple accounts, and if there is only a single mnemonic backed account,
// return a set containing only the single account paired with the account id passed as function
// argument.
pub(crate) fn unwrap_into_multiple_accounts(self, id: AccountId) -> MultipleAccounts {
match self {
StoredLogin::Mnemonic(ref account) => account.clone().into_multiple(id),
StoredLogin::Mnemonic(ref account) => {
vec![WalletAccount::from_mnemonic_account(id, account.clone())].into()
}
StoredLogin::Multiple(ref accounts) => accounts.clone(),
}
}
}
/// An account backed by a unique mnemonic.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub(crate) struct MnemonicAccount {
mnemonic: bip39::Mnemonic,
#[serde(with = "display_hd_path")]
hd_path: DerivationPath,
}
impl MnemonicAccount {
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
&self.mnemonic
}
pub(crate) fn hd_path(&self) -> &DerivationPath {
&self.hd_path
}
pub(crate) fn into_wallet_account(self, id: AccountId) -> WalletAccount {
WalletAccount {
id,
account: self.into(),
}
}
pub(crate) fn into_multiple(self, id: AccountId) -> MultipleAccounts {
MultipleAccounts::new(self.into_wallet_account(id))
}
}
impl Zeroize for MnemonicAccount {
fn zeroize(&mut self) {
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
// and the memory would have been filled with zeroes.
//
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
// of overwriting it with a fresh mnemonic that was never used before
//
// note: this function can only fail on an invalid word count, which clearly is not the case here
self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap();
// further note: we don't really care about the hd_path, there's nothing secret about it.
}
}
impl Drop for MnemonicAccount {
fn drop(&mut self) {
self.zeroize()
}
}
/// Multiple stored accounts, each entry having an id and a data field.
#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
pub(crate) struct MultipleAccounts {
@@ -236,18 +192,12 @@ pub(crate) struct MultipleAccounts {
}
impl MultipleAccounts {
pub(crate) fn empty() -> Self {
pub(crate) fn new() -> Self {
MultipleAccounts {
accounts: Vec::new(),
}
}
pub(crate) fn new(account: WalletAccount) -> Self {
MultipleAccounts {
accounts: vec![account],
}
}
pub(crate) fn get_accounts(&self) -> impl Iterator<Item = &WalletAccount> {
self.accounts.iter()
}
@@ -317,11 +267,20 @@ impl WalletAccount {
) -> Self {
Self {
id,
account: AccountData::new_mnemonic_backed_account(mnemonic, hd_path),
account: AccountData::Mnemonic(MnemonicAccount::new(mnemonic, hd_path)),
}
}
pub(crate) fn from_mnemonic_account(id: AccountId, mnemonic_account: MnemonicAccount) -> Self {
Self {
id,
account: AccountData::Mnemonic(mnemonic_account),
}
}
}
/// An account usually is a mnemonic account, but in the future it might be backed by a private
/// key.
#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
#[serde(untagged)]
#[zeroize(drop)]
@@ -331,13 +290,6 @@ pub(crate) enum AccountData {
}
impl AccountData {
pub(crate) fn new_mnemonic_backed_account(
mnemonic: bip39::Mnemonic,
hd_path: DerivationPath,
) -> AccountData {
AccountData::Mnemonic(MnemonicAccount { mnemonic, hd_path })
}
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
match self {
AccountData::Mnemonic(account) => account.mnemonic(),
@@ -352,9 +304,47 @@ impl AccountData {
}
}
impl From<MnemonicAccount> for AccountData {
fn from(mnemonic_account: MnemonicAccount) -> Self {
AccountData::Mnemonic(mnemonic_account)
/// An account backed by a unique mnemonic.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub(crate) struct MnemonicAccount {
mnemonic: bip39::Mnemonic,
#[serde(with = "display_hd_path")]
hd_path: DerivationPath,
}
impl MnemonicAccount {
pub(crate) fn new(mnemonic: bip39::Mnemonic, hd_path: DerivationPath) -> Self {
Self { mnemonic, hd_path }
}
pub(crate) fn mnemonic(&self) -> &bip39::Mnemonic {
&self.mnemonic
}
#[cfg(test)]
pub(crate) fn hd_path(&self) -> &DerivationPath {
&self.hd_path
}
}
impl Zeroize for MnemonicAccount {
fn zeroize(&mut self) {
// in ideal world, Mnemonic would have had zeroize defined on it (there's an almost year old PR that introduces it)
// and the memory would have been filled with zeroes.
//
// we really don't want to keep our real mnemonic in memory, so let's do the semi-nasty thing
// of overwriting it with a fresh mnemonic that was never used before
//
// note: this function can only fail on an invalid word count, which clearly is not the case here
self.mnemonic = bip39::Mnemonic::generate(self.mnemonic.word_count()).unwrap();
// further note: we don't really care about the hd_path, there's nothing secret about it.
}
}
impl Drop for MnemonicAccount {
fn drop(&mut self) {
self.zeroize()
}
}
+18 -35
View File
@@ -6,7 +6,6 @@ pub(crate) use crate::wallet_storage::password::{AccountId, LoginId, UserPasswor
use crate::error::BackendError;
use crate::platform_constants::{STORAGE_DIR_NAME, WALLET_INFO_FILENAME};
use crate::wallet_storage::encryption::encrypt_struct;
use cosmrs::bip32::DerivationPath;
use std::fs::{self, create_dir_all, OpenOptions};
use std::path::PathBuf;
@@ -108,12 +107,9 @@ fn store_wallet_login_at_file(
return Err(BackendError::WalletDifferentPasswordDetected);
}
let new_account = StoredLogin::new_mnemonic_backed_account(mnemonic, hd_path);
let new_encrypted_account = EncryptedLogin {
id,
account: encrypt_struct(&new_account, password)?,
};
let new_account = account_data::MnemonicAccount::new(mnemonic, hd_path);
let new_login = StoredLogin::Mnemonic(new_account);
let new_encrypted_account = EncryptedLogin::encrypt(id, &new_login, password)?;
stored_wallet.add_encrypted_login(new_encrypted_account)?;
let file = OpenOptions::new()
@@ -157,18 +153,10 @@ fn store_wallet_login_with_multiple_accounts_at_file(
return Err(BackendError::WalletDifferentPasswordDetected);
}
let mut new_accounts = account_data::MultipleAccounts::empty();
new_accounts.add(
DEFAULT_FIRST_ACCOUNT_NAME.to_string().into(),
mnemonic,
hd_path,
)?;
let mut new_accounts = account_data::MultipleAccounts::new();
new_accounts.add(DEFAULT_FIRST_ACCOUNT_NAME.into(), mnemonic, hd_path)?;
let new_login = StoredLogin::Multiple(new_accounts);
let new_encrypted_login = EncryptedLogin {
id,
account: encrypt_struct(&new_login, password)?,
};
let new_encrypted_login = EncryptedLogin::encrypt(id, &new_login, password)?;
stored_wallet.add_encrypted_login(new_encrypted_login)?;
@@ -222,10 +210,7 @@ fn append_account_to_wallet_login_at_file(
let mut accounts = decrypted_login.unwrap_into_multiple_accounts(first_id_when_converting);
accounts.add(inner_id, mnemonic, hd_path)?;
let encrypted_accounts = EncryptedLogin {
id,
account: encrypt_struct(&StoredLogin::Multiple(accounts), password)?,
};
let encrypted_accounts = EncryptedLogin::encrypt(id, &StoredLogin::Multiple(accounts), password)?;
stored_wallet.replace_encrypted_login(encrypted_accounts)?;
@@ -305,6 +290,7 @@ fn remove_account_from_wallet_login_at_file(
let mut decrypted_login = stored_wallet.decrypt_login(id, password)?;
// Remove the account
let is_empty = match decrypted_login {
StoredLogin::Mnemonic(_) => {
log::warn!("Encountered mnemonic login instead of list of accounts, aborting");
@@ -316,19 +302,17 @@ fn remove_account_from_wallet_login_at_file(
}
};
// Remove the login, or encrypt the new updated login
if is_empty {
stored_wallet
.remove_encrypted_login(id)
.ok_or(BackendError::NoSuchIdInWallet)?;
} else {
// Replace the encrypted login with the pruned one.
let encrypted_accounts = EncryptedLogin {
id: id.clone(),
account: encrypt_struct(&decrypted_login, password)?,
};
let encrypted_accounts = EncryptedLogin::encrypt(id.clone(), &decrypted_login, password)?;
stored_wallet.replace_encrypted_login(encrypted_accounts)?;
}
// Remove the file, or write the new file
if stored_wallet.is_empty() {
log::info!("Removing file: {:#?}", filepath);
Ok(fs::remove_file(filepath)?)
@@ -785,7 +769,7 @@ mod tests {
#[test]
fn store_two_mnemonic_accounts_using_two_logins() {
let store_dir = tempdir().unwrap();
let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME);
let wallet = store_dir.path().join(WALLET_INFO_FILENAME);
let dummy_account1 = bip39::Mnemonic::generate(24).unwrap();
let dummy_account2 = bip39::Mnemonic::generate(24).unwrap();
@@ -799,7 +783,7 @@ mod tests {
// Store the first account
store_wallet_login_at_file(
wallet_file.clone(),
wallet.clone(),
dummy_account1.clone(),
cosmos_hd_path.clone(),
id1.clone(),
@@ -807,15 +791,14 @@ mod tests {
)
.unwrap();
let loaded_account =
load_existing_wallet_login_at_file(wallet_file.clone(), &id1, &password).unwrap();
let acc = loaded_account.as_mnemonic_account().unwrap();
let login = load_existing_wallet_login_at_file(wallet.clone(), &id1, &password).unwrap();
let acc = login.as_mnemonic_account().unwrap();
assert_eq!(&dummy_account1, acc.mnemonic());
assert_eq!(&cosmos_hd_path, acc.hd_path());
// Add an extra account
store_wallet_login_at_file(
wallet_file.clone(),
wallet.clone(),
dummy_account2.clone(),
different_hd_path.clone(),
id2.clone(),
@@ -825,12 +808,12 @@ mod tests {
// first account should be unchanged
let loaded_account =
load_existing_wallet_login_at_file(wallet_file.clone(), &id1, &password).unwrap();
load_existing_wallet_login_at_file(wallet.clone(), &id1, &password).unwrap();
let acc1 = loaded_account.as_mnemonic_account().unwrap();
assert_eq!(&dummy_account1, acc1.mnemonic());
assert_eq!(&cosmos_hd_path, acc1.hd_path());
let loaded_account = load_existing_wallet_login_at_file(wallet_file, &id2, &password).unwrap();
let loaded_account = load_existing_wallet_login_at_file(wallet, &id2, &password).unwrap();
let acc2 = loaded_account.as_mnemonic_account().unwrap();
assert_eq!(&dummy_account2, acc2.mnemonic());
assert_eq!(&different_hd_path, acc2.hd_path());