@radixdlt/hardware-ledger
Version:
Ledger Nano hardware wallet connection
162 lines • 5.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RadixAPDU = void 0;
const crypto_1 = require("@radixdlt/crypto");
const _types_1 = require("./_types");
// ##### Follows https://github.com/radixdlt/radixdlt-ledger-app/blob/main/APDUSPEC.md #####
const hdPathComponentsToBuffer = (hdPath) => {
if (hdPath.coinType.value() !== crypto_1.RADIX_COIN_TYPE ||
!hdPath.coinType.isHardened) {
throw new Error(`Expected coinType to be ${crypto_1.RADIX_COIN_TYPE}'`);
}
const bytesPerComponent = 4;
const data = Buffer.alloc(bytesPerComponent * hdPath.pathComponents.length);
const write = (pathComponent, offset) => {
data.writeUInt32BE(pathComponent.index, offset);
};
hdPath.pathComponents.forEach((component, index) => {
write(component, index * bytesPerComponent);
});
return data;
};
const hdPathToBuffer = (hdPath) => {
const bipPathsData = hdPathComponentsToBuffer(hdPath);
const bipPathsLength = hdPath.pathComponents.length;
const bipPathsLengthAsSingleByte = Buffer.alloc(1);
bipPathsLengthAsSingleByte.writeUInt8(bipPathsLength);
return Buffer.concat([bipPathsLengthAsSingleByte, bipPathsData]);
};
const makeAPDU = (input) => {
var _a, _b, _c;
return ({
cla: _types_1.radixCLA,
ins: input.ins,
p1: (_a = input.p1) !== null && _a !== void 0 ? _a : 0,
p2: (_b = input.p2) !== null && _b !== void 0 ? _b : 0,
data: input.data,
requiredResponseStatusCodeFromDevice: (_c = input.requiredResponseStatusCodeFromDevice) !== null && _c !== void 0 ? _c : [
_types_1.LedgerResponseCodes.SW_OK,
],
});
};
const getVersion = () => makeAPDU({
ins: _types_1.LedgerInstruction.GET_VERSION,
});
const parameterValueForDisplayAddressOnLedger = (input) => (input.display ? 0x01 : 0x00);
const getPublicKey = (input) => {
const p1 = parameterValueForDisplayAddressOnLedger(input);
let p2 = 0;
if (input.verifyAddressOnly !== undefined) {
p2 = input.verifyAddressOnly ? 0x01 : 0x00;
}
const data = hdPathToBuffer(input.path);
return makeAPDU({
ins: _types_1.LedgerInstruction.GET_PUBLIC_KEY,
p1,
p2,
data,
});
};
const doKeyExchange = (path, publicKeyOfOtherParty, display) => {
const p1 = display === 'encrypt' ? 0x01 : display === 'decrypt' ? 0x02 : 0x00;
const publicKeyUncompressedData = publicKeyOfOtherParty.asData({
compressed: false,
});
const publicKeyLengthBuf = Buffer.alloc(1);
publicKeyLengthBuf.writeUInt8(publicKeyUncompressedData.length);
const publicKeyData = Buffer.concat([
publicKeyLengthBuf,
publicKeyUncompressedData,
]);
const pathData = hdPathToBuffer(path);
const data = Buffer.concat([pathData, publicKeyData]);
return makeAPDU({
ins: _types_1.LedgerInstruction.DO_KEY_EXCHANGE,
p1,
data,
});
};
const doSignHash = (input) => {
const pathData = hdPathToBuffer(input.path);
const hashLenBuf = Buffer.alloc(1);
const hashedMessageByteCount = input.hashToSign.length;
hashLenBuf.writeUInt8(hashedMessageByteCount);
const hashData = Buffer.concat([hashLenBuf, input.hashToSign]);
const data = Buffer.concat([pathData, hashData]);
return makeAPDU({
ins: _types_1.LedgerInstruction.DO_SIGN_HASH,
data,
});
};
var SignTxAPDUType;
(function (SignTxAPDUType) {
SignTxAPDUType[SignTxAPDUType["SINGLE_RADIX_ENGINE_INSTRUCTION_APDU"] = 73] = "SINGLE_RADIX_ENGINE_INSTRUCTION_APDU";
SignTxAPDUType[SignTxAPDUType["FIRST_METADATA_APDU"] = 77] = "FIRST_METADATA_APDU";
})(SignTxAPDUType || (SignTxAPDUType = {}));
const signTxInitialSetupPackage = (input) => {
const p1 = SignTxAPDUType.FIRST_METADATA_APDU.valueOf();
const pathData = hdPathToBuffer(input.path);
const sizeOfTXAsData = Buffer.alloc(4);
sizeOfTXAsData.writeUInt32BE(input.txByteCount);
const instructionCountAsData = Buffer.alloc(2);
instructionCountAsData.writeUInt16BE(input.numberOfInstructions);
const hrpLen = input.nonNativeTokenRriHRP === undefined
? 0
: input.nonNativeTokenRriHRP.length;
if (hrpLen > 255) {
throw new Error(`Non native token HRP must not longer than 255.`);
}
const nonNativeTokenHrpLengthAsData = Buffer.alloc(1);
nonNativeTokenHrpLengthAsData.writeUInt8(hrpLen);
const nonNativeTokenHrpData = input.nonNativeTokenRriHRP === undefined
? Buffer.alloc(0)
: Buffer.from(input.nonNativeTokenRriHRP, 'utf8');
const hrpData = Buffer.concat([
nonNativeTokenHrpLengthAsData,
nonNativeTokenHrpData,
]);
const data = Buffer.concat([
pathData,
sizeOfTXAsData,
instructionCountAsData,
hrpData,
]);
return makeAPDU({
ins: _types_1.LedgerInstruction.DO_SIGN_TX,
p1,
data,
});
};
const signTxSingleInstruction = (input) => {
const p1 = SignTxAPDUType.SINGLE_RADIX_ENGINE_INSTRUCTION_APDU.valueOf();
let p2 = 0b00000000;
if (input.displayInstructionContentsOnLedgerDevice) {
const bitMask_displayInstructionContentsOnLedgerDevice = 0b1 << 0;
p2 = p2 ^ bitMask_displayInstructionContentsOnLedgerDevice;
}
if (input.displayTXSummaryOnLedgerDevice) {
const bitMask_displayTXSummaryOnLedgerDevice = 0b1 << 1;
p2 = p2 ^ bitMask_displayTXSummaryOnLedgerDevice;
}
return makeAPDU({
ins: _types_1.LedgerInstruction.DO_SIGN_TX,
p1,
p2,
data: input.instructionBytes,
});
};
const getAppName = () => makeAPDU({
ins: _types_1.LedgerInstruction.GET_APP_NAME,
});
exports.RadixAPDU = {
getAppName,
getVersion,
getPublicKey,
doKeyExchange,
doSignHash,
signTX: {
initialSetup: signTxInitialSetupPackage,
singleInstruction: signTxSingleInstruction,
},
};
//# sourceMappingURL=apdu.js.map