@ledgerhq/hw-transport-webhid
Advanced tools
Comparing version 4.74.3-alpha.6 to 4.77.0
@@ -6,48 +6,97 @@ "use strict"; | ||
}); | ||
exports.default = void 0; | ||
var _hwTransport = _interopRequireDefault(require("@ledgerhq/hw-transport")); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _hidFraming = _interopRequireDefault(require("@ledgerhq/devices/lib/hid-framing")); | ||
var requestLedgerDevice = function () { | ||
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | ||
var device; | ||
return regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
_context.next = 2; | ||
return getHID().requestDevice({ filters: ledgerDevices }); | ||
var _devices = require("@ledgerhq/devices"); | ||
case 2: | ||
device = _context.sent; | ||
return _context.abrupt("return", device); | ||
var _logs = require("@ledgerhq/logs"); | ||
case 4: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, this); | ||
})); | ||
var _errors = require("@ledgerhq/errors"); | ||
return function requestLedgerDevice() { | ||
return _ref.apply(this, arguments); | ||
}; | ||
}(); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var getLedgerDevices = function () { | ||
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { | ||
var devices; | ||
return regeneratorRuntime.wrap(function _callee2$(_context2) { | ||
while (1) { | ||
switch (_context2.prev = _context2.next) { | ||
case 0: | ||
_context2.next = 2; | ||
return getHID().getDevices(); | ||
const ledgerDevices = [{ | ||
vendorId: _devices.ledgerUSBVendorId | ||
}]; | ||
case 2: | ||
devices = _context2.sent; | ||
return _context2.abrupt("return", devices.filter(function (d) { | ||
return d.vendorId === _devices.ledgerUSBVendorId; | ||
})); | ||
const isSupported = () => Promise.resolve(!!(global.navigator && global.navigator.hid)); | ||
case 4: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
} | ||
}, _callee2, this); | ||
})); | ||
const getHID = () => { | ||
// $FlowFixMe | ||
const { | ||
hid | ||
} = navigator; | ||
if (!hid) throw new _errors.TransportError("navigator.hid is not supported", "HIDNotSupported"); | ||
return hid; | ||
}; | ||
return function getLedgerDevices() { | ||
return _ref2.apply(this, arguments); | ||
}; | ||
}(); | ||
async function requestLedgerDevice() { | ||
const device = await getHID().requestDevice({ | ||
filters: ledgerDevices | ||
}); | ||
return device; | ||
} | ||
var getFirstLedgerDevice = function () { | ||
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { | ||
var existingDevices; | ||
return regeneratorRuntime.wrap(function _callee3$(_context3) { | ||
while (1) { | ||
switch (_context3.prev = _context3.next) { | ||
case 0: | ||
_context3.next = 2; | ||
return getLedgerDevices(); | ||
async function getLedgerDevices() { | ||
const devices = await getHID().getDevices(); | ||
return devices.filter(d => d.vendorId === _devices.ledgerUSBVendorId); | ||
} | ||
case 2: | ||
existingDevices = _context3.sent; | ||
async function getFirstLedgerDevice() { | ||
const existingDevices = await getLedgerDevices(); | ||
if (existingDevices.length > 0) return existingDevices[0]; | ||
return requestLedgerDevice(); | ||
} | ||
if (!(existingDevices.length > 0)) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
return _context3.abrupt("return", existingDevices[0]); | ||
case 5: | ||
return _context3.abrupt("return", requestLedgerDevice()); | ||
case 6: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
} | ||
}, _callee3, this); | ||
})); | ||
return function getFirstLedgerDevice() { | ||
return _ref3.apply(this, arguments); | ||
}; | ||
}(); | ||
/** | ||
@@ -62,168 +111,380 @@ * WebHID Transport implementation | ||
class TransportWebHID extends _hwTransport.default { | ||
constructor(device) { | ||
super(); | ||
this.device = void 0; | ||
this.deviceModel = void 0; | ||
this.channel = Math.floor(Math.random() * 0xffff); | ||
this.packetSize = 64; | ||
this.inputs = []; | ||
this.inputCallback = void 0; | ||
var _hwTransport = require("@ledgerhq/hw-transport"); | ||
this.read = () => { | ||
if (this.inputs.length) { | ||
return Promise.resolve(this.inputs.shift()); | ||
} | ||
var _hwTransport2 = _interopRequireDefault(_hwTransport); | ||
return new Promise(success => { | ||
this.inputCallback = success; | ||
}); | ||
}; | ||
var _hidFraming = require("@ledgerhq/devices/lib/hid-framing"); | ||
this.onInputReport = e => { | ||
const buffer = new Buffer(e.data.buffer); | ||
var _hidFraming2 = _interopRequireDefault(_hidFraming); | ||
if (this.inputCallback) { | ||
this.inputCallback(buffer); | ||
this.inputCallback = null; | ||
} else { | ||
this.inputs.push(buffer); | ||
} | ||
}; | ||
var _devices = require("@ledgerhq/devices"); | ||
this._disconnectEmitted = false; | ||
var _logs = require("@ledgerhq/logs"); | ||
this._emitDisconnect = e => { | ||
if (this._disconnectEmitted) return; | ||
this._disconnectEmitted = true; | ||
this.emit("disconnect", e); | ||
}; | ||
var _errors = require("@ledgerhq/errors"); | ||
this.exchange = apdu => this.exchangeAtomicImpl(async () => { | ||
const { | ||
channel, | ||
packetSize | ||
} = this; | ||
(0, _logs.log)("apdu", "=> " + apdu.toString("hex")); | ||
const framing = (0, _hidFraming.default)(channel, packetSize); // Write... | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const blocks = framing.makeBlocks(apdu); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
for (let i = 0; i < blocks.length; i++) { | ||
(0, _logs.log)("hid-frame", "=> " + blocks[i].toString("hex")); | ||
await this.device.sendReport(0, blocks[i]); | ||
} // Read... | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
let result; | ||
let acc; | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
while (!(result = framing.getReducedResult(acc))) { | ||
const buffer = await this.read(); | ||
(0, _logs.log)("hid-frame", "<= " + buffer.toString("hex")); | ||
acc = framing.reduceResponse(acc, buffer); | ||
} | ||
var ledgerDevices = [{ vendorId: _devices.ledgerUSBVendorId }]; | ||
(0, _logs.log)("apdu", "<= " + result.toString("hex")); | ||
return result; | ||
}).catch(e => { | ||
if (e && e.message && e.message.includes("write")) { | ||
this._emitDisconnect(e); | ||
var isSupported = function isSupported() { | ||
return Promise.resolve(!!(global.navigator && global.navigator.hid)); | ||
}; | ||
throw new _errors.DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
var getHID = function getHID() { | ||
// $FlowFixMe | ||
var _navigator = navigator, | ||
hid = _navigator.hid; | ||
throw e; | ||
}); | ||
if (!hid) throw new _errors.TransportError("navigator.hid is not supported", "HIDNotSupported"); | ||
return hid; | ||
}; | ||
this.device = device; | ||
this.deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
device.addEventListener("inputreport", this.onInputReport); | ||
var TransportWebHID = function (_Transport) { | ||
_inherits(TransportWebHID, _Transport); | ||
function TransportWebHID(device) { | ||
_classCallCheck(this, TransportWebHID); | ||
var _this = _possibleConstructorReturn(this, (TransportWebHID.__proto__ || Object.getPrototypeOf(TransportWebHID)).call(this)); | ||
_initialiseProps.call(_this); | ||
_this.device = device; | ||
_this.deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
device.addEventListener("inputreport", _this.onInputReport); | ||
return _this; | ||
} | ||
/** | ||
* Similar to create() except it will always display the device permission (even if some devices are already accepted). | ||
* Check if WebUSB transport is supported. | ||
*/ | ||
static async request() { | ||
const device = await requestLedgerDevice(); | ||
return TransportWebHID.open(device); | ||
} | ||
/** | ||
* Similar to create() except it will never display the device permission (it returns a Promise<?Transport>, null if it fails to find a device). | ||
* List the WebUSB devices that was previously authorized by the user. | ||
*/ | ||
static async openConnected() { | ||
const devices = await getLedgerDevices(); | ||
if (devices.length === 0) return null; | ||
return TransportWebHID.open(devices[0]); | ||
} | ||
/** | ||
* Create a Ledger transport with a HIDDevice | ||
* Actively listen to WebUSB devices and emit ONE device | ||
* that was either accepted before, if not it will trigger the native permission UI. | ||
* | ||
* Important: it must be called in the context of a UI click! | ||
*/ | ||
static async open(device) { | ||
await device.open(); | ||
const transport = new TransportWebHID(device); | ||
_createClass(TransportWebHID, [{ | ||
key: "close", | ||
const onDisconnect = e => { | ||
if (device === e.device) { | ||
getHID().removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new _errors.DisconnectedDevice()); | ||
/** | ||
* Release the transport device | ||
*/ | ||
value: function () { | ||
var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() { | ||
return regeneratorRuntime.wrap(function _callee4$(_context4) { | ||
while (1) { | ||
switch (_context4.prev = _context4.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return this.exchangeBusyPromise; | ||
case 2: | ||
this.device.removeEventListener("inputreport", this.onInputReport); | ||
_context4.next = 5; | ||
return this.device.close(); | ||
case 5: | ||
case "end": | ||
return _context4.stop(); | ||
} | ||
} | ||
}, _callee4, this); | ||
})); | ||
function close() { | ||
return _ref4.apply(this, arguments); | ||
} | ||
}; | ||
getHID().addEventListener("disconnect", onDisconnect); | ||
return transport; | ||
} | ||
return close; | ||
}() | ||
/** | ||
* Release the transport device | ||
*/ | ||
async close() { | ||
await this.exchangeBusyPromise; | ||
this.device.removeEventListener("inputreport", this.onInputReport); | ||
await this.device.close(); | ||
} | ||
/** | ||
* Exchange with the device using APDU protocol. | ||
* @param apdu | ||
* @returns a promise of apdu response | ||
*/ | ||
/** | ||
* Exchange with the device using APDU protocol. | ||
* @param apdu | ||
* @returns a promise of apdu response | ||
*/ | ||
}, { | ||
key: "setScrambleKey", | ||
value: function setScrambleKey() {} | ||
}], [{ | ||
key: "request", | ||
setScrambleKey() {} | ||
} | ||
/** | ||
* Similar to create() except it will always display the device permission (even if some devices are already accepted). | ||
*/ | ||
value: function () { | ||
var _ref5 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() { | ||
var device; | ||
return regeneratorRuntime.wrap(function _callee5$(_context5) { | ||
while (1) { | ||
switch (_context5.prev = _context5.next) { | ||
case 0: | ||
_context5.next = 2; | ||
return requestLedgerDevice(); | ||
exports.default = TransportWebHID; | ||
case 2: | ||
device = _context5.sent; | ||
return _context5.abrupt("return", TransportWebHID.open(device)); | ||
case 4: | ||
case "end": | ||
return _context5.stop(); | ||
} | ||
} | ||
}, _callee5, this); | ||
})); | ||
function request() { | ||
return _ref5.apply(this, arguments); | ||
} | ||
return request; | ||
}() | ||
/** | ||
* Similar to create() except it will never display the device permission (it returns a Promise<?Transport>, null if it fails to find a device). | ||
*/ | ||
}, { | ||
key: "openConnected", | ||
value: function () { | ||
var _ref6 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() { | ||
var devices; | ||
return regeneratorRuntime.wrap(function _callee6$(_context6) { | ||
while (1) { | ||
switch (_context6.prev = _context6.next) { | ||
case 0: | ||
_context6.next = 2; | ||
return getLedgerDevices(); | ||
case 2: | ||
devices = _context6.sent; | ||
if (!(devices.length === 0)) { | ||
_context6.next = 5; | ||
break; | ||
} | ||
return _context6.abrupt("return", null); | ||
case 5: | ||
return _context6.abrupt("return", TransportWebHID.open(devices[0])); | ||
case 6: | ||
case "end": | ||
return _context6.stop(); | ||
} | ||
} | ||
}, _callee6, this); | ||
})); | ||
function openConnected() { | ||
return _ref6.apply(this, arguments); | ||
} | ||
return openConnected; | ||
}() | ||
/** | ||
* Create a Ledger transport with a HIDDevice | ||
*/ | ||
}, { | ||
key: "open", | ||
value: function () { | ||
var _ref7 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee7(device) { | ||
var transport, onDisconnect; | ||
return regeneratorRuntime.wrap(function _callee7$(_context7) { | ||
while (1) { | ||
switch (_context7.prev = _context7.next) { | ||
case 0: | ||
_context7.next = 2; | ||
return device.open(); | ||
case 2: | ||
transport = new TransportWebHID(device); | ||
onDisconnect = function onDisconnect(e) { | ||
if (device === e.device) { | ||
getHID().removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new _errors.DisconnectedDevice()); | ||
} | ||
}; | ||
getHID().addEventListener("disconnect", onDisconnect); | ||
return _context7.abrupt("return", transport); | ||
case 6: | ||
case "end": | ||
return _context7.stop(); | ||
} | ||
} | ||
}, _callee7, this); | ||
})); | ||
function open(_x) { | ||
return _ref7.apply(this, arguments); | ||
} | ||
return open; | ||
}() | ||
}]); | ||
return TransportWebHID; | ||
}(_hwTransport2.default); | ||
TransportWebHID.isSupported = isSupported; | ||
TransportWebHID.list = getLedgerDevices; | ||
TransportWebHID.listen = observer => { | ||
let unsubscribed = false; | ||
getFirstLedgerDevice().then(device => { | ||
TransportWebHID.listen = function (observer) { | ||
var unsubscribed = false; | ||
getFirstLedgerDevice().then(function (device) { | ||
if (!unsubscribed) { | ||
const deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
observer.next({ | ||
type: "add", | ||
descriptor: device, | ||
deviceModel | ||
}); | ||
var deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
observer.next({ type: "add", descriptor: device, deviceModel: deviceModel }); | ||
observer.complete(); | ||
} | ||
}, error => { | ||
}, function (error) { | ||
observer.error(new _errors.TransportOpenUserCancelled(error.message)); | ||
}); | ||
function unsubscribe() { | ||
unsubscribed = true; | ||
} | ||
return { unsubscribe: unsubscribe }; | ||
}; | ||
return { | ||
unsubscribe | ||
var _initialiseProps = function _initialiseProps() { | ||
var _this2 = this; | ||
this.channel = Math.floor(Math.random() * 0xffff); | ||
this.packetSize = 64; | ||
this.inputs = []; | ||
this.read = function () { | ||
if (_this2.inputs.length) { | ||
return Promise.resolve(_this2.inputs.shift()); | ||
} | ||
return new Promise(function (success) { | ||
_this2.inputCallback = success; | ||
}); | ||
}; | ||
this.onInputReport = function (e) { | ||
var buffer = new Buffer(e.data.buffer); | ||
if (_this2.inputCallback) { | ||
_this2.inputCallback(buffer); | ||
_this2.inputCallback = null; | ||
} else { | ||
_this2.inputs.push(buffer); | ||
} | ||
}; | ||
this._disconnectEmitted = false; | ||
this._emitDisconnect = function (e) { | ||
if (_this2._disconnectEmitted) return; | ||
_this2._disconnectEmitted = true; | ||
_this2.emit("disconnect", e); | ||
}; | ||
this.exchange = function (apdu) { | ||
return _this2.exchangeAtomicImpl(_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee8() { | ||
var channel, packetSize, framing, blocks, i, result, acc, buffer; | ||
return regeneratorRuntime.wrap(function _callee8$(_context8) { | ||
while (1) { | ||
switch (_context8.prev = _context8.next) { | ||
case 0: | ||
channel = _this2.channel, packetSize = _this2.packetSize; | ||
(0, _logs.log)("apdu", "=> " + apdu.toString("hex")); | ||
framing = (0, _hidFraming2.default)(channel, packetSize); | ||
// Write... | ||
blocks = framing.makeBlocks(apdu); | ||
i = 0; | ||
case 5: | ||
if (!(i < blocks.length)) { | ||
_context8.next = 12; | ||
break; | ||
} | ||
(0, _logs.log)("hid-frame", "=> " + blocks[i].toString("hex")); | ||
_context8.next = 9; | ||
return _this2.device.sendReport(0, blocks[i]); | ||
case 9: | ||
i++; | ||
_context8.next = 5; | ||
break; | ||
case 12: | ||
// Read... | ||
result = void 0; | ||
acc = void 0; | ||
case 14: | ||
if (result = framing.getReducedResult(acc)) { | ||
_context8.next = 22; | ||
break; | ||
} | ||
_context8.next = 17; | ||
return _this2.read(); | ||
case 17: | ||
buffer = _context8.sent; | ||
(0, _logs.log)("hid-frame", "<= " + buffer.toString("hex")); | ||
acc = framing.reduceResponse(acc, buffer); | ||
_context8.next = 14; | ||
break; | ||
case 22: | ||
(0, _logs.log)("apdu", "<= " + result.toString("hex")); | ||
return _context8.abrupt("return", result); | ||
case 24: | ||
case "end": | ||
return _context8.stop(); | ||
} | ||
} | ||
}, _callee8, _this2); | ||
}))).catch(function (e) { | ||
if (e && e.message && e.message.includes("write")) { | ||
_this2._emitDisconnect(e); | ||
throw new _errors.DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
}); | ||
}; | ||
}; | ||
exports.default = TransportWebHID; | ||
//# sourceMappingURL=TransportWebHID.js.map |
{ | ||
"name": "@ledgerhq/hw-transport-webhid", | ||
"version": "4.74.3-alpha.6+0750e69", | ||
"version": "4.77.0", | ||
"description": "Ledger Hardware Wallet WebHID implementation of the communication layer", | ||
@@ -27,6 +27,6 @@ "keywords": [ | ||
"dependencies": { | ||
"@ledgerhq/devices": "^4.74.3-alpha.6+0750e69", | ||
"@ledgerhq/errors": "^4.74.3-alpha.6+0750e69", | ||
"@ledgerhq/hw-transport": "^4.74.3-alpha.6+0750e69", | ||
"@ledgerhq/logs": "^4.74.3-alpha.6+0750e69" | ||
"@ledgerhq/devices": "^4.77.0", | ||
"@ledgerhq/errors": "^4.77.0", | ||
"@ledgerhq/hw-transport": "^4.77.0", | ||
"@ledgerhq/logs": "^4.72.0" | ||
}, | ||
@@ -43,3 +43,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "0750e6985bbd4a0faec44c9014352a46b094c66e" | ||
"gitHead": "b25792920f55ec855fd74778ce37240ef7dbde01" | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
56265
621
0
0
- Removed@ledgerhq/logs@4.74.3-alpha.6(transitive)
Updated@ledgerhq/devices@^4.77.0
Updated@ledgerhq/errors@^4.77.0
Updated@ledgerhq/logs@^4.72.0