Skip to content

Commit

Permalink
Merge pull request #10 from sohobase/encrypt-pks
Browse files Browse the repository at this point in the history
Store hexSeed and wif in expo's SecureStorage
  • Loading branch information
dvdbng committed Nov 28, 2017
2 parents 21b3761 + 66d892b commit ba821ba
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 27 deletions.
2 changes: 0 additions & 2 deletions src/config/shape.js
Expand Up @@ -54,7 +54,5 @@ export default {
readOnly: bool,
trend: number,
type: string,
wif: string,
hexSeed: string,
},
};
10 changes: 7 additions & 3 deletions src/containers/ModalMnemonic.js
@@ -1,3 +1,4 @@
import { SecureStore } from 'expo';
import { bool, func, shape } from 'prop-types';
import React, { Component } from 'react';
import { View } from 'react-native';
Expand Down Expand Up @@ -25,7 +26,10 @@ class ModalMnemonic extends Component {
this._onValue = this._onValue.bind(this);
}

componentWillReceiveProps({ wallet: { hexSeed } }) {
async componentWillReceiveProps({ visible, wallet: { address, coin } }) {
if (!visible) return;
const hexSeed = address && await SecureStore.getItemAsync(`${coin}_${address}`);

this.setState({
words: (hexSeed) ? MnemonicService.backup(hexSeed) : Array(WORDS_LENGTH).fill(''),
});
Expand All @@ -50,9 +54,9 @@ class ModalMnemonic extends Component {

render() {
const {
_onBackup, _onRecover, _onValue, props: { onClose, visible, wallet: { hexSeed } }, state: { words = [] },
_onBackup, _onRecover, _onValue, props: { onClose, visible, wallet: { address } }, state: { words = [] },
} = this;
const readOnly = hexSeed !== undefined;
const readOnly = !!address;

return (
<Modal
Expand Down
7 changes: 4 additions & 3 deletions src/containers/ModalWallet.js
Expand Up @@ -46,10 +46,11 @@ class ModalWallet extends Component {
props: {
onClose, visible,
wallet: {
address, backup, hexSeed, type,
address, backup, imported, readOnly, type,
},
},
} = this;
const created = !imported && !readOnly;

return (
<Modal title="Wallet" visible={visible} onClose={onClose}>
Expand All @@ -58,7 +59,7 @@ class ModalWallet extends Component {
<Text style={styles.address}>{address}</Text>
</View>
<View style={[STYLE.COL]}>
{ hexSeed &&
{ created &&
<Option
caption="Create a backup"
hint="The simplest way to have control of your wallet."
Expand All @@ -69,7 +70,7 @@ class ModalWallet extends Component {
caption="Archive this wallet"
hint="If you do not want to use this wallet anymore."
icon="remove"
disabled={hexSeed !== undefined && backup === undefined}
disabled={created && !backup}
onPress={_onArchive}
/>
{ type !== PRO &&
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Main/Main.js
Expand Up @@ -92,11 +92,11 @@ class Main extends Component {
} = this;
const wallet = wallets[index];
const focus = !showTransaction && !showWallet && !showWalletNew;
const readOnly = wallet && !wallet.hexSeed && !wallet.wif;
const { readOnly, coin } = wallet || { readOnly: false };

return (
<View style={STYLE.SCREEN}>
<View style={[STYLE.LAYOUT_TOP, (wallet && STYLE[wallet.coin])]}>
<View style={[STYLE.LAYOUT_TOP, (wallet && STYLE[coin])]}>
<Header symbol="USD" />
<Swiper
bounces
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Main/components/Transactions.js
Expand Up @@ -8,7 +8,7 @@ import { updateTransactionsAction } from '../../../store/actions';
import TransactionItem from './TransactionItem';
import styles from './Transactions.style';

const { STATE: { ARCHIVED } } = C;
const { STATE: { ARCHIVED, REQUESTED } } = C;
const { TRANSACTION, WALLET } = SHAPE;

class Transactions extends Component {
Expand Down Expand Up @@ -77,7 +77,7 @@ const mapStateToProps = ({ device, transactions = [] }, { wallet = {} }) => ({
state !== ARCHIVED &&
(
[from.address, to.address].includes(wallet.address) ||
[from.device, to.device].includes(device.id)
(state === REQUESTED && [from.device, to.device].includes(device.id))
)
)).sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)),
});
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Main/components/WalletItem.js
Expand Up @@ -27,7 +27,7 @@ const WalletItem = ({
currencies, data, device: { currency }, onOption, onPress, style,
}) => {
const {
balance = 0, coin = 'BTC', hexSeed, name = '', trend = 0, type, wif,
balance = 0, coin = 'BTC', name = '', readOnly, trend = 0, type,
} = data || {};

return (
Expand Down Expand Up @@ -55,7 +55,7 @@ const WalletItem = ({
<Icon value={trend > 0 ? 'trendingUp' : 'trendingDown'} style={styles.trend} />
<Text style={[styles.label]}>{`${trend.toFixed(2)}%`}</Text>
<View style={styles.tags}>
{ !hexSeed && !wif && <View style={styles.tag}><Text style={styles.tagLabel}>{READ_ONLY}</Text></View> }
{ readOnly && <View style={styles.tag}><Text style={styles.tagLabel}>{READ_ONLY}</Text></View> }
{ type === PRO && <View style={[styles.tag, styles.pro]}><Text style={styles.tagLabel}>PRO</Text></View> }
</View>
</View>
Expand Down
12 changes: 7 additions & 5 deletions src/services/transaction.js
@@ -1,6 +1,7 @@
import BitcoinJS from 'bitcoinjs-lib';
import { service } from './modules';
import { C } from '../config';
import { SecureStore } from '../store';
import { service } from './modules';

const { NETWORKS } = C;

Expand All @@ -18,13 +19,14 @@ export default {
return service('transaction/request', { method: 'POST', body: JSON.stringify(props) });
},

async send(props, { coin, wif, hexSeed }) {
async send(props, { coin, address, imported }) {
const network = BitcoinJS.networks[NETWORKS[coin]];
const { tx: hexTx, fee } = await service('transaction/prepare', { method: 'POST', body: JSON.stringify(props) });
const tx = BitcoinJS.TransactionBuilder.fromTransaction(BitcoinJS.Transaction.fromHex(hexTx), network);
const ECPair = wif
? BitcoinJS.ECPair.fromWIF(wif, network)
: BitcoinJS.HDNode.fromSeedHex(hexSeed, network).keyPair;
const secret = await SecureStore.get(`${coin}_${address}`);
const ECPair = imported
? BitcoinJS.ECPair.fromWIF(secret, network)
: BitcoinJS.HDNode.fromSeedHex(secret, network).keyPair;

// @TODO: verify outputs are what we expect
tx.inputs.forEach((_, i) => {
Expand Down
20 changes: 12 additions & 8 deletions src/services/wallet.js
@@ -1,8 +1,9 @@
import BitcoinJS from 'bitcoinjs-lib';
import { service } from './modules';
import { C } from '../config';
import { PushService } from './';
import { csprng } from '../modules';
import { SecureStore } from '../store';
import { service } from './modules';
import { PushService } from './';

const { CRYPTO: { BTC }, NETWORKS } = C;

Expand All @@ -13,7 +14,8 @@ export default {
const hexSeed = params.hexSeed || (await csprng()).substring(0, 32);
const address = params.address || BitcoinJS.HDNode.fromSeedHex(hexSeed, network).getAddress();

const wallet = await service('wallet', {
if (hexSeed) await SecureStore.set(`${coin}_${address}`, hexSeed);
const wallet = service('wallet', {
method: 'POST',
body: JSON.stringify({
address,
Expand All @@ -23,30 +25,32 @@ export default {
}),
});

return ({ ...wallet, hexSeed });
return wallet; // @TODO: Dispatch error if doesnt exist.
},

async import(props) {
const {
wif,
coin = BTC,
address,
address: readOnlyAddress,
...inherit
} = props;
const network = BitcoinJS.networks[NETWORKS[coin]];
const address = wif ? BitcoinJS.ECPair.fromWIF(wif, network).getAddress() : readOnlyAddress;

const wallet = await service('wallet', {
if (wif) await SecureStore.set(`${coin}_${address}`, wif);
const wallet = service('wallet', {
method: 'POST',
body: JSON.stringify({
...inherit,
address: wif ? BitcoinJS.ECPair.fromWIF(wif, network).getAddress() : address,
coin,
address,
imported: true,
readOnly: (wif === undefined),
}),
});

return ({ ...wallet, wif });
return wallet; // @TODO: Dispatch error if doesnt exist.
},

async archive(props) {
Expand Down
2 changes: 2 additions & 0 deletions src/store/index.js
@@ -1,9 +1,11 @@
import actions from './actions';
import initialize from './initialize';
import reducer from './reducer';
import SecureStore from './secure';

export {
actions,
initialize,
reducer,
SecureStore,
};
14 changes: 14 additions & 0 deletions src/store/secure.js
@@ -0,0 +1,14 @@
import { SecureStore } from 'expo';

const PROPS = { keychainAccessible: SecureStore.WHEN_UNLOCKED };

export default {

async set(key, value) {
return SecureStore.setItemAsync(key, value, PROPS);
},

async get(key) {
return SecureStore.getItemAsync(key);
},
};

0 comments on commit ba821ba

Please sign in to comment.