@ledgerhq/react-native-hid
Version:
Ledger Hardware Wallet Web implementation of the communication layer, using U2F api
136 lines • 4.88 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_native_1 = require("react-native");
const devices_1 = require("@ledgerhq/devices");
const errors_1 = require("@ledgerhq/errors");
const logs_1 = require("@ledgerhq/logs");
const hw_transport_1 = __importDefault(require("@ledgerhq/hw-transport"));
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const disconnectedErrors = [
"I/O error",
"Attempt to invoke virtual method 'int android.hardware.usb.UsbDevice.getDeviceClass()' on a null object reference",
"Invalid channel",
"Permission denied by user for device",
];
const listLedgerDevices = async () => {
const devices = await react_native_1.NativeModules.HID.getDeviceList();
return devices.filter(d => d.vendorId === devices_1.ledgerUSBVendorId);
};
const liveDeviceEventsSubject = new rxjs_1.Subject();
react_native_1.DeviceEventEmitter.addListener("onDeviceConnect", (device) => {
if (device.vendorId !== devices_1.ledgerUSBVendorId)
return;
const deviceModel = (0, devices_1.identifyUSBProductId)(device.productId);
liveDeviceEventsSubject.next({
type: "add",
descriptor: device,
deviceModel,
});
});
react_native_1.DeviceEventEmitter.addListener("onDeviceDisconnect", (device) => {
if (device.vendorId !== devices_1.ledgerUSBVendorId)
return;
const deviceModel = (0, devices_1.identifyUSBProductId)(device.productId);
liveDeviceEventsSubject.next({
type: "remove",
descriptor: device,
deviceModel,
});
});
const liveDeviceEvents = liveDeviceEventsSubject;
/**
* Ledger's React Native HID Transport implementation
* @example
* import TransportHID from "@ledgerhq/react-native-hid";
* ...
* TransportHID.create().then(transport => ...)
*/
class HIDTransport extends hw_transport_1.default {
id;
deviceModel;
constructor(nativeId, productId) {
super();
this.id = nativeId;
this.deviceModel = (0, devices_1.identifyUSBProductId)(productId);
}
/**
* Check if the transport is supported (basically true on Android)
*/
static isSupported = () => Promise.resolve(!!react_native_1.NativeModules.HID);
/**
* List currently connected devices.
* @returns Promise of devices
*/
static async list() {
if (!react_native_1.NativeModules.HID)
return Promise.resolve([]);
return await listLedgerDevices();
}
/**
* Listen to ledger devices events
*/
static listen(observer) {
if (!react_native_1.NativeModules.HID)
return {
unsubscribe: () => { },
};
return (0, rxjs_1.concat)((0, rxjs_1.from)(listLedgerDevices()).pipe((0, operators_1.mergeMap)(devices => (0, rxjs_1.from)(devices.map(device => ({
type: "add",
descriptor: device,
deviceModel: (0, devices_1.identifyUSBProductId)(device.productId),
}))))), liveDeviceEvents).subscribe(observer);
}
/**
* Open a the transport with a Ledger device
*/
static async open(deviceObj) {
try {
const nativeObj = await react_native_1.NativeModules.HID.openDevice(deviceObj);
return new HIDTransport(nativeObj.id, deviceObj.productId);
}
catch (error) {
if (disconnectedErrors.includes(error.message)) {
throw new errors_1.DisconnectedDevice(error.message);
}
throw error;
}
}
/**
* @param {*} apdu input value
* @returns Promise of apdu response
*/
async exchange(apdu) {
return this.exchangeAtomicImpl(async () => {
try {
const apduHex = apdu.toString("hex");
(0, logs_1.log)("apdu", "=> " + apduHex);
const resultHex = await react_native_1.NativeModules.HID.exchange(this.id, apduHex);
const res = Buffer.from(resultHex, "hex");
(0, logs_1.log)("apdu", "<= " + resultHex);
return res;
}
catch (error) {
if (disconnectedErrors.includes(error.message)) {
this.emit("disconnect", error);
throw new errors_1.DisconnectedDeviceDuringOperation(error.message);
}
throw error;
}
});
}
/**
* Close the transport
* @returns Promise
*/
async close() {
await this.exchangeBusyPromise;
return react_native_1.NativeModules.HID.closeDevice(this.id);
}
setScrambleKey() { }
}
exports.default = HIDTransport;
//# sourceMappingURL=index.js.map