mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 09:50:15 +01:00
REF: decrypt storage & advanced mode switch
This commit is contained in:
parent
5a233e8e2d
commit
59838a9433
@ -25,7 +25,8 @@ export class AppStorage {
|
||||
static ELECTRUM_TCP_PORT = 'electrum_tcp_port';
|
||||
static PREFERRED_CURRENCY = 'preferredCurrency';
|
||||
static ADVANCED_MODE_ENABLED = 'advancedmodeenabled';
|
||||
static DELETEWALLETAFTERUNINSTALLKEY = 'deleteWalletAfterUninstall';
|
||||
static DELETE_WALLET_AFTER_UNINSTALL = 'deleteWalletAfterUninstall';
|
||||
|
||||
constructor() {
|
||||
/** {Array.<AbstractWallet>} */
|
||||
this.wallets = [];
|
||||
@ -91,8 +92,12 @@ export class AppStorage {
|
||||
}
|
||||
|
||||
async setResetOnAppUninstallTo(value) {
|
||||
await this.setItem('deleteWalletAfterUninstall', value === true ? '1' : '');
|
||||
await RNSecureKeyStore.setResetOnAppUninstallTo(value);
|
||||
await this.setItem(AppStorage.DELETE_WALLET_AFTER_UNINSTALL, value ? '1' : '');
|
||||
try {
|
||||
await RNSecureKeyStore.setResetOnAppUninstallTo(value);
|
||||
} catch (Error) {
|
||||
console.warn(Error);
|
||||
}
|
||||
}
|
||||
|
||||
async storageIsEncrypted() {
|
||||
@ -110,21 +115,19 @@ export class AppStorage {
|
||||
try {
|
||||
let data = await this.getItem('data');
|
||||
data = this.decryptData(data, password);
|
||||
if (data !== null && data !== undefined && data !== false) {
|
||||
return true;
|
||||
}
|
||||
return !!data;
|
||||
} catch (_e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all values of `data` trying to
|
||||
* decrypt each one, and returns first one successfully decrypted
|
||||
*
|
||||
* @param data String (serialized array)
|
||||
* @param data {string} Serialized array
|
||||
* @param password
|
||||
* @returns {boolean|string} Either STRING of storage data (which is stringified JSON) or FALSE, which means failure
|
||||
*/
|
||||
decryptData(data, password) {
|
||||
data = JSON.parse(data);
|
||||
@ -147,8 +150,7 @@ export class AppStorage {
|
||||
async decryptStorage(password) {
|
||||
if (password === this.cachedPassword) {
|
||||
this.cachedPassword = undefined;
|
||||
await this.setItem(AppStorage.FLAG_ENCRYPTED, '');
|
||||
await this.setItem('deleteWalletAfterUninstall', '1');
|
||||
await this.setResetOnAppUninstallTo(true);
|
||||
await this.saveToDisk();
|
||||
this.wallets = [];
|
||||
this.tx_metadata = [];
|
||||
@ -161,7 +163,7 @@ export class AppStorage {
|
||||
async isDeleteWalletAfterUninstallEnabled() {
|
||||
let deleteWalletsAfterUninstall;
|
||||
try {
|
||||
deleteWalletsAfterUninstall = await this.getItem('deleteWalletAfterUninstall');
|
||||
deleteWalletsAfterUninstall = await this.getItem(AppStorage.DELETE_WALLET_AFTER_UNINSTALL);
|
||||
} catch (_e) {
|
||||
deleteWalletsAfterUninstall = true;
|
||||
}
|
||||
@ -343,8 +345,6 @@ export class AppStorage {
|
||||
async saveToDisk() {
|
||||
let walletsToSave = [];
|
||||
for (let key of this.wallets) {
|
||||
if (typeof key === 'boolean') continue;
|
||||
if (typeof key === 'string') key = JSON.parse(key);
|
||||
if (typeof key === 'boolean' || key.type === PlaceholderWallet.type) continue;
|
||||
if (key.prepareForSerialization) key.prepareForSerialization();
|
||||
walletsToSave.push(JSON.stringify({ ...key, type: key.type }));
|
||||
@ -506,6 +506,17 @@ export class AppStorage {
|
||||
return finalBalance;
|
||||
}
|
||||
|
||||
async isAdancedModeEnabled() {
|
||||
try {
|
||||
return !!(await this.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
} catch (_) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
async setIsAdancedModeEnabled(value) {
|
||||
await this.setItem(AppStorage.ADVANCED_MODE_ENABLED, value ? '1' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple async sleeper function
|
||||
*
|
||||
|
@ -4,8 +4,8 @@ import { ScrollView } from 'react-native';
|
||||
import { BlueLoading, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle, BlueSpacing20 } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../BlueApp');
|
||||
import { AppStorage } from '../class';
|
||||
let BlueApp: AppStorage = require('../BlueApp');
|
||||
let prompt = require('../prompt');
|
||||
let EV = require('../events');
|
||||
let loc = require('../loc');
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
BlueText,
|
||||
} from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { AppStorage } from '../../class';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
import Biometric from '../../class/biometrics';
|
||||
@ -42,7 +41,7 @@ export default class EncryptStorage extends Component {
|
||||
const biometricsType = (await Biometric.biometricType()) || 'biometrics';
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
advancedModeEnabled: (await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED)) || false,
|
||||
advancedModeEnabled: await BlueApp.isAdancedModeEnabled(),
|
||||
storageIsEncrypted: await BlueApp.storageIsEncrypted(),
|
||||
deleteWalletsAfterUninstall: await BlueApp.isDeleteWalletAfterUninstallEnabled(),
|
||||
biometrics: { isBiometricsEnabled, isDeviceBiometricCapable, biometricsType },
|
||||
|
@ -9,10 +9,9 @@ import {
|
||||
BlueHeaderDefaultSub,
|
||||
BlueListItem,
|
||||
} from '../../BlueComponents';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import { AppStorage } from '../../class';
|
||||
import { useNavigation } from 'react-navigation-hooks';
|
||||
const BlueApp = require('../../BlueApp');
|
||||
const BlueApp: AppStorage = require('../../BlueApp');
|
||||
const loc = require('../../loc');
|
||||
|
||||
const Settings = () => {
|
||||
@ -23,18 +22,14 @@ const Settings = () => {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
setAdvancedModeEnabled(!!(await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED)));
|
||||
setAdvancedModeEnabled(await BlueApp.isAdancedModeEnabled());
|
||||
setIsLoading(false);
|
||||
})();
|
||||
});
|
||||
|
||||
const onAdvancedModeSwitch = async value => {
|
||||
if (value) {
|
||||
await AsyncStorage.setItem(AppStorage.ADVANCED_MODE_ENABLED, '1');
|
||||
} else {
|
||||
await AsyncStorage.removeItem(AppStorage.ADVANCED_MODE_ENABLED);
|
||||
}
|
||||
setAdvancedModeEnabled(value);
|
||||
await BlueApp.setIsAdancedModeEnabled(value);
|
||||
};
|
||||
|
||||
const onShowAdvancedOptions = () => {
|
||||
@ -50,12 +45,7 @@ const Settings = () => {
|
||||
{BlueApp.getWallets().length > 1 && (
|
||||
<BlueListItem component={TouchableOpacity} onPress={() => navigate('DefaultView')} title="On Launch" />
|
||||
)}
|
||||
<BlueListItem
|
||||
title="Security"
|
||||
onPress={() => navigate('EncryptStorage')}
|
||||
component={TouchableOpacity}
|
||||
testID="EncryptStorageButton"
|
||||
/>
|
||||
<BlueListItem title="Security" onPress={() => navigate('EncryptStorage')} component={TouchableOpacity} testID="SecurityButton" />
|
||||
<BlueListItem title={loc.settings.lightning_settings} component={TouchableOpacity} onPress={() => navigate('LightningSettings')} />
|
||||
<BlueListItem title={loc.settings.language} component={TouchableOpacity} onPress={() => navigate('Language')} />
|
||||
<BlueListItem title={loc.settings.currency} component={TouchableOpacity} onPress={() => navigate('Currency')} />
|
||||
|
@ -34,8 +34,7 @@ import { AppStorage, HDSegwitBech32Wallet, SegwitP2SHWallet } from '../../class'
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
let EV = require('../../events');
|
||||
let A = require('../../analytics');
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
let BlueApp: AppStorage = require('../../BlueApp');
|
||||
let loc = require('../../loc');
|
||||
export default class WalletsAdd extends Component {
|
||||
static navigationOptions = ({ navigation }) => ({
|
||||
@ -54,7 +53,7 @@ export default class WalletsAdd extends Component {
|
||||
|
||||
async componentDidMount() {
|
||||
let walletBaseURI = await AsyncStorage.getItem(AppStorage.LNDHUB);
|
||||
let isAdvancedOptionsEnabled = !!(await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED));
|
||||
let isAdvancedOptionsEnabled = await BlueApp.isAdancedModeEnabled();
|
||||
walletBaseURI = walletBaseURI || '';
|
||||
|
||||
this.setState({
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { AppStorage } from '../../class';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import RNFS from 'react-native-fs';
|
||||
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
|
||||
@ -15,7 +16,7 @@ export default class WalletMigrate {
|
||||
if (firstLaunch === undefined || firstLaunch === null || firstLaunch === false || firstLaunch === '') {
|
||||
try {
|
||||
await RNSecureKeyStore.setResetOnAppUninstallTo(false);
|
||||
const deleteWalletsFromKeychain = await RNSecureKeyStore.get('deleteWalletAfterUninstall');
|
||||
const deleteWalletsFromKeychain = await RNSecureKeyStore.get(AppStorage.DELETE_WALLET_AFTER_UNINSTALL);
|
||||
await RNSecureKeyStore.setResetOnAppUninstallTo(deleteWalletsFromKeychain === '1');
|
||||
} catch (_e) {}
|
||||
await AsyncStorage.setItem('RnSksIsAppInstalled', '1');
|
||||
|
@ -25,10 +25,10 @@ describe('BlueWallet UI Tests', () => {
|
||||
// go to settings
|
||||
await expect(element(by.id('SettingsButton'))).toBeVisible();
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await expect(element(by.id('EncryptStorageButton'))).toBeVisible();
|
||||
await expect(element(by.id('SecurityButton'))).toBeVisible();
|
||||
|
||||
// go to Security page where we will enable encryption
|
||||
await element(by.id('EncryptStorageButton')).tap();
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
await expect(element(by.id('EncyptedAndPasswordProtected'))).toBeVisible();
|
||||
await expect(element(by.id('PlausibleDeniabilityButton'))).toBeNotVisible();
|
||||
|
||||
@ -79,8 +79,8 @@ describe('BlueWallet UI Tests', () => {
|
||||
|
||||
// go to settings -> security screen -> plausible deniability screen
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await expect(element(by.id('EncryptStorageButton'))).toBeVisible();
|
||||
await element(by.id('EncryptStorageButton')).tap();
|
||||
await expect(element(by.id('SecurityButton'))).toBeVisible();
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
await expect(element(by.id('EncyptedAndPasswordProtected'))).toBeVisible();
|
||||
await expect(element(by.id('PlausibleDeniabilityButton'))).toBeVisible();
|
||||
await element(by.id('PlausibleDeniabilityButton')).tap();
|
||||
@ -155,7 +155,7 @@ describe('BlueWallet UI Tests', () => {
|
||||
await yo('WalletsList');
|
||||
await helperCreateWallet();
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await element(by.id('EncryptStorageButton')).tap();
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
if (device.getPlatform() === 'ios') {
|
||||
console.warn('Android only test skipped');
|
||||
return;
|
||||
@ -201,7 +201,7 @@ describe('BlueWallet UI Tests', () => {
|
||||
|
||||
// now go to settings, and decrypt
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await element(by.id('EncryptStorageButton')).tap();
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
|
||||
// putting FAKE storage password. should not succeed
|
||||
await element(by.type('android.widget.CompoundButton')).tap(); // thats a switch lol
|
||||
@ -228,7 +228,7 @@ describe('BlueWallet UI Tests', () => {
|
||||
await yo('WalletsList');
|
||||
await helperCreateWallet();
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await element(by.id('EncryptStorageButton')).tap();
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
if (device.getPlatform() === 'ios') {
|
||||
console.warn('Android only test skipped');
|
||||
return;
|
||||
@ -273,7 +273,7 @@ describe('BlueWallet UI Tests', () => {
|
||||
|
||||
// now go to settings, and decrypt
|
||||
await element(by.id('SettingsButton')).tap();
|
||||
await element(by.id('EncryptStorageButton')).tap();
|
||||
await element(by.id('SecurityButton')).tap();
|
||||
|
||||
// putting MAIN storage password. should not succeed
|
||||
await element(by.type('android.widget.CompoundButton')).tap(); // thats a switch lol
|
@ -6,6 +6,12 @@ jest.mock('react-native-watch-connectivity', () => {
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock('react-native-secure-key-store', () => {
|
||||
return {
|
||||
setResetOnAppUninstallTo: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock('react-native-quick-actions', () => {
|
||||
return {
|
||||
clearShortcutItems: jest.fn(),
|
||||
|
@ -1,9 +1,8 @@
|
||||
/* global it, jest */
|
||||
/* global it */
|
||||
import { SegwitP2SHWallet, AppStorage } from '../../class';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
global.crypto = require('crypto'); // shall be used by tests under nodejs CLI, but not in RN environment
|
||||
let assert = require('assert');
|
||||
jest.useFakeTimers();
|
||||
|
||||
it('Appstorage - loadFromDisk works', async () => {
|
||||
/** @type {AppStorage} */
|
||||
|
Loading…
Reference in New Issue
Block a user