UNPKG

@thorchain/ledger-thorchain

Version:
251 lines 12.2 kB
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 (_) 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 }; } }; import { CLA, errorCodeToString, INS, LedgerError, LedgerErrorType, PAYLOAD_TYPE, ledgerErrorFromResponse, } from "./common"; import crypto from "crypto"; import Ripemd160 from "ripemd160"; import { bech32 } from "bech32"; export function serializeHRP(hrp) { if (!hrp || hrp.length < 3 || hrp.length > 83) { throw new LedgerError(LedgerErrorType.HPRInvalid); } var buf = Buffer.alloc(1 + hrp.length); buf.writeUInt8(hrp.length, 0); buf.write(hrp, 1); return buf; } export function getBech32FromPK(hrp, pk) { if (pk.length !== 33) { throw new LedgerError(LedgerErrorType.PKInvalidBytes); } var hashSha256 = crypto.createHash("sha256").update(pk).digest(); var hashRip = new Ripemd160().update(hashSha256).digest(); var words = bech32.toWords(hashRip); return bech32.encode(hrp, words); } export function serializePath(path) { if (!path || path.length !== 5) { throw new Error("Invalid path."); } var buf = Buffer.alloc(20); buf.writeUInt32LE(0x80000000 + path[0], 0); buf.writeUInt32LE(0x80000000 + path[1], 4); buf.writeUInt32LE(0x80000000 + path[2], 8); buf.writeUInt32LE(path[3], 12); buf.writeUInt32LE(path[4], 16); return buf; } export function signSendChunk(transport, chunkIdx, chunkNum, chunk) { return __awaiter(this, void 0, void 0, function () { var payloadType, response, errorCodeData, returnCode, errorMessage; return __generator(this, function (_a) { switch (_a.label) { case 0: payloadType = PAYLOAD_TYPE.ADD; if (chunkIdx === 1) { payloadType = PAYLOAD_TYPE.INIT; } if (chunkIdx === chunkNum) { payloadType = PAYLOAD_TYPE.LAST; } return [4 /*yield*/, transport.send(CLA, INS.SIGN_SECP256K1, payloadType, 0, chunk, [ LedgerErrorType.NoErrors, LedgerErrorType.DataIsInvalid, LedgerErrorType.BadKeyHandle, LedgerErrorType.SignVerifyError, ])]; case 1: response = _a.sent(); errorCodeData = response.slice(-2); returnCode = errorCodeData[0] * 256 + errorCodeData[1]; errorMessage = errorCodeToString(returnCode); if (returnCode === LedgerErrorType.BadKeyHandle || returnCode === LedgerErrorType.DataIsInvalid || returnCode === LedgerErrorType.SignVerifyError) { errorMessage = errorMessage + " : " + response.slice(0, response.length - 2).toString("ascii"); throw new LedgerError(returnCode, errorMessage); } if (returnCode === LedgerErrorType.NoErrors && response.length > 2) { return [2 /*return*/, { returnCode: returnCode, errorMessage: errorMessage, signature: response.slice(0, response.length - 2), }]; } return [2 /*return*/, { returnCode: returnCode, errorMessage: errorMessage, }]; } }); }); } export function getVersion(transport) { return __awaiter(this, void 0, void 0, function () { var response, errorCodeData, returnCode, targetId, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, transport.send(CLA, INS.GET_VERSION, 0, 0)]; case 1: response = _a.sent(); errorCodeData = response.slice(-2); returnCode = (errorCodeData[0] * 256 + errorCodeData[1]); targetId = 0; if (response.length >= 9) { /* eslint-disable no-bitwise */ targetId = (response[5] << 24) + (response[6] << 16) + (response[7] << 8) + (response[8] << 0); /* eslint-enable no-bitwise */ } return [2 /*return*/, { returnCode: returnCode, errorMessage: errorCodeToString(returnCode), testMode: response[0] !== 0, major: response[1], minor: response[2], patch: response[3], deviceLocked: response[4] === 1, targetId: targetId.toString(16), }]; case 2: error_1 = _a.sent(); throw ledgerErrorFromResponse(error_1); case 3: return [2 /*return*/]; } }); }); } export function getPublicKey(transport, data) { return __awaiter(this, void 0, void 0, function () { var response, errorCodeData, returnCode, compressedPk, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, transport.send(CLA, INS.GET_ADDR_SECP256K1, 0, 0, data, [ LedgerErrorType.NoErrors, ])]; case 1: response = _a.sent(); errorCodeData = response.slice(-2); returnCode = errorCodeData[0] * 256 + errorCodeData[1]; compressedPk = Buffer.from(response.slice(0, 33)); return [2 /*return*/, { compressedPk: compressedPk, returnCode: returnCode, errorMessage: errorCodeToString(returnCode), }]; case 2: error_2 = _a.sent(); throw ledgerErrorFromResponse(error_2); case 3: return [2 /*return*/]; } }); }); } /** * Takes raw output from Ledger device which is TLV encoded in the format "0x30 L 0x02 Lr r 0x02 Ls s" * where L is length of entire message (after 2nd byte), Lr is length of r and Ls is length of s. * This function extracts r and s, ensures they are 32 bytes each, joins into a single 64 byte Array. * This value returned should be base64 encoded to a string to add to the JSON 'signatures' to send to RPC. * * Example raw sig: * [ 48, 69, 2, 33, * 0, 161, 207, 217, 127, 151, 190, 23, 98, 217, 186, 108, 82, 102, 19, 222, 20, 171, 6, 240, 134, 195, 251, 98, 190, 246, 228, 243, 215, 95, 166, 121, 165, * 2, 32, * 69, 188, 53, 213, 24, 9, 191, 90, 244, 21, 213, 146, 240, 109, 156, 221, 247, 63, 131, 52, 150, 253, 199, 153, 132, 76, 91, 239, 28, 254, 68, 80 ] * * 48 69 -- length is 69 * 2 33 -- length of r is 33 bytes * <33 bytes of r> -- (the leading zero here can be dropped) * 2 32 -- length of s is 32 bytes * <32 bytes of s> */ export function extractSignatureFromTLV(signatureArray) { // Check Type Length Value encoding if (signatureArray.length < 64) { throw Error("Invalid Signature: Too short"); } if (signatureArray[0] !== 0x30) { throw Error("Invalid Ledger Signature TLV encoding: expected first byte 0x30"); } if (signatureArray[1] + 2 !== signatureArray.length) { throw Error("Invalid Signature: signature length does not match TLV"); } if (signatureArray[2] !== 0x02) { throw Error("Invalid Ledger Signature TLV encoding: expected length type 0x02"); } // r signature var rLength = signatureArray[3]; var rSignature = signatureArray.slice(4, rLength + 4); // Drop leading zero on some 'r' signatures that are 33 bytes. if (rSignature.length === 33 && rSignature[0] === 0) { rSignature = rSignature.slice(1, 33); } else if (rSignature.length === 33) { throw Error('Invalid signature: "r" too long'); } // add leading zero's to pad to 32 bytes while (rSignature.length < 32) { Buffer.concat([Buffer.from([0]), rSignature]); } // s signature if (signatureArray[rLength + 4] !== 0x02) { throw Error("Invalid Ledger Signature TLV encoding: expected length type 0x02"); } var sLength = signatureArray[rLength + 5]; if (4 + rLength + 2 + sLength != signatureArray.length) { throw Error("Invalid Ledger Signature: TLV byte lengths do not match message length"); } var sSignature = signatureArray.slice(rLength + 6, signatureArray.length); // Drop leading zero on 's' signatures that are 33 bytes. This shouldn't occur since ledger signs using "Small s" math. But just to be sure... if (sSignature.length === 33 && sSignature[0] === 0) { sSignature = sSignature.slice(1, 33); } else if (sSignature.length === 33) { throw Error('Invalid signature: "s" too long'); } // add leading zero's to pad to 32 bytes while (sSignature.length < 32) { Buffer.concat([Buffer.from([0]), sSignature]); } if (rSignature.length !== 32 || sSignature.length !== 32) { throw Error("Invalid signatures: must be 32 bytes each"); } return Buffer.concat([rSignature, sSignature]); } //# sourceMappingURL=helper.js.map