@cityofzion/neon-ledger
Version:
Neon Ledger integration for Node.js
121 lines • 4.73 kB
JavaScript
import { u } from "@cityofzion/neon-core";
import { StatusWord, evalTransportError, looksLikeTransportStatusError, } from "./ErrorCode";
import { DerToHexSignature } from "./utils";
const DEFAULT_STATUSLIST = [StatusWord.OK];
var Command;
(function (Command) {
Command[Command["GET_APP_NAME"] = 0] = "GET_APP_NAME";
Command[Command["GET_VERSION"] = 1] = "GET_VERSION";
Command[Command["SIGN_TX"] = 2] = "SIGN_TX";
Command[Command["GET_PUBLIC_KEY"] = 4] = "GET_PUBLIC_KEY";
})(Command || (Command = {}));
/**
* Helper to send data chunks to sign.
* @param ledger - Ledger instance
* @param msg - data up to 510 character
* @param chunk - data sequence number. Start at 0, increase by 1
* @param finalChunk - set to true if this is the last chunk for the command
*/
async function sendDataToSign(ledger, msg, chunk, finalChunk = false) {
return await ledger.send(0x80, Command.SIGN_TX, chunk, finalChunk ? 0x00 : 0x80, Buffer.from(msg, "hex"), DEFAULT_STATUSLIST);
}
/**
* Requests the open application name from the Ledger.
* @param ledger - Ledger instance
* @returns the ledger application name. Expected "NEO3"
*/
export async function getAppName(ledger) {
try {
const response = await ledger.send(0x80, Command.GET_APP_NAME, 0x00, 0x00, undefined, DEFAULT_STATUSLIST);
const version = response.toString("ascii");
return version.substring(0, version.length - 2); // take of status word
}
catch (e) {
if (looksLikeTransportStatusError(e)) {
throw evalTransportError(e);
}
throw e;
}
}
/**
* Requests the application version from the Ledger.
* @param ledger - Ledger instance
* @returns the application version in Major.Minor.Patch format i.e. "0.1.0"
*/
export async function getAppVersion(ledger) {
try {
const response = await ledger.send(0x80, Command.GET_VERSION, 0x00, 0x00, undefined, DEFAULT_STATUSLIST);
const major = response.readUInt8(0);
const minor = response.readUInt8(1);
const patch = response.readUInt8(2);
return major.toString() + "." + minor.toString() + "." + patch.toString();
}
catch (e) {
if (looksLikeTransportStatusError(e)) {
throw evalTransportError(e);
}
throw e;
}
}
/**
* Returns the list of connected Ledger devices. Throw if Ledger is not supported by the computer.
* @param ledgerLibrary - Ledger library
*/
export async function getDevicePaths(ledgerLibrary) {
const supported = await ledgerLibrary.isSupported();
if (!supported) {
throw new Error(`Your computer does not support the ledger!`);
}
return await ledgerLibrary.list();
}
/**
* Requests the public key of a requested address from the Ledger.
* @param ledger - Ledger instance
* @param bip44String - BIP44 string (40 bytes)
* @param showAddressOnDevice - whether to show the public key as NEO3 address
* on the Ledger
* @returns An unencoded public key (65 bytes)
*/
export async function getPublicKey(ledger, bip44String, showAddressOnDevice = false) {
try {
const response = await ledger.send(0x80, Command.GET_PUBLIC_KEY, 0x00, showAddressOnDevice ? 0x01 : 0x0, Buffer.from(bip44String, "hex"), DEFAULT_STATUSLIST);
return response.toString("hex").substring(0, 130);
}
catch (e) {
if (looksLikeTransportStatusError(e)) {
throw evalTransportError(e);
}
throw e;
}
}
/**
* Requests the device to sign a message using the NEO application.
* @param ledger - Ledger instance
* @param payload - message to sign as a hexstring.
* @param bip44String - BIP44 string (40 bytes)
* @param network - MainNet, TestNet or custom network number
* @returns Signature as a hexstring (64 bytes)
*/
export async function getSignature(ledger, payload, bip44String, network) {
await sendDataToSign(ledger, bip44String, 0);
await sendDataToSign(ledger, u.num2hexstring(network, 4, true), 1);
const chunks = payload.match(/.{1,510}/g) || [];
try {
for (let i = 0; i < chunks.length - 1; i++) {
await sendDataToSign(ledger, chunks[i], 2 + i);
}
const response = await sendDataToSign(ledger, chunks[chunks.length - 1], 2 + chunks.length, true);
// Expected signature + 2 status bytes to be returned here upon completion
if (response.length <= 2) {
throw new Error(`No more data but Ledger did not return signature!`);
}
return DerToHexSignature(response.toString("hex"));
}
catch (e) {
if (looksLikeTransportStatusError(e)) {
throw evalTransportError(e);
}
throw e;
}
}
//# sourceMappingURL=main.js.map