UNPKG

@zondax/ledger-filecoin

Version:

Node API for Filecoin App (Ledger Nano S/X)

519 lines (516 loc) 22.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _hwAppEth = _interopRequireDefault(require("@ledgerhq/hw-app-eth")); var _helperV = require("./helperV1"); var _common = require("./common"); /** ****************************************************************************** * (c) 2019 ZondaX GmbH * (c) 2016-2017 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************* */ var varint = require("varint"); function processGetAddrResponse(response) { var partialResponse = response; var errorCodeData = partialResponse.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; var pk = Buffer.from(partialResponse.slice(0, _common.PKLEN)); partialResponse = partialResponse.slice(_common.PKLEN); var addrByteLength = partialResponse[0]; partialResponse = partialResponse.slice(1); var addrByte = Buffer.from(partialResponse.slice(0, addrByteLength)); partialResponse = partialResponse.slice(addrByteLength); var addrStringLength = partialResponse[0]; partialResponse = partialResponse.slice(1); var addrString = Buffer.from(partialResponse.slice(0, addrStringLength)).toString(); return { addrByte: addrByte, addrString: addrString, compressed_pk: pk, return_code: returnCode, error_message: (0, _common.errorCodeToString)(returnCode) }; } var FilecoinApp = exports.default = /*#__PURE__*/function () { function FilecoinApp(transport) { var scrambleKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _common.APP_KEY; var ethScrambleKey = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "w0w"; var ethLoadConfig = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; (0, _classCallCheck2.default)(this, FilecoinApp); if (!transport) { throw new Error("Transport has not been defined"); } this.transport = transport; this.eth = new _hwAppEth.default(transport, ethScrambleKey, ethLoadConfig); transport.decorateAppAPIMethods(this, ["getVersion", "appInfo", "deviceInfo", "getAddressAndPubKey", "sign"], scrambleKey); } return (0, _createClass2.default)(FilecoinApp, [{ key: "serializePath", value: function () { var _serializePath = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(path) { return _regenerator.default.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _context.next = 2; return (0, _common.getVersion)(this.transport); case 2: this.versionResponse = _context.sent; if (!(this.versionResponse.return_code !== _common.ERROR_CODE.NoError)) { _context.next = 5; break; } throw this.versionResponse; case 5: _context.t0 = this.versionResponse.major; _context.next = _context.t0 === 0 ? 8 : _context.t0 === 1 ? 8 : _context.t0 === 2 ? 8 : 9; break; case 8: return _context.abrupt("return", (0, _helperV.serializePathv1)(path)); case 9: return _context.abrupt("return", { return_code: 0x6400, error_message: "App Version is not supported" }); case 10: case "end": return _context.stop(); } }, _callee, this); })); function serializePath(_x) { return _serializePath.apply(this, arguments); } return serializePath; }() }, { key: "signGetChunks", value: function () { var _signGetChunks = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2(path, message) { var serializedPath; return _regenerator.default.wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _context2.next = 2; return this.serializePath(path); case 2: serializedPath = _context2.sent; return _context2.abrupt("return", FilecoinApp.prepareChunks(serializedPath, message)); case 4: case "end": return _context2.stop(); } }, _callee2, this); })); function signGetChunks(_x2, _x3) { return _signGetChunks.apply(this, arguments); } return signGetChunks; }() }, { key: "getVersion", value: function () { var _getVersion2 = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() { var _this = this; return _regenerator.default.wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: return _context3.abrupt("return", (0, _common.getVersion)(this.transport).then(function (response) { _this.versionResponse = response; return response; }).catch(function (err) { return (0, _common.processErrorResponse)(err); })); case 1: case "end": return _context3.stop(); } }, _callee3, this); })); function getVersion() { return _getVersion2.apply(this, arguments); } return getVersion; }() }, { key: "appInfo", value: function () { var _appInfo = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() { return _regenerator.default.wrap(function _callee4$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: return _context4.abrupt("return", this.transport.send(0xb0, 0x01, 0, 0).then(function (response) { var errorCodeData = response.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; var result = {}; var appName = "err"; var appVersion = "err"; var flagLen = 0; var flagsValue = 0; if (response[0] !== 1) { // Ledger responds with format ID 1. There is no spec for any format != 1 result.error_message = "response format ID not recognized"; result.return_code = 0x9001; } else { var appNameLen = response[1]; appName = response.slice(2, 2 + appNameLen).toString("ascii"); var idx = 2 + appNameLen; var appVersionLen = response[idx]; idx += 1; appVersion = response.slice(idx, idx + appVersionLen).toString("ascii"); idx += appVersionLen; var appFlagsLen = response[idx]; idx += 1; flagLen = appFlagsLen; flagsValue = response[idx]; } return { return_code: returnCode, error_message: (0, _common.errorCodeToString)(returnCode), // // appName: appName, appVersion: appVersion, flagLen: flagLen, flagsValue: flagsValue, flag_recovery: (flagsValue & 1) !== 0, flag_signed_mcu_code: (flagsValue & 2) !== 0, flag_onboarded: (flagsValue & 4) !== 0, flag_pin_validated: (flagsValue & 128) !== 0 }; }, _common.processErrorResponse)); case 1: case "end": return _context4.stop(); } }, _callee4, this); })); function appInfo() { return _appInfo.apply(this, arguments); } return appInfo; }() }, { key: "deviceInfo", value: function () { var _deviceInfo = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5() { return _regenerator.default.wrap(function _callee5$(_context5) { while (1) switch (_context5.prev = _context5.next) { case 0: return _context5.abrupt("return", this.transport.send(0xe0, 0x01, 0, 0, Buffer.from([]), [_common.ERROR_CODE.NoError, 0x6e00]).then(function (response) { var errorCodeData = response.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; if (returnCode === 0x6e00) { return { return_code: returnCode, error_message: "This command is only available in the Dashboard" }; } var targetId = response.slice(0, 4).toString("hex"); var pos = 4; var secureElementVersionLen = response[pos]; pos += 1; var seVersion = response.slice(pos, pos + secureElementVersionLen).toString(); pos += secureElementVersionLen; var flagsLen = response[pos]; pos += 1; var flag = response.slice(pos, pos + flagsLen).toString("hex"); pos += flagsLen; var mcuVersionLen = response[pos]; pos += 1; // Patch issue in mcu version var tmp = response.slice(pos, pos + mcuVersionLen); if (tmp[mcuVersionLen - 1] === 0) { tmp = response.slice(pos, pos + mcuVersionLen - 1); } var mcuVersion = tmp.toString(); return { return_code: returnCode, error_message: (0, _common.errorCodeToString)(returnCode), // // targetId: targetId, seVersion: seVersion, flag: flag, mcuVersion: mcuVersion }; }, _common.processErrorResponse)); case 1: case "end": return _context5.stop(); } }, _callee5, this); })); function deviceInfo() { return _deviceInfo.apply(this, arguments); } return deviceInfo; }() }, { key: "getAddressAndPubKey", value: function () { var _getAddressAndPubKey = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6(path) { var _this2 = this; return _regenerator.default.wrap(function _callee6$(_context6) { while (1) switch (_context6.prev = _context6.next) { case 0: return _context6.abrupt("return", this.serializePath(path).then(function (data) { return _this2.transport.send(_common.CLA, _common.INS.GET_ADDR_SECP256K1, _common.P1_VALUES.ONLY_RETRIEVE, 0, data, [0x9000]).then(processGetAddrResponse, _common.processErrorResponse); }).catch(function (err) { return (0, _common.processErrorResponse)(err); })); case 1: case "end": return _context6.stop(); } }, _callee6, this); })); function getAddressAndPubKey(_x4) { return _getAddressAndPubKey.apply(this, arguments); } return getAddressAndPubKey; }() }, { key: "showAddressAndPubKey", value: function () { var _showAddressAndPubKey = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7(path) { var _this3 = this; return _regenerator.default.wrap(function _callee7$(_context7) { while (1) switch (_context7.prev = _context7.next) { case 0: return _context7.abrupt("return", this.serializePath(path).then(function (data) { return _this3.transport.send(_common.CLA, _common.INS.GET_ADDR_SECP256K1, _common.P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, data, [0x9000]).then(processGetAddrResponse, _common.processErrorResponse); }).catch(function (err) { return (0, _common.processErrorResponse)(err); })); case 1: case "end": return _context7.stop(); } }, _callee7, this); })); function showAddressAndPubKey(_x5) { return _showAddressAndPubKey.apply(this, arguments); } return showAddressAndPubKey; }() }, { key: "signSendChunk", value: function () { var _signSendChunk = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8(chunkIdx, chunkNum, chunk, ins) { return _regenerator.default.wrap(function _callee8$(_context8) { while (1) switch (_context8.prev = _context8.next) { case 0: _context8.t0 = this.versionResponse.major; _context8.next = _context8.t0 === 0 ? 3 : _context8.t0 === 1 ? 3 : _context8.t0 === 2 ? 3 : 4; break; case 3: return _context8.abrupt("return", (0, _helperV.signSendChunkv1)(this, chunkIdx, chunkNum, chunk, ins)); case 4: return _context8.abrupt("return", { return_code: 0x6400, error_message: "App Version is not supported" }); case 5: case "end": return _context8.stop(); } }, _callee8, this); })); function signSendChunk(_x6, _x7, _x8, _x9) { return _signSendChunk.apply(this, arguments); } return signSendChunk; }() }, { key: "signGeneric", value: function () { var _signGeneric = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee10(path, message, ins) { var _this4 = this; return _regenerator.default.wrap(function _callee10$(_context10) { while (1) switch (_context10.prev = _context10.next) { case 0: return _context10.abrupt("return", this.signGetChunks(path, message).then(function (chunks) { return _this4.signSendChunk(1, chunks.length, chunks[0], ins, [_common.ERROR_CODE.NoError]).then(/*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9(response) { var result, i; return _regenerator.default.wrap(function _callee9$(_context9) { while (1) switch (_context9.prev = _context9.next) { case 0: result = { return_code: response.return_code, error_message: response.error_message, signature_compact: null, signature_der: null }; i = 1; case 2: if (!(i < chunks.length)) { _context9.next = 11; break; } _context9.next = 5; return _this4.signSendChunk(1 + i, chunks.length, chunks[i], ins); case 5: result = _context9.sent; if (!(result.return_code !== _common.ERROR_CODE.NoError)) { _context9.next = 8; break; } return _context9.abrupt("break", 11); case 8: i += 1; _context9.next = 2; break; case 11: return _context9.abrupt("return", { return_code: result.return_code, error_message: result.error_message, // /// signature_compact: result.signature_compact, signature_der: result.signature_der }); case 12: case "end": return _context9.stop(); } }, _callee9); })); return function (_x13) { return _ref.apply(this, arguments); }; }(), _common.processErrorResponse); }, _common.processErrorResponse)); case 1: case "end": return _context10.stop(); } }, _callee10, this); })); function signGeneric(_x10, _x11, _x12) { return _signGeneric.apply(this, arguments); } return signGeneric; }() }, { key: "sign", value: function () { var _sign = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee11(path, message) { return _regenerator.default.wrap(function _callee11$(_context11) { while (1) switch (_context11.prev = _context11.next) { case 0: return _context11.abrupt("return", this.signGeneric(path, message, _common.INS.SIGN_SECP256K1)); case 1: case "end": return _context11.stop(); } }, _callee11, this); })); function sign(_x14, _x15) { return _sign.apply(this, arguments); } return sign; }() }, { key: "signRawBytes", value: function () { var _signRawBytes = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee12(path, message) { var msg, len, data; return _regenerator.default.wrap(function _callee12$(_context12) { while (1) switch (_context12.prev = _context12.next) { case 0: msg = Buffer.from(message); len = Buffer.from(varint.encode(msg.length)); data = Buffer.concat([len, message]); return _context12.abrupt("return", this.signGeneric(path, data, _common.INS.SIGN_RAW_BYTES)); case 4: case "end": return _context12.stop(); } }, _callee12, this); })); function signRawBytes(_x16, _x17) { return _signRawBytes.apply(this, arguments); } return signRawBytes; }() }, { key: "signETHTransaction", value: function () { var _signETHTransaction = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee13(path, rawTxHex) { var resolution, _args13 = arguments; return _regenerator.default.wrap(function _callee13$(_context13) { while (1) switch (_context13.prev = _context13.next) { case 0: resolution = _args13.length > 2 && _args13[2] !== undefined ? _args13[2] : null; return _context13.abrupt("return", this.eth.signTransaction(path, rawTxHex, resolution)); case 2: case "end": return _context13.stop(); } }, _callee13, this); })); function signETHTransaction(_x18, _x19) { return _signETHTransaction.apply(this, arguments); } return signETHTransaction; }() }, { key: "getETHAddress", value: function () { var _getETHAddress = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee14(path) { var boolDisplay, boolChaincode, _args14 = arguments; return _regenerator.default.wrap(function _callee14$(_context14) { while (1) switch (_context14.prev = _context14.next) { case 0: boolDisplay = _args14.length > 1 && _args14[1] !== undefined ? _args14[1] : false; boolChaincode = _args14.length > 2 && _args14[2] !== undefined ? _args14[2] : false; return _context14.abrupt("return", this.eth.getAddress(path, boolDisplay, boolChaincode)); case 3: case "end": return _context14.stop(); } }, _callee14, this); })); function getETHAddress(_x20) { return _getETHAddress.apply(this, arguments); } return getETHAddress; }() }], [{ key: "prepareChunks", value: function prepareChunks(serializedPathBuffer, message) { var chunks = []; // First chunk (only path) chunks.push(serializedPathBuffer); var messageBuffer = Buffer.from(message); var buffer = Buffer.concat([messageBuffer]); for (var i = 0; i < buffer.length; i += _common.CHUNK_SIZE) { var end = i + _common.CHUNK_SIZE; if (i > buffer.length) { end = buffer.length; } chunks.push(buffer.slice(i, end)); } return chunks; } }]); }();