@zondax/ledger-filecoin
Version:
Node API for Filecoin App (Ledger Nano S/X)
519 lines (516 loc) • 22.1 kB
JavaScript
"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;
}
}]);
}();