@ledgerhq/hw-transport-webhid
Advanced tools
Comparing version 5.51.1 to 6.0.0-typescript.0
@@ -0,1 +1,68 @@ | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
if (typeof b !== "function" && b !== null) | ||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
import Transport from "@ledgerhq/hw-transport"; | ||
@@ -5,36 +72,65 @@ import hidFraming from "@ledgerhq/devices/lib/hid-framing"; | ||
import { log } from "@ledgerhq/logs"; | ||
import { TransportOpenUserCancelled, DisconnectedDeviceDuringOperation, DisconnectedDevice, TransportError } from "@ledgerhq/errors"; | ||
const ledgerDevices = [{ | ||
vendorId: ledgerUSBVendorId | ||
}]; | ||
const isSupported = () => Promise.resolve(!!(global.navigator && global.navigator.hid)); | ||
const getHID = () => { | ||
// $FlowFixMe | ||
const { | ||
hid | ||
} = navigator; | ||
if (!hid) throw new TransportError("navigator.hid is not supported", "HIDNotSupported"); | ||
return hid; | ||
import { TransportOpenUserCancelled, DisconnectedDeviceDuringOperation, DisconnectedDevice, TransportError, } from "@ledgerhq/errors"; | ||
var ledgerDevices = [ | ||
{ | ||
vendorId: ledgerUSBVendorId | ||
}, | ||
]; | ||
var isSupported = function () { | ||
return Promise.resolve(!!(window.navigator && window.navigator.hid)); | ||
}; | ||
async function requestLedgerDevices() { | ||
const device = await getHID().requestDevice({ | ||
filters: ledgerDevices | ||
}); | ||
if (Array.isArray(device)) return device; | ||
return [device]; | ||
var getHID = function () { | ||
// $FlowFixMe | ||
var hid = navigator.hid; | ||
if (!hid) | ||
throw new TransportError("navigator.hid is not supported", "HIDNotSupported"); | ||
return hid; | ||
}; | ||
function requestLedgerDevices() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var device; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getHID().requestDevice({ | ||
filters: ledgerDevices | ||
})]; | ||
case 1: | ||
device = _a.sent(); | ||
if (Array.isArray(device)) | ||
return [2 /*return*/, device]; | ||
return [2 /*return*/, [device]]; | ||
} | ||
}); | ||
}); | ||
} | ||
async function getLedgerDevices() { | ||
const devices = await getHID().getDevices(); | ||
return devices.filter(d => d.vendorId === ledgerUSBVendorId); | ||
function getLedgerDevices() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var devices; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getHID().getDevices()]; | ||
case 1: | ||
devices = _a.sent(); | ||
return [2 /*return*/, devices.filter(function (d) { return d.vendorId === ledgerUSBVendorId; })]; | ||
} | ||
}); | ||
}); | ||
} | ||
async function getFirstLedgerDevice() { | ||
const existingDevices = await getLedgerDevices(); | ||
if (existingDevices.length > 0) return existingDevices[0]; | ||
const devices = await requestLedgerDevices(); | ||
return devices[0]; | ||
function getFirstLedgerDevice() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var existingDevices, devices; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getLedgerDevices()]; | ||
case 1: | ||
existingDevices = _a.sent(); | ||
if (existingDevices.length > 0) | ||
return [2 /*return*/, existingDevices[0]]; | ||
return [4 /*yield*/, requestLedgerDevices()]; | ||
case 2: | ||
devices = _a.sent(); | ||
return [2 /*return*/, devices[0]]; | ||
} | ||
}); | ||
}); | ||
} | ||
@@ -48,168 +144,221 @@ /** | ||
*/ | ||
export default class TransportWebHID extends Transport { | ||
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; | ||
this.read = () => { | ||
if (this.inputs.length) { | ||
return Promise.resolve(this.inputs.shift()); | ||
} | ||
return new Promise(success => { | ||
this.inputCallback = success; | ||
}); | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
var TransportWebHID = /** @class */ (function (_super) { | ||
__extends(TransportWebHID, _super); | ||
function TransportWebHID(device) { | ||
var _this = _super.call(this) || this; | ||
_this.channel = Math.floor(Math.random() * 0xffff); | ||
_this.packetSize = 64; | ||
_this.inputs = []; | ||
_this.read = function () { | ||
if (_this.inputs.length) { | ||
return Promise.resolve(_this.inputs.shift()); | ||
} | ||
return new Promise(function (success) { | ||
_this.inputCallback = success; | ||
}); | ||
}; | ||
_this.onInputReport = function (e) { | ||
var buffer = Buffer.from(e.data.buffer); | ||
if (_this.inputCallback) { | ||
_this.inputCallback(buffer); | ||
_this.inputCallback = null; | ||
} | ||
else { | ||
_this.inputs.push(buffer); | ||
} | ||
}; | ||
_this._disconnectEmitted = false; | ||
_this._emitDisconnect = function (e) { | ||
if (_this._disconnectEmitted) | ||
return; | ||
_this._disconnectEmitted = true; | ||
_this.emit("disconnect", e); | ||
}; | ||
/** | ||
* Exchange with the device using APDU protocol. | ||
* @param apdu | ||
* @returns a promise of apdu response | ||
*/ | ||
_this.exchange = function (apdu) { return __awaiter(_this, void 0, void 0, function () { | ||
var b; | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.exchangeAtomicImpl(function () { return __awaiter(_this, void 0, void 0, function () { | ||
var _a, channel, packetSize, framing, blocks, i, result, acc, buffer; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_a = this, channel = _a.channel, packetSize = _a.packetSize; | ||
log("apdu", "=> " + apdu.toString("hex")); | ||
framing = hidFraming(channel, packetSize); | ||
blocks = framing.makeBlocks(apdu); | ||
i = 0; | ||
_b.label = 1; | ||
case 1: | ||
if (!(i < blocks.length)) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, this.device.sendReport(0, blocks[i])]; | ||
case 2: | ||
_b.sent(); | ||
_b.label = 3; | ||
case 3: | ||
i++; | ||
return [3 /*break*/, 1]; | ||
case 4: | ||
if (!!(result = framing.getReducedResult(acc))) return [3 /*break*/, 6]; | ||
return [4 /*yield*/, this.read()]; | ||
case 5: | ||
buffer = _b.sent(); | ||
acc = framing.reduceResponse(acc, buffer); | ||
return [3 /*break*/, 4]; | ||
case 6: | ||
log("apdu", "<= " + result.toString("hex")); | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); })["catch"](function (e) { | ||
if (e && e.message && e.message.includes("write")) { | ||
_this._emitDisconnect(e); | ||
throw new DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
})]; | ||
case 1: | ||
b = _a.sent(); | ||
return [2 /*return*/, b]; | ||
} | ||
}); | ||
}); }; | ||
_this.device = device; | ||
_this.deviceModel = | ||
typeof device.productId === "number" | ||
? identifyUSBProductId(device.productId) | ||
: undefined; | ||
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). | ||
*/ | ||
TransportWebHID.request = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _a, device; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, requestLedgerDevices()]; | ||
case 1: | ||
_a = __read.apply(void 0, [_b.sent(), 1]), device = _a[0]; | ||
return [2 /*return*/, TransportWebHID.open(device)]; | ||
} | ||
}); | ||
}); | ||
}; | ||
this.onInputReport = e => { | ||
const buffer = Buffer.from(e.data.buffer); | ||
if (this.inputCallback) { | ||
this.inputCallback(buffer); | ||
this.inputCallback = null; | ||
} else { | ||
this.inputs.push(buffer); | ||
} | ||
/** | ||
* Similar to create() except it will never display the device permission (it returns a Promise<?Transport>, null if it fails to find a device). | ||
*/ | ||
TransportWebHID.openConnected = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var devices; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getLedgerDevices()]; | ||
case 1: | ||
devices = _a.sent(); | ||
if (devices.length === 0) | ||
return [2 /*return*/, null]; | ||
return [2 /*return*/, TransportWebHID.open(devices[0])]; | ||
} | ||
}); | ||
}); | ||
}; | ||
this._disconnectEmitted = false; | ||
this._emitDisconnect = e => { | ||
if (this._disconnectEmitted) return; | ||
this._disconnectEmitted = true; | ||
this.emit("disconnect", e); | ||
/** | ||
* Create a Ledger transport with a HIDDevice | ||
*/ | ||
TransportWebHID.open = function (device) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var transport, onDisconnect; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, device.open()]; | ||
case 1: | ||
_a.sent(); | ||
transport = new TransportWebHID(device); | ||
onDisconnect = function (e) { | ||
if (device === e.device) { | ||
getHID().removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new DisconnectedDevice()); | ||
} | ||
}; | ||
getHID().addEventListener("disconnect", onDisconnect); | ||
return [2 /*return*/, transport]; | ||
} | ||
}); | ||
}); | ||
}; | ||
this.exchange = apdu => this.exchangeAtomicImpl(async () => { | ||
const { | ||
channel, | ||
packetSize | ||
} = this; | ||
log("apdu", "=> " + apdu.toString("hex")); | ||
const framing = hidFraming(channel, packetSize); // Write... | ||
const blocks = framing.makeBlocks(apdu); | ||
for (let i = 0; i < blocks.length; i++) { | ||
await this.device.sendReport(0, blocks[i]); | ||
} // Read... | ||
let result; | ||
let acc; | ||
while (!(result = framing.getReducedResult(acc))) { | ||
const buffer = await this.read(); | ||
acc = framing.reduceResponse(acc, buffer); | ||
} | ||
log("apdu", "<= " + result.toString("hex")); | ||
return result; | ||
}).catch(e => { | ||
if (e && e.message && e.message.includes("write")) { | ||
this._emitDisconnect(e); | ||
throw new DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
}); | ||
this.device = device; | ||
this.deviceModel = identifyUSBProductId(device.productId); | ||
device.addEventListener("inputreport", this.onInputReport); | ||
} | ||
/** | ||
* Similar to create() except it will always display the device permission (even if some devices are already accepted). | ||
*/ | ||
static async request() { | ||
const [device] = await requestLedgerDevices(); | ||
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). | ||
*/ | ||
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 | ||
*/ | ||
static async open(device) { | ||
await device.open(); | ||
const transport = new TransportWebHID(device); | ||
const onDisconnect = e => { | ||
if (device === e.device) { | ||
getHID().removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new DisconnectedDevice()); | ||
} | ||
/** | ||
* Release the transport device | ||
*/ | ||
TransportWebHID.prototype.close = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.exchangeBusyPromise]; | ||
case 1: | ||
_a.sent(); | ||
this.device.removeEventListener("inputreport", this.onInputReport); | ||
return [4 /*yield*/, this.device.close()]; | ||
case 2: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
getHID().addEventListener("disconnect", onDisconnect); | ||
return transport; | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
setScrambleKey() {} | ||
} | ||
TransportWebHID.isSupported = isSupported; | ||
TransportWebHID.list = getLedgerDevices; | ||
TransportWebHID.listen = observer => { | ||
let unsubscribed = false; | ||
getFirstLedgerDevice().then(device => { | ||
if (!device) { | ||
observer.error(new TransportOpenUserCancelled("Access denied to use Ledger device")); | ||
} else if (!unsubscribed) { | ||
const deviceModel = identifyUSBProductId(device.productId); | ||
observer.next({ | ||
type: "add", | ||
descriptor: device, | ||
deviceModel | ||
}); | ||
observer.complete(); | ||
} | ||
}, error => { | ||
observer.error(new TransportOpenUserCancelled(error.message)); | ||
}); | ||
function unsubscribe() { | ||
unsubscribed = true; | ||
} | ||
return { | ||
unsubscribe | ||
}; | ||
}; | ||
TransportWebHID.prototype.setScrambleKey = function () { }; | ||
/** | ||
* Check if WebUSB transport is supported. | ||
*/ | ||
TransportWebHID.isSupported = isSupported; | ||
/** | ||
* List the WebUSB devices that was previously authorized by the user. | ||
*/ | ||
TransportWebHID.list = getLedgerDevices; | ||
/** | ||
* 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! | ||
*/ | ||
TransportWebHID.listen = function (observer) { | ||
var unsubscribed = false; | ||
getFirstLedgerDevice().then(function (device) { | ||
if (!device) { | ||
observer.error(new TransportOpenUserCancelled("Access denied to use Ledger device")); | ||
} | ||
else if (!unsubscribed) { | ||
var deviceModel = typeof device.productId === "number" | ||
? identifyUSBProductId(device.productId) | ||
: undefined; | ||
observer.next({ | ||
type: "add", | ||
descriptor: device, | ||
deviceModel: deviceModel | ||
}); | ||
observer.complete(); | ||
} | ||
}, function (error) { | ||
observer.error(new TransportOpenUserCancelled(error.message)); | ||
}); | ||
function unsubscribe() { | ||
unsubscribed = true; | ||
} | ||
return { | ||
unsubscribe: unsubscribe | ||
}; | ||
}; | ||
return TransportWebHID; | ||
}(Transport)); | ||
export default TransportWebHID; | ||
//# sourceMappingURL=TransportWebHID.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = void 0; | ||
var _hwTransport = _interopRequireDefault(require("@ledgerhq/hw-transport")); | ||
var _hidFraming = _interopRequireDefault(require("@ledgerhq/devices/lib/hid-framing")); | ||
var _devices = require("@ledgerhq/devices"); | ||
var _logs = require("@ledgerhq/logs"); | ||
var _errors = require("@ledgerhq/errors"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const ledgerDevices = [{ | ||
vendorId: _devices.ledgerUSBVendorId | ||
}]; | ||
const isSupported = () => Promise.resolve(!!(global.navigator && global.navigator.hid)); | ||
const getHID = () => { | ||
// $FlowFixMe | ||
const { | ||
hid | ||
} = navigator; | ||
if (!hid) throw new _errors.TransportError("navigator.hid is not supported", "HIDNotSupported"); | ||
return hid; | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
if (typeof b !== "function" && b !== null) | ||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
async function requestLedgerDevices() { | ||
const device = await getHID().requestDevice({ | ||
filters: ledgerDevices | ||
}); | ||
if (Array.isArray(device)) return device; | ||
return [device]; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
exports.__esModule = true; | ||
var hw_transport_1 = __importDefault(require("@ledgerhq/hw-transport")); | ||
var hid_framing_1 = __importDefault(require("@ledgerhq/devices/lib/hid-framing")); | ||
var devices_1 = require("@ledgerhq/devices"); | ||
var logs_1 = require("@ledgerhq/logs"); | ||
var errors_1 = require("@ledgerhq/errors"); | ||
var ledgerDevices = [ | ||
{ | ||
vendorId: devices_1.ledgerUSBVendorId | ||
}, | ||
]; | ||
var isSupported = function () { | ||
return Promise.resolve(!!(window.navigator && window.navigator.hid)); | ||
}; | ||
var getHID = function () { | ||
// $FlowFixMe | ||
var hid = navigator.hid; | ||
if (!hid) | ||
throw new errors_1.TransportError("navigator.hid is not supported", "HIDNotSupported"); | ||
return hid; | ||
}; | ||
function requestLedgerDevices() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var device; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getHID().requestDevice({ | ||
filters: ledgerDevices | ||
})]; | ||
case 1: | ||
device = _a.sent(); | ||
if (Array.isArray(device)) | ||
return [2 /*return*/, device]; | ||
return [2 /*return*/, [device]]; | ||
} | ||
}); | ||
}); | ||
} | ||
async function getLedgerDevices() { | ||
const devices = await getHID().getDevices(); | ||
return devices.filter(d => d.vendorId === _devices.ledgerUSBVendorId); | ||
function getLedgerDevices() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var devices; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getHID().getDevices()]; | ||
case 1: | ||
devices = _a.sent(); | ||
return [2 /*return*/, devices.filter(function (d) { return d.vendorId === devices_1.ledgerUSBVendorId; })]; | ||
} | ||
}); | ||
}); | ||
} | ||
async function getFirstLedgerDevice() { | ||
const existingDevices = await getLedgerDevices(); | ||
if (existingDevices.length > 0) return existingDevices[0]; | ||
const devices = await requestLedgerDevices(); | ||
return devices[0]; | ||
function getFirstLedgerDevice() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var existingDevices, devices; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getLedgerDevices()]; | ||
case 1: | ||
existingDevices = _a.sent(); | ||
if (existingDevices.length > 0) | ||
return [2 /*return*/, existingDevices[0]]; | ||
return [4 /*yield*/, requestLedgerDevices()]; | ||
case 2: | ||
devices = _a.sent(); | ||
return [2 /*return*/, devices[0]]; | ||
} | ||
}); | ||
}); | ||
} | ||
@@ -61,170 +148,221 @@ /** | ||
*/ | ||
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; | ||
this.read = () => { | ||
if (this.inputs.length) { | ||
return Promise.resolve(this.inputs.shift()); | ||
} | ||
return new Promise(success => { | ||
this.inputCallback = success; | ||
}); | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
var TransportWebHID = /** @class */ (function (_super) { | ||
__extends(TransportWebHID, _super); | ||
function TransportWebHID(device) { | ||
var _this = _super.call(this) || this; | ||
_this.channel = Math.floor(Math.random() * 0xffff); | ||
_this.packetSize = 64; | ||
_this.inputs = []; | ||
_this.read = function () { | ||
if (_this.inputs.length) { | ||
return Promise.resolve(_this.inputs.shift()); | ||
} | ||
return new Promise(function (success) { | ||
_this.inputCallback = success; | ||
}); | ||
}; | ||
_this.onInputReport = function (e) { | ||
var buffer = Buffer.from(e.data.buffer); | ||
if (_this.inputCallback) { | ||
_this.inputCallback(buffer); | ||
_this.inputCallback = null; | ||
} | ||
else { | ||
_this.inputs.push(buffer); | ||
} | ||
}; | ||
_this._disconnectEmitted = false; | ||
_this._emitDisconnect = function (e) { | ||
if (_this._disconnectEmitted) | ||
return; | ||
_this._disconnectEmitted = true; | ||
_this.emit("disconnect", e); | ||
}; | ||
/** | ||
* Exchange with the device using APDU protocol. | ||
* @param apdu | ||
* @returns a promise of apdu response | ||
*/ | ||
_this.exchange = function (apdu) { return __awaiter(_this, void 0, void 0, function () { | ||
var b; | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.exchangeAtomicImpl(function () { return __awaiter(_this, void 0, void 0, function () { | ||
var _a, channel, packetSize, framing, blocks, i, result, acc, buffer; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_a = this, channel = _a.channel, packetSize = _a.packetSize; | ||
logs_1.log("apdu", "=> " + apdu.toString("hex")); | ||
framing = hid_framing_1["default"](channel, packetSize); | ||
blocks = framing.makeBlocks(apdu); | ||
i = 0; | ||
_b.label = 1; | ||
case 1: | ||
if (!(i < blocks.length)) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, this.device.sendReport(0, blocks[i])]; | ||
case 2: | ||
_b.sent(); | ||
_b.label = 3; | ||
case 3: | ||
i++; | ||
return [3 /*break*/, 1]; | ||
case 4: | ||
if (!!(result = framing.getReducedResult(acc))) return [3 /*break*/, 6]; | ||
return [4 /*yield*/, this.read()]; | ||
case 5: | ||
buffer = _b.sent(); | ||
acc = framing.reduceResponse(acc, buffer); | ||
return [3 /*break*/, 4]; | ||
case 6: | ||
logs_1.log("apdu", "<= " + result.toString("hex")); | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); })["catch"](function (e) { | ||
if (e && e.message && e.message.includes("write")) { | ||
_this._emitDisconnect(e); | ||
throw new errors_1.DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
})]; | ||
case 1: | ||
b = _a.sent(); | ||
return [2 /*return*/, b]; | ||
} | ||
}); | ||
}); }; | ||
_this.device = device; | ||
_this.deviceModel = | ||
typeof device.productId === "number" | ||
? devices_1.identifyUSBProductId(device.productId) | ||
: undefined; | ||
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). | ||
*/ | ||
TransportWebHID.request = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _a, device; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, requestLedgerDevices()]; | ||
case 1: | ||
_a = __read.apply(void 0, [_b.sent(), 1]), device = _a[0]; | ||
return [2 /*return*/, TransportWebHID.open(device)]; | ||
} | ||
}); | ||
}); | ||
}; | ||
this.onInputReport = e => { | ||
const buffer = Buffer.from(e.data.buffer); | ||
if (this.inputCallback) { | ||
this.inputCallback(buffer); | ||
this.inputCallback = null; | ||
} else { | ||
this.inputs.push(buffer); | ||
} | ||
/** | ||
* Similar to create() except it will never display the device permission (it returns a Promise<?Transport>, null if it fails to find a device). | ||
*/ | ||
TransportWebHID.openConnected = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var devices; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getLedgerDevices()]; | ||
case 1: | ||
devices = _a.sent(); | ||
if (devices.length === 0) | ||
return [2 /*return*/, null]; | ||
return [2 /*return*/, TransportWebHID.open(devices[0])]; | ||
} | ||
}); | ||
}); | ||
}; | ||
this._disconnectEmitted = false; | ||
this._emitDisconnect = e => { | ||
if (this._disconnectEmitted) return; | ||
this._disconnectEmitted = true; | ||
this.emit("disconnect", e); | ||
/** | ||
* Create a Ledger transport with a HIDDevice | ||
*/ | ||
TransportWebHID.open = function (device) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var transport, onDisconnect; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, device.open()]; | ||
case 1: | ||
_a.sent(); | ||
transport = new TransportWebHID(device); | ||
onDisconnect = function (e) { | ||
if (device === e.device) { | ||
getHID().removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new errors_1.DisconnectedDevice()); | ||
} | ||
}; | ||
getHID().addEventListener("disconnect", onDisconnect); | ||
return [2 /*return*/, transport]; | ||
} | ||
}); | ||
}); | ||
}; | ||
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... | ||
const blocks = framing.makeBlocks(apdu); | ||
for (let i = 0; i < blocks.length; i++) { | ||
await this.device.sendReport(0, blocks[i]); | ||
} // Read... | ||
let result; | ||
let acc; | ||
while (!(result = framing.getReducedResult(acc))) { | ||
const buffer = await this.read(); | ||
acc = framing.reduceResponse(acc, buffer); | ||
} | ||
(0, _logs.log)("apdu", "<= " + result.toString("hex")); | ||
return result; | ||
}).catch(e => { | ||
if (e && e.message && e.message.includes("write")) { | ||
this._emitDisconnect(e); | ||
throw new _errors.DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
}); | ||
this.device = device; | ||
this.deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
device.addEventListener("inputreport", this.onInputReport); | ||
} | ||
/** | ||
* Similar to create() except it will always display the device permission (even if some devices are already accepted). | ||
*/ | ||
static async request() { | ||
const [device] = await requestLedgerDevices(); | ||
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). | ||
*/ | ||
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 | ||
*/ | ||
static async open(device) { | ||
await device.open(); | ||
const transport = new TransportWebHID(device); | ||
const onDisconnect = e => { | ||
if (device === e.device) { | ||
getHID().removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new _errors.DisconnectedDevice()); | ||
} | ||
/** | ||
* Release the transport device | ||
*/ | ||
TransportWebHID.prototype.close = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.exchangeBusyPromise]; | ||
case 1: | ||
_a.sent(); | ||
this.device.removeEventListener("inputreport", this.onInputReport); | ||
return [4 /*yield*/, this.device.close()]; | ||
case 2: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
getHID().addEventListener("disconnect", onDisconnect); | ||
return transport; | ||
} | ||
/** | ||
* 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 | ||
*/ | ||
setScrambleKey() {} | ||
} | ||
exports.default = TransportWebHID; | ||
TransportWebHID.isSupported = isSupported; | ||
TransportWebHID.list = getLedgerDevices; | ||
TransportWebHID.listen = observer => { | ||
let unsubscribed = false; | ||
getFirstLedgerDevice().then(device => { | ||
if (!device) { | ||
observer.error(new _errors.TransportOpenUserCancelled("Access denied to use Ledger device")); | ||
} else if (!unsubscribed) { | ||
const deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
observer.next({ | ||
type: "add", | ||
descriptor: device, | ||
deviceModel | ||
}); | ||
observer.complete(); | ||
} | ||
}, error => { | ||
observer.error(new _errors.TransportOpenUserCancelled(error.message)); | ||
}); | ||
function unsubscribe() { | ||
unsubscribed = true; | ||
} | ||
return { | ||
unsubscribe | ||
}; | ||
}; | ||
TransportWebHID.prototype.setScrambleKey = function () { }; | ||
/** | ||
* Check if WebUSB transport is supported. | ||
*/ | ||
TransportWebHID.isSupported = isSupported; | ||
/** | ||
* List the WebUSB devices that was previously authorized by the user. | ||
*/ | ||
TransportWebHID.list = getLedgerDevices; | ||
/** | ||
* 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! | ||
*/ | ||
TransportWebHID.listen = function (observer) { | ||
var unsubscribed = false; | ||
getFirstLedgerDevice().then(function (device) { | ||
if (!device) { | ||
observer.error(new errors_1.TransportOpenUserCancelled("Access denied to use Ledger device")); | ||
} | ||
else if (!unsubscribed) { | ||
var deviceModel = typeof device.productId === "number" | ||
? devices_1.identifyUSBProductId(device.productId) | ||
: undefined; | ||
observer.next({ | ||
type: "add", | ||
descriptor: device, | ||
deviceModel: deviceModel | ||
}); | ||
observer.complete(); | ||
} | ||
}, function (error) { | ||
observer.error(new errors_1.TransportOpenUserCancelled(error.message)); | ||
}); | ||
function unsubscribe() { | ||
unsubscribed = true; | ||
} | ||
return { | ||
unsubscribe: unsubscribe | ||
}; | ||
}; | ||
return TransportWebHID; | ||
}(hw_transport_1["default"])); | ||
exports["default"] = TransportWebHID; | ||
//# sourceMappingURL=TransportWebHID.js.map |
{ | ||
"name": "@ledgerhq/hw-transport-webhid", | ||
"version": "5.51.1", | ||
"version": "6.0.0-typescript.0", | ||
"description": "Ledger Hardware Wallet WebHID implementation of the communication layer", | ||
@@ -26,11 +26,12 @@ "keywords": [ | ||
"module": "lib-es/TransportWebHID.js", | ||
"types": "lib/TransportWebHID.d.ts", | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
"@ledgerhq/devices": "^5.51.1", | ||
"@ledgerhq/errors": "^5.50.0", | ||
"@ledgerhq/hw-transport": "^5.51.1", | ||
"@ledgerhq/logs": "^5.50.0" | ||
"@ledgerhq/devices": "^6.0.0-typescript.0", | ||
"@ledgerhq/errors": "^6.0.0-typescript.0", | ||
"@ledgerhq/hw-transport": "^6.0.0-typescript.0", | ||
"@ledgerhq/logs": "^6.0.0-typescript.0" | ||
}, | ||
"devDependencies": { | ||
"flow-bin": "^0.149.0" | ||
"@types/w3c-web-hid": "^1.0.0" | ||
}, | ||
@@ -42,5 +43,5 @@ "scripts": { | ||
"watch": "bash ../../script/watch.sh", | ||
"doc": "bash ../../script/doc.sh" | ||
"doc": "bash ../../script/doc-ts.sh" | ||
}, | ||
"gitHead": "991888009774c7cdd2b0e0711bdbf870c260e7a5" | ||
"gitHead": "3f8eef5e86a232eb16f4c018212e38f13799eeb2" | ||
} |
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
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
13
1083
72896
1
1
+ Added@ledgerhq/devices@6.27.18.4.4(transitive)
+ Added@ledgerhq/errors@6.19.1(transitive)
+ Added@ledgerhq/hw-transport@6.31.4(transitive)
+ Added@ledgerhq/logs@6.12.0(transitive)
+ Addedrxjs@7.8.1(transitive)
+ Addedtslib@2.7.0(transitive)
- Removed@ledgerhq/devices@5.51.1(transitive)
- Removed@ledgerhq/errors@5.50.0(transitive)
- Removed@ledgerhq/hw-transport@5.51.1(transitive)
- Removed@ledgerhq/logs@5.50.0(transitive)