UNPKG

@taquito/ledger-signer

Version:

Ledger signer provider

208 lines (207 loc) 10.4 kB
"use strict"; /** * @packageDocumentation * @module @taquito/ledger-signer */ 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LedgerSigner = exports.VERSION = exports.HDPathTemplate = exports.InvalidDerivationTypeError = exports.DerivationType = exports.InvalidDerivationPathError = void 0; const utils_1 = require("@taquito/utils"); const utils_2 = require("./utils"); const blake2b_1 = require("@stablelib/blake2b"); const errors_1 = require("./errors"); const core_1 = require("@taquito/core"); var core_2 = require("@taquito/core"); Object.defineProperty(exports, "InvalidDerivationPathError", { enumerable: true, get: function () { return core_2.InvalidDerivationPathError; } }); var DerivationType; (function (DerivationType) { DerivationType[DerivationType["ED25519"] = 0] = "ED25519"; DerivationType[DerivationType["SECP256K1"] = 1] = "SECP256K1"; DerivationType[DerivationType["P256"] = 2] = "P256"; DerivationType[DerivationType["BIP32_ED25519"] = 3] = "BIP32_ED25519"; })(DerivationType || (exports.DerivationType = DerivationType = {})); var errors_2 = require("./errors"); Object.defineProperty(exports, "InvalidDerivationTypeError", { enumerable: true, get: function () { return errors_2.InvalidDerivationTypeError; } }); const HDPathTemplate = (account) => { return `44'/1729'/${account}'/0'`; }; exports.HDPathTemplate = HDPathTemplate; var version_1 = require("./version"); Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return version_1.VERSION; } }); /** * * @description Implementation of the Signer interface that will allow signing operation from a Ledger Nano device * * @param transport A transport instance from LedgerJS libraries depending on the platform used (e.g. Web, Node) * @param path The ledger derivation path (default is "44'/1729'/0'/0'") * @param prompt Whether to prompt the ledger for public key (default is true) * @param derivationType The value which defines the curve to use (DerivationType.ED25519(default), DerivationType.SECP256K1, DerivationType.P256, DerivationType.BIP32_ED25519) * * @example * ``` * import TransportNodeHid from "@ledgerhq/hw-transport-node-hid"; * const transport = await TransportNodeHid.create(); * const ledgerSigner = new LedgerSigner(transport, "44'/1729'/0'/0'", false, DerivationType.ED25519); * ``` * * @example * ``` * import TransportU2F from "@ledgerhq/hw-transport-u2f"; * const transport = await TransportU2F.create(); * const ledgerSigner = new LedgerSigner(transport, "44'/1729'/0'/0'", true, DerivationType.SECP256K1); * ``` * * @example * ``` * import TransportU2F from "@ledgerhq/hw-transport-u2f"; * const transport = await TransportU2F.create(); * const ledgerSigner = new LedgerSigner(transport, "44'/1729'/6'/0'", true, DerivationType.BIP32_ED25519); * ``` */ class LedgerSigner { constructor(transport, path = "44'/1729'/0'/0'", prompt = true, derivationType = DerivationType.ED25519) { this.transport = transport; this.path = path; this.prompt = prompt; this.derivationType = derivationType; // constants for APDU requests (https://github.com/obsidiansystems/ledger-app-tezos/blob/master/APDUs.md) this.CLA = 0x80; // Instruction class (always 0x80) this.INS_GET_PUBLIC_KEY = 0x02; // Instruction code to get the ledger’s internal public key without prompt this.INS_PROMPT_PUBLIC_KEY = 0x03; // Instruction code to get the ledger’s internal public key with prompt this.INS_SIGN = 0x04; // Sign a message with the ledger’s key this.FIRST_MESSAGE_SEQUENCE = 0x00; this.LAST_MESSAGE_SEQUENCE = 0x81; this.OTHER_MESSAGE_SEQUENCE = 0x01; this.transport.setScrambleKey('XTZ'); if (!path.startsWith(`44'/1729'`)) { throw new core_1.InvalidDerivationPathError(path, `${(0, utils_1.invalidDetail)(utils_1.ValidationResult.NO_PREFIX_MATCHED)} expecting prefix "44'/1729'".`); } if (!Object.values(DerivationType).includes(derivationType)) { throw new errors_1.InvalidDerivationTypeError(derivationType.toString()); } } publicKeyHash() { return __awaiter(this, void 0, void 0, function* () { if (!this._publicKeyHash) { yield this.publicKey(); } if (this._publicKeyHash) { return this._publicKeyHash; } throw new errors_1.PublicKeyHashRetrievalError(); }); } publicKey() { return __awaiter(this, void 0, void 0, function* () { if (this._publicKey) { return this._publicKey; } const responseLedger = yield this.getLedgerPublicKey(); const publicKeyLength = responseLedger[0]; const rawPublicKey = responseLedger.slice(1, 1 + publicKeyLength); const compressedPublicKey = (0, utils_2.compressPublicKey)(rawPublicKey, this.derivationType); const prefixes = this.getPrefixes(); const publicKey = (0, utils_1.b58cencode)(compressedPublicKey, prefixes.prefPk); const publicKeyHash = (0, utils_1.b58cencode)((0, blake2b_1.hash)(compressedPublicKey, 20), prefixes.prefPkh); this._publicKey = publicKey; this._publicKeyHash = publicKeyHash; return publicKey; }); } getLedgerPublicKey() { return __awaiter(this, void 0, void 0, function* () { try { let ins = this.INS_PROMPT_PUBLIC_KEY; if (this.prompt === false) { ins = this.INS_GET_PUBLIC_KEY; } const responseLedger = yield this.transport.send(this.CLA, ins, this.FIRST_MESSAGE_SEQUENCE, this.derivationType, (0, utils_2.transformPathToBuffer)(this.path)); return responseLedger; } catch (error) { throw new errors_1.PublicKeyRetrievalError(error); } }); } secretKey() { return __awaiter(this, void 0, void 0, function* () { throw new core_1.ProhibitedActionError('Secret key cannot be exposed'); }); } sign(bytes, watermark) { return __awaiter(this, void 0, void 0, function* () { const watermarkedBytes = (0, utils_2.appendWatermark)(bytes, watermark); const watermarkedBytes2buff = Buffer.from(watermarkedBytes, 'hex'); let messageToSend = []; messageToSend.push((0, utils_2.transformPathToBuffer)(this.path)); messageToSend = (0, utils_2.chunkOperation)(messageToSend, watermarkedBytes2buff); const ledgerResponse = yield this.signWithLedger(messageToSend); let signature; if (this.derivationType === DerivationType.ED25519 || this.derivationType === DerivationType.BIP32_ED25519) { signature = ledgerResponse.slice(0, ledgerResponse.length - 2).toString('hex'); } else { if (!(0, utils_2.validateResponse)(ledgerResponse)) { throw new errors_1.InvalidLedgerResponseError('Invalid signature return by ledger unable to parse the response'); } const idxLengthRVal = 3; // Third element of response is length of r value const rValue = (0, utils_2.extractValue)(idxLengthRVal, ledgerResponse); const idxLengthSVal = rValue.idxValueStart + rValue.length + 1; const sValue = (0, utils_2.extractValue)(idxLengthSVal, ledgerResponse); const signatureBuffer = Buffer.concat([rValue.buffer, sValue.buffer]); signature = signatureBuffer.toString('hex'); } return { bytes, sig: (0, utils_1.b58cencode)(signature, utils_1.prefix[utils_1.Prefix.SIG]), prefixSig: (0, utils_1.b58cencode)(signature, this.getPrefixes().prefSig), sbytes: bytes + signature, }; }); } signWithLedger(message) { return __awaiter(this, void 0, void 0, function* () { // first element of the message represents the path let ledgerResponse = yield this.transport.send(this.CLA, this.INS_SIGN, this.FIRST_MESSAGE_SEQUENCE, this.derivationType, message[0]); for (let i = 1; i < message.length; i++) { const p1 = i === message.length - 1 ? this.LAST_MESSAGE_SEQUENCE : this.OTHER_MESSAGE_SEQUENCE; ledgerResponse = yield this.transport.send(this.CLA, this.INS_SIGN, p1, this.derivationType, message[i]); } return ledgerResponse; }); } getPrefixes() { if (this.derivationType === DerivationType.ED25519 || this.derivationType === DerivationType.BIP32_ED25519) { return { prefPk: utils_1.prefix[utils_1.Prefix.EDPK], prefPkh: utils_1.prefix[utils_1.Prefix.TZ1], prefSig: utils_1.prefix[utils_1.Prefix.EDSIG], }; } else if (this.derivationType === DerivationType.SECP256K1) { return { prefPk: utils_1.prefix[utils_1.Prefix.SPPK], prefPkh: utils_1.prefix[utils_1.Prefix.TZ2], prefSig: utils_1.prefix[utils_1.Prefix.SPSIG], }; } else { return { prefPk: utils_1.prefix[utils_1.Prefix.P2PK], prefPkh: utils_1.prefix[utils_1.Prefix.TZ3], prefSig: utils_1.prefix[utils_1.Prefix.P2SIG], }; } } } exports.LedgerSigner = LedgerSigner;