UNPKG

@zondax/ledger-stacks

Version:

Node API for Stacks App (Ledger Nano S/S+/X)

426 lines (425 loc) 23.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; exports.__esModule = true; exports.LedgerError = void 0; var helper_1 = require("./helper"); var common_1 = require("./common"); exports.LedgerError = common_1.LedgerError; var varuint_bitcoin_1 = require("varuint-bitcoin"); __exportStar(require("./types"), exports); function processGetAddrResponse(response) { var partialResponse = response; var errorCodeData = partialResponse.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; var publicKey = Buffer.from(partialResponse.slice(0, common_1.PKLEN)); partialResponse = partialResponse.slice(common_1.PKLEN); var address = Buffer.from(partialResponse.slice(0, -2)).toString(); return { publicKey: publicKey, address: address, returnCode: returnCode, errorMessage: (0, common_1.errorCodeToString)(returnCode) }; } var StacksApp = /** @class */ (function () { function StacksApp(transport) { this.transport = transport; if (!transport) { throw new Error('Transport has not been defined'); } } StacksApp.prepareChunks = function (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_1.CHUNK_SIZE) { var end = i + common_1.CHUNK_SIZE; if (i > buffer.length) { end = buffer.length; } chunks.push(buffer.slice(i, end)); } return chunks; }; StacksApp.prototype.signGetChunks = function (path, message) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, StacksApp.prepareChunks((0, helper_1.serializePath)(path), message)]; }); }); }; StacksApp.prototype.getVersion = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, (0, common_1.getVersion)(this.transport)["catch"](function (err) { return (0, common_1.processErrorResponse)(err); })]; }); }); }; StacksApp.prototype.getAppInfo = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*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.errorMessage = 'response format ID not recognized'; result.returnCode = common_1.LedgerError.DeviceIsBusy; } 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 { returnCode: returnCode, errorMessage: (0, common_1.errorCodeToString)(returnCode), // appName: appName, appVersion: appVersion, flagLen: flagLen, flagsValue: flagsValue, flagRecovery: (flagsValue & 1) !== 0, // eslint-disable-next-line no-bitwise flagSignedMcuCode: (flagsValue & 2) !== 0, // eslint-disable-next-line no-bitwise flagOnboarded: (flagsValue & 4) !== 0, // eslint-disable-next-line no-bitwise flagPINValidated: (flagsValue & 128) !== 0 }; }, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.getAddressAndPubKey = function (path, version) { return __awaiter(this, void 0, void 0, function () { var serializedPath; return __generator(this, function (_a) { serializedPath = (0, helper_1.serializePath)(path); return [2 /*return*/, this.transport .send(common_1.CLA, common_1.INS.GET_ADDR_SECP256K1, common_1.P1_VALUES.ONLY_RETRIEVE, version, serializedPath, [0x9000]) .then(processGetAddrResponse, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.getIdentityPubKey = function (path) { return __awaiter(this, void 0, void 0, function () { var serializedPath; return __generator(this, function (_a) { serializedPath = (0, helper_1.serializePath)(path); return [2 /*return*/, this.transport .send(common_1.CLA, common_1.INS.GET_AUTH_PUBKEY, common_1.P1_VALUES.ONLY_RETRIEVE, 0, serializedPath, [0x9000]) .then(processGetAddrResponse, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.showAddressAndPubKey = function (path, version) { return __awaiter(this, void 0, void 0, function () { var serializedPath; return __generator(this, function (_a) { serializedPath = (0, helper_1.serializePath)(path); return [2 /*return*/, this.transport .send(common_1.CLA, common_1.INS.GET_ADDR_SECP256K1, common_1.P1_VALUES.SHOW_ADDRESS_IN_DEVICE, version, serializedPath, [common_1.LedgerError.NoErrors]) .then(processGetAddrResponse, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.signSendChunk = function (chunkIdx, chunkNum, chunk, ins) { return __awaiter(this, void 0, void 0, function () { var payloadType; return __generator(this, function (_a) { payloadType = common_1.PAYLOAD_TYPE.ADD; if (chunkIdx === 1) { payloadType = common_1.PAYLOAD_TYPE.INIT; } if (chunkIdx === chunkNum) { payloadType = common_1.PAYLOAD_TYPE.LAST; } return [2 /*return*/, this.transport .send(common_1.CLA, ins, payloadType, 0, chunk, [ common_1.LedgerError.NoErrors, common_1.LedgerError.DataIsInvalid, common_1.LedgerError.BadKeyHandle, common_1.LedgerError.SignVerifyError, ]) .then(function (response) { var errorCodeData = response.slice(-2); var returnCode = errorCodeData[0] * 256 + errorCodeData[1]; var errorMessage = (0, common_1.errorCodeToString)(returnCode); var errorDescription = ''; var postSignHash = Buffer.alloc(0); var signatureCompact = Buffer.alloc(0); var signatureVRS = Buffer.alloc(0); var signatureDER = Buffer.alloc(0); if (returnCode === common_1.LedgerError.BadKeyHandle || returnCode === common_1.LedgerError.DataIsInvalid || returnCode === common_1.LedgerError.SignVerifyError) { errorMessage = "".concat(errorMessage, " : ").concat(response .slice(0, response.length - 2) .toString('ascii')); } if (returnCode === common_1.LedgerError.NoErrors && response.length > 2) { postSignHash = response.slice(0, 32); signatureCompact = response.slice(32, 97); signatureVRS = Buffer.alloc(65); signatureVRS[0] = signatureCompact[signatureCompact.length - 1]; Buffer.from(signatureCompact).copy(signatureVRS, 1, 0, 64); signatureDER = response.slice(97, response.length - 2); return { postSignHash: postSignHash, signatureCompact: signatureCompact, signatureVRS: signatureVRS, signatureDER: signatureDER, returnCode: returnCode, errorMessage: errorMessage }; } return { returnCode: returnCode, errorMessage: errorMessage }; }, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.sign = function (path, message) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { return [2 /*return*/, this.signGetChunks(path, message).then(function (chunks) { return _this.signSendChunk(1, chunks.length, chunks[0], common_1.INS.SIGN_SECP256K1).then(function (response) { return __awaiter(_this, void 0, void 0, function () { var result, i; return __generator(this, function (_a) { switch (_a.label) { case 0: result = { returnCode: response.returnCode, errorMessage: response.errorMessage, postSignHash: null, signatureCompact: null, signatureDER: null }; i = 1; _a.label = 1; case 1: if (!(i < chunks.length)) return [3 /*break*/, 4]; return [4 /*yield*/, this.signSendChunk(1 + i, chunks.length, chunks[i], common_1.INS.SIGN_SECP256K1)]; case 2: // eslint-disable-next-line no-await-in-loop result = _a.sent(); if (result.returnCode !== common_1.LedgerError.NoErrors) { return [3 /*break*/, 4]; } _a.label = 3; case 3: i += 1; return [3 /*break*/, 1]; case 4: return [2 /*return*/, result]; } }); }); }, common_1.processErrorResponse); }, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.sign_msg = function (path, message) { return __awaiter(this, void 0, void 0, function () { var len, stacks_message, blob, ins; var _this = this; return __generator(this, function (_a) { len = (0, varuint_bitcoin_1.encode)(message.length); stacks_message = '\x17Stacks Signed Message:\n'; blob = Buffer.concat([Buffer.from(stacks_message), len, Buffer.from(message)]); ins = common_1.INS.SIGN_SECP256K1; return [2 /*return*/, this.signGetChunks(path, blob).then(function (chunks) { return _this.signSendChunk(1, chunks.length, chunks[0], ins).then(function (response) { return __awaiter(_this, void 0, void 0, function () { var result, i; return __generator(this, function (_a) { switch (_a.label) { case 0: result = { returnCode: response.returnCode, errorMessage: response.errorMessage, postSignHash: null, signatureCompact: null, signatureDER: null }; i = 1; _a.label = 1; case 1: if (!(i < chunks.length)) return [3 /*break*/, 4]; return [4 /*yield*/, this.signSendChunk(1 + i, chunks.length, chunks[i], ins)]; case 2: // eslint-disable-next-line no-await-in-loop result = _a.sent(); if (result.returnCode !== common_1.LedgerError.NoErrors) { return [3 /*break*/, 4]; } _a.label = 3; case 3: i += 1; return [3 /*break*/, 1]; case 4: return [2 /*return*/, result]; } }); }); }, common_1.processErrorResponse); }, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.sign_jwt = function (path, message) { return __awaiter(this, void 0, void 0, function () { var len, blob, ins; var _this = this; return __generator(this, function (_a) { len = message.length; blob = Buffer.from(message); ins = common_1.INS.SIGN_JWT_SECP256K1; return [2 /*return*/, this.signGetChunks(path, blob).then(function (chunks) { return _this.signSendChunk(1, chunks.length, chunks[0], ins).then(function (response) { return __awaiter(_this, void 0, void 0, function () { var result, i; return __generator(this, function (_a) { switch (_a.label) { case 0: result = { returnCode: response.returnCode, errorMessage: response.errorMessage, postSignHash: null, signatureCompact: null, signatureDER: null }; i = 1; _a.label = 1; case 1: if (!(i < chunks.length)) return [3 /*break*/, 4]; return [4 /*yield*/, this.signSendChunk(1 + i, chunks.length, chunks[i], ins)]; case 2: // eslint-disable-next-line no-await-in-loop result = _a.sent(); if (result.returnCode !== common_1.LedgerError.NoErrors) { return [3 /*break*/, 4]; } _a.label = 3; case 3: i += 1; return [3 /*break*/, 1]; case 4: return [2 /*return*/, result]; } }); }); }, common_1.processErrorResponse); }, common_1.processErrorResponse)]; }); }); }; StacksApp.prototype.sign_structured_msg = function (path, domain, message) { return __awaiter(this, void 0, void 0, function () { var len, header, blob, ins; var _this = this; return __generator(this, function (_a) { len = (0, varuint_bitcoin_1.encode)(message.length); header = 'SIP018'; blob = Buffer.concat([Buffer.from(header), Buffer.from(domain, 'hex'), Buffer.from(message, 'hex')]); ins = common_1.INS.SIGN_SECP256K1; return [2 /*return*/, this.signGetChunks(path, blob).then(function (chunks) { return _this.signSendChunk(1, chunks.length, chunks[0], ins).then(function (response) { return __awaiter(_this, void 0, void 0, function () { var result, i; return __generator(this, function (_a) { switch (_a.label) { case 0: result = { returnCode: response.returnCode, errorMessage: response.errorMessage, postSignHash: null, signatureCompact: null, signatureDER: null }; i = 1; _a.label = 1; case 1: if (!(i < chunks.length)) return [3 /*break*/, 4]; return [4 /*yield*/, this.signSendChunk(1 + i, chunks.length, chunks[i], ins)]; case 2: // eslint-disable-next-line no-await-in-loop result = _a.sent(); if (result.returnCode !== common_1.LedgerError.NoErrors) { return [3 /*break*/, 4]; } _a.label = 3; case 3: i += 1; return [3 /*break*/, 1]; case 4: return [2 /*return*/, result]; } }); }); }, common_1.processErrorResponse); }, common_1.processErrorResponse)]; }); }); }; return StacksApp; }()); exports["default"] = StacksApp;