@terra-money/ledger-terra-js
Version:
API for Terra App (Ledger Nano S/X)
294 lines • 11.8 kB
JavaScript
;
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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sign = exports.showAddressAndPubKey = exports.getAddressAndPubKey = exports.publicKey = exports.getDeviceInfo = exports.getAppInfo = exports.getVersion = exports.serializePath = void 0;
const constants_1 = require("./constants");
function errorCodeToString(statusCode) {
if (statusCode in constants_1.ERROR_DESCRIPTION) {
return constants_1.ERROR_DESCRIPTION[statusCode];
}
return `Unknown Status Code: ${statusCode}`;
}
function isDict(v) {
return typeof v === "object" && v !== null && !(v instanceof Array) && !(v instanceof Date);
}
function processErrorResponse(response) {
if (response) {
if (isDict(response)) {
if (Object.prototype.hasOwnProperty.call(response, "statusCode")) {
return {
return_code: response.statusCode,
error_message: errorCodeToString(response.statusCode),
};
}
if (Object.prototype.hasOwnProperty.call(response, "return_code") &&
Object.prototype.hasOwnProperty.call(response, "error_message")) {
return response;
}
}
return {
return_code: 0xffff,
error_message: response.toString(),
};
}
return {
return_code: 0xffff,
error_message: response.toString(),
};
}
function serializePath(path) {
if (!path || path.length !== 5) {
throw new TypeError("Invalid path.");
}
const 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;
}
exports.serializePath = serializePath;
function getVersion(transport) {
return transport
.send(constants_1.CLA, constants_1.INS.GET_VERSION, 0, 0)
.then((response) => {
const errorCodeData = response.slice(-2);
const return_code = errorCodeData[0] * 256 + errorCodeData[1];
let targetId = 0;
if (response.length >= 9) {
targetId = (response[5] << 24) + (response[6] << 16) + (response[7] << 8) + (response[8] << 0);
}
return {
return_code: return_code,
error_message: errorCodeToString(return_code),
test_mode: response[0] !== 0,
major: response[1],
minor: response[2],
patch: response[3],
device_locked: response[4] === 1,
target_id: targetId.toString(16),
};
})
.catch(processErrorResponse);
}
exports.getVersion = getVersion;
function getAppInfo(transport) {
return transport
.send(0xb0, 0x01, 0, 0)
.then((response) => {
const errorCodeData = response.slice(-2);
let return_code = errorCodeData[0] * 256 + errorCodeData[1];
let error_message;
let app_name = "err";
let app_version = "err";
let flag_len = 0;
let flags_value = 0;
if (response[0] !== 1) {
error_message = "response format ID not recognized";
return_code = 0x9001;
}
else {
error_message = "No errors";
const appNameLen = response[1];
app_name = response.slice(2, 2 + appNameLen).toString("ascii");
let idx = 2 + appNameLen;
const appVersionLen = response[idx];
idx += 1;
app_version = response.slice(idx, idx + appVersionLen).toString("ascii");
idx += appVersionLen;
const appFlagsLen = response[idx];
idx += 1;
flag_len = appFlagsLen;
flags_value = response[idx];
}
return {
return_code,
error_message,
app_name,
app_version,
flag_len,
flags_value,
flag_recovery: (flags_value & 1) !== 0,
flag_signed_mcu_code: (flags_value & 2) !== 0,
flag_onboarded: (flags_value & 4) !== 0,
flag_pin_validated: (flags_value & 128) !== 0,
};
})
.catch(processErrorResponse);
}
exports.getAppInfo = getAppInfo;
function getDeviceInfo(transport) {
return __awaiter(this, void 0, void 0, function* () {
return transport
.send(0xe0, 0x01, 0, 0, Buffer.from([]), [constants_1.ERROR_CODE.NoError, 0x6e00])
.then((response) => {
const errorCodeData = response.slice(-2);
const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
if (returnCode === 0x6e00) {
return {
return_code: returnCode,
error_message: "This command is only available in the Dashboard",
};
}
const target_id = response.slice(0, 4).toString("hex");
let pos = 4;
const secureElementVersionLen = response[pos];
pos += 1;
const se_version = response.slice(pos, pos + secureElementVersionLen).toString();
pos += secureElementVersionLen;
const flagsLen = response[pos];
pos += 1;
const flag = response.slice(pos, pos + flagsLen).toString("hex");
pos += flagsLen;
const mcuVersionLen = response[pos];
pos += 1;
let tmp = response.slice(pos, pos + mcuVersionLen);
if (tmp[mcuVersionLen - 1] === 0) {
tmp = response.slice(pos, pos + mcuVersionLen - 1);
}
const mcu_version = tmp.toString();
return {
return_code: returnCode,
error_message: errorCodeToString(returnCode),
target_id,
se_version,
flag,
mcu_version,
};
})
.catch(processErrorResponse);
});
}
exports.getDeviceInfo = getDeviceInfo;
function publicKey(transport, data) {
return transport
.send(constants_1.CLA, constants_1.INS.GET_ADDR_SECP256K1, 0, 0, data, [constants_1.ERROR_CODE.NoError])
.then((response) => {
const errorCodeData = response.slice(-2);
const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
const compressedPk = response ? Buffer.from(response.slice(0, 33)) : null;
return {
pk: "OBSOLETE PROPERTY",
compressed_pk: compressedPk ? compressedPk.toJSON() : compressedPk,
return_code: returnCode,
error_message: errorCodeToString(returnCode),
};
})
.catch(processErrorResponse);
}
exports.publicKey = publicKey;
function getAddressAndPubKey(transport, data) {
return transport
.send(constants_1.CLA, constants_1.INS.GET_ADDR_SECP256K1, constants_1.P1_VALUES.ONLY_RETRIEVE, 0, data, [constants_1.ERROR_CODE.NoError])
.then((response) => {
const errorCodeData = response.slice(-2);
const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
const compressedPk = Buffer.from(response.slice(0, 33));
const bech32Address = Buffer.from(response.slice(33, -2)).toString();
return {
bech32_address: bech32Address,
compressed_pk: compressedPk.toJSON(),
return_code: returnCode,
error_message: errorCodeToString(returnCode),
};
})
.catch(processErrorResponse);
}
exports.getAddressAndPubKey = getAddressAndPubKey;
function showAddressAndPubKey(transport, data) {
return transport
.send(constants_1.CLA, constants_1.INS.GET_ADDR_SECP256K1, constants_1.P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, data, [constants_1.ERROR_CODE.NoError])
.then((response) => {
const errorCodeData = response.slice(-2);
const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
const compressedPk = Buffer.from(response.slice(0, 33));
const bech32Address = Buffer.from(response.slice(33, -2)).toString();
return {
bech32_address: bech32Address,
compressed_pk: compressedPk.toJSON(),
return_code: returnCode,
error_message: errorCodeToString(returnCode),
};
})
.catch(processErrorResponse);
}
exports.showAddressAndPubKey = showAddressAndPubKey;
function signGetChunks(path, message) {
const result = serializePath(path);
const chunks = [];
if (result instanceof Buffer) {
chunks.push(result);
const buffer = Buffer.from(message);
for (let i = 0; i < buffer.length; i += constants_1.CHUNK_SIZE) {
let end = i + constants_1.CHUNK_SIZE;
if (i > buffer.length) {
end = buffer.length;
}
chunks.push(buffer.slice(i, end));
}
}
return chunks;
}
function signSendChunk(transport, chunkIdx, chunkNum, chunk) {
let payloadType = constants_1.PAYLOAD_TYPE.ADD;
if (chunkIdx === 1) {
payloadType = constants_1.PAYLOAD_TYPE.INIT;
}
if (chunkIdx === chunkNum) {
payloadType = constants_1.PAYLOAD_TYPE.LAST;
}
return transport
.send(constants_1.CLA, constants_1.INS.SIGN_SECP256K1, payloadType, 0, chunk, [constants_1.ERROR_CODE.NoError, 0x6984, 0x6a80])
.then((response) => {
const errorCodeData = response.slice(-2);
const returnCode = errorCodeData[0] * 256 + errorCodeData[1];
let errorMessage = errorCodeToString(returnCode);
if (returnCode === 0x6a80 || returnCode === 0x6984) {
errorMessage = `${errorMessage} : ${response.slice(0, response.length - 2).toString("ascii")}`;
}
let signature = Buffer.from([]);
if (response.length > 2) {
signature = response.slice(0, response.length - 2);
}
return {
signature: signature.toJSON(),
return_code: returnCode,
error_message: errorMessage,
};
})
.catch(processErrorResponse);
}
function sign(transport, path, message) {
const chunks = signGetChunks(path, message);
return signSendChunk(transport, 1, chunks.length, chunks[0])
.then((response) => __awaiter(this, void 0, void 0, function* () {
let result = {
return_code: response.return_code,
error_message: response.error_message,
signature: { type: "Buffer", data: [] },
};
for (let i = 1; i < chunks.length; i += 1) {
result = yield signSendChunk(transport, 1 + i, chunks.length, chunks[i]);
if (result.return_code !== constants_1.ERROR_CODE.NoError) {
break;
}
}
return {
return_code: result.return_code,
error_message: result.error_message,
signature: result.signature,
};
}))
.catch(processErrorResponse);
}
exports.sign = sign;
//# sourceMappingURL=device.js.map