Skip to content
This repository was archived by the owner on Aug 12, 2022. It is now read-only.

Commit 9ae31a4

Browse files
authored
9.1.0 Passphrase redesign (#107)
* new passphrase flow * trezor-link 1.6.7-new-passphrase-flow * 9.0.0-new-passphrase-flow.0 * 9.0.0-new-passphrase-flow.1 * cleanup * 9.0.0-new-passphrase-flow.2 * 9.0.0-new-passphrase-flow.3 * filter session_id * 9.0.0-new-passphrase-flow.4 * 9.0.0-new-passphrase-flow.5 * normalize passphrase * trezor-link 1.7.0 * 9.1.0 * lockfile
1 parent 613295d commit 9ae31a4

File tree

6 files changed

+132
-82
lines changed

6 files changed

+132
-82
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "trezor.js",
3-
"version": "9.0.0",
3+
"version": "9.1.0",
44
"author": "TREZOR <info@trezor.io>",
55
"homepage": "https://github.com/trezor/trezor.js",
66
"description": "High-level Javascript API for Bitcoin Trezor",
@@ -69,7 +69,7 @@
6969
"node-fetch": "^1.6.0",
7070
"randombytes": "^2.0.1",
7171
"semver-compare": "1.0.0",
72-
"trezor-link": "1.6.0-bridge-fallback.2",
72+
"trezor-link": "1.7.0",
7373
"unorm": "^1.3.3",
7474
"whatwg-fetch": "0.11.0"
7575
},

src/device.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default class Device extends EventEmitter {
7373
disconnectEvent: Event0 = new Event0('disconnect', this);
7474
buttonEvent: Event1<string> = new Event1('button', this);
7575
errorEvent: Event1<Error> = new Event1('error', this);
76-
passphraseEvent: Event1<(e: ?Error, passphrase?: ?string) => void> = new Event1('passphrase', this);
76+
passphraseEvent: Event1<(e: ?Error, passphrase?: ?string, onDevice?: boolean) => void> = new Event1('passphrase', this);
7777
wordEvent: Event1<(e: ?Error, word?: ?string) => void> = new Event1('word', this);
7878
changedSessionsEvent: Event2<boolean, boolean> = new Event2('changedSessions', this);
7979
pinEvent: Event2<string, (e: ?Error, pin?: ?string) => void> = new Event2('pin', this);
@@ -489,8 +489,8 @@ export default class Device extends EventEmitter {
489489
}
490490

491491
// See comment on device-list option getPassphraseHash
492-
forwardPassphrase(source: Event1<(e: ?Error, passphrase?: ?string) => void>) {
493-
source.on((arg: (e: ?Error, passphrase?: ?string) => void) => {
492+
forwardPassphrase(source: Event1<(e: ?Error, passphrase?: ?string, onDevice?: boolean) => void>) {
493+
source.on((arg: (e: ?Error, passphrase?: ?string, onDevice?: boolean) => void) => {
494494
if (this.rememberedPlaintextPasshprase != null) {
495495
const p: string = this.rememberedPlaintextPasshprase;
496496

@@ -503,8 +503,8 @@ export default class Device extends EventEmitter {
503503
return;
504504
}
505505

506-
const argAndRemember = (e: ?Error, passphrase: ?string) => {
507-
if (this.rememberPlaintextPassphrase) {
506+
const argAndRemember = (e: ?Error, passphrase?: ?string, onDevice?: boolean) => {
507+
if (this.rememberPlaintextPassphrase && !onDevice) {
508508
if (passphrase != null) {
509509
const checkPasshprase = this.checkPassphraseHash(passphrase);
510510
if (!checkPasshprase) {
@@ -515,7 +515,7 @@ export default class Device extends EventEmitter {
515515

516516
this.rememberedPlaintextPasshprase = passphrase;
517517
}
518-
arg(e, passphrase);
518+
arg(e, passphrase, onDevice);
519519
};
520520
this.passphraseEvent.emit(argAndRemember);
521521
});

src/session.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default class Session extends EventEmitter {
5858
errorEvent: Event1<Error> = new Event1('error', this);
5959
buttonEvent: Event1<string> = new Event1('button', this);
6060
pinEvent: Event2<string, (e: ?Error, pin?: ?string) => void> = new Event2('pin', this);
61-
passphraseEvent: Event1<(e: ?Error, passphrase?: ?string) => void> = new Event1('passphrase', this);
61+
passphraseEvent: Event1<(e: ?Error, passphrase?: ?string, onDevice?: boolean) => void> = new Event1('passphrase', this);
6262
wordEvent: Event1<(e: ?Error, word?: ?string) => void> = new Event1('word', this);
6363

6464
static LABEL_MAX_LENGTH: number = 16;
@@ -120,8 +120,11 @@ export default class Session extends EventEmitter {
120120
}
121121

122122
initialize(): Promise<MessageResponse<trezor.Features>> {
123+
if (this.device && this.device.features.session_id) {
124+
return this.typedCall('Initialize', 'Features', { session_id: this.device.features.session_id });
125+
}
123126
if (this.device && this.device.passphraseState) {
124-
return this.typedCall('Initialize', 'Features', { state: this.device.passphraseState });
127+
return this.typedCall('Initialize', 'Features', { session_id: this.device.passphraseState });
125128
}
126129
return this.typedCall('Initialize', 'Features', {});
127130
}

src/trezortypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type Features = {
3737
bootloader_major_version: number | null;
3838
bootloader_minor_version: number | null;
3939
bootloader_patch_version: number | null;
40+
session_id?: string;
4041
};
4142

4243
export type ResetDeviceSettings = {

src/utils/call.js

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export class CallHelper {
179179
);
180180
}
181181

182-
if (res.type === 'PassphraseStateRequest') {
182+
if (res.type === 'Deprecated_PassphraseStateRequest') {
183183
if (this.session.device) {
184184
const currentState = this.session.device.passphraseState;
185185
const receivedState = res.message.state;
@@ -191,28 +191,40 @@ export class CallHelper {
191191
});
192192
}
193193
this.session.device.passphraseState = receivedState;
194-
return this._commonCall('PassphraseStateAck', { });
194+
return this._commonCall('Deprecated_PassphraseStateAck', { });
195195
}
196196
// ??? nowhere to save the state, throwing error
197197
return Promise.reject(new Error('Nowhere to save passphrase state.'));
198198
}
199199

200200
if (res.type === 'PassphraseRequest') {
201-
if (res.message.on_device) {
201+
if (res.message._on_device) {
202202
// "fake" button event
203203
this.session.buttonEvent.emit('PassphraseOnDevice');
204204
if (this.session.device && this.session.device.passphraseState) {
205-
return this._commonCall('PassphraseAck', { state: this.session.device.passphraseState });
205+
return this._commonCall('PassphraseAck', { _state: this.session.device.passphraseState });
206206
}
207207
return this._commonCall('PassphraseAck', { });
208208
}
209209
return this._promptPassphrase().then(
210-
passphrase => {
211-
if (this.session.device && this.session.device.passphraseState) {
212-
return this._commonCall('PassphraseAck', { passphrase: passphrase, state: this.session.device.passphraseState });
210+
(res: Object) => {
211+
const { passphrase, onDevice } = res;
212+
const session_id = this.session.device && this.session.device.features.session_id;
213+
const passphraseState = this.session.device && this.session.device.passphraseState;
214+
if (session_id) {
215+
return this._commonCall(
216+
'PassphraseAck',
217+
onDevice ? { on_device: true } : { passphrase }
218+
);
219+
} else if (passphraseState) {
220+
return this._commonCall(
221+
'PassphraseAck', {
222+
passphrase,
223+
_state: passphraseState,
224+
});
225+
} else {
226+
return this._commonCall('PassphraseAck', { passphrase });
213227
}
214-
215-
return this._commonCall('PassphraseAck', { passphrase: passphrase });
216228
},
217229
err => {
218230
return this._commonCall('Cancel', {}).catch(e => {
@@ -233,6 +245,16 @@ export class CallHelper {
233245
);
234246
}
235247

248+
// Initialize response for fw >= 2.3.0 || fw >= 1.9.0 with session_id=string
249+
// GetFeatures always response with session_id null
250+
// We need to avoid overwriting saved session_id in order to keep it for next Initialize call
251+
if (res.type === 'Features') {
252+
const oldSessionId = this.session.device && this.session.device.features.session_id;
253+
if (oldSessionId && !res.message.session_id) {
254+
res.message.session_id = oldSessionId;
255+
}
256+
}
257+
236258
return Promise.resolve(res);
237259
}
238260

@@ -253,14 +275,16 @@ export class CallHelper {
253275
});
254276
}
255277

256-
_promptPassphrase(): Promise<string> {
278+
_promptPassphrase(): Promise<Object> {
257279
return new Promise((resolve, reject) => {
258-
if (!this.session.passphraseEvent.emit((err, passphrase) => {
259-
if (err || passphrase == null) {
260-
reject(err);
261-
} else {
262-
resolve(passphrase.normalize('NFKD'));
280+
if (!this.session.passphraseEvent.emit((err, passphrase, onDevice) => {
281+
if (err || (!onDevice && passphrase == null)) {
282+
return reject(err);
283+
}
284+
if (typeof passphrase === 'string') {
285+
passphrase = passphrase.normalize('NFKD');
263286
}
287+
return resolve({ passphrase, onDevice });
264288
})) {
265289
if (this.session.debug) {
266290
console.warn('[trezor.js] [call] Passphrase callback not configured, cancelling request');

0 commit comments

Comments
 (0)