UNPKG

trezor-link

Version:
411 lines (370 loc) 11.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = undefined; 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 _desc, _value, _class; exports.storageGet = storageGet; exports.storageSet = storageSet; var _debugDecorator = require('../debug-decorator'); var _defered = require('../defered'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; } var TREZOR_DESCS = [{ vendorId: 0x534c, productId: 0x0001 }, { vendorId: 0x1209, productId: 0x53c1 }]; var FORBIDDEN_DESCRIPTORS = [0xf1d0, 0xff01]; var REPORT_ID = 63; function deviceToJson(device) { return { path: device.deviceId.toString() }; } function hidEnumerate() { return new Promise(function (resolve, reject) { try { chrome.hid.getDevices({ filters: TREZOR_DESCS }, function (devices) { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError)); } else { resolve(devices); } }); } catch (e) { reject(e); } }); } function hidSend(id, reportId, data) { // if send (of one data packet!) does not happen within 10 seconds, the connection // probably failed in an unexpected way => return an error!! var rejecting = (0, _defered.rejectTimeoutPromise)(10 * 1000, new Error('Cannot send data to device, check the cable.')); var sendRes = new Promise(function (resolve, reject) { try { chrome.hid.send(id, reportId, data, function () { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError)); } else { resolve(); } }); } catch (e) { reject(e); } }); return Promise.race([rejecting, sendRes]); } function hidReceive(id) { return new Promise(function (resolve, reject) { try { chrome.hid.receive(id, function (reportId, data) { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve({ data: data, reportId: reportId }); } }); } catch (e) { reject(e); } }); } function hidConnect(id) { return new Promise(function (resolve, reject) { try { chrome.hid.connect(id, function (connection) { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(connection.connectionId); } }); } catch (e) { reject(e); } }); } // Disconnects from trezor. // First parameter is connection ID (*not* device ID!) function hidDisconnect(id) { return new Promise(function (resolve, reject) { try { chrome.hid.disconnect(id, function () { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(); } }); } catch (e) { reject(e); } }); } // encapsulating chrome's platform info into Promise API function platformInfo() { return new Promise(function (resolve, reject) { try { chrome.runtime.getPlatformInfo(function (info) { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { if (info == null) { reject(new Error('info is null')); } else { resolve(info); } } }); } catch (e) { reject(e); } }); } function storageGet(key) { return new Promise(function (resolve, reject) { try { chrome.storage.local.get(key, function (items) { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { if (items[key] === null || items[key] === undefined) { resolve(null); } else { resolve(items[key]); } resolve(items); } }); } catch (e) { reject(e); } }); } // Set to storage function storageSet(key, value) { return new Promise(function (resolve, reject) { try { var obj = {}; obj[key] = value; chrome.storage.local.set(obj, function () { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(undefined); } }); } catch (e) { reject(e); } }); } var ChromeHidPlugin = (_class = function () { function ChromeHidPlugin() { _classCallCheck(this, ChromeHidPlugin); this.name = 'ChromeHidPlugin'; this._hasReportId = {}; this._udevError = false; this.version = "0.2.109"; this.debug = false; this.allowsWriteAndEnumerate = true; this.requestNeeded = false; } _createClass(ChromeHidPlugin, [{ key: 'init', value: function init(debug) { this.debug = !!debug; try { if (chrome.hid.getDevices == null) { return Promise.reject(new Error('chrome.hid.getDevices is null')); } else { return Promise.resolve(); } } catch (e) { return Promise.reject(e); } } }, { key: '_catchUdevError', value: function _catchUdevError(error) { var errMessage = error; if (errMessage.message !== undefined) { errMessage = error.message; } // A little heuristics. // If error message is one of these and the type of original message is initialization, it's // probably udev error. if (errMessage === 'Failed to open HID device.' || errMessage === 'Transfer failed.') { this._udevError = true; } throw error; } }, { key: '_isLinux', value: function _isLinux() { var _this = this; if (this._isLinuxCached != null) { return Promise.resolve(this._isLinuxCached); } return platformInfo().then(function (info) { var linux = info.os === 'linux'; _this._isLinuxCached = linux; return linux; }); } }, { key: '_isAfterInstall', value: function _isAfterInstall() { return storageGet('afterInstall').then(function (afterInstall) { return afterInstall !== false; }); } }, { key: 'showUdevError', value: function showUdevError() { var _this2 = this; return this._isLinux().then(function (isLinux) { if (!isLinux) { return false; } return _this2._isAfterInstall().then(function (isAfterInstall) { if (isAfterInstall) { return true; } else { return _this2._udevError; } }); }); } }, { key: 'clearUdevError', value: function clearUdevError() { this._udevError = false; return storageSet('afterInstall', true); } }, { key: 'enumerate', value: function enumerate() { var _this3 = this; return hidEnumerate().then(function (devices) { return devices.filter(function (device) { return !FORBIDDEN_DESCRIPTORS.some(function (des) { return des === device.collections[0].usagePage; }); }); }).then(function (devices) { _this3._hasReportId = {}; devices.forEach(function (device) { _this3._hasReportId[device.deviceId.toString()] = device.collections[0].reportIds.length !== 0; }); return devices; }).then(function (devices) { return devices.map(function (device) { return deviceToJson(device); }); }); } }, { key: 'send', value: function send(device, session, data) { var _this4 = this; var sessionNu = parseInt(session); if (isNaN(sessionNu)) { return Promise.reject(new Error('Session ' + session + ' is not a number')); } var hasReportId = this._hasReportId[device.toString()]; var reportId = hasReportId ? REPORT_ID : 0; var ab = data; if (!hasReportId) { var newArray = new Uint8Array(64); newArray[0] = 63; newArray.set(new Uint8Array(data), 1); ab = newArray.buffer; } return hidSend(sessionNu, reportId, ab).catch(function (e) { return _this4._catchUdevError(e); }); } }, { key: 'receive', value: function receive(device, session) { var _this5 = this; var sessionNu = parseInt(session); if (isNaN(sessionNu)) { return Promise.reject(new Error('Session ' + session + ' is not a number')); } return hidReceive(sessionNu).then(function (_ref) { var data = _ref.data, reportId = _ref.reportId; if (reportId !== 0) { return data; } else { return data.slice(1); } }).then(function (res) { return _this5.clearUdevError().then(function () { return res; }); }).catch(function (e) { return _this5._catchUdevError(e); }); } }, { key: 'connect', value: function connect(device) { var _this6 = this; var deviceNu = parseInt(device); if (isNaN(deviceNu)) { return Promise.reject(new Error('Device ' + deviceNu + ' is not a number')); } return hidConnect(deviceNu).then(function (d) { return d.toString(); }).catch(function (e) { return _this6._catchUdevError(e); }); } }, { key: 'disconnect', value: function disconnect(path, session) { var sessionNu = parseInt(session); if (isNaN(sessionNu)) { return Promise.reject(new Error('Session ' + session + ' is not a number')); } return hidDisconnect(sessionNu); } }, { key: 'requestDevice', value: function requestDevice() { return Promise.reject(); } }]); return ChromeHidPlugin; }(), (_applyDecoratedDescriptor(_class.prototype, 'init', [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, 'init'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'connect', [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, 'connect'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'disconnect', [_debugDecorator.debugInOut], Object.getOwnPropertyDescriptor(_class.prototype, 'disconnect'), _class.prototype)), _class); exports.default = ChromeHidPlugin;