From 809f9cae60ec6c1cc9697e61334e7095aa2a8f75 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sat, 31 Mar 2018 14:43:08 +0100 Subject: [PATCH] storage encryption --- App.js | 9 +-- BlueApp.js | 11 ++-- class/app-storage.js | 8 ++- prompt.js | 8 --- screen/about.js | 128 +++++++++++++++++++++++++++++++++++++++++ screen/settings.js | 89 +++++++++++++++------------- screen/wallets/list.js | 1 - 7 files changed, 196 insertions(+), 58 deletions(-) create mode 100644 screen/about.js diff --git a/App.js b/App.js index 035bb58e..d5f4ab86 100644 --- a/App.js +++ b/App.js @@ -5,6 +5,7 @@ import { Text, ScrollView, StyleSheet } from 'react-native'; import { DrawerNavigator, SafeAreaView } from 'react-navigation'; import MainBottomTabs from './MainBottomTabs'; import Selftest from './screen/selftest'; +import About from './screen/about'; require('./BlueApp'); @@ -53,9 +54,6 @@ const styles = StyleSheet.create({ }, }); -/* import scanQrWifLegacyAddress from './screen/wallets/scanQrWifLegacyAddress' -import scanQrWifSegwitP2SHAddress from './screen/wallets/scanQrWifSegwitP2SHAddress' */ - const TabsInDrawer = DrawerNavigator( { MainBottomTabs: { @@ -66,11 +64,14 @@ const TabsInDrawer = DrawerNavigator( }), }, }, - Selftest: { screen: Selftest, navigationOptions: {}, }, + About: { + screen: About, + navigationOptions: {}, + }, }, { contentComponent: CustomDrawerContentComponent, diff --git a/BlueApp.js b/BlueApp.js index 3a264fb4..9673fe07 100644 --- a/BlueApp.js +++ b/BlueApp.js @@ -1,4 +1,3 @@ -/* global alert */ /** * @exports {AppStorage} */ @@ -9,11 +8,11 @@ let EV = require('./events'); /** @type {AppStorage} */ let BlueApp = new AppStorage(); -(async () => { +async function startAndDecrypt(retry) { let password = false; if (await BlueApp.storageIsEncrypted()) { password = await prompt( - 'Enter password', + (retry && 'Bad pasword, try again') || 'Enter password', 'Your storage is encrypted. Password is required to decrypt it', ); } @@ -26,8 +25,10 @@ let BlueApp = new AppStorage(); if (!success && password) { // we had password and yet could not load/decrypt - alert('Could not decrypt. Bad password.'); + await startAndDecrypt(true); } -})(); +} + +startAndDecrypt(); module.exports = BlueApp; diff --git a/class/app-storage.js b/class/app-storage.js index d5ea1af1..aa1c908d 100644 --- a/class/app-storage.js +++ b/class/app-storage.js @@ -78,8 +78,11 @@ export class AppStorage { try { let data = await AsyncStorage.getItem('data'); if (password) { - this.cachedPassword = password; data = this.decryptData(data, password); + if (data) { + // password is good, cache it + this.cachedPassword = password; + } } if (data !== null) { data = JSON.parse(data); @@ -167,9 +170,12 @@ export class AppStorage { newData.push( encryption.encrypt(JSON.stringify(data), this.cachedPassword), ); + await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, '1'); } } data = newData; + } else { + await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, ''); // drop the flag } return AsyncStorage.setItem('data', JSON.stringify(data)); diff --git a/prompt.js b/prompt.js index a5fc16f5..4d7e5e51 100644 --- a/prompt.js +++ b/prompt.js @@ -6,14 +6,6 @@ module.exports = function(title, text) { title, text, [ - { - text: 'Cancel', - onPress: () => { - console.log('Cancel Pressed'); - reject(new Error('Cancel Pressed')); - }, - style: 'cancel', - }, { text: 'OK', onPress: password => { diff --git a/screen/about.js b/screen/about.js new file mode 100644 index 00000000..afd8a02d --- /dev/null +++ b/screen/about.js @@ -0,0 +1,128 @@ +import React, { Component } from 'react'; +import { ScrollView, Linking } from 'react-native'; +import Ionicons from 'react-native-vector-icons/Ionicons'; +import { Icon } from 'react-native-elements'; +import { + BlueLoading, + BlueSpacing20, + BlueButton, + SafeBlueArea, + BlueCard, + BlueText, + BlueHeader, +} from '../BlueComponents'; +import PropTypes from 'prop-types'; +/** @type {AppStorage} */ +let BlueApp = require('../BlueApp'); + +export default class About extends Component { + static navigationOptions = { + tabBarLabel: 'About', + tabBarIcon: ({ tintColor, focused }) => ( + + ), + }; + + constructor(props) { + super(props); + this.state = { + isLoading: true, + }; + } + + async componentDidMount() { + this.setState({ + isLoading: false, + }); + } + + render() { + if (this.state.isLoading) { + return ; + } + + return ( + + this.props.navigation.navigate('DrawerToggle')} + /> + } + centerComponent={{ + text: 'About', + style: { color: '#fff', fontSize: 25 }, + }} + /> + + + + About + + + + Blue Wallet is free and opensource Bitcoin wallet + + + Warning: Alpha version, don't use to store large amouts! + + { + Linking.openURL('https://github.com/Overtorment/BlueWallet'); + }} + title="github.com/Overtorment/BlueWallet" + /> + + + Licensed MIT + + + Built with awesome: + + * React Native + * Bitcoinjs-lib + * blockcypher.com API + * Nodejs + * Expo + * react-native-elements + * rn-nodeify + * bignumber.js + * https://github.com/StefanoBalocco/isaac.js + + * Design by https://dribbble.com/chrometaphore + + + { + this.props.navigation.navigate('Selftest'); + }} + title="Run self test" + /> + { + this.props.navigation.goBack(); + }} + /> + + + + ); + } +} + +About.propTypes = { + navigation: PropTypes.shape({ + navigate: PropTypes.func, + goBack: PropTypes.func, + }), +}; diff --git a/screen/settings.js b/screen/settings.js index 7b2da6bb..ed6a5a85 100644 --- a/screen/settings.js +++ b/screen/settings.js @@ -1,7 +1,8 @@ +/* global alert */ import React, { Component } from 'react'; -import { ScrollView, Linking } from 'react-native'; +import { ScrollView, View } from 'react-native'; import Ionicons from 'react-native-vector-icons/Ionicons'; -import { Icon } from 'react-native-elements'; +import { Icon, FormValidationMessage } from 'react-native-elements'; import { BlueLoading, BlueSpacing20, @@ -12,7 +13,9 @@ import { BlueHeader, } from '../BlueComponents'; import PropTypes from 'prop-types'; +/** @type {AppStorage} */ let BlueApp = require('../BlueApp'); +let prompt = require('../prompt'); export default class Settings extends Component { static navigationOptions = { @@ -36,6 +39,7 @@ export default class Settings extends Component { async componentDidMount() { this.setState({ isLoading: false, + storageIsEncrypted: await BlueApp.storageIsEncrypted(), }); } @@ -63,47 +67,54 @@ export default class Settings extends Component { - About - - Blue Wallet is free and opensource Bitcoin wallet - - - Warning: Alpha version, don't use to store large amouts! - - { - Linking.openURL('https://github.com/Overtorment/BlueWallet'); - }} - title="github.com/Overtorment/BlueWallet" - /> - - - Licensed MIT - - - Built with awesome: - - * React Native - * Bitcoinjs-lib - * blockcypher.com API - * Nodejs - * Expo - * react-native-elements - * rn-nodeify - * bignumber.js - * https://github.com/StefanoBalocco/isaac.js - - * Design by https://dribbble.com/chrometaphore - + {(() => { + if (this.state.storageIsEncrypted) { + return ( + + Storage: encrypted + + ); + } else { + return ( + + + Storage: not encrypted + + { + this.setState({ isLoading: true }); + let p1 = await prompt( + 'Password', + 'Create the password you will use to decrypt the storage', + ); + let p2 = await prompt( + 'Password', + 'Re-type the password', + ); + if (p1 === p2) { + await BlueApp.encryptStorage(p1); + this.setState({ + isLoading: false, + storageIsEncrypted: await BlueApp.storageIsEncrypted(), + }); + } else { + this.setState({ isLoading: false }); + alert('Passwords do not match. Please try again'); + } + }} + title="Encrypt storage" + /> + + ); + } + })()} { - this.props.navigation.navigate('Selftest'); - }} - title="Run self test" + onPress={() => this.props.navigation.navigate('About')} + title="About" /> diff --git a/screen/wallets/list.js b/screen/wallets/list.js index 2388debc..1c87ba2c 100644 --- a/screen/wallets/list.js +++ b/screen/wallets/list.js @@ -40,7 +40,6 @@ export default class WalletsList extends Component { } async componentDidMount() { - console.log('wallets/list - componentDidMount'); this.refreshFunction(); } // end of componendDidMount