Browse Source

ADD: Decrypt Storage

settingsui
Marcos Rodriguez 5 years ago
parent
commit
9f9b1e1c20
  1. 41
      class/app-storage.js
  2. 2
      loc/en.js
  3. 9
      screen/plausibledeniability.js
  4. 182
      screen/settings/encryptStorage.js
  5. 15
      screen/wallets/walletMigrate.js

41
class/app-storage.js

@ -99,6 +99,19 @@ export class AppStorage {
return !!data;
}
async isPasswordInUse(password) {
try {
let data = await this.getItem('data');
data = this.decryptData(data, password);
if (data !== null && data !== undefined && data !== false) {
return true;
}
} catch (_e) {
return false;
}
return false;
}
/**
* Iterates through all values of `data` trying to
* decrypt each one, and returns first one successfully decrypted
@ -124,6 +137,33 @@ export class AppStorage {
return false;
}
async decryptStorage(password) {
try {
let storage = await this.getItem('data');
if (password) {
let parsedStorage = JSON.parse(storage);
let mainStorage = parsedStorage[0];
mainStorage = JSON.stringify([mainStorage]);
let decrypted = this.decryptData(mainStorage, password);
if (!decrypted) {
throw new Error('Wrong password for main storage.');
}
const decryptedParsed = JSON.parse(decrypted)
if (decrypted.wallets !== null) {
this.wallets = decryptedParsed.wallets;
this.tx_metadata = decryptedParsed.tx_metadata;
this.cachedPassword = undefined;
await RNSecureKeyStore.set(AppStorage.FLAG_ENCRYPTED, '', { accessible: ACCESSIBLE.WHEN_UNLOCKED });
await RNSecureKeyStore.set('deleteWalletAfterUninstall', true, { accessible: ACCESSIBLE.WHEN_UNLOCKED });
return this.saveToDisk();
}
}
} catch (e) {
console.log(e);
throw new Error(e);
}
}
async encryptStorage(password) {
// assuming the storage is not yet encrypted
await this.saveToDisk();
@ -289,6 +329,7 @@ export class AppStorage {
for (let key of this.wallets) {
if (typeof key === 'boolean') continue;
if (key.prepareForSerialization) key.prepareForSerialization();
if (typeof key === 'string') key = JSON.parse(key);
walletsToSave.push(JSON.stringify({ ...key, type: key.type }));
}

2
loc/en.js

@ -211,7 +211,7 @@ module.exports = {
go_back: 'Go Back',
create_password: 'Create a password',
create_password_explanation: 'Password for fake storage should not match password for your main storage',
password_should_not_match: 'Password for fake storage should not match password for your main storage',
password_should_not_match: 'Password is currently in use. Please, try a different password.',
retype_password: 'Retype password',
passwords_do_not_match: 'Passwords do not match, try again',
success: 'Success',

9
screen/plausibledeniability.js

@ -3,6 +3,7 @@ import React, { Component } from 'react';
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');
let prompt = require('../prompt');
@ -54,22 +55,24 @@ export default class PlausibleDeniability extends Component {
title={loc.plausibledeniability.create_fake_storage}
onPress={async () => {
let p1 = await prompt(loc.plausibledeniability.create_password, loc.plausibledeniability.create_password_explanation);
if (p1 === BlueApp.cachedPassword) {
const isPasswordInUse = p1 === BlueApp.cachedPassword || (await BlueApp.isPasswordInUse(p1));
if (isPasswordInUse) {
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
return alert(loc.plausibledeniability.password_should_not_match);
}
if (!p1) {
return;
}
let p2 = await prompt(loc.plausibledeniability.retype_password);
if (p1 !== p2) {
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
return alert(loc.plausibledeniability.passwords_do_not_match);
}
await BlueApp.createFakeStorage(p1);
EV(EV.enum.WALLETS_COUNT_CHANGED);
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
alert(loc.plausibledeniability.success);
this.props.navigation.navigate('Wallets');
}}

182
screen/settings/encryptStorage.js

@ -1,9 +1,12 @@
/* global alert */
import React, { Component } from 'react';
import { View } from 'react-native';
import { FormValidationMessage } from 'react-native-elements';
import { BlueLoading, BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueNavigationStyle } from '../../BlueComponents';
import { View, Alert, Platform, TouchableOpacity } from 'react-native';
import { BlueLoading, BlueHeaderDefaultSub, BlueListItem, SafeBlueArea, BlueNavigationStyle } from '../../BlueComponents';
import PropTypes from 'prop-types';
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
import AsyncStorage from '@react-native-community/async-storage';
import { AppStorage } from '../../class';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let prompt = require('../../prompt');
@ -12,9 +15,11 @@ let loc = require('../../loc');
export default class EncryptStorage extends Component {
static navigationOptions = () => ({
...BlueNavigationStyle(),
title: loc.settings.encrypt_storage,
title: loc.settings.security,
});
static deleteWalletAfterUninstall = 'deleteWalletAfterUninstall';
constructor(props) {
super(props);
this.state = {
@ -24,12 +29,97 @@ export default class EncryptStorage extends Component {
}
async componentDidMount() {
let deleteWalletsAfterUninstall = true;
try {
deleteWalletsAfterUninstall = await RNSecureKeyStore.get(EncryptStorage.deleteWalletAfterUninstall);
deleteWalletsAfterUninstall =
deleteWalletsAfterUninstall === true || deleteWalletsAfterUninstall === '1' || deleteWalletsAfterUninstall === 1;
} catch (_e) {
deleteWalletsAfterUninstall = true;
}
this.setState({
isLoading: false,
advancedModeEnabled: (await AsyncStorage.getItem(AppStorage.ADVANCED_MODE_ENABLED)) || false,
storageIsEncrypted: await BlueApp.storageIsEncrypted(),
deleteWalletsAfterUninstall,
});
}
decryptStorage = async () => {
const password = await prompt(loc.settings.password, loc._.storage_is_encrypted).catch(() => {
this.setState({ isLoading: false });
});
try {
await BlueApp.decryptStorage(password);
this.setState({
isLoading: false,
storageIsEncrypted: await BlueApp.storageIsEncrypted(),
deleteWalletAfterUninstall: await RNSecureKeyStore.get(EncryptStorage.deleteWalletAfterUninstall),
});
} catch (e) {
console.log(e);
alert(loc._.bad_password);
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
this.setState({
isLoading: false,
storageIsEncrypted: await BlueApp.storageIsEncrypted(),
deleteWalletAfterUninstall: await RNSecureKeyStore.get(EncryptStorage.deleteWalletAfterUninstall),
});
}
};
onDeleteWalletsAfterUninstallSwitch = async value => {
await RNSecureKeyStore.setResetOnAppUninstallTo(value);
await RNSecureKeyStore.set(EncryptStorage.deleteWalletAfterUninstall, value, { accessible: ACCESSIBLE.WHEN_UNLOCKED });
this.setState({ deleteWalletsAfterUninstall: value });
};
onEncryptStorageSwitch = value => {
this.setState({ isLoading: true }, async () => {
if (value === true) {
let p1 = await prompt(loc.settings.password, loc.settings.password_explain).catch(() => {
this.setState({ isLoading: false });
p1 = undefined;
});
if (!p1) {
this.setState({ isLoading: false });
return;
}
let p2 = await prompt(loc.settings.password, loc.settings.retype_password).catch(() => {
this.setState({ isLoading: false });
});
if (p1 === p2) {
await BlueApp.encryptStorage(p1);
this.setState({
isLoading: false,
storageIsEncrypted: await BlueApp.storageIsEncrypted(),
});
} else {
this.setState({ isLoading: false });
alert(loc.settings.passwords_do_not_match);
}
} else {
Alert.alert(
'Decrypt Storage',
'Are you sure you want to decrypt your storage?',
[
{
text: loc.send.details.cancel,
style: 'cancel',
onPress: () => this.setState({ isLoading: false }),
},
{
text: loc._.ok,
style: 'destructive',
onPress: this.decryptStorage,
},
],
{ cancelable: false },
);
}
});
};
render() {
if (this.state.isLoading) {
return <BlueLoading />;
@ -37,61 +127,35 @@ export default class EncryptStorage extends Component {
return (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
<BlueCard>
{(() => {
if (this.state.storageIsEncrypted) {
return (
<View>
<BlueText>{loc.settings.storage_encrypted}</BlueText>
<BlueSpacing20 />
<BlueButton
onPress={() => this.props.navigation.navigate('PlausibleDeniability')}
title={loc.settings.plausible_deniability}
/>
</View>
);
} else {
return (
<View>
<FormValidationMessage>{loc.settings.storage_not_encrypted}</FormValidationMessage>
<BlueSpacing20 />
<BlueButton
icon={{
name: 'shield',
type: 'font-awesome',
color: BlueApp.settings.buttonTextColor,
}}
onPress={async () => {
this.setState({ isLoading: true });
let p1 = await prompt(loc.settings.password, loc.settings.password_explain).catch(() => {
this.setState({ isLoading: false });
p1 = undefined;
});
if (!p1) {
this.setState({ isLoading: false });
return;
}
let p2 = await prompt(loc.settings.password, loc.settings.retype_password).catch(() => {
this.setState({ isLoading: false });
});
if (p1 === p2) {
await BlueApp.encryptStorage(p1);
this.setState({
isLoading: false,
storageIsEncrypted: await BlueApp.storageIsEncrypted(),
});
} else {
this.setState({ isLoading: false });
alert(loc.settings.passwords_do_not_match);
}
}}
title={loc.settings.encrypt_storage}
/>
</View>
);
}
})()}
</BlueCard>
<View>
<BlueHeaderDefaultSub leftText="storage" rightComponent={null} />
<BlueListItem
hideChevron
title="Encypted and Password protected"
switchButton
onSwitch={this.onEncryptStorageSwitch}
switched={this.state.storageIsEncrypted}
/>
{Platform.OS === 'ios' && this.state.storageIsEncrypted && (
<BlueListItem
hideChevron
disabled={!this.state.storageIsEncrypted}
switchDisabled={!this.state.storageIsEncrypted}
title="Delete if BlueWallet is uninstalled"
switchButton
onSwitch={this.onDeleteWalletsAfterUninstallSwitch}
switched={this.state.deleteWalletsAfterUninstall}
/>
)}
{this.state.storageIsEncrypted && (
<TouchableOpacity
disabled={!this.state.storageIsEncrypted}
onPress={() => this.props.navigation.navigate('PlausibleDeniability')}
>
<BlueListItem disabled={!this.state.storageIsEncrypted} title={loc.settings.plausible_deniability} />
</TouchableOpacity>
)}
</View>
</SafeBlueArea>
);
}

15
screen/wallets/walletMigrate.js

@ -8,8 +8,19 @@ import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
const expoDataDirectory = RNFS.DocumentDirectoryPath + '/ExponentExperienceData/%40overtorment%2Fbluewallet/RCTAsyncLocalStorage';
export default class WalletMigrate extends Component {
componentDidMount() {
this.migrateDataFromExpo();
async componentDidMount() {
const firstLaunch = await AsyncStorage.getItem('RnSksIsAppInstalled');
if (firstLaunch === undefined || firstLaunch === null || firstLaunch === false || firstLaunch === '') {
try {
await RNSecureKeyStore.setResetOnAppUninstallTo(false);
const deleteWalletsFromKeychain = await RNSecureKeyStore.get('deleteWalletAfterUninstall');
if (deleteWalletsFromKeychain) {
await RNSecureKeyStore.setResetOnAppUninstallTo(deleteWalletsFromKeychain === '1');
}
} catch (_e) {}
await AsyncStorage.setItem('RnSksIsAppInstalled', '1');
}
await this.migrateDataFromExpo();
}
migrationComplete() {

Loading…
Cancel
Save