@hazae41/ledger
Version:
Private and supply-chain hardened Ledger controller for TypeScript
183 lines (179 loc) • 8.39 kB
JavaScript
;
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