UNPKG

@zondax/ledger-icp

Version:

Node API for the Internet Computer App (Ledger Nano S/X/S+/Stax/Flex)

352 lines 16.2 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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SIGN_VALUES_P2 = void 0; /** ****************************************************************************** * (c) 2019-2020 Zondax AG * (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. ******************************************************************************* */ const consts_1 = require("./consts"); const ledger_js_1 = __importStar(require("@zondax/ledger-js")); const helper_1 = require("./helper"); __exportStar(require("./types"), exports); var consts_2 = require("./consts"); Object.defineProperty(exports, "SIGN_VALUES_P2", { enumerable: true, get: function () { return consts_2.SIGN_VALUES_P2; } }); class InternetComputerApp extends ledger_js_1.default { constructor(transport) { if (transport == null) throw new Error("Transport has not been defined"); const params = { cla: 0x11, ins: { GET_VERSION: 0x00, GET_ADDR_SECP256K1: 0x01, SIGN_SECP256K1: 0x02, SIGN_COMBINED: 0x03, SAVE_CONSENT: 0x04, SAVE_CANISTER_CALL: 0x05, SAVE_CERITIFACE_AND_VERIFY: 0x06, GET_REGISTRY_LEN: 0x07, GET_TOKEN_I: 0x08, }, p1Values: { ONLY_RETRIEVE: 0x00, SHOW_ADDRESS_IN_DEVICE: 0x01, }, acceptedPathLengths: [5], chunkSize: 250, }; super(transport, params); } async getAddressAndPubKey(path) { const serializedPath = this.serializePath(path); return await this.transport .send(this.CLA, this.INS.GET_ADDR_SECP256K1, this.P1_VALUES.ONLY_RETRIEVE, 0, serializedPath, [0x9000]) .then(helper_1.processGetAddrResponse, ledger_js_1.processErrorResponse); } async showAddressAndPubKey(path) { const serializedPath = this.serializePath(path); return await this.transport .send(this.CLA, this.INS.GET_ADDR_SECP256K1, this.P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, serializedPath, [36864 /* LedgerError.NoErrors */]) .then(helper_1.processGetAddrResponse, ledger_js_1.processErrorResponse); } async signSendChunk(chunkIdx, chunkNum, chunk, txtype, ins) { let payloadType = ledger_js_1.PAYLOAD_TYPE.ADD; if (chunkIdx === 1) { payloadType = ledger_js_1.PAYLOAD_TYPE.INIT; } if (chunkIdx === chunkNum) { payloadType = ledger_js_1.PAYLOAD_TYPE.LAST; } return await this.transport .send(this.CLA, ins, payloadType, txtype, chunk, [ 36864 /* LedgerError.NoErrors */, 27012 /* LedgerError.DataIsInvalid */, 27264 /* LedgerError.BadKeyHandle */, 28417 /* LedgerError.SignVerifyError */, ]) .then((response) => { const errorCodeData = response.subarray(-2); const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; let errorMessage = (0, ledger_js_1.errorCodeToString)(returnCode); let preSignHash = Buffer.alloc(0); let signatureRS = Buffer.alloc(0); let signatureDER = Buffer.alloc(0); if (returnCode === 27264 /* LedgerError.BadKeyHandle */ || returnCode === 27012 /* LedgerError.DataIsInvalid */ || returnCode === 28417 /* LedgerError.SignVerifyError */) { errorMessage = `${errorMessage} : ${response.subarray(0, response.length - 2).toString("ascii")}`; } if (returnCode === 36864 /* LedgerError.NoErrors */ && response.length > 2) { preSignHash = response.subarray(0, consts_1.PREHASH_LEN); signatureRS = response.subarray(consts_1.PREHASH_LEN, consts_1.PREHASH_LEN + consts_1.SIGRSLEN); signatureDER = response.subarray(consts_1.PREHASH_LEN + consts_1.SIGRSLEN + 1, response.length - 2); return { preSignHash, signatureRS, signatureDER, returnCode, errorMessage, }; } return { returnCode, errorMessage, }; }, ledger_js_1.processErrorResponse); } async sign(path, message, txtype) { const chunks = this.prepareChunks(path, message); return await this.signSendChunk(1, chunks.length, chunks[0], txtype % 256, this.INS.SIGN_SECP256K1).then(async (response) => { let result = { returnCode: response.returnCode, errorMessage: response.errorMessage, }; for (let i = 1; i < chunks.length; i += 1) { // eslint-disable-next-line no-await-in-loop result = await this.signSendChunk(1 + i, chunks.length, chunks[i], txtype % 256, this.INS.SIGN_SECP256K1); if (result.returnCode !== 36864 /* LedgerError.NoErrors */) { break; } } return result; }, ledger_js_1.processErrorResponse); } async signSendChunkUpdateCall(chunkIdx, chunkNum, chunk, txtype) { let payloadType = ledger_js_1.PAYLOAD_TYPE.ADD; if (chunkIdx === 1) { payloadType = ledger_js_1.PAYLOAD_TYPE.INIT; } if (chunkIdx === chunkNum) { payloadType = ledger_js_1.PAYLOAD_TYPE.LAST; } return await this.transport .send(this.CLA, this.INS.SIGN_COMBINED, payloadType, txtype, chunk, [ 36864 /* LedgerError.NoErrors */, 27012 /* LedgerError.DataIsInvalid */, 27264 /* LedgerError.BadKeyHandle */, 28417 /* LedgerError.SignVerifyError */, ]) .then((response) => { const errorCodeData = response.subarray(-2); const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; let errorMessage = (0, ledger_js_1.errorCodeToString)(returnCode); let RequestHash = Buffer.alloc(0); let RequestSignatureRS = Buffer.alloc(0); let StatusReadHash = Buffer.alloc(0); let StatusReadSignatureRS = Buffer.alloc(0); if (returnCode === 27264 /* LedgerError.BadKeyHandle */ || returnCode === 27012 /* LedgerError.DataIsInvalid */ || returnCode === 28417 /* LedgerError.SignVerifyError */) { errorMessage = `${errorMessage} : ${response.subarray(0, response.length - 2).toString("ascii")}`; } if (returnCode === 36864 /* LedgerError.NoErrors */ && response.length > 2) { RequestHash = response.subarray(0, 32); RequestSignatureRS = response.subarray(32, 96); StatusReadHash = response.subarray(96, 128); StatusReadSignatureRS = response.subarray(128, 192); return { RequestHash, RequestSignatureRS, StatusReadHash, StatusReadSignatureRS, returnCode, errorMessage, }; } return { returnCode, errorMessage, }; }, ledger_js_1.processErrorResponse); } async signUpdateCall(path, request, checkStatus, txtype) { const message = Buffer.alloc(8 + request.byteLength + checkStatus.byteLength); message.writeUInt32LE(checkStatus.byteLength, 0); checkStatus.copy(message, 4); message.writeUInt32LE(request.byteLength, 4 + checkStatus.byteLength); request.copy(message, 8 + checkStatus.byteLength); console.log(message.toString("hex")); const chunks = this.prepareChunks(path, message); return await this.signSendChunk(1, chunks.length, chunks[0], txtype % 256, this.INS.SIGN_SECP256K1).then(async (response) => { let result = { returnCode: response.returnCode, errorMessage: response.errorMessage, }; for (let i = 1; i < chunks.length; i += 1) { // eslint-disable-next-line no-await-in-loop result = await this.signSendChunkUpdateCall(1 + i, chunks.length, chunks[i], txtype % 256); if (result.returnCode !== 36864 /* LedgerError.NoErrors */) { break; } } return result; }, ledger_js_1.processErrorResponse); } async sendChunk(chunkIdx, chunkNum, chunk, ins) { let payloadType = ledger_js_1.PAYLOAD_TYPE.ADD; const p2 = 0; if (chunkIdx === 1) { payloadType = ledger_js_1.PAYLOAD_TYPE.INIT; } if (chunkIdx === chunkNum) { payloadType = ledger_js_1.PAYLOAD_TYPE.LAST; } return await this.transport .send(this.CLA, ins, payloadType, p2, chunk, [ 36864 /* LedgerError.NoErrors */, 27012 /* LedgerError.DataIsInvalid */, 27264 /* LedgerError.BadKeyHandle */, 28417 /* LedgerError.SignVerifyError */, ]) .then((response) => { const errorCodeData = response.subarray(-2); const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; let errorMessage = (0, ledger_js_1.errorCodeToString)(returnCode); return { returnCode, errorMessage, }; }, ledger_js_1.processErrorResponse); } async sendData(path, data, instruction) { const data_buf = Buffer.from(data, "hex"); const chunks = this.prepareChunks(path, data_buf); return await this.sendChunk(1, chunks.length, chunks[0], instruction).then(async (response) => { let result = { returnCode: response.returnCode, errorMessage: response.errorMessage, }; for (let i = 1; i < chunks.length; i += 1) { // eslint-disable-next-line no-await-in-loop result = await this.sendChunk(1 + i, chunks.length, chunks[i], instruction); if (result.returnCode !== 36864 /* LedgerError.NoErrors */) { break; } } return result; }, ledger_js_1.processErrorResponse); } async sendCertificateAndSig(path, data) { const chunks = this.prepareChunks(path, Buffer.from(data, "hex")); return await this.signSendChunk(1, chunks.length, chunks[0], 0, this.INS.SAVE_CERITIFACE_AND_VERIFY).then(async (response) => { let result = { returnCode: response.returnCode, errorMessage: response.errorMessage, }; for (let i = 1; i < chunks.length; i += 1) { // eslint-disable-next-line no-await-in-loop result = await this.signSendChunk(1 + i, chunks.length, chunks[i], 0, this.INS.SAVE_CERITIFACE_AND_VERIFY); if (result.returnCode !== 36864 /* LedgerError.NoErrors */) { break; } } return result; }, ledger_js_1.processErrorResponse); } async signBls(path, consent_request, canister_call, certificate) { // Check if all strings are not empty if (!consent_request || !canister_call || !certificate) { throw new Error("All parameters must be non-empty strings"); } let result; // Send consent_request result = await this.sendData(path, consent_request, this.INS.SAVE_CONSENT); if (result.returnCode !== 36864 /* LedgerError.NoErrors */) return result; // Send canister_call result = await this.sendData(path, canister_call, this.INS.SAVE_CANISTER_CALL); if (result.returnCode !== 36864 /* LedgerError.NoErrors */) return result; // Send certificate and sign return await this.sendCertificateAndSig(path, certificate); } async _getTokenRegistrySize() { return await this.transport .send(this.CLA, this.INS.GET_REGISTRY_LEN, 0, 0) .then(helper_1.processTokenRegistrySizeResponse, ledger_js_1.processErrorResponse); } async tokenRegistry() { const registrySize = await this._getTokenRegistrySize(); if (registrySize.returnCode !== 36864 /* LedgerError.NoErrors */ || registrySize.RegistrySize === undefined) { return { returnCode: registrySize.returnCode, errorMessage: registrySize.errorMessage, }; } let tokenRegistry = []; for (let i = 0; i < registrySize.RegistrySize; i += 1) { const response = await this.transport .send(this.CLA, this.INS.GET_TOKEN_I, i, 0) .then((response) => (0, helper_1.processTokenInfoResponse)(response), (error) => (0, ledger_js_1.processErrorResponse)(error)); // Type guard to check if response is ResponseTokenInfo if (!response || "tokenInfo" in response === false || response.returnCode !== 36864 /* LedgerError.NoErrors */) { return { returnCode: response?.returnCode || 25600 /* LedgerError.ExecutionError */, errorMessage: response?.errorMessage || "Failed to get token info", }; } // At this point TypeScript knows response has tokenInfo if (response.tokenInfo) { tokenRegistry.push(response.tokenInfo); } } return { returnCode: 36864 /* LedgerError.NoErrors */, errorMessage: "No errors", tokenRegistry: tokenRegistry, }; } } exports.default = InternetComputerApp; //# sourceMappingURL=index.js.map