Socket
Socket
Sign inDemoInstall

@ledgerhq/hw-transport-node-hid

Package Overview
Dependencies
Maintainers
11
Versions
399
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ledgerhq/hw-transport-node-hid - npm Package Compare versions

Comparing version 4.35.0 to 4.35.1-beta.28

LICENSE

5

lib/getDevices.js

@@ -12,2 +12,4 @@ "use strict";

var _devices = require("@ledgerhq/devices");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -19,6 +21,7 @@

};
function getDevices() {
// $FlowFixMe bug in HID flow def
return _nodeHid2.default.devices(0x2c97, 0x0).filter(filterInterface);
return _nodeHid2.default.devices(_devices.ledgerUSBVendorId, 0x0).filter(filterInterface);
}
//# sourceMappingURL=getDevices.js.map

505

lib/TransportNodeHid.js

@@ -17,2 +17,10 @@ "use strict";

var _hidFraming = require("@ledgerhq/devices/lib/hid-framing");
var _hidFraming2 = _interopRequireDefault(_hidFraming);
var _devices = require("@ledgerhq/devices");
var _errors = require("@ledgerhq/errors");
var _getDevices = require("./getDevices");

@@ -36,14 +44,2 @@

// FIXME drop
function defer() {
var resolve = void 0,
reject = void 0;
var promise = new Promise(function (success, failure) {
resolve = success;
reject = failure;
});
if (!resolve || !reject) throw new Error("defer() error"); // this never happens and is just to make flow happy
return { promise: promise, resolve: resolve, reject: reject };
}
var listenDevicesDebounce = 500;

@@ -55,2 +51,6 @@ var listenDevicesPollingSkip = function listenDevicesPollingSkip() {

var isDisconnectedError = function isDisconnectedError(e) {
return e && e.message && e.message.indexOf("HID") >= 0;
};
/**

@@ -67,286 +67,3 @@ * node-hid Transport implementation

function TransportNodeHid(device) // FIXME not used?
{
var ledgerTransport = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var timeout = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
_classCallCheck(this, TransportNodeHid);
var _this = _possibleConstructorReturn(this, (TransportNodeHid.__proto__ || Object.getPrototypeOf(TransportNodeHid)).call(this));
_this.device = device;
_this.ledgerTransport = ledgerTransport;
_this.timeout = timeout;
_this.exchangeStack = [];
return _this;
}
/**
*/
_createClass(TransportNodeHid, [{
key: "exchange",
value: function exchange(apdu) {
var _this2 = this;
function ledgerWrap(channel, command, packetSize) {
var sequenceIdx = 0;
var offset = 0;
var tmp = Buffer.alloc(7);
tmp.writeUInt16BE(channel, 0);
tmp[2] = 0x05; // TAG_APDU
tmp.writeUInt16BE(sequenceIdx, 3);
sequenceIdx++;
tmp.writeUInt16BE(command.length, 5);
var blockSize = command.length > packetSize - 7 ? packetSize - 7 : command.length;
var result = Buffer.concat([tmp, command.slice(offset, offset + blockSize)], blockSize + 7);
offset += blockSize;
while (offset !== command.length) {
tmp = Buffer.alloc(5);
tmp.writeUInt16BE(channel, 0);
tmp[2] = 0x05; // TAG_APDU
tmp.writeUInt16BE(sequenceIdx, 3);
sequenceIdx++;
blockSize = command.length - offset > packetSize - 5 ? packetSize - 5 : command.length - offset;
result = Buffer.concat([result, tmp, command.slice(offset, offset + blockSize)], result.length + blockSize + 5);
offset += blockSize;
}
return result;
}
function ledgerUnwrap(channel, data, packetSize) {
var offset = 0;
var responseLength = void 0;
var sequenceIdx = 0;
var response = void 0;
if (typeof data === "undefined" || data.length < 7 + 5) {
return;
}
if (data[offset++] !== channel >> 8) {
throw new _hwTransport.TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== (channel & 0xff)) {
throw new _hwTransport.TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== 0x05) {
throw new _hwTransport.TransportError("Invalid tag", "InvalidTag");
}
if (data[offset++] !== 0x00) {
throw new _hwTransport.TransportError("Invalid sequence", "InvalidSequence");
}
if (data[offset++] !== 0x00) {
throw new _hwTransport.TransportError("Invalid sequence", "InvalidSequence");
}
responseLength = (data[offset++] & 0xff) << 8;
responseLength |= data[offset++] & 0xff;
if (data.length < 7 + responseLength) {
return;
}
var blockSize = responseLength > packetSize - 7 ? packetSize - 7 : responseLength;
response = data.slice(offset, offset + blockSize);
offset += blockSize;
while (response.length !== responseLength) {
sequenceIdx++;
if (offset === data.length) {
return;
}
if (data[offset++] !== channel >> 8) {
throw new _hwTransport.TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== (channel & 0xff)) {
throw new _hwTransport.TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== 0x05) {
throw new _hwTransport.TransportError("Invalid tag", "InvalidTag");
}
if (data[offset++] !== sequenceIdx >> 8) {
throw new _hwTransport.TransportError("Invalid sequence", "InvalidSequence");
}
if (data[offset++] !== (sequenceIdx & 0xff)) {
throw new _hwTransport.TransportError("Invalid sequence", "InvalidSequence");
}
blockSize = responseLength - response.length > packetSize - 5 ? packetSize - 5 : responseLength - response.length;
if (blockSize > data.length - offset) {
return;
}
response = Buffer.concat([response, data.slice(offset, offset + blockSize)], response.length + blockSize);
offset += blockSize;
}
return response;
}
var debug = this.debug;
var deferred = defer();
if (debug) {
debug("=>" + apdu.toString("hex"));
deferred.promise.then(function (result) {
debug("<= " + result.toString("hex"));
});
}
var exchangeTimeout = void 0;
var transport = void 0;
if (!this.ledgerTransport) {
transport = apdu;
} else {
transport = ledgerWrap(0x0101, apdu, 64);
}
if (this.timeout !== 0) {
exchangeTimeout = setTimeout(function () {
// Node.js supports timeouts
deferred.reject(new _hwTransport.TransportError("timeout", "timeout"));
}, this.timeout);
}
// enter the exchange wait list
this.exchangeStack.push(deferred);
if (this.exchangeStack.length === 1) {
var processNextExchange = function processNextExchange() {
// don't pop it now, to avoid multiple at once
var deferred = _this2.exchangeStack[0];
var send = function send(content) {
var data = [0x00];
for (var i = 0; i < content.length; i++) {
data.push(content[i]);
}
_this2.device.write(data);
return Promise.resolve(content.length);
};
var recv = function recv() {
return new Promise(function (resolve, reject) {
return _this2.device.read(function (err, res) {
if (err || !res) reject(err);else {
var buffer = Buffer.from(res);
resolve(buffer);
}
});
});
};
var performExchange = function performExchange() {
var offsetSent = 0;
var firstReceived = true;
var toReceive = 0;
var received = Buffer.alloc(0);
var sendPart = function sendPart() {
if (offsetSent === transport.length) {
return receivePart();
}
var blockSize = transport.length - offsetSent > 64 ? 64 : transport.length - offsetSent;
var block = transport.slice(offsetSent, offsetSent + blockSize);
var paddingSize = 64 - block.length;
if (paddingSize !== 0) {
var padding = Buffer.alloc(paddingSize).fill(0);
block = Buffer.concat([block, padding], block.length + paddingSize);
}
return send(block).then(function () {
offsetSent += blockSize;
return sendPart();
});
};
var receivePart = function receivePart() {
if (!_this2.ledgerTransport) {
return recv().then(function (result) {
received = Buffer.concat([received, result], received.length + result.length);
if (firstReceived) {
firstReceived = false;
if (received.length === 2 || received[0] !== 0x61) {
return received;
} else {
toReceive = received[1];
if (toReceive === 0) {
toReceive = 256;
}
toReceive += 2;
}
}
if (toReceive < 64) {
return received;
} else {
toReceive -= 64;
return receivePart();
}
});
} else {
return recv().then(function (result) {
received = Buffer.concat([received, result], received.length + result.length);
var response = ledgerUnwrap(0x0101, received, 64);
if (typeof response !== "undefined") {
return response;
} else {
return receivePart();
}
});
}
};
return sendPart();
};
performExchange().then(function (result) {
var response = void 0,
resultBin = result;
if (!_this2.ledgerTransport) {
if (resultBin.length === 2 || resultBin[0] !== 0x61) {
response = resultBin;
} else {
var size = resultBin[1];
// fake T0
if (size === 0) {
size = 256;
}
response = resultBin.slice(2);
}
} else {
response = resultBin;
}
// build the response
if (_this2.timeout !== 0) {
clearTimeout(exchangeTimeout);
}
return response;
}).then(function (response) {
// consume current promise
_this2.exchangeStack.shift();
// schedule next exchange
if (_this2.exchangeStack.length > 0) {
processNextExchange();
}
return response;
}, function (err) {
if (_this2.timeout !== 0) {
clearTimeout(exchangeTimeout);
}
throw err;
})
// plug to deferred
.then(deferred.resolve, deferred.reject);
};
// schedule next exchange
processNextExchange();
}
// the exchangeStack will process the promise when possible
return deferred.promise;
}
}, {
key: "setScrambleKey",
value: function setScrambleKey() {}
}, {
key: "close",
value: function close() {
this.device.close();
return Promise.resolve();
}
}], [{
_createClass(TransportNodeHid, null, [{
key: "open",

@@ -380,3 +97,3 @@

throw new _hwTransport.TransportError("NoDevice", "NoDevice");
throw new _errors.TransportError("NoDevice", "NoDevice");

@@ -394,3 +111,3 @@ case 5:

function open(_x3) {
function open(_x) {
return _ref.apply(this, arguments);

@@ -401,4 +118,55 @@ }

}()
/**
*/
}]);
function TransportNodeHid(device) {
_classCallCheck(this, TransportNodeHid);
var _this = _possibleConstructorReturn(this, (TransportNodeHid.__proto__ || Object.getPrototypeOf(TransportNodeHid)).call(this));
_initialiseProps.call(_this);
_this.device = device;
// $FlowFixMe
var info = device.getDeviceInfo();
_this.deviceModel = info && info.serialNumber ? (0, _devices.identifyUSBProductId)(parseInt(info.serialNumber, 16)) : null;
return _this;
}
_createClass(TransportNodeHid, [{
key: "setScrambleKey",
value: function setScrambleKey() {}
}, {
key: "close",
value: function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 2;
return this.exchangeBusyPromise;
case 2:
this.device.close();
case 3:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
function close() {
return _ref2.apply(this, arguments);
}
return close;
}()
}]);
return TransportNodeHid;

@@ -451,3 +219,4 @@ }(_hwTransport2.default);

var descriptor = device.path;
observer.next({ type: "add", descriptor: descriptor, device: device });
var deviceModel = (0, _devices.identifyUSBProductId)(device.productId);
observer.next({ type: "add", descriptor: descriptor, deviceModel: deviceModel });
}

@@ -477,7 +246,17 @@ }

if (unsubscribed || !device) return;
observer.next({ type: "add", descriptor: device.path, device: device });
var deviceModel = (0, _devices.identifyUSBProductId)(device.productId);
observer.next({
type: "add",
descriptor: device.path,
deviceModel: deviceModel
});
};
var onRemove = function onRemove(device) {
if (unsubscribed || !device) return;
observer.next({ type: "remove", descriptor: device.path, device: device });
var deviceModel = (0, _devices.identifyUSBProductId)(device.productId);
observer.next({
type: "remove",
descriptor: device.path,
deviceModel: deviceModel
});
};

@@ -495,3 +274,127 @@ events.on("add", onAdd);

var _initialiseProps = function _initialiseProps() {
var _this2 = this;
this.channel = Math.floor(Math.random() * 0xffff);
this.packetSize = 64;
this.disconnected = false;
this.setDisconnected = function () {
if (!_this2.disconnected) {
_this2.emit("disconnect");
_this2.disconnected = true;
}
};
this.writeHID = function (content) {
var data = [0x00];
for (var i = 0; i < content.length; i++) {
data.push(content[i]);
}
try {
_this2.device.write(data);
return Promise.resolve();
} catch (e) {
if (isDisconnectedError(e)) {
_this2.setDisconnected();
return Promise.reject(new _errors.DisconnectedDevice(e.message));
}
return Promise.reject(e);
}
};
this.readHID = function () {
return new Promise(function (resolve, reject) {
return _this2.device.read(function (e, res) {
if (!res) {
return reject(new _errors.DisconnectedDevice());
}
if (e) {
if (isDisconnectedError(e)) {
_this2.setDisconnected();
return reject(new _errors.DisconnectedDevice(e.message));
}
reject(e);
} else {
var buffer = Buffer.from(res);
resolve(buffer);
}
});
});
};
this.exchange = function (apdu) {
return _this2.exchangeAtomicImpl(_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
var debug, channel, packetSize, framing, blocks, i, result, acc, buffer;
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
debug = _this2.debug, channel = _this2.channel, packetSize = _this2.packetSize;
if (debug) {
debug("=>" + apdu.toString("hex"));
}
framing = (0, _hidFraming2.default)(channel, packetSize);
// Write...
blocks = framing.makeBlocks(apdu);
i = 0;
case 5:
if (!(i < blocks.length)) {
_context3.next = 11;
break;
}
_context3.next = 8;
return _this2.writeHID(blocks[i]);
case 8:
i++;
_context3.next = 5;
break;
case 11:
// Read...
result = void 0;
acc = void 0;
case 13:
if (result = framing.getReducedResult(acc)) {
_context3.next = 20;
break;
}
_context3.next = 16;
return _this2.readHID();
case 16:
buffer = _context3.sent;
acc = framing.reduceResponse(acc, buffer);
_context3.next = 13;
break;
case 20:
if (debug) {
debug("<=" + result.toString("hex"));
}
return _context3.abrupt("return", result);
case 22:
case "end":
return _context3.stop();
}
}
}, _callee3, _this2);
})));
};
};
exports.default = TransportNodeHid;
//# sourceMappingURL=TransportNodeHid.js.map
{
"name": "@ledgerhq/hw-transport-node-hid",
"version": "4.35.0",
"version": "4.35.1-beta.28+0f83986",
"description": "Ledger Hardware Wallet Node implementation of the communication layer, using node-hid",

@@ -28,10 +28,12 @@ "keywords": [

"dependencies": {
"@ledgerhq/hw-transport": "^4.35.0",
"@ledgerhq/devices": "^4.35.1-beta.28+0f83986",
"@ledgerhq/errors": "^4.35.1-beta.28+0f83986",
"@ledgerhq/hw-transport": "^4.35.1-beta.28+0f83986",
"lodash": "^4.17.11",
"node-hid": "^0.7.2",
"usb": "^1.3.3"
"node-hid": "^0.7.6",
"usb": "^1.5.0"
},
"devDependencies": {
"flow-bin": "^0.78.0",
"flow-typed": "^2.4.0"
"flow-bin": "^0.92.1",
"flow-typed": "^2.5.1"
},

@@ -42,4 +44,6 @@ "scripts": {

"build": "bash ../../script/build.sh",
"watch": "bash ../../script/watch.sh"
}
"watch": "bash ../../script/watch.sh",
"doc": "bash ../../script/doc.sh"
},
"gitHead": "0f839862fcbb7b74581b74162c1534c9897463cf"
}
<img src="https://user-images.githubusercontent.com/211411/34776833-6f1ef4da-f618-11e7-8b13-f0697901d6a8.png" height="100" />
[Github](https://github.com/LedgerHQ/ledgerjs/),
[API Doc](http://ledgerhq.github.io/ledgerjs/),
[Ledger Devs Slack](https://ledger-dev.slack.com/)
## @ledgerhq/hw-transport-node-hid
Library for Ledger Hardware Wallets.
Allows to communicate with Ledger Hardware Wallets.
[Github](https://github.com/LedgerHQ/ledgerjs/),
[API Doc](http://ledgerhq.github.io/ledgerjs/),
[Ledger Devs Slack](https://ledger-dev.slack.com/)
**[Node]**/Electron **(HID)** – uses `node-hid` and `usb`.
## API
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
#### Table of Contents
- [TransportNodeHid](#transportnodehid)
- [Parameters](#parameters)
- [Examples](#examples)
- [listen](#listen)
- [Parameters](#parameters-1)
- [open](#open)
- [Parameters](#parameters-2)
### TransportNodeHid
**Extends Transport**
node-hid Transport implementation
#### Parameters
- `device` **HID.HID**
#### Examples
```javascript
import TransportNodeHid from "@ledgerhq/hw-transport-node-u2f";
...
TransportNodeHid.create().then(transport => ...)
```
#### listen
##### Parameters
- `observer` **Observer&lt;DescriptorEvent&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>>**
Returns **Subscription**
#### open
if path="" is not provided, the library will take the first device
##### Parameters
- `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
// @flow
import HID from "node-hid";
import { ledgerUSBVendorId } from "@ledgerhq/devices";

@@ -12,3 +13,3 @@ const filterInterface = device =>

// $FlowFixMe bug in HID flow def
return HID.devices(0x2c97, 0x0).filter(filterInterface);
return HID.devices(ledgerUSBVendorId, 0x0).filter(filterInterface);
}
//@flow
import HID from "node-hid";
import Transport, { TransportError } from "@ledgerhq/hw-transport";
import Transport from "@ledgerhq/hw-transport";
import type {

@@ -10,21 +10,9 @@ Observer,

} from "@ledgerhq/hw-transport";
import hidFraming from "@ledgerhq/devices/lib/hid-framing";
import { identifyUSBProductId } from "@ledgerhq/devices";
import type { DeviceModel } from "@ledgerhq/devices";
import { TransportError, DisconnectedDevice } from "@ledgerhq/errors";
import getDevices from "./getDevices";
import listenDevices from "./listenDevices";
// FIXME drop
type Defer<T> = {
promise: Promise<T>,
resolve: T => void,
reject: any => void
};
function defer<T>(): Defer<T> {
let resolve, reject;
let promise = new Promise(function(success, failure) {
resolve = success;
reject = failure;
});
if (!resolve || !reject) throw new Error("defer() error"); // this never happens and is just to make flow happy
return { promise, resolve, reject };
}
let listenDevicesDebounce = 500;

@@ -34,2 +22,5 @@ let listenDevicesPollingSkip = () => false;

const isDisconnectedError = e =>
e && e.message && e.message.indexOf("HID") >= 0;
/**

@@ -43,19 +34,2 @@ * node-hid Transport implementation

export default class TransportNodeHid extends Transport<string> {
device: HID.HID;
ledgerTransport: boolean;
timeout: number;
exchangeStack: Array<*>;
constructor(
device: HID.HID,
ledgerTransport: boolean = true, // FIXME not used?
timeout: number = 0 // FIXME not used?
) {
super();
this.device = device;
this.ledgerTransport = ledgerTransport;
this.timeout = timeout;
this.exchangeStack = [];
}
static isSupported = (): Promise<boolean> =>

@@ -80,4 +54,4 @@ Promise.resolve(typeof HID.HID === "function");

: debug
? (...log) => console.log("[listenDevices]", ...log)
: () => {};
? (...log) => console.log("[listenDevices]", ...log)
: () => {};
};

@@ -96,3 +70,4 @@

const descriptor: string = device.path;
observer.next({ type: "add", descriptor, device });
const deviceModel = identifyUSBProductId(device.productId);
observer.next({ type: "add", descriptor, deviceModel });
}

@@ -109,7 +84,17 @@ }

if (unsubscribed || !device) return;
observer.next({ type: "add", descriptor: device.path, device });
const deviceModel = identifyUSBProductId(device.productId);
observer.next({
type: "add",
descriptor: device.path,
deviceModel
});
};
const onRemove = device => {
if (unsubscribed || !device) return;
observer.next({ type: "remove", descriptor: device.path, device });
const deviceModel = identifyUSBProductId(device.productId);
observer.next({
type: "remove",
descriptor: device.path,
deviceModel
});
};

@@ -139,292 +124,98 @@ events.on("add", onAdd);

exchange(apdu: Buffer): Promise<Buffer> {
function ledgerWrap(channel, command, packetSize) {
let sequenceIdx = 0;
let offset = 0;
device: HID.HID;
deviceModel: ?DeviceModel;
let tmp = Buffer.alloc(7);
tmp.writeUInt16BE(channel, 0);
tmp[2] = 0x05; // TAG_APDU
tmp.writeUInt16BE(sequenceIdx, 3);
sequenceIdx++;
tmp.writeUInt16BE(command.length, 5);
let blockSize =
command.length > packetSize - 7 ? packetSize - 7 : command.length;
let result = Buffer.concat(
[tmp, command.slice(offset, offset + blockSize)],
blockSize + 7
);
offset += blockSize;
while (offset !== command.length) {
tmp = Buffer.alloc(5);
tmp.writeUInt16BE(channel, 0);
tmp[2] = 0x05; // TAG_APDU
tmp.writeUInt16BE(sequenceIdx, 3);
sequenceIdx++;
blockSize =
command.length - offset > packetSize - 5
? packetSize - 5
: command.length - offset;
result = Buffer.concat(
[result, tmp, command.slice(offset, offset + blockSize)],
result.length + blockSize + 5
);
offset += blockSize;
}
return result;
}
channel = Math.floor(Math.random() * 0xffff);
packetSize = 64;
disconnected = false;
function ledgerUnwrap(channel, data, packetSize) {
let offset = 0;
let responseLength;
let sequenceIdx = 0;
let response;
if (typeof data === "undefined" || data.length < 7 + 5) {
return;
}
if (data[offset++] !== channel >> 8) {
throw new TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== (channel & 0xff)) {
throw new TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== 0x05) {
throw new TransportError("Invalid tag", "InvalidTag");
}
if (data[offset++] !== 0x00) {
throw new TransportError("Invalid sequence", "InvalidSequence");
}
if (data[offset++] !== 0x00) {
throw new TransportError("Invalid sequence", "InvalidSequence");
}
responseLength = (data[offset++] & 0xff) << 8;
responseLength |= data[offset++] & 0xff;
if (data.length < 7 + responseLength) {
return;
}
let blockSize =
responseLength > packetSize - 7 ? packetSize - 7 : responseLength;
response = data.slice(offset, offset + blockSize);
offset += blockSize;
while (response.length !== responseLength) {
sequenceIdx++;
if (offset === data.length) {
return;
}
if (data[offset++] !== channel >> 8) {
throw new TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== (channel & 0xff)) {
throw new TransportError("Invalid channel", "InvalidChannel");
}
if (data[offset++] !== 0x05) {
throw new TransportError("Invalid tag", "InvalidTag");
}
if (data[offset++] !== sequenceIdx >> 8) {
throw new TransportError("Invalid sequence", "InvalidSequence");
}
if (data[offset++] !== (sequenceIdx & 0xff)) {
throw new TransportError("Invalid sequence", "InvalidSequence");
}
blockSize =
responseLength - response.length > packetSize - 5
? packetSize - 5
: responseLength - response.length;
if (blockSize > data.length - offset) {
return;
}
response = Buffer.concat(
[response, data.slice(offset, offset + blockSize)],
response.length + blockSize
);
offset += blockSize;
}
return response;
}
constructor(device: HID.HID) {
super();
this.device = device;
// $FlowFixMe
const info = device.getDeviceInfo();
this.deviceModel =
info && info.serialNumber
? identifyUSBProductId(parseInt(info.serialNumber, 16))
: null;
}
const { debug } = this;
const deferred = defer();
if (debug) {
debug("=>" + apdu.toString("hex"));
deferred.promise.then(result => {
debug("<= " + result.toString("hex"));
});
setDisconnected = () => {
if (!this.disconnected) {
this.emit("disconnect");
this.disconnected = true;
}
};
let exchangeTimeout;
let transport;
if (!this.ledgerTransport) {
transport = apdu;
} else {
transport = ledgerWrap(0x0101, apdu, 64);
writeHID = (content: Buffer): Promise<void> => {
const data = [0x00];
for (let i = 0; i < content.length; i++) {
data.push(content[i]);
}
if (this.timeout !== 0) {
exchangeTimeout = setTimeout(() => {
// Node.js supports timeouts
deferred.reject(new TransportError("timeout", "timeout"));
}, this.timeout);
try {
this.device.write(data);
return Promise.resolve();
} catch (e) {
if (isDisconnectedError(e)) {
this.setDisconnected();
return Promise.reject(new DisconnectedDevice(e.message));
}
return Promise.reject(e);
}
};
// enter the exchange wait list
this.exchangeStack.push(deferred);
if (this.exchangeStack.length === 1) {
const processNextExchange = () => {
// don't pop it now, to avoid multiple at once
const deferred = this.exchangeStack[0];
const send = content => {
const data = [0x00];
for (let i = 0; i < content.length; i++) {
data.push(content[i]);
readHID = (): Promise<Buffer> =>
new Promise((resolve, reject) =>
this.device.read((e, res) => {
if (!res) {
return reject(new DisconnectedDevice());
}
if (e) {
if (isDisconnectedError(e)) {
this.setDisconnected();
return reject(new DisconnectedDevice(e.message));
}
this.device.write(data);
return Promise.resolve(content.length);
};
reject(e);
} else {
const buffer = Buffer.from(res);
resolve(buffer);
}
})
);
const recv = () =>
new Promise((resolve, reject) =>
this.device.read((err, res) => {
if (err || !res) reject(err);
else {
const buffer = Buffer.from(res);
resolve(buffer);
}
})
);
exchange = (apdu: Buffer): Promise<Buffer> =>
this.exchangeAtomicImpl(async () => {
const { debug, channel, packetSize } = this;
if (debug) {
debug("=>" + apdu.toString("hex"));
}
const performExchange = () => {
let offsetSent = 0;
let firstReceived = true;
let toReceive = 0;
const framing = hidFraming(channel, packetSize);
let received = Buffer.alloc(0);
const sendPart = () => {
if (offsetSent === transport.length) {
return receivePart();
}
const blockSize =
transport.length - offsetSent > 64
? 64
: transport.length - offsetSent;
let block = transport.slice(offsetSent, offsetSent + blockSize);
const paddingSize = 64 - block.length;
if (paddingSize !== 0) {
let padding = Buffer.alloc(paddingSize).fill(0);
block = Buffer.concat(
[block, padding],
block.length + paddingSize
);
}
return send(block).then(() => {
offsetSent += blockSize;
return sendPart();
});
};
// Write...
const blocks = framing.makeBlocks(apdu);
for (let i = 0; i < blocks.length; i++) {
await this.writeHID(blocks[i]);
}
const receivePart = () => {
if (!this.ledgerTransport) {
return recv().then(result => {
received = Buffer.concat(
[received, result],
received.length + result.length
);
if (firstReceived) {
firstReceived = false;
if (received.length === 2 || received[0] !== 0x61) {
return received;
} else {
toReceive = received[1];
if (toReceive === 0) {
toReceive = 256;
}
toReceive += 2;
}
}
if (toReceive < 64) {
return received;
} else {
toReceive -= 64;
return receivePart();
}
});
} else {
return recv().then(result => {
received = Buffer.concat(
[received, result],
received.length + result.length
);
const response = ledgerUnwrap(0x0101, received, 64);
if (typeof response !== "undefined") {
return response;
} else {
return receivePart();
}
});
}
};
return sendPart();
};
// Read...
let result;
let acc;
while (!(result = framing.getReducedResult(acc))) {
const buffer = await this.readHID();
acc = framing.reduceResponse(acc, buffer);
}
performExchange()
.then(result => {
let response,
resultBin = result;
if (!this.ledgerTransport) {
if (resultBin.length === 2 || resultBin[0] !== 0x61) {
response = resultBin;
} else {
let size = resultBin[1];
// fake T0
if (size === 0) {
size = 256;
}
response = resultBin.slice(2);
}
} else {
response = resultBin;
}
// build the response
if (this.timeout !== 0) {
clearTimeout(exchangeTimeout);
}
return response;
})
.then(
response => {
// consume current promise
this.exchangeStack.shift();
if (debug) {
debug("<=" + result.toString("hex"));
}
return result;
});
// schedule next exchange
if (this.exchangeStack.length > 0) {
processNextExchange();
}
return response;
},
(err: Error) => {
if (this.timeout !== 0) {
clearTimeout(exchangeTimeout);
}
throw err;
}
)
// plug to deferred
.then(deferred.resolve, deferred.reject);
};
// schedule next exchange
processNextExchange();
}
// the exchangeStack will process the promise when possible
return deferred.promise;
}
setScrambleKey() {}
close(): Promise<void> {
async close(): Promise<void> {
await this.exchangeBusyPromise;
this.device.close();
return Promise.resolve();
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc