on master help sign in
Merge pull request #94 from urbit/jt-optional-passphrase

Support optional BIP39 passphrases

commit71940042f3982214fedbe2adc360f30ddce9d0d6 authorJared Tobin <jared@jtobin.io> dateFri 5 Apr 2019, 05:40 (8 years ago) parentf5a87a89eab177
5 files changed+186−61
package-lock.json+45−16 src/bridge/lib/wallet.js+2−2 src/bridge/views/Mnemonic.js+53−11 src/bridge/views/Shards.js+44−15 src/bridge/views/Ticket.js+42−17
package-lock.json+45−16
@@ -1,6 +1,6 @@
11{
22 "name": "bridge",
3− "version": "1.1.0",
3+ "version": "1.2.1",
44 "lockfileVersion": 1,
55 "requires": true,
66 "dependencies": {
@@ -18700,24 +18700,27 @@
1870018700 "integrity": "sha512-zbfndGg//cMW4aytXuF4dkBuUu/SUBA94U2zkmDoRwcplS9hCCn/S3VDp4A7CXw5tVxeCm9F6zdfHzWzei6nXg=="
1870118701 },
1870218702 "trezor-connect": {
18703− "version": "6.0.3",
18704− "resolved": "https://registry.npmjs.org/trezor-connect/-/trezor-connect-6.0.3.tgz",
18705− "integrity": "sha512-QqgnulN9uQstVjxYTF9+gcNk01kBUQA4xXy2abyPOgxh8Wio9iiSGftQa12KkFfWLZKkZD1sL03KvQ2ievL05g==",
18703+ "version": "7.0.2",
18704+ "resolved": "https://registry.npmjs.org/trezor-connect/-/trezor-connect-7.0.2.tgz",
18705+ "integrity": "sha512-KAFOqxEHHaFvrG8NGLFlM/QxHcwIa3gwfXcgTjCYM0g0zRpwIQBwe35AKsjAQO5yiTJQGa0Cu5MZufGJRGYjjw==",
1870618706 "requires": {
18707− "babel-runtime": "^6.26.0",
18708− "events": "^1.1.1",
18709− "whatwg-fetch": "^2.0.4"
18707+ "@babel/runtime": "^7.3.1",
18708+ "events": "^3.0.0",
18709+ "whatwg-fetch": "^3.0.0"
1871018710 },
1871118711 "dependencies": {
18712− "events": {
18713− "version": "1.1.1",
18714− "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
18715− "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
18712+ "@babel/runtime": {
18713+ "version": "7.4.3",
18714+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
18715+ "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
18716+ "requires": {
18717+ "regenerator-runtime": "^0.13.2"
18718+ }
1871618719 },
18717− "whatwg-fetch": {
18718− "version": "2.0.4",
18719− "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
18720− "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
18720+ "regenerator-runtime": {
18721+ "version": "0.13.2",
18722+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
18723+ "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
1872118724 }
1872218725 }
1872318726 },
@@ -18791,6 +18794,14 @@
1879118794 "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
1879218795 "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
1879318796 },
18797+ "typedarray-to-buffer": {
18798+ "version": "3.1.5",
18799+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
18800+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
18801+ "requires": {
18802+ "is-typedarray": "^1.0.0"
18803+ }
18804+ },
1879418805 "typeforce": {
1879518806 "version": "1.18.0",
1879618807 "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
@@ -19599,7 +19610,20 @@
1959919610 "integrity": "sha1-j93qQuGbvyUh7IeVRkV6Yjqdye8=",
1960019611 "requires": {
1960119612 "underscore": "1.8.3",
19602− "web3-core-helpers": "1.0.0-beta.33"
19613+ "web3-core-helpers": "1.0.0-beta.33",
19614+ "websocket": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible"
19615+ },
19616+ "dependencies": {
19617+ "websocket": {
19618+ "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
19619+ "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
19620+ "requires": {
19621+ "debug": "^2.2.0",
19622+ "nan": "^2.3.3",
19623+ "typedarray-to-buffer": "^3.1.2",
19624+ "yaeti": "^0.0.6"
19625+ }
19626+ }
1960319627 }
1960419628 },
1960519629 "web3-shh": {
@@ -20700,6 +20724,11 @@
2070020724 "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
2070120725 "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
2070220726 },
20727+ "yaeti": {
20728+ "version": "0.0.6",
20729+ "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
20730+ "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
20731+ },
2070320732 "yallist": {
2070420733 "version": "2.1.2",
2070520734 "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
src/bridge/lib/wallet.js+2−2
@@ -86,10 +86,10 @@ const toChecksumAddress = (address) => {
8686const eqAddr = (addr0, addr1) =>
8787 toChecksumAddress(addr0) === toChecksumAddress(addr1)
8888
89−const walletFromMnemonic = (mnemonic, hdpath) => {
89+const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
9090 const seed =
9191 bip39.validateMnemonic(mnemonic)
92− ? Maybe.Just(bip39.mnemonicToSeed(mnemonic))
92+ ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase))
9393 : Maybe.Nothing()
9494
9595 const toWallet = (sd, path) => {
src/bridge/views/Mnemonic.js+53−11
@@ -1,8 +1,13 @@
11import * as bip39 from 'bip39'
2−import Maybe from 'folktale/maybe'
2+import { Just, Nothing } from 'folktale/maybe'
33import React from 'react'
44import { Button } from '../components/Base'
5−import { Input, MnemonicInput, InnerLabel, InputCaption } from '../components/Base'
5+import {
6+ Input,
7+ MnemonicInput,
8+ InnerLabel,
9+ InputCaption
10+ } from '../components/Base'
611import { Row, Col, H1 } from '../components/Base'
712
813import { ROUTE_NAMES } from '../lib/router'
@@ -16,28 +21,47 @@ class Mnemonic extends React.Component {
1621
1722 this.state = {
1823 mnemonic: '',
24+ passphrase: '',
1925 hdpath: DEFAULT_HD_PATH,
2026 }
2127
2228 this.handleMnemonicInput = this.handleMnemonicInput.bind(this)
29+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
2330 this.handleHdPathInput = this.handleHdPathInput.bind(this)
2431 }
2532
2633 componentDidMount() {
27− const { mnemonic, hdpath } = this.state
28− this.attemptWalletDerivation(mnemonic, hdpath)
34+ const { mnemonic, hdpath, passphrase } = this.state
35+ this.attemptWalletDerivation(mnemonic, hdpath, passphrase)
2936
3037 this.setState({
3138 exampleMnemonic: bip39.generateMnemonic(),
3239 })
3340 }
3441
42+ handlePassphraseInput(passphrase) {
43+ this.setState((state, _) => {
44+ const mnemonic = state.mnemonic
45+ const hdpath = state.hdpath
46+ this.attemptWalletDerivation(
47+ mnemonic,
48+ hdpath === '' ? DEFAULT_HD_PATH : hdpath,
49+ passphrase
50+ )
51+ return {
52+ passphrase
53+ }
54+ })
55+ }
56+
3557 handleMnemonicInput(mnemonic) {
3658 this.setState((state, _) => {
3759 const hdpath = state.hdpath
60+ const passphrase = state.passphrase
3861 this.attemptWalletDerivation(
3962 mnemonic,
40− hdpath === '' ? DEFAULT_HD_PATH : hdpath
63+ hdpath === '' ? DEFAULT_HD_PATH : hdpath,
64+ passphrase
4165 )
4266 return {
4367 mnemonic
@@ -48,9 +72,11 @@ class Mnemonic extends React.Component {
4872 handleHdPathInput(hdpath) {
4973 this.setState((state, _) => {
5074 const mnemonic = state.mnemonic
75+ const passphrase = state.passphrase
5176 this.attemptWalletDerivation(
5277 mnemonic,
53− hdpath === '' ? DEFAULT_HD_PATH : hdpath
78+ hdpath === '' ? DEFAULT_HD_PATH : hdpath,
79+ passphrase
5480 )
5581 return {
5682 hdpath
@@ -58,11 +84,11 @@ class Mnemonic extends React.Component {
5884 })
5985 }
6086
61− attemptWalletDerivation(mnemonic, hdpath) {
87+ attemptWalletDerivation(mnemonic, hdpath, passphrase) {
6288 const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props
63− const wallet = walletFromMnemonic(mnemonic, hdpath)
89+ const wallet = walletFromMnemonic(mnemonic, hdpath, passphrase)
6490 setWallet(wallet)
65− setAuthMnemonic(Maybe.Just(mnemonic))
91+ setAuthMnemonic(Just(mnemonic))
6692 setWalletHdPath(hdpath)
6793 }
6894
@@ -71,7 +97,8 @@ class Mnemonic extends React.Component {
7197 const {
7298 mnemonic,
7399 hdpath,
74− exampleMnemonic
100+ exampleMnemonic,
101+ passphrase
75102 } = this.state
76103
77104 return (
@@ -96,6 +123,21 @@ class Mnemonic extends React.Component {
96123 <InnerLabel>{'Mnemonic'}</InnerLabel>
97124 </MnemonicInput>
98125
126+ <InputCaption>
127+ {`If your wallet requires a passphrase, you may enter it below.`}
128+ </InputCaption>
129+
130+ <Input
131+ className='pt-8'
132+ prop-size='md'
133+ prop-format='innerLabel'
134+ name='passphrase'
135+ type='password'
136+ value={ passphrase }
137+ autocomplete='off'
138+ onChange={ this.handlePassphraseInput }>
139+ <InnerLabel>{'Passphrase'}</InnerLabel>
140+ </Input>
99141
100142 <InputCaption>
101143 {`If you'd like to use a custom derivation path, you may enter it below.`}
@@ -114,7 +156,7 @@ class Mnemonic extends React.Component {
114156
115157 <Button
116158 className={'mt-10'}
117− disabled={ Maybe.Nothing.hasInstance(wallet) }
159+ disabled={ Nothing.hasInstance(wallet) }
118160 onClick={ () => {
119161 popRoute()
120162 pushRoute(ROUTE_NAMES.SHIPS)
src/bridge/views/Shards.js+44−15
@@ -1,7 +1,14 @@
1−import Maybe from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe'
22import React from 'react'
33import { Button } from '../components/Base'
4−import { InnerLabel, ValidatedSigil, PointInput, ShardInput } from '../components/Base'
4+import {
5+ InnerLabel,
6+ ValidatedSigil,
7+ PointInput,
8+ ShardInput,
9+ InputCaption,
10+ Input
11+ } from '../components/Base'
512import { Row, Col, H1, P } from '../components/Base'
613import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
714import * as ob from 'urbit-ob'
@@ -33,12 +40,14 @@ class Shards extends React.Component {
3340 shard1: '',
3441 shard2: '',
3542 shard3: '',
43+ passphrase: '',
3644 pointName: ''
3745 }
3846
3947 this.pointPlaceholder = placeholder(4)
4048 this.ticketPlaceholder = placeholder(16)
4149
50+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
4251 this.handleShardInput = this.handleShardInput.bind(this)
4352 this.handlePointNameInput = this.handlePointNameInput.bind(this)
4453 }
@@ -53,19 +62,17 @@ class Shards extends React.Component {
5362 }
5463 }
5564
65+ handlePassphraseInput(passphrase) {
66+ this.setState({ passphrase })
67+ }
68+
5669 handlePointNameInput(pointName) {
5770 if (pointName.length < 15) {
5871 this.setState({ pointName })
5972 }
6073 }
6174
62− // buttonTriState = (wallet) => {
63− // if (wallet.isNothing()) return 'blue'
64− // if (wallet === false) return 'yellow'
65− // if (Maybe.Nothing.hasInstance(wallet)) return 'green'
66− // }
67−
68− async walletFromShards(shard1, shard2, shard3, pointName) {
75+ async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
6976 const { setWallet, setUrbitWallet } = this.props
7077
7178 const s1 = shard1 === '' ? undefined : shard1
@@ -82,18 +89,19 @@ class Shards extends React.Component {
8289 if (ticket !== undefined) {
8390 const urbitWallet = await kg.generateWallet({
8491 ticket: ticket,
85− ship: ob.patp2dec(pointName)
92+ ship: ob.patp2dec(pointName),
93+ passphrase: passphrase
8694 })
8795 const mnemonic = urbitWallet.ownership.seed
88− const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH)
96+ const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH, passphrase)
8997 setWallet(wallet)
90− setUrbitWallet(Maybe.Just(urbitWallet))
98+ setUrbitWallet(Just(urbitWallet))
9199 }
92100 }
93101
94102 render() {
95103 const { popRoute, pushRoute, wallet } = this.props
96− const { shard1, shard2, shard3, pointName } = this.state
104+ const { shard1, shard2, shard3, pointName, passphrase } = this.state
97105
98106 const phPoint = this.pointPlaceholder
99107 const phTick = this.ticketPlaceholder
@@ -169,13 +177,34 @@ class Shards extends React.Component {
169177 <InnerLabel>{ 'Shard 3' }</InnerLabel>
170178 </ShardInput>
171179
180+ <InputCaption>
181+ { 'If your wallet requires a passphrase, you may enter it below.' }
182+ </InputCaption>
183+
184+ <Input
185+ className='pt-8'
186+ prop-size='md'
187+ prop-format='innerLabel'
188+ name='passphrase'
189+ type='password'
190+ value={ passphrase }
191+ autocomplete='off'
192+ onChange={ this.handlePassphraseInput }>
193+ <InnerLabel>{'Passphrase'}</InnerLabel>
194+ </Input>
195+
172196 <Button
173197 disabled={ !ready }
174198 className={'mt-8'}
175199 prop-size={'lg wide'}
176200 // prop-color={this.buttonTriState(wallet)}
177201 onClick={() =>
178− this.walletFromShards(shard1, shard2, shard3, pointName)
202+ this.walletFromShards(
203+ shard1,
204+ shard2,
205+ shard3,
206+ pointName,
207+ passphrase)
179208 }>
180209 {'Unlock Wallet →'}
181210 </Button>
@@ -183,7 +212,7 @@ class Shards extends React.Component {
183212 <Button
184213 className={'mt-4'}
185214 prop-size={'xl wide'}
186− disabled={ Maybe.Nothing.hasInstance(wallet) }
215+ disabled={ Nothing.hasInstance(wallet) }
187216 onClick={ () => {
188217 popRoute()
189218 pushRoute(ROUTE_NAMES.SHIPS)
src/bridge/views/Ticket.js+42−17
@@ -1,7 +1,14 @@
1−import Maybe from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe'
22import React from 'react'
33import { Button } from '../components/Base'
4−import { InnerLabel, ValidatedSigil, PointInput, TicketInput } from '../components/Base'
4+import {
5+ InnerLabel,
6+ ValidatedSigil,
7+ PointInput,
8+ TicketInput,
9+ Input,
10+ InputCaption
11+ } from '../components/Base'
512import { Row, Col, H1, P } from '../components/Base'
613import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
714import * as ob from 'urbit-ob'
@@ -26,6 +33,7 @@ class Ticket extends React.Component {
2633 this.state = {
2734 ticket: '',
2835 pointName: '',
36+ passphrase: '',
2937 isUnlocking: false
3038 }
3139
@@ -33,6 +41,7 @@ class Ticket extends React.Component {
3341 this.ticketPlaceholder = placeholder(8)
3442
3543 this.handleTicketInput = this.handleTicketInput.bind(this)
44+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
3645 this.handlePointNameInput = this.handlePointNameInput.bind(this)
3746 }
3847
@@ -40,19 +49,17 @@ class Ticket extends React.Component {
4049 this.setState({ ticket })
4150 }
4251
52+ handlePassphraseInput(passphrase) {
53+ this.setState({ passphrase })
54+ }
55+
4356 handlePointNameInput(pointName) {
4457 if (pointName.length < 15) {
4558 this.setState({ pointName })
4659 }
4760 }
4861
49− // buttonTriState = (wallet) => {
50− // if (wallet.isNothing()) return 'blue'
51− // if (wallet === false) return 'yellow'
52− // if (Maybe.Nothing.hasInstance(wallet)) return 'green'
53− // }
54−
55− async walletFromTicket(ticket, pointName) {
62+ async walletFromTicket(ticket, pointName, passphrase) {
5663 const { setWallet, setUrbitWallet } = this.props
5764
5865 this.setState({
@@ -61,12 +68,13 @@ class Ticket extends React.Component {
6168
6269 const urbitWallet = await kg.generateWallet({
6370 ticket: ticket,
64− ship: ob.patp2dec(pointName)
71+ ship: ob.patp2dec(pointName),
72+ passphrase: passphrase
6573 })
6674 const mnemonic = urbitWallet.ownership.seed
67− const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH)
75+ const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH, passphrase)
6876 setWallet(wallet)
69− setUrbitWallet(Maybe.Just(urbitWallet))
77+ setUrbitWallet(Just(urbitWallet))
7078
7179 this.setState({
7280 isUnlocking: false
@@ -75,7 +83,7 @@ class Ticket extends React.Component {
7583
7684 render() {
7785 const { popRoute, pushRoute, wallet } = this.props
78− const { ticket, pointName } = this.state
86+ const { ticket, pointName, passphrase } = this.state
7987
8088 const phPoint = this.pointPlaceholder
8189 const phTick = this.ticketPlaceholder
@@ -119,12 +127,29 @@ class Ticket extends React.Component {
119127 <InnerLabel>{ 'Ticket' }</InnerLabel>
120128 </TicketInput>
121129
130+ <InputCaption>
131+ { 'If your wallet requires a passphrase, you may enter it below.' }
132+ </InputCaption>
133+
134+ <Input
135+ className='pt-8'
136+ prop-size='md'
137+ prop-format='innerLabel'
138+ name='passphrase'
139+ type='password'
140+ value={ passphrase }
141+ autocomplete='off'
142+ onChange={ this.handlePassphraseInput }>
143+ <InnerLabel>{'Passphrase'}</InnerLabel>
144+ </Input>
145+
122146 <Button
123147 className={'mt-8'}
124148 prop-size={'lg wide'}
125− disabled={this.state.isUnlocking || Maybe.Just.hasInstance(wallet)}
126− // prop-color={this.buttonTriState(wallet)}
127− onClick={() => this.walletFromTicket(ticket, pointName)}>
149+ disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
150+ onClick={
151+ () => this.walletFromTicket(ticket, pointName, passphrase)
152+ }>
128153
129154 <span className="relative">
130155 {this.state.isUnlocking &&
@@ -137,7 +162,7 @@ class Ticket extends React.Component {
137162 <Button
138163 className={'mt-4'}
139164 prop-size={'xl wide'}
140− disabled={ Maybe.Nothing.hasInstance(wallet) }
165+ disabled={ Nothing.hasInstance(wallet) }
141166 onClick={ () => {
142167 popRoute()
143168 pushRoute(ROUTE_NAMES.SHIPS)