UNPKG

2.83 kBJavaScriptView Raw
1/* @flow */
2
3declare var __VERSION__: string;
4
5import * as HID from 'node-hid';
6
7import type {HID as HIDDevice, HIDDeviceDescription} from 'node-hid';
8
9import {debugInOut} from '../debug-decorator';
10
11const REPORT_ID = 63;
12
13const TREZOR_DESC = {
14 vendorId: 0x534c,
15 productId: 0x0001,
16 interface: 0,
17};
18
19type TrezorDeviceInfo = {path:string};
20
21export default class NodeHidPlugin {
22 name: string = `NodeHidPlugin`;
23
24 _sessionCounter: number = 0;
25
26 // path => device
27 _devices: {[path: string]: HIDDevice} = {};
28
29 version: string = __VERSION__;
30
31 debug: boolean = false;
32
33 @debugInOut
34 async init(debug: ?boolean): Promise<void> {
35 // try if it's a Node environment
36 if (typeof process === `object` && process != null && process.toString() === `[object process]`) {
37 return;
38 }
39 HID.devices(); // try to read devices once, to maybe get an error now before it's too late
40 throw new Error(`Not a node environment.`);
41 }
42
43 async enumerate(): Promise<Array<TrezorDeviceInfo>> {
44 const devices = HID.devices()
45 .filter(d =>
46 d.vendorId === TREZOR_DESC.vendorId &&
47 d.productId === TREZOR_DESC.productId &&
48 d.interface === TREZOR_DESC.interface
49 )
50 .map((device: HIDDeviceDescription): TrezorDeviceInfo => {
51 const path = device.path;
52 return {
53 path,
54 };
55 });
56 return devices;
57 }
58
59 async send(path: string, session: string, data: ArrayBuffer): Promise<void> {
60 const device = this._devices[path];
61 const toWrite = [REPORT_ID].concat(Array.from(new Uint8Array(data)));
62 device.write(toWrite);
63 }
64
65 receive(path: string, session: string): Promise<ArrayBuffer> {
66 const device: HIDDevice = this._devices[path];
67 return new Promise((resolve, reject) => {
68 device.read((error, data) => {
69 if (error != null) {
70 reject(error);
71 } else {
72 if (data == null) {
73 reject(new Error(`Data is null`));
74 } else {
75 if (data[0] !== 63) {
76 reject(new Error(`Invalid data; first byte should be 63, is ${data[0]}`));
77 }
78 resolve(nodeBuffer2arrayBuffer(data.slice(1)));
79 }
80 }
81 });
82 });
83 }
84
85 @debugInOut
86 async connect(path: string): Promise<string> {
87 const counter = this._sessionCounter;
88 this._sessionCounter++;
89 const device: HIDDevice = new HID.HID(path);
90 this._devices[path] = device;
91 // I am pausing, since I am not using the EventEmitter API, but the read() API
92 device.pause();
93 return counter.toString();
94 }
95
96 @debugInOut
97 async disconnect(path: string, session: string): Promise<void> {
98 const device = this._devices[path];
99 device.close();
100 delete this._devices[path];
101 }
102}
103
104function nodeBuffer2arrayBuffer(b: Buffer): ArrayBuffer {
105 return new Uint8Array(b).buffer;
106}