UNPKG

@zondax/ledger-cosmos-js

Version:

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

202 lines 9.93 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const ripemd160_1 = require("@noble/hashes/ripemd160"); const sha256_1 = require("@noble/hashes/sha256"); const base_1 = require("@scure/base"); const ledger_js_1 = __importStar(require("@zondax/ledger-js")); const byteStream_1 = require("@zondax/ledger-js/dist/byteStream"); const consts_1 = require("./consts"); var CosmosCostumError; (function (CosmosCostumError) { CosmosCostumError[CosmosCostumError["ErrorTransactionDataExceedsBufferCapacity"] = 27016] = "ErrorTransactionDataExceedsBufferCapacity"; CosmosCostumError[CosmosCostumError["ErrorInvalidHDPathValue"] = 27017] = "ErrorInvalidHDPathValue"; CosmosCostumError[CosmosCostumError["ErrorHrpWrongLength"] = 27018] = "ErrorHrpWrongLength"; CosmosCostumError[CosmosCostumError["ErrorInvalidHDPathCoinValue"] = 27019] = "ErrorInvalidHDPathCoinValue"; CosmosCostumError[CosmosCostumError["ErrorChainConfigNotSupported"] = 27020] = "ErrorChainConfigNotSupported"; })(CosmosCostumError || (CosmosCostumError = {})); const COSMOS_CUSTOM_ERROR_DESCRIPTIONS = { ...ledger_js_1.ERROR_DESCRIPTION_OVERRIDE, [CosmosCostumError.ErrorTransactionDataExceedsBufferCapacity]: "Transaction data exceeds the device's internal buffer capacity", [CosmosCostumError.ErrorInvalidHDPathValue]: 'Invalid HD Path Value. Expert Mode required.', [CosmosCostumError.ErrorHrpWrongLength]: 'Invalid HRP length', [CosmosCostumError.ErrorInvalidHDPathCoinValue]: 'Invalid HD Path Coin Value', [CosmosCostumError.ErrorChainConfigNotSupported]: 'Chain config not supported', }; class CosmosApp extends ledger_js_1.default { /** * Constructs a new CosmosApp instance. * @param transport - The transport instance. * @throws {Error} - If the transport is not defined. */ constructor(transport) { super(transport, CosmosApp._params); if (!this.transport) { throw new Error('Transport has not been defined'); } } serializeHRP(hrp) { if (!hrp || hrp.length < 3 || hrp.length > 83) { throw new Error('Invalid HRP'); } const buf = new byteStream_1.ByteStream(); buf.appendUint8(hrp.length); buf.appendBytes(Buffer.from(hrp)); return buf.getCompleteBuffer(); } serializeCosmosPath(path) { if (typeof path !== 'string') { throw new ledger_js_1.ResponseError(ledger_js_1.LedgerError.GenericError, "Path should be a string (e.g \"m/44'/461'/5'/0/3\")"); } if (!path.startsWith('m/')) { throw new ledger_js_1.ResponseError(ledger_js_1.LedgerError.GenericError, 'Path should start with "m/" (e.g "m/44\'/461\'/5\'/0/3")'); } const pathArray = path.split('/').slice(1); if (this.REQUIRED_PATH_LENGTHS && this.REQUIRED_PATH_LENGTHS.length > 0 && !this.REQUIRED_PATH_LENGTHS.includes(pathArray.length)) { throw new ledger_js_1.ResponseError(ledger_js_1.LedgerError.GenericError, "Invalid path length. (e.g \"m/44'/5757'/5'/0/3\")"); } const buf = new byteStream_1.ByteStream(); pathArray.forEach((child) => { let value = 0; if (child.endsWith("'")) { value += ledger_js_1.HARDENED; child = child.slice(0, -1); } const numChild = Number(child); if (Number.isNaN(numChild)) { throw new ledger_js_1.ResponseError(ledger_js_1.LedgerError.GenericError, `Invalid path : ${child} is not a number. (e.g "m/44'/461'/5'/0/3")`); } value += numChild; buf.appendUint32(value); }); return buf.getCompleteBuffer(); } async publicKey(path) { const serializedPath = this.serializeCosmosPath(path); const data = Buffer.concat([this.serializeHRP('cosmos'), serializedPath]); try { const responseBuffer = await this.transport.send(this.CLA, this.INS.GET_ADDR_SECP256K1, 0, 0, data); const response = (0, ledger_js_1.processResponse)(responseBuffer); const compressed_pk = Buffer.from(response.readBytes(consts_1.PKLEN)); const bech32_address = response.readBytes(response.length()).toString(); return { compressed_pk, bech32_address }; } catch (e) { throw (0, ledger_js_1.processErrorResponse)(e, COSMOS_CUSTOM_ERROR_DESCRIPTIONS); } } async getAddressAndPubKey(path, hrp) { const serializedPath = this.serializeCosmosPath(path); const data = Buffer.concat([this.serializeHRP(hrp), serializedPath]); try { const responseBuffer = await this.transport.send(this.CLA, this.INS.GET_ADDR_SECP256K1, 0 /* P1_VALUES.ONLY_RETRIEVE */, 0, data); const response = (0, ledger_js_1.processResponse)(responseBuffer); const compressed_pk = response.readBytes(consts_1.PKLEN); const bech32_address = response.readBytes(response.length()).toString(); return { compressed_pk, bech32_address }; } catch (e) { throw (0, ledger_js_1.processErrorResponse)(e, COSMOS_CUSTOM_ERROR_DESCRIPTIONS); } } async showAddressAndPubKey(path, hrp) { const serializedPath = this.serializeCosmosPath(path); const data = Buffer.concat([this.serializeHRP(hrp), serializedPath]); try { const responseBuffer = await this.transport.send(this.CLA, this.INS.GET_ADDR_SECP256K1, 1 /* P1_VALUES.SHOW_ADDRESS_IN_DEVICE */, 0, data); const response = (0, ledger_js_1.processResponse)(responseBuffer); const compressed_pk = response.readBytes(consts_1.PKLEN); const bech32_address = response.readBytes(response.length()).toString(); return { compressed_pk, bech32_address }; } catch (e) { throw (0, ledger_js_1.processErrorResponse)(e, COSMOS_CUSTOM_ERROR_DESCRIPTIONS); } } static getBech32FromPK(hrp, pk) { if (pk.length !== 33) { throw new Error('expected compressed public key [31 bytes]'); } const hashSha256 = (0, sha256_1.sha256)(pk); const hashRip = (0, ripemd160_1.ripemd160)(hashSha256); return base_1.bech32.encode(hrp, base_1.bech32.toWords(hashRip)); } async prepareChunks_hrp(path, buffer, hrp) { const serializedPath = this.serializeCosmosPath(path); const firstChunk = hrp ? Buffer.concat([serializedPath, this.serializeHRP(hrp)]) : serializedPath; const chunks = [firstChunk]; for (let i = 0; i < buffer.length; i += this.CHUNK_SIZE) { const end = Math.min(i + this.CHUNK_SIZE, buffer.length); chunks.push(buffer.subarray(i, end)); } return chunks; } async signSendCosmosChunk(chunkIdx, chunkNum, chunk, txtype = 0 /* P2_VALUES.JSON */) { const payloadType = chunkIdx === 1 ? ledger_js_1.PAYLOAD_TYPE.INIT : chunkIdx === chunkNum ? ledger_js_1.PAYLOAD_TYPE.LAST : ledger_js_1.PAYLOAD_TYPE.ADD; const statusList = [ledger_js_1.LedgerError.NoErrors, ledger_js_1.LedgerError.DataIsInvalid, ledger_js_1.LedgerError.BadKeyHandle]; const responseBuffer = await this.transport.send(this.CLA, this.INS.SIGN_SECP256K1, payloadType, txtype, chunk, statusList); return (0, ledger_js_1.processResponse)(responseBuffer); } async signImpl(path, buffer, txtype, hrp) { const chunks = await this.prepareChunks_hrp(path, buffer, hrp); try { let result = await this.signSendCosmosChunk(1, chunks.length, chunks[0], txtype); for (let i = 1; i < chunks.length; i++) { result = await this.signSendCosmosChunk(i + 1, chunks.length, chunks[i], txtype); } return { signature: result.readBytes(result.length()) }; } catch (e) { throw (0, ledger_js_1.processErrorResponse)(e, COSMOS_CUSTOM_ERROR_DESCRIPTIONS); } } async sign(path, buffer, hrp, txtype = 0 /* P2_VALUES.JSON */) { return this.signImpl(path, buffer, txtype, hrp); } } CosmosApp._INS = { GET_VERSION: 0x00, SIGN_SECP256K1: 0x02, GET_ADDR_SECP256K1: 0x04, }; CosmosApp._params = { cla: consts_1.CLA, ins: { ...CosmosApp._INS }, p1Values: { ONLY_RETRIEVE: 0x00, SHOW_ADDRESS_IN_DEVICE: 0x01 }, chunkSize: 250, requiredPathLengths: [5], }; exports.default = CosmosApp; //# sourceMappingURL=index.js.map