UNPKG

@xmr-core/xmr-crypto-utils

Version:

Core crypto operations for Monero and implementations and interfaces of various hardware devices for creating Monero transactions securely

870 lines 36.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const commands_1 = require("./commands"); const types_1 = require("../types"); const crypto = __importStar(require("../../crypto-ops")); const xmr_str_utils_1 = require("@xmr-core/xmr-str-utils"); var RCT; (function (RCT) { RCT[RCT["RCTTypeNull"] = 0] = "RCTTypeNull"; RCT[RCT["RCTTypeFull"] = 1] = "RCTTypeFull"; RCT[RCT["RCTTypeSimple"] = 2] = "RCTTypeSimple"; RCT[RCT["RCTTypeFullBulletproof"] = 3] = "RCTTypeFullBulletproof"; RCT[RCT["RCTTypeSimpleBulletproof"] = 4] = "RCTTypeSimpleBulletproof"; })(RCT || (RCT = {})); class Keymap { constructor() { this.map = {}; } add(keys) { if (this.map[keys.Pout]) { throw Error(`Cannot add key to map, Pout: ${keys.Pout} already exists`); } this.map[keys.Pout] = keys; } find(Pout) { return this.map[Pout]; } clear() { this.map = {}; } } const ledgerLog = (fnname, obj, extra) => xmr_str_utils_1.JSONPrettyPrint(`LedgerDevice ${fnname}`, obj, extra); // tslint:disable-next-line:max-classes-per-file class LedgerDevice { constructor(transport) { this.null_skey = this.hexString(); this.key_map = new Keymap(); this.transport = transport; this.name = ""; this.mode = types_1.DeviceMode.NONE; this.has_view_key = false; this.privateViewKey = this.hexString(); transport.decorateAppAPIMethods(this, [ "set_mode", "put_key", "get_public_address", "get_secret_keys", "derive_subaddress_public_key", "get_subaddress_spend_public_key", "get_subaddress", "get_subaddress_secret_key", "verify_keys", "scalarmultKey", "scalarmultBase", "sc_secret_add", "generate_keys", "generate_key_derivation", "derivation_to_scalar", "derive_secret_key", "derive_public_key", "secret_key_to_public_key", "generate_key_image", "open_tx", "encrypt_payment_id", "ecdhEncode", "ecdhDecode", "mlsag_prehash", "mlsag_prepare", "mlsag_hash", "mlsag_sign", "close_tx", ], "MOON"); transport.setScrambleKey("MOON"); } /* ======================================================================= */ /* MISC */ /* ======================================================================= */ // #region MISC reset() { return this.send(commands_1.INS.RESET, 0x00, 0x00); } // #endregion MISC /* ======================================================================= */ /* SETUP/TEARDOWN */ /* ======================================================================= */ // #region SETUP/TEARDOWN set_name(name) { this.name = name; return true; } get_name() { return this.name; } set_mode(mode) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("set_mode", { mode }, "args"); switch (mode) { case types_1.DeviceMode.TRANSACTION_CREATE_REAL: case types_1.DeviceMode.TRANSACTION_CREATE_FAKE: yield this.send(commands_1.INS.SET_SIGNATURE_MODE, 0x01, 0x00, [ 0x00, mode, ]); this.mode = mode; break; case types_1.DeviceMode.TRANSACTION_PARSE: case types_1.DeviceMode.NONE: this.mode = mode; break; default: throw Error(`device_ledger::set_mode(unsigned int mode): invalid mode ${mode}`); } console.log(`Switched to mode: ${mode}`); return true; }); } // #endregion SETUP/TEARDOWN /* ======================================================================= */ /* WALLET & ADDRESS */ /* ======================================================================= */ // #region WALLET & ADDRESS put_key(privViewKey, pubViewKey, privSpendKey, pubSpendKey, _b58PubKey) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("put_key", { privViewKey, pubViewKey, privSpendKey, pubSpendKey, _b58PubKey, }, "args"); yield this.send(commands_1.INS.PUT_KEY, 0x00, 0x00, [ 0x00, privViewKey, pubViewKey, privSpendKey, pubSpendKey, // dummy pub key just to bypass length verification // isnt used in blue-app-monero code for anything else "bc3b105abc2f939571e5b107ab58dc4f6ea22923d8189be54a47d107d187d901bc3b105abc2f939571e5b107ab58dc4f6ea22923d8189be54a47d107d187d901bc3b105abc2f939571e5b107ab58dc4f6ea22923d8189be54a47d107d187", ]); return true; }); } get_public_address() { return __awaiter(this, void 0, void 0, function* () { ledgerLog("get_public_address", { command: "get_public_address" }); const [view_public_key, spend_public_key] = yield this.send(commands_1.INS.GET_KEY, 0x01, 0x00, [0x00], [32, 64]); ledgerLog("get_public_address", { view_public_key, spend_public_key, }, "ret"); return { view_public_key, spend_public_key, }; }); } /** * * @description Retrives the secret view key if the user allows the export * Skips retriving the view key if already previously exported * @returns Fake view and send private key * @memberof XMR */ get_secret_keys() { return __awaiter(this, void 0, void 0, function* () { // secret keys are represented as fake keys on the wallet side // because secret keys are always in possesion of the ledger device const vkey = this.hexString(); const skey = this.hexString(0xff); ledgerLog("get_secret_keys", { command: "get_secret_keys" }); if (this.is_fake_view_key(this.privateViewKey)) { const [viewKey] = yield this.send(commands_1.INS.GET_KEY, 0x02, 0x00, [0x00], [32]); this.privateViewKey = viewKey; } this.has_view_key = !this.is_fake_view_key(this.privateViewKey); ledgerLog("get_secret_keys", { viewKey: vkey, spendKey: skey }, "ret"); return { viewKey: vkey, spendKey: skey }; }); } export_private_view_key() { return __awaiter(this, void 0, void 0, function* () { ledgerLog("export_private_view_key", { command: "export_private_view_key", }); if (this.is_fake_view_key(this.privateViewKey)) { yield this.get_secret_keys(); } ledgerLog("export_private_view_key", { viewKey: this.privateViewKey }, "ret"); return this.privateViewKey; }); } generate_chacha_key(_keys) { return __awaiter(this, void 0, void 0, function* () { const [] = yield this.send(commands_1.INS.GET_CHACHA8_PREKEY, 0x00, 0x00, [0x00], [200]); return this.notSupported(); }); } // #endregion WALLET & ADDRESS /* ======================================================================= */ /* SUB ADDRESS */ /* ======================================================================= */ // #region SUB ADDRESS /** * * @param {PublicKey} pub * @param {KeyDerivation} derivation * @param {number} output_index * @returns {Promise<PublicKey>} * @memberof XMR */ derive_subaddress_public_key(pub, derivation, output_index) { return __awaiter(this, void 0, void 0, function* () { if (this.mode === types_1.DeviceMode.TRANSACTION_PARSE && this.has_view_key) { //If we are in TRANSACTION_PARSE, the given derivation has been retrieved decrypted (without the help //of the device), so continue that way. return this.notSupported(); // return this.extern.derive_subaddress_public_key( // pub, // derivation, // output_index, // ); } else { const [derived_pub] = yield this.send(commands_1.INS.DERIVE_SUBADDRESS_PUBLIC_KEY, 0x00, 0x00, [ 0x00, pub, derivation, output_index >> 24, output_index >> 16, output_index >> 8, output_index >> 0, ], [32]); return derived_pub; } }); } get_subaddress_spend_public_key(keys, index) { return __awaiter(this, void 0, void 0, function* () { if (index.isZero()) { return keys.m_account_address.spend_public_key; } // decompress index, taking 4+4 bytes const [D] = yield this.send(commands_1.INS.GET_SUBADDRESS_SPEND_PUBLIC_KEY, 0x00, 0x00, [0x00, index.major, index.minor], [32]); return D; }); } get_subaddress_spend_public_keys(keys, account, begin, end) { return __awaiter(this, void 0, void 0, function* () { const pkeys = []; for (let index = begin; index < end; index++) { pkeys.push(yield this.get_subaddress_spend_public_key(keys, { major: account, minor: index, isZero: () => false, })); } return pkeys; }); } get_subaddress(keys, index) { return __awaiter(this, void 0, void 0, function* () { if (index.isZero()) { return keys.m_account_address; } const [view_public_key, spend_public_key] = yield this.send(commands_1.INS.GET_SUBADDRESS, 0x00, 0x00, [0x00, index.major, index.minor], [32, 64]); return { view_public_key, spend_public_key }; }); } get_subaddress_secret_key(sec, index) { return __awaiter(this, void 0, void 0, function* () { const [sub_sec] = yield this.send(commands_1.INS.GET_SUBADDRESS_SECRET_KEY, 0x00, 0x00, [0x00, sec, index.major, index.minor], [32]); return sub_sec; }); } // #endregion SUB ADDRESS /* ======================================================================= */ /* DERIVATION & KEY */ /* ======================================================================= */ // #region DERIVATION & KEY verify_keys(secret_key, public_key) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("verify_keys", { secret_key, public_key }, "args"); const verifyArr = yield this.send(commands_1.INS.VERIFY_KEY, 0x00, 0x00, [0x00, secret_key, public_key], [1, 2, 3, 4]).then(arr => arr.map(str => parseInt(str, 16))); // TODO: support full 32 bit return value in the future // for any verification return value changes // but for now, we just need to check the last 4 bytes // to see if the last bit is 1 or not // const verified = // (verifyArr[0] << 24) | // (verifyArr[1] << 16) | // (verifyArr[2] << 8) | // (verifyArr[3] << 0); const verified = verifyArr[3]; ledgerLog("verify_keys", { verified: verified === 1 }, "ret"); return verified === 1; }); } scalarmultKey(P, a) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("scalarmultKey", { P, a }, "args"); const [aP] = yield this.send(commands_1.INS.SECRET_SCAL_MUL_KEY, 0x00, 0x00, [0x00, P, a], [32]); ledgerLog("scalarmultKey", { aP }, "ret"); return aP; }); } scalarmultBase(a) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("scalarmultBase", { a }, "args"); const [aG] = yield this.send(commands_1.INS.SECRET_SCAL_MUL_BASE, 0x00, 0x00, [0x00, a], [32]); ledgerLog("scalarmultBase", { aG }, "args"); return aG; }); } sc_secret_add(a, b) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("sc_secret_add", { a, b }, "args"); const [r] = yield this.send(commands_1.INS.SECRET_KEY_ADD, 0x00, 0x00, [0x00, a, b], [32]); ledgerLog("sc_secret_add", { r }, "args"); return r; }); } generate_keys(recovery_key) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("generate_keys", { command: "generate_keys" }); if (recovery_key) { throw Error("Ledger device method generate_keys does not support recover"); } const [pub, sec] = yield this.send(commands_1.INS.GENERATE_KEYPAIR, 0x00, 0x00, [0x00], [32, 64]); ledgerLog("generate_keys", { pub, sec }, "ret"); return { pub, sec }; }); } generate_key_derivation(pub, sec) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("generate_key_derivation", { pub, sec }, "args"); if (this.mode === types_1.DeviceMode.TRANSACTION_PARSE && this.has_view_key) { // When a derivation is requested in PARSE mode and the view key is available, // Perform the derivation via extern library and return the derivation unencrypted console.log("generate_key_derivation : PARSE mode with known viewkey"); //Note derivation in PARSE mode can only happen with viewkey, so assert it! (?) if (this.is_fake_view_key(this.privateViewKey)) { throw Error("Derivation in PARSE mode can only happen with viewkey"); } const derivation = crypto.derivation.generate_key_derivation(pub, this.privateViewKey); return derivation; } else { const [derivation] = yield this.send(commands_1.INS.GEN_KEY_DERIVATION, 0x00, 0x00, [0x00, pub, sec], [32]); ledgerLog("generate_key_derivation", { derivation }, "ret"); return derivation; } }); } conceal_derivation(derivation, tx_pub_key, additional_tx_pub_keys, main_derivation, additional_derivations) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("conceal_derivation", { derivation, tx_pub_key, additional_tx_pub_keys, main_derivation, additional_derivations, }, "args"); let pubKey; if (derivation === main_derivation) { pubKey = tx_pub_key; console.log("conceal derivation with main tx pub key"); } else { console.warn("conceal_derivation NOTE: if size of additional_derivations > Number.MAX_INTEGER, then we have a problem"); const derivationIdx = additional_derivations.indexOf(derivation); if (derivationIdx !== -1) { pubKey = additional_tx_pub_keys[derivationIdx]; } console.log("conceal derivation with additional tx pub key"); } if (pubKey === undefined) { throw Error("Mismatched derivation on scan info"); } ledgerLog("conceal_derivation", { derivation: this.generate_key_derivation(pubKey, this.null_skey), }, "ret"); return this.generate_key_derivation(pubKey, this.null_skey); }); } derivation_to_scalar(derivation, output_index) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("derivation_to_scalar", { derivation, output_index, }, "args"); const [scalar] = yield this.send(commands_1.INS.DERIVATION_TO_SCALAR, 0x00, 0x00, [ 0x00, derivation, output_index >> 24, output_index >> 16, output_index >> 8, output_index >> 0, ], [32]); ledgerLog("derivation_to_scalar", { scalar, }, "ret"); return scalar; }); } derive_secret_key(derivation, output_index, sec) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("derive_secret_key", { derivation, output_index, sec, }, "args"); const [derivedSec] = yield this.send(commands_1.INS.DERIVE_SECRET_KEY, 0x00, 0x00, [ 0x00, derivation, output_index >> 24, output_index >> 16, output_index >> 8, output_index >> 0, sec, ], [32]); ledgerLog("derive_secret_key", { derivedSec, }, "args"); return derivedSec; }); } derive_public_key(derivation, output_index, pub) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("derive_public_key", { derivation, output_index, pub, }, "args"); const [derived_pub] = yield this.send(commands_1.INS.DERIVE_PUBLIC_KEY, 0x00, 0x00, [ 0x00, derivation, output_index >> 24, output_index >> 16, output_index >> 8, output_index >> 0, pub, ], [32]); ledgerLog("derive_public_key", { derived_pub, }, "ret"); return derived_pub; }); } secret_key_to_public_key(sec) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("secret_key_to_public_key", { sec, }, "args"); const [pub] = yield this.send(commands_1.INS.SECRET_KEY_TO_PUBLIC_KEY, 0x00, 0x00, [0x00, sec], [32]); ledgerLog("secret_key_to_public_key", { pub, }, "ret"); return pub; }); } generate_key_image(pub, sec) { return __awaiter(this, void 0, void 0, function* () { const [ki] = yield this.send(commands_1.INS.GEN_KEY_IMAGE, 0x00, 0x00, [0x00, pub, sec], [32]); return ki; }); } // #endregion DERIVATION & KEY /* ======================================================================= */ /* TRANSACTION */ /* ======================================================================= */ // #region TRANSACTION open_tx() { return __awaiter(this, void 0, void 0, function* () { ledgerLog("open_tx", { command: "open_tx", }); const options = 0x00; const account = [0x00, 0x00, 0x00, 0x00]; // skip over R and grab encrypted r instead const [, enc_r] = yield this.send(commands_1.INS.OPEN_TX, 0x01, 0x00, [options, ...account], [32, 64]); const sec_tx_key = enc_r; ledgerLog("open_tx", { sec_tx_key, }, "ret"); return sec_tx_key; }); } encrypt_payment_id(paymentId, public_key, secret_key) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("encrypt_payment_id", { paymentId, public_key, secret_key, }, "args"); const [enc_pid] = yield this.send(commands_1.INS.STEALTH, 0x00, 0x00, [0x00, public_key, secret_key, paymentId], [8]); ledgerLog("encrypt_payment_id", { enc_pid, }, "ret"); return enc_pid; }); } decrypt_payment_id(paymentId, public_key, secret_key) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("decrypt_payment_id", { paymentId, public_key, secret_key, }, "args"); return yield this.encrypt_payment_id(paymentId, public_key, secret_key); }); } /** * @description store keys during construct_tx_with_tx_key to be later used during genRct -> mlsag_prehash * @param {PublicKey} Aout * @param {PublicKey} Bout * @param {boolean} is_subaddress * @param {number} real_output_index * @param {Key} amount_key * @param {PublicKey} out_eph_public_key * @returns {Promise<boolean>} */ add_output_key_mapping(Aout, Bout, is_subaddress, real_output_index, amount_key, out_eph_public_key) { ledgerLog("add_output_key_mapping", { Aout: Aout, Bout: Bout, is_subaddress, index: real_output_index, Pout: out_eph_public_key, AKout: amount_key, }, "args"); this.key_map.add({ Aout: Aout, Bout: Bout, is_subaddress, index: real_output_index, Pout: out_eph_public_key, AKout: amount_key, }); return true; } ecdhEncode(unmasked, AKout) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("ecdhEncode", { unmasked, AKout, }, "args"); // AKout -> Amount key for output // AKout = encrypted private derivation data computed during the processing of output transaction keys // derivation data = generate_key_derivation(Kv (recipent view public key), r (tx_key) ) = r.Kv // scalar = hash_to_scalar(Kv.r) // AKout = rct::sk2rct(Hn(rKv)) where rct::sk2rct just typecasts type crypto::secret_key to rct::key const [blindAmount, blindMask] = yield this.send(commands_1.INS.BLIND, 0x00, 0x00, [0x00, AKout, unmasked.mask, unmasked.amount], [32, 64]); ledgerLog("ecdhEncode", { blindAmount, blindMask, }, "ret"); return { amount: blindAmount, mask: blindMask }; }); } ecdhDecode(masked, AKout) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("ecdhDecode", { masked, AKout, }, "args"); const [unmaskedAmount, unmaskedMask] = yield this.send(commands_1.INS.UNBLIND, 0x00, 0x00, [0x00, AKout, masked.mask, masked.amount], [32, 64]); ledgerLog("ecdhDecode", { unmaskedAmount, unmaskedMask, }, "ret"); return { amount: unmaskedAmount, mask: unmaskedMask }; }); } mlsag_prehash(blob, inputs_size, // 64 bits outputs_size, // 64 bits hashes, outPk) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("mlsag_prehash", { blob, inputs_size, outputs_size, hashes, outPk, }, "args"); const data = Buffer.from(blob, "hex"); const options = inputs_size === 0 ? 0x00 : 0x80; const type = data[0]; const txnFee = []; let data_offset = 1; while (data[data_offset] & 0x80) { txnFee.push(data[data_offset]); data_offset += 1; } ledgerLog("mlsag_prehash", {}, "INS.VALIDATE p2 === 1"); // monero_apdu_mlsag_prehash_init p2 === 1 yield this.send(commands_1.INS.VALIDATE, 0x01, 0x01, [ options, type, ...txnFee, data[data_offset], ]); data_offset += 1; // monero_apdu_mlsag_prehash_init p2 > 1 // pseudoOuts if (type === RCT.RCTTypeSimple || type === RCT.RCTTypeSimpleBulletproof) { for (let i = 0; i < inputs_size; i++) { const p1 = 0x01; const p2 = i + 0x02; const opts = i === inputs_size - 1 ? 0x00 : 0x80; // slice 32 bytes starting from data_offset const pseudoOut = data .slice(data_offset, data_offset + 32) .toString("hex"); ledgerLog("mlsag_prehash", {}, `INS.VALIDATE p2 === ${p2}`); yield this.send(commands_1.INS.VALIDATE, p1, p2, [opts, pseudoOut]); data_offset += 32; } } // ====== Aout, Bout, AKout, C, v, k ====== // where k is the mask // and v is the amount // Aout, Bout is the receiver main view/spend public keys // keccak: 2nd group generator, such H = h.G and keccak is unknown // C is the commitment to v where Cv = k.G + v.H // monero_apdu_mlsag_prehash_update let kv_offset = data_offset; let C_offset = kv_offset + 32 * 2 * outputs_size; for (let i = 0; i < outputs_size; i++) { const outKeys = this.key_map.find(outPk[i].dest); if (!outKeys) { throw Error(`Pout not found: ${outPk[i].dest} `); } const p1 = 0x02; const p2 = i + 0x01; const opts = i === outputs_size - 1 ? 0x00 : 0x80; const data_buf = [ opts, outKeys.is_subaddress, outKeys.Aout, outKeys.Bout, outKeys.AKout, ]; // C data_buf.push(data.slice(C_offset, C_offset + 32).toString("hex")); C_offset += 32; // k data_buf.push(data.slice(kv_offset, kv_offset + 32).toString("hex")); kv_offset += 32; //v data_buf.push(data.slice(kv_offset, kv_offset + 32).toString("hex")); kv_offset += 32; ledgerLog("mlsag_prehash", { outKeys, p1, p2, opts, data_buf, C_offset, kv_offset, }, `INS.VALIDATE p2 === ${p2}`); yield this.send(commands_1.INS.VALIDATE, p1, p2, data_buf); } // ====== C[], message, proof====== let _i = 0; C_offset = kv_offset; for (_i = 0; _i < outputs_size; _i++) { const p1 = 0x03; const p2 = _i + 0x01; const opts = 0x80; // C const C = data.slice(C_offset, C_offset + 32).toString("hex"); C_offset += 32; ledgerLog("mlsag_prehash", { p1, p2, opts, C, C_offset, kv_offset, }, `INS.VALIDATE p2 === ${p2}`); yield this.send(commands_1.INS.VALIDATE, p1, p2, [opts, C]); } const [prehash] = yield this.send(commands_1.INS.VALIDATE, 0x03, _i + 0x01, [0x00, hashes[0], hashes[2]], [32]); ledgerLog("mlsag_prehash", { prehash }, "ret"); return prehash; }); } mlsag_prepare(H, xx) { return __awaiter(this, void 0, void 0, function* () { if (!H || !xx) { const [a, aG] = yield this.send(commands_1.INS.MLSAG, 0x01, 0x00, [0x00], [32, 64]); return { a, aG }; } else { ledgerLog("mlsag_prepare", { H, xx }, "args"); // a -> alpha -> one time secret key for tx // aG -> alpha.G -> one time public key for tx const [a, aG, aHP, II] = yield this.send(commands_1.INS.MLSAG, 0x01, 0x00, [0x00, H, xx], [32, 64, 96, 128]); ledgerLog("mlsag_prepare", { a, aG, aHP, II }, "ret"); return { a, aG, aHP, II }; } }); } mlsag_hash(long_message) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("mlsag_hash", { long_message }, "args"); // cnt is size_t const cnt = long_message.length; let res = ""; for (let i = 0; i < cnt; i++) { [res] = yield this.send(commands_1.INS.MLSAG, 0x02, i + 0x01, [i === cnt - 1 ? 0x00 : 0x80, long_message[i]], [32]); ledgerLog("mlsag_hash", { res }, `iteration ${i}`); } if (!res) { throw Error("Return value of last exchange is empty string"); } ledgerLog("mlsag_hash", { res }, `ret`); return res; }); } mlsag_sign(c, xx, alpha, rows, dsRows, ss) { return __awaiter(this, void 0, void 0, function* () { ledgerLog("mlsag_sign", { c, xx, alpha, rows, dsRows, ss, }, `args`); if (dsRows >= rows) { throw Error("dsRows greater than rows"); } if (xx.length !== rows) { throw Error("xx size does not match rows"); } if (alpha.length !== rows) { throw Error("alpha size does not match rows"); } for (let j = 0; j < dsRows; j++) { // ss[j] const [res] = yield this.send(commands_1.INS.MLSAG, 0x03, j + 1, [j === dsRows - 1 ? 0x80 : 0x00, xx[j], alpha[j]], [32]); ss[j] = res; ledgerLog("mlsag_sign", { "ss[j]": ss[j], ss, }, `iteration ${j}`); } for (let j = dsRows; j < rows; j++) { // sc_mulsub(const unsigned char *a, const unsigned char *b, const unsigned char *c) -> unsigned char *s // c - a.b mod l ss[j] = crypto.primitive_ops.sc_mulsub(c, xx[j], alpha[j]); ledgerLog("mlsag_sign", { "ss[j]": ss[j], ss }, `sc_mulsub j:${j}`); } ledgerLog("mlsag_sign", { ss }, "ret"); return ss; }); } close_tx() { return __awaiter(this, void 0, void 0, function* () { ledgerLog("close_tx", { command: "close_tx" }); yield this.send(commands_1.INS.CLOSE_TX, 0x00, 0x00, [0x00]); return true; }); } // #endregion TRANSACTION // #region Internal private methods is_fake_view_key(viewKey) { return viewKey === this.hexString(); } /** * @description Create a hex string by filling a array with the supplied value * and then converting it to a byte buffer, then to a string * @private * @param {number} [byteValue=0x00] * @param {number} [length=32] * @returns * @memberof XMR */ hexString(byteValue = 0x00, length = 32) { return Buffer.alloc(length, byteValue, "hex").toString("hex"); } /** * * @description Generates hex string slices from a buffer * @private * @param {Buffer} buffer to buffer to slice and convert into hex strings * @param {number[]} endingIndicesToSliceAt An array of ending indices to slice at * * Ex. If [32,64] is supplied, the following slices will be returned: * * [buffer.slice(0,32).toString("hex"), buffer.slice(32,64).toString("hex")] * @memberof XMR */ bufferToSlicedHexString(buffer, endingIndicesToSliceAt) { function sliceBufToHex(buf, start, end) { // initialize a buffer of required size // so we dont slice out of bounds if returned bytes // is less than slice size const zeroBuf = Buffer.alloc(end); // copy data into zero buffer buf.copy(zeroBuf); // slice const slice = zeroBuf.slice(start, end).toString("hex"); return slice; } const res = endingIndicesToSliceAt.reduce((prev, currEndSliceIdx, idx, slicingIndices) => { return [ ...prev, sliceBufToHex(buffer, !idx ? 0 : slicingIndices[idx - 1], currEndSliceIdx), ]; }, []); return res; } arrLikeToBuf(arrLike) { return Array.isArray(arrLike) ? arrLike.reduce((accu, curr) => typeof curr === "string" ? Buffer.concat([accu, Buffer.from(curr, "hex")]) : Buffer.concat([accu, Buffer.from([curr])]), Buffer.alloc(0)) : arrLike; } // #endregion Internal private methods send(ins, p1, p2, data, endingIndicesToSliceAt) { return __awaiter(this, void 0, void 0, function* () { const serializedData = data ? this.arrLikeToBuf(data) : undefined; ledgerLog("send", { ins, p1, p2, data, serializedDataHex: serializedData ? serializedData.toString("hex") : undefined, endingIndicesToSliceAt, }, "before_send_to_ledger"); const buf = yield this.transport.send(0x00, ins, p1, p2, serializedData); if (!endingIndicesToSliceAt) { return; } else { ledgerLog("send", { bufHex: buf.toString("hex") }, "returned buffer converted to hex string"); const res = this.bufferToSlicedHexString(buf, endingIndicesToSliceAt); ledgerLog("send", { res }, "ret"); return res; } }); } notSupported() { throw Error("This device function is not supported"); } } exports.LedgerDevice = LedgerDevice; //# sourceMappingURL=device-ledger.js.map