@metamask/eth-ledger-bridge-keyring
Version:
A MetaMask compatible keyring, for ledger hardware wallets
1 lines • 9.82 kB
Source Map (JSON)
{"version":3,"file":"eth-get-app-configuration-command.mjs","sourceRoot":"","sources":["../../src/dmk/eth-get-app-configuration-command.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Apdu,\n ApduResponse,\n Command,\n CommandResult,\n DeviceExchangeError,\n} from '@ledgerhq/device-management-kit';\nimport {\n ApduBuilder,\n ApduParser,\n CommandResultFactory,\n InvalidStatusWordError,\n} from '@ledgerhq/device-management-kit';\n\nexport type EthGetAppConfigurationResponse = {\n readonly blindSigningEnabled: boolean;\n readonly web3ChecksEnabled: boolean;\n readonly web3ChecksOptIn: boolean;\n readonly version: string;\n};\n\nconst CLA = 0xe0;\nconst INS = 0x06;\n// Feature flags defined by the Ethereum app `getAppConfiguration` APDU spec.\n// See: https://github.com/LedgerHQ/app-ethereum/blob/master/doc/ethapp.adoc#get-app-configuration\n// 0x01 - arbitrary data signature enabled by user (blind signing)\n// 0x02 - ERC 20 Token information needs to be provided externally\n// 0x10 - Transaction Check enabled (web3 checks)\n// 0x20 - Transaction Check Opt-In done (web3 checks opt-in)\nconst BLIND_SIGNING_FLAG = 0x01;\nconst WEB3_CHECKS_ENABLED_FLAG = 0x10;\nconst WEB3_CHECKS_OPT_IN_FLAG = 0x20;\nconst SUCCESS_STATUS_WORD_HI = 0x90;\nconst SUCCESS_STATUS_WORD_LO = 0x00;\n\n/**\n * Discriminator (`_tag`) used by `EthGetAppConfigurationCommand` when raising\n * a {@link DeviceExchangeError}. Exported so tests and translators can\n * reference the canonical string instead of repeating a magic literal.\n */\nexport const ETH_APP_COMMAND_ERROR_TAG = 'EthAppCommandError';\n\n/**\n * Reads the Ethereum app configuration via the Ethereum application's\n * `getAppConfiguration` APDU (CLA `0xE0`, INS `0x06`).\n *\n * ## Wire format\n *\n * Defined by the Ethereum application APDU specification\n * ([`doc/ethapp.adoc`](https://github.com/LedgerHQ/app-ethereum/blob/master/doc/ethapp.adoc#get-app-configuration),\n * § *GET APP CONFIGURATION*).\n *\n * The 4-byte response payload is laid out as:\n *\n * ```\n * byte 0 flags bitmask\n * bytes 1..3 app version (major, minor, patch)\n * ```\n *\n * The `flags` byte is defined by the spec as:\n *\n * ```\n * 0x01 arbitrary data signature enabled by user (blind signing)\n * 0x02 ERC 20 Token information needs to be provided externally\n * 0x10 Transaction Check enabled (web3 checks)\n * 0x20 Transaction Check Opt-In done (web3 checks opt-in)\n * ```\n *\n * This command mirrors the implementation in\n * [`@ledgerhq/device-signer-kit-ethereum`](https://github.com/LedgerHQ/device-sdk-ts/blob/main/packages/signer/signer-eth/src/internal/app-binder/command/GetAppConfigurationCommand.ts)\n * (`internal/app-binder/command/GetAppConfigurationCommand`), which is not\n * re-exported from that package's public entry point. Until the signer kit\n * exposes `GetAppConfiguration` publicly, this module is the only way to\n * invoke the Ethereum `getAppConfiguration` APDU through the DMK\n * `sendCommand` API.\n *\n * ## Related APDU docs\n *\n * The other Ethereum APDUs the DMK signer kit sends on our behalf are also\n * defined in the same `ethapp.adoc` document:\n *\n * - `GET ETH PUBLIC ADDRESS` — INS `0x02`\n * - `SIGN ETH TRANSACTION` — INS `0x04`\n * - `SIGN ETH PERSONAL MESSAGE` — INS `0x08`\n * - `SIGN ETH EIP 712` — INS `0x0C`\n * - `SIGN EIP 7702 AUTHORIZATION` — INS `0x34`\n *\n * ## Distinguishing DMK commands\n *\n * Note: this command is distinct from the DMK OS-level\n * [`GetAppAndVersionCommand`](https://github.com/LedgerHQ/device-sdk-ts/blob/main/packages/device-management-kit/src/api/command/os/GetAppAndVersionCommand.ts)\n * (`@ledgerhq/device-management-kit`), which returns only the app's name and\n * version and does not expose Ethereum-specific feature flags.\n */\nexport class EthGetAppConfigurationCommand implements Command<\n EthGetAppConfigurationResponse,\n void,\n string\n> {\n readonly name = 'getAppConfiguration';\n\n getApdu(): Apdu {\n return new ApduBuilder({ cla: CLA, ins: INS, p1: 0, p2: 0 }).build();\n }\n\n parseResponse(\n response: ApduResponse,\n ): CommandResult<EthGetAppConfigurationResponse, string> {\n if (!isSuccessStatusCode(response.statusCode)) {\n return CommandResultFactory({\n error: createEthAppCommandError(response.statusCode),\n });\n }\n\n const parser = new ApduParser(response);\n\n const flags = parser.extract8BitUInt();\n if (flags === undefined) {\n return CommandResultFactory({\n error: new InvalidStatusWordError(\n 'Cannot extract config flags from response body',\n ),\n });\n }\n\n const major = parser.extract8BitUInt();\n const minor = parser.extract8BitUInt();\n const patch = parser.extract8BitUInt();\n if (major === undefined || minor === undefined || patch === undefined) {\n return CommandResultFactory({\n error: new InvalidStatusWordError(\n 'Cannot extract version bytes from response body',\n ),\n });\n }\n\n return CommandResultFactory({\n data: {\n blindSigningEnabled: isFlagSet(flags, BLIND_SIGNING_FLAG),\n web3ChecksEnabled: isFlagSet(flags, WEB3_CHECKS_ENABLED_FLAG),\n web3ChecksOptIn: isFlagSet(flags, WEB3_CHECKS_OPT_IN_FLAG),\n version: `${major}.${minor}.${patch}`,\n },\n });\n }\n}\n\n/**\n * Test whether a single-bit flag is set in a bitmask.\n *\n * Per the Ethereum `getAppConfiguration` APDU spec\n * ([ethapp.adoc](https://github.com/LedgerHQ/app-ethereum/blob/master/doc/ethapp.adoc#get-app-configuration)),\n * each supported feature occupies one bit of the `flags` byte. The spec\n * defines *which* bits carry meaning (`0x01`, `0x10`, `0x20`); it does not\n * prescribe an implementation technique for testing them. Bitwise AND is\n * the idiomatic way to test a single bit in any language, and is the\n * same formulation used by Ledger's own\n * [`GetAppConfiguration`](https://github.com/LedgerHQ/device-sdk-ts/blob/main/packages/signer/signer-eth/src/internal/app-binder/command/GetAppConfigurationCommand.ts)\n * implementation upstream.\n *\n * @param flags - The bitmask (the first response byte).\n * @param flag - The single-bit value to test (e.g. `0x01`, `0x10`, `0x20`).\n * @returns `true` if the bit is set, `false` otherwise.\n */\nfunction isFlagSet(flags: number, flag: number): boolean {\n // Bitwise AND is the idiomatic way to test a single-bit flag in a\n // bitmask and matches the upstream Ledger implementation; the\n // `ethapp.adoc` spec defines the flag *values* (0x01, 0x10, 0x20) but\n // leaves the testing technique to the implementer.\n // eslint-disable-next-line no-bitwise\n return (flags & flag) !== 0;\n}\n\nfunction isSuccessStatusCode(statusCode: Uint8Array): boolean {\n return (\n statusCode[0] === SUCCESS_STATUS_WORD_HI &&\n statusCode[1] === SUCCESS_STATUS_WORD_LO\n );\n}\n\nfunction createEthAppCommandError(\n statusCode: Uint8Array,\n): DeviceExchangeError<string> {\n const errorCode = Array.from(statusCode)\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n\n return {\n _tag: ETH_APP_COMMAND_ERROR_TAG,\n errorCode,\n message: `Ledger Ethereum app command failed with status 0x${errorCode}.`,\n originalError: undefined,\n };\n}\n"]}