UNPKG

@hazae41/ledger

Version:

Private and supply-chain hardened Ledger controller for TypeScript

183 lines (179 loc) 8.39 kB
'use strict'; var binary = require('@hazae41/binary'); var bytes = require('@hazae41/bytes'); var cubane = require('@hazae41/cubane'); var cursor = require('@hazae41/cursor'); var paths = require('../binary/paths.cjs'); async function getAppConfigOrThrow(device) { const request = { cla: 0xe0, ins: 0x06, p1: 0x00, p2: 0x00, fragment: new binary.Empty() }; const response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); const arbitraryDataEnabled = Boolean(response[0] & 0x01); const erc20ProvisioningNecessary = Boolean(response[0] & 0x02); const starkEnabled = Boolean(response[0] & 0x04); const starkv2Supported = Boolean(response[0] & 0x08); const version = `${response[1]}.${response[2]}.${response[3]}`; return { arbitraryDataEnabled, erc20ProvisioningNecessary, starkEnabled, starkv2Supported, version }; } /** * Just get the address * @param device * @param path * @returns */ async function getAddressOrThrow(device, path) { const paths$1 = paths.Paths.from(path); const bytes$1 = binary.Writable.writeToBytesOrThrow(paths$1); const request = { cla: 0xe0, ins: 0x02, p1: 0x00, p2: 0x01, fragment: new binary.Opaque(bytes$1) }; const response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); const cursor$1 = new cursor.Cursor(response); const uncompressedPublicKeyLength = cursor$1.readUint8OrThrow(); const uncompressedPublicKey = cursor$1.readAndCopyOrThrow(uncompressedPublicKeyLength); const addressLength = cursor$1.readUint8OrThrow(); const address = `0x${bytes.Bytes.toAscii(cursor$1.readOrThrow(addressLength))}`; const chaincode = cursor$1.readAndCopyOrThrow(32); return { uncompressedPublicKey, address, chaincode }; } /** * Ask the user to verify the address and get it * @param device * @param path * @returns */ async function verifyAndGetAddressOrThrow(device, path) { const paths$1 = paths.Paths.from(path); const bytes$1 = binary.Writable.writeToBytesOrThrow(paths$1); const request = { cla: 0xe0, ins: 0x02, p1: 0x01, p2: 0x01, fragment: new binary.Opaque(bytes$1) }; const response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); const cursor$1 = new cursor.Cursor(response); const uncompressedPublicKeyLength = cursor$1.readUint8OrThrow(); const uncompressedPublicKey = cursor$1.readAndCopyOrThrow(uncompressedPublicKeyLength); const addressLength = cursor$1.readUint8OrThrow(); const address = `0x${bytes.Bytes.toAscii(cursor$1.readOrThrow(addressLength))}`; const chaincode = cursor$1.readAndCopyOrThrow(32); return { uncompressedPublicKey, address, chaincode }; } async function signPersonalMessageOrThrow(device, path, message) { const paths$1 = paths.Paths.from(path); const reader = new cursor.Cursor(message); let response; { const head = paths$1.sizeOrThrow() + 4; const body = Math.min(150 - head, reader.remaining); const chunk = reader.readOrThrow(body); const writer = new cursor.Cursor(new Uint8Array(head + body)); paths$1.writeOrThrow(writer); writer.writeUint32OrThrow(message.length); writer.writeOrThrow(chunk); const request = { cla: 0xe0, ins: 0x08, p1: 0x00, p2: 0x00, fragment: new binary.Opaque(writer.bytes) }; response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); } while (reader.remaining) { const body = Math.min(150, reader.remaining); const chunk = reader.readOrThrow(body); const request = { cla: 0xe0, ins: 0x08, p1: 0x80, p2: 0x00, fragment: new binary.Opaque(chunk) }; response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); } const cursor$1 = new cursor.Cursor(response); const v = cursor$1.readUint8OrThrow() - 27; const r = cursor$1.readAndCopyOrThrow(32); const s = cursor$1.readAndCopyOrThrow(32); return cubane.RsvSignature.create({ r, s, v }); } /** * Get the unprotected part of a legacy replay-protected transaction * @param transaction * @returns */ function readLegacyUnprotectedOrThrow(transaction) { /** * This is not a legacy transaction (EIP-2718) */ if (transaction[0] < 0x80) return undefined; /** * Decode the bytes as RLP */ const rlp = binary.Readable.readFromBytesOrThrow(cubane.Rlp, transaction).intoOrThrow(); if (!Array.isArray(rlp)) throw new Error(`Wrong RLP type for transaction`); /** * This is not a replay-protected transaction (EIP-155) */ if (rlp.length !== 9) return undefined; /** * Take only the first 6 parameters instead of the 9 */ const [nonce, gasprice, startgas, to, value, data] = rlp; /** * Encode them as RLP */ return binary.Writable.writeToBytesOrThrow(cubane.Rlp.fromOrThrow([nonce, gasprice, startgas, to, value, data])); } async function signTransactionOrThrow(device, path, transaction) { const paths$1 = paths.Paths.from(path); const reader = new cursor.Cursor(transaction); const unprotected = readLegacyUnprotectedOrThrow(transaction); let response; { const head = paths$1.sizeOrThrow(); let body = Math.min(150 - head, reader.remaining); /** * Make sure that the chunk doesn't end right on the replay protection marker (EIP-155) * If it goes further than the unprotected part, then send the (few) remaining bytes of the protection */ if (unprotected != null && reader.offset + body >= unprotected.length) body = reader.remaining; const chunk = reader.readOrThrow(body); const writer = new cursor.Cursor(new Uint8Array(head + body)); paths$1.writeOrThrow(writer); writer.writeOrThrow(chunk); const request = { cla: 0xe0, ins: 0x04, p1: 0x00, p2: 0x00, fragment: new binary.Opaque(writer.bytes) }; response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); } while (reader.remaining) { let body = Math.min(150, reader.remaining); /** * Make sure that the chunk doesn't end right on the replay protection marker (EIP-155) * If it goes further than the unprotected part, then send the (few) remaining bytes of the protection */ if (unprotected != null && reader.offset + body >= unprotected.length) body = reader.remaining; const chunk = reader.readOrThrow(body); const request = { cla: 0xe0, ins: 0x04, p1: 0x80, p2: 0x00, fragment: new binary.Opaque(chunk) }; response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); } const cursor$1 = new cursor.Cursor(response); const v = cursor$1.readUint8OrThrow(); const r = cursor$1.readAndCopyOrThrow(32); const s = cursor$1.readAndCopyOrThrow(32); // if ((((chainId * 2) + 35) + 1) > 255) { // const parity = Math.abs(v0 - (((chainId * 2) + 35) % 256)) // if (transaction.type == null) // v = ((chainId * 2) + 35) + parity // else // v = (parity % 2) == 1 ? 0 : 1; // } return cubane.RsvSignature.create({ r, s, v }); } async function signEIP712HashedMessageOrThrow(device, path, domain, message) { const paths$1 = paths.Paths.from(path); const writer = new cursor.Cursor(new Uint8Array(paths$1.sizeOrThrow() + 32 + 32)); paths$1.writeOrThrow(writer); writer.writeOrThrow(domain); writer.writeOrThrow(message); const request = { cla: 0xe0, ins: 0x0c, p1: 0x00, p2: 0x00, fragment: new binary.Opaque(writer.bytes) }; const response = await device.requestOrThrow(request).then(r => r.getOrThrow().bytes); const reader = new cursor.Cursor(response); const v = reader.readUint8OrThrow() - 27; const r = reader.readAndCopyOrThrow(32); const s = reader.readAndCopyOrThrow(32); return cubane.RsvSignature.create({ r, s, v }); } exports.getAddressOrThrow = getAddressOrThrow; exports.getAppConfigOrThrow = getAppConfigOrThrow; exports.signEIP712HashedMessageOrThrow = signEIP712HashedMessageOrThrow; exports.signPersonalMessageOrThrow = signPersonalMessageOrThrow; exports.signTransactionOrThrow = signTransactionOrThrow; exports.verifyAndGetAddressOrThrow = verifyAndGetAddressOrThrow; //# sourceMappingURL=index.cjs.map