UNPKG

ledger-bitsong-js

Version:
625 lines (531 loc) 23.3 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 _crypto = _interopRequireDefault(require("crypto")); var _ripemd = _interopRequireDefault(require("ripemd160")); var _bech = _interopRequireDefault(require("bech32")); var _helperV = require("./helperV1"); var _helperV2 = require("./helperV2"); var _common = require("./common"); /** ****************************************************************************** * (c) 2021 Bitsong * (c) 2019 - 2021 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 BitsongApp = /*#__PURE__*/function () { function BitsongApp(transport) { var scrambleKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _common.APP_KEY; (0, _classCallCheck2.default)(this, BitsongApp); if (!transport) { throw new Error("Transport has not been defined"); } this.transport = transport; transport.decorateAppAPIMethods(this, ["getVersion", "sign", "getAddressAndPubKey", "appInfo", "deviceInfo", "getBech32FromPK"], scrambleKey); } (0, _createClass2.default)(BitsongApp, [{ 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 === 1 ? 8 : _context.t0 === 2 ? 9 : 10; break; case 8: return _context.abrupt("return", (0, _helperV.serializePathv1)(path)); case 9: return _context.abrupt("return", (0, _helperV2.serializePathv2)(path)); case 10: return _context.abrupt("return", { return_code: 0x6400, error_message: "App Version is not supported" }); case 11: 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, chunks, buffer, i, end; 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; chunks = []; chunks.push(serializedPath); buffer = Buffer.from(message); for (i = 0; i < buffer.length; i += _common.CHUNK_SIZE) { end = i + _common.CHUNK_SIZE; if (i > buffer.length) { end = buffer.length; } chunks.push(buffer.slice(i, end)); } return _context2.abrupt("return", chunks); case 8: 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() { return _regenerator.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.prev = 0; _context3.next = 3; return (0, _common.getVersion)(this.transport); case 3: this.versionResponse = _context3.sent; return _context3.abrupt("return", this.versionResponse); case 7: _context3.prev = 7; _context3.t0 = _context3["catch"](0); return _context3.abrupt("return", (0, _common.processErrorResponse)(_context3.t0)); case 10: case "end": return _context3.stop(); } } }, _callee3, this, [[0, 7]]); })); 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, // eslint-disable-next-line no-bitwise flag_recovery: (flagsValue & 1) !== 0, // eslint-disable-next-line no-bitwise flag_signed_mcu_code: (flagsValue & 2) !== 0, // eslint-disable-next-line no-bitwise flag_onboarded: (flagsValue & 4) !== 0, // eslint-disable-next-line no-bitwise 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: "publicKey", value: function () { var _publicKey = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(path) { var serializedPath, data; return _regenerator.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: _context6.prev = 0; _context6.next = 3; return this.serializePath(path); case 3: serializedPath = _context6.sent; _context6.t0 = this.versionResponse.major; _context6.next = _context6.t0 === 1 ? 7 : _context6.t0 === 2 ? 8 : 10; break; case 7: return _context6.abrupt("return", (0, _helperV.publicKeyv1)(this, serializedPath)); case 8: data = Buffer.concat([BitsongApp.serializeHRP("bitsong"), serializedPath]); return _context6.abrupt("return", (0, _helperV2.publicKeyv2)(this, data)); case 10: return _context6.abrupt("return", { return_code: 0x6400, error_message: "App Version is not supported" }); case 11: _context6.next = 16; break; case 13: _context6.prev = 13; _context6.t1 = _context6["catch"](0); return _context6.abrupt("return", (0, _common.processErrorResponse)(_context6.t1)); case 16: case "end": return _context6.stop(); } } }, _callee6, this, [[0, 13]]); })); function publicKey(_x4) { return _publicKey.apply(this, arguments); } return publicKey; }() }, { key: "getAddressAndPubKey", value: function () { var _getAddressAndPubKey = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(path, hrp) { var _this = this; return _regenerator.default.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: _context7.prev = 0; return _context7.abrupt("return", this.serializePath(path).then(function (serializedPath) { var data = Buffer.concat([BitsongApp.serializeHRP(hrp), serializedPath]); return _this.transport.send(_common.CLA, _common.INS.GET_ADDR_SECP256K1, _common.P1_VALUES.ONLY_RETRIEVE, 0, data, [_common.ERROR_CODE.NoError]).then(function (response) { var errorCodeData = response.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; var compressedPk = Buffer.from(response.slice(0, 33)); var bech32Address = Buffer.from(response.slice(33, -2)).toString(); return { bech32_address: bech32Address, compressed_pk: compressedPk, return_code: returnCode, error_message: (0, _common.errorCodeToString)(returnCode) }; }, _common.processErrorResponse); }).catch(function (err) { return (0, _common.processErrorResponse)(err); })); case 4: _context7.prev = 4; _context7.t0 = _context7["catch"](0); return _context7.abrupt("return", (0, _common.processErrorResponse)(_context7.t0)); case 7: case "end": return _context7.stop(); } } }, _callee7, this, [[0, 4]]); })); function getAddressAndPubKey(_x5, _x6) { return _getAddressAndPubKey.apply(this, arguments); } return getAddressAndPubKey; }() }, { key: "showAddressAndPubKey", value: function () { var _showAddressAndPubKey = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(path, hrp) { var _this2 = this; return _regenerator.default.wrap(function _callee8$(_context8) { while (1) { switch (_context8.prev = _context8.next) { case 0: _context8.prev = 0; return _context8.abrupt("return", this.serializePath(path).then(function (serializedPath) { var data = Buffer.concat([BitsongApp.serializeHRP(hrp), serializedPath]); return _this2.transport.send(_common.CLA, _common.INS.GET_ADDR_SECP256K1, _common.P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, data, [_common.ERROR_CODE.NoError]).then(function (response) { var errorCodeData = response.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; var compressedPk = Buffer.from(response.slice(0, 33)); var bech32Address = Buffer.from(response.slice(33, -2)).toString(); return { bech32_address: bech32Address, compressed_pk: compressedPk, return_code: returnCode, error_message: (0, _common.errorCodeToString)(returnCode) }; }, _common.processErrorResponse); }).catch(function (err) { return (0, _common.processErrorResponse)(err); })); case 4: _context8.prev = 4; _context8.t0 = _context8["catch"](0); return _context8.abrupt("return", (0, _common.processErrorResponse)(_context8.t0)); case 7: case "end": return _context8.stop(); } } }, _callee8, this, [[0, 4]]); })); function showAddressAndPubKey(_x7, _x8) { return _showAddressAndPubKey.apply(this, arguments); } return showAddressAndPubKey; }() }, { key: "signSendChunk", value: function () { var _signSendChunk = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9(chunkIdx, chunkNum, chunk) { return _regenerator.default.wrap(function _callee9$(_context9) { while (1) { switch (_context9.prev = _context9.next) { case 0: _context9.t0 = this.versionResponse.major; _context9.next = _context9.t0 === 1 ? 3 : _context9.t0 === 2 ? 4 : 5; break; case 3: return _context9.abrupt("return", (0, _helperV.signSendChunkv1)(this, chunkIdx, chunkNum, chunk)); case 4: return _context9.abrupt("return", (0, _helperV2.signSendChunkv2)(this, chunkIdx, chunkNum, chunk)); case 5: return _context9.abrupt("return", { return_code: 0x6400, error_message: "App Version is not supported" }); case 6: case "end": return _context9.stop(); } } }, _callee9, this); })); function signSendChunk(_x9, _x10, _x11) { return _signSendChunk.apply(this, arguments); } return signSendChunk; }() }, { key: "sign", value: function () { var _sign = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11(path, message) { var _this3 = this; return _regenerator.default.wrap(function _callee11$(_context11) { while (1) { switch (_context11.prev = _context11.next) { case 0: return _context11.abrupt("return", this.signGetChunks(path, message).then(function (chunks) { return _this3.signSendChunk(1, chunks.length, chunks[0], [_common.ERROR_CODE.NoError]).then( /*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10(response) { var result, i; return _regenerator.default.wrap(function _callee10$(_context10) { while (1) { switch (_context10.prev = _context10.next) { case 0: result = { return_code: response.return_code, error_message: response.error_message, signature: null }; i = 1; case 2: if (!(i < chunks.length)) { _context10.next = 11; break; } _context10.next = 5; return _this3.signSendChunk(1 + i, chunks.length, chunks[i]); case 5: result = _context10.sent; if (!(result.return_code !== _common.ERROR_CODE.NoError)) { _context10.next = 8; break; } return _context10.abrupt("break", 11); case 8: i += 1; _context10.next = 2; break; case 11: return _context10.abrupt("return", { return_code: result.return_code, error_message: result.error_message, // /// signature: result.signature }); case 12: case "end": return _context10.stop(); } } }, _callee10); })); return function (_x14) { return _ref.apply(this, arguments); }; }(), _common.processErrorResponse); }, _common.processErrorResponse)); case 1: case "end": return _context11.stop(); } } }, _callee11, this); })); function sign(_x12, _x13) { return _sign.apply(this, arguments); } return sign; }() }], [{ key: "serializeHRP", value: function serializeHRP(hrp) { if (hrp == null || hrp.length < 3 || hrp.length > 83) { throw new Error("Invalid HRP"); } var buf = Buffer.alloc(1 + hrp.length); buf.writeUInt8(hrp.length, 0); buf.write(hrp, 1); return buf; } }, { key: "getBech32FromPK", value: function getBech32FromPK(hrp, pk) { if (pk.length !== 33) { throw new Error("expected compressed public key [31 bytes]"); } var hashSha256 = _crypto.default.createHash("sha256").update(pk).digest(); var hashRip = new _ripemd.default().update(hashSha256).digest(); return _bech.default.encode(hrp, _bech.default.toWords(hashRip)); } }]); return BitsongApp; }(); exports.default = BitsongApp;