From 2f6defbf3953c040f76ce12fc661098fba0edcb8 Mon Sep 17 00:00:00 2001 From: Kevin Le Seigle Date: Wed, 2 Oct 2024 11:04:44 +0200 Subject: [PATCH] feat: add ledger key ring protocol commands in cli --- apps/cli/README.md | 19 ++ apps/cli/package.json | 2 + apps/cli/src/commands-index.ts | 2 + .../commands/live/ledgerKeyRingProtocol.ts | 195 ++++++++++++++++++ package.json | 2 +- pnpm-lock.yaml | 6 + 6 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 apps/cli/src/commands/live/ledgerKeyRingProtocol.ts diff --git a/apps/cli/README.md b/apps/cli/README.md index aa6ce8532356..933e6f52fe81 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -376,6 +376,25 @@ Usage: ledger-live i18n # Test e2e functionality for device localization s Usage: ledger-live listApps # list all installed apps on the device -d, --device : provide a specific HID path of a device +Usage: ledger-live ledgerKeyRingProtocol # Ledger Key Ring Protocol command + -d, --device : provide a specific HID path of a device + --initMemberCredentials : Init member credentials for Ledger Key Ring Protocol + --getKeyRingTree : Get or create a Ledger Key Ring Protocol Tree + --encryptUserData : Encrypt user data with the current private key secured by the Ledger Key Ring Protocol + --decryptUserData : Encrypt user data with the current private key secured by the Ledger Key Ring Protocol + --getMembers : Get members of the Ledger Key Ring Protocol Tree + --restoreKeyRingTree : Restore a Ledger Key Ring Protocol Tree + --destroyKeyRingTree : Destroy a Ledger Key Ring Protocol Tree + --pubKey : pubkey for Ledger Key Ring Protocol Tree retrieved from initMemberCredentials result + --privateKey : privatekey for Ledger Key Ring Protocol Tree retrieved from initMemberCredentials result + --rootId : The immutable id of the Tree root retrieved from getKeyRingTree result + --walletSyncEncryptionKey : The secret used to encrypt/decrypt the wallet sync data retrieved from getKeyRingTree result + --applicationPath : privatekey for Ledger Key Ring Protocol Tree from initMemberCredentials result + --message : message to be encrypted/decrypted + --applicationId : application identifier + --name : name of the instance + --apiBaseUrl : api base url for Ledger Key Ring Protocol + Usage: ledger-live liveData # utility for Ledger Live app.json file -d, --device : provide a specific HID path of a device --xpub : use an xpub (alternatively to --device) [DEPRECATED: prefer use of id] diff --git a/apps/cli/package.json b/apps/cli/package.json index 5ec14679285b..09f51b957a63 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -33,7 +33,9 @@ "@ledgerhq/device-core": "workspace:^", "@ledgerhq/devices": "workspace:^", "@ledgerhq/errors": "workspace:^", + "@ledgerhq/ledger-key-ring-protocol": "workspace:^", "@ledgerhq/hw-app-btc": "workspace:^", + "@ledgerhq/hw-ledger-key-ring-protocol": "workspace:^", "@ledgerhq/hw-transport": "workspace:^", "@ledgerhq/hw-transport-http": "workspace:^", "@ledgerhq/hw-transport-mocker": "workspace:^", diff --git a/apps/cli/src/commands-index.ts b/apps/cli/src/commands-index.ts index fb05590763c9..1bc96ff9383e 100644 --- a/apps/cli/src/commands-index.ts +++ b/apps/cli/src/commands-index.ts @@ -54,6 +54,7 @@ import balanceHistory from "./commands/live/balanceHistory"; import countervalues from "./commands/live/countervalues"; import envs from "./commands/live/envs"; import exportAccounts from "./commands/live/exportAccounts"; +import ledgerKeyRingProtocol from "./commands/live/ledgerKeyRingProtocol"; import liveData from "./commands/live/liveData"; import portfolio from "./commands/live/portfolio"; import synchronousOnboarding from "./commands/live/synchronousOnboarding"; @@ -118,6 +119,7 @@ export default { countervalues, envs, exportAccounts, + ledgerKeyRingProtocol, liveData, portfolio, synchronousOnboarding, diff --git a/apps/cli/src/commands/live/ledgerKeyRingProtocol.ts b/apps/cli/src/commands/live/ledgerKeyRingProtocol.ts new file mode 100644 index 000000000000..1698eebeb009 --- /dev/null +++ b/apps/cli/src/commands/live/ledgerKeyRingProtocol.ts @@ -0,0 +1,195 @@ +import { getSdk } from "@ledgerhq/ledger-key-ring-protocol/index"; +import { crypto } from "@ledgerhq/hw-ledger-key-ring-protocol"; +import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess"; +import { getEnv } from "@ledgerhq/live-env"; +import { deviceOpt } from "../../scan"; + +export default { + description: "Ledger Key Ring Protocol command", + args: [ + deviceOpt, + { + name: "initMemberCredentials", + type: Boolean, + desc: "Init member credentials for Ledger Key Ring Protocol", + }, + { + name: "getKeyRingTree", + type: Boolean, + desc: "Get or create a Ledger Key Ring Protocol Tree", + }, + { + name: "encryptUserData", + type: Boolean, + desc: "Encrypt user data with the current private key secured by the Ledger Key Ring Protocol", + }, + { + name: "decryptUserData", + type: Boolean, + desc: "Encrypt user data with the current private key secured by the Ledger Key Ring Protocol", + }, + { + name: "getMembers", + type: Boolean, + desc: "Get members of the Ledger Key Ring Protocol Tree", + }, + { + name: "restoreKeyRingTree", + type: Boolean, + desc: "Restore a Ledger Key Ring Protocol Tree", + }, + { + name: "destroyKeyRingTree", + type: Boolean, + desc: "Destroy a Ledger Key Ring Protocol Tree", + }, + { + name: "pubKey", + type: String, + desc: "pubkey for Ledger Key Ring Protocol Tree retrieved from initMemberCredentials result", + }, + { + name: "privateKey", + type: String, + desc: "privatekey for Ledger Key Ring Protocol Tree retrieved from initMemberCredentials result", + }, + { + name: "rootId", + type: String, + desc: "The immutable id of the Tree root retrieved from getKeyRingTree result", + }, + { + name: "walletSyncEncryptionKey", + type: String, + desc: "The secret used to encrypt/decrypt the wallet sync data retrieved from getKeyRingTree result", + }, + { + name: "applicationPath", + type: String, + desc: "privatekey for Ledger Key Ring Protocol Tree from initMemberCredentials result", + }, + { + name: "message", + type: String, + desc: "message to be encrypted/decrypted", + }, + { + name: "applicationId", + type: Number, + default: 16, + desc: "application identifier", + }, + { + name: "name", + type: String, + default: "CLI", + desc: "name of the instance", + }, + { + name: "apiBaseUrl", + type: String, + default: getEnv("TRUSTCHAIN_API_STAGING"), + desc: "api base url for Ledger Key Ring Protocol", + }, + ], + job: ({ + device, + initMemberCredentials, + getKeyRingTree, + encryptUserData, + decryptUserData, + getMembers, + restoreKeyRingTree, + destroyKeyRingTree, + pubKey, + privateKey, + rootId, + walletSyncEncryptionKey, + applicationPath, + message, + applicationId = 16, + name = "CLI", + apiBaseUrl = getEnv("TRUSTCHAIN_API_STAGING"), + }: Partial<{ + device: string; + initMemberCredentials: boolean; + getKeyRingTree: boolean; + getMembers: boolean; + encryptUserData: boolean; + decryptUserData: boolean; + restoreKeyRingTree: boolean; + destroyKeyRingTree: boolean; + pubKey: string; + privateKey: string; + rootId: string; + walletSyncEncryptionKey: string; + applicationPath: string; + message: string; + applicationId: number; + name: string; + apiBaseUrl: string; + }>) => { + if (!applicationId) return "applicationId is required"; + if (!name) return "name is required"; + if (!apiBaseUrl) return "apiBaseUrl is required"; + + const context = { + applicationId, + name, + apiBaseUrl, + }; + const sdk = getSdk(false, context, withDevice); + + if (initMemberCredentials) { + return sdk.initMemberCredentials(); + } + + if (getKeyRingTree) { + if (!pubKey || !privateKey) return "pubKey and privateKey are required"; + return sdk + .getOrCreateTrustchain(device || "", { pubkey: pubKey, privatekey: privateKey }) + .then(result => result.trustchain); + } + + if (getMembers || restoreKeyRingTree || destroyKeyRingTree) { + if (!pubKey || !privateKey) return "pubKey and privateKey are required"; + if (!rootId) return "pubKey and privateKey are required"; + if (!walletSyncEncryptionKey) return "walletSyncEncryptionKey is required"; + if (!applicationPath) return "applicationPath is required"; + + const sdkMethod = getMembers + ? "getMembers" + : restoreKeyRingTree + ? "restoreTrustchain" + : "destroyTrustchain"; + return sdk[sdkMethod]( + { rootId, walletSyncEncryptionKey, applicationPath }, + { pubkey: pubKey, privatekey: privateKey }, + ); + } + + if (encryptUserData || decryptUserData) { + if (!rootId) return "rootId is required"; + if (!walletSyncEncryptionKey) return "walletSyncEncryptionKey is required"; + if (!applicationPath) return "applicationPath is required"; + if (!message) return "message is required"; + + if (encryptUserData) { + return sdk + .encryptUserData( + { rootId, walletSyncEncryptionKey, applicationPath }, + new TextEncoder().encode(message), + ) + .then(array => Buffer.from(array).toString("hex")); + } + return sdk + .decryptUserData( + { rootId, walletSyncEncryptionKey, applicationPath }, + crypto.from_hex(message), + ) + .then(array => new TextDecoder().decode(array)); + } + + return "command does not exist"; + }, +}; diff --git a/package.json b/package.json index c08e3c2b846f..316b37e42a0b 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "doc:ljs": "pnpm turbo doc --no-daemon --filter=\"./libs/ledgerjs/**\"", "watch:ljs": "pnpm turbo watch --no-daemon --filter=\"./libs/ledgerjs/**\"", "watch:common": "pnpm turbo watch --no-daemon --filter=./libs/ledger-live-common", - "dev:cli": "pnpm turbo watch --no-daemon --filter=@ledgerhq/live-cli", + "dev:cli": "pnpm turbo watch --filter=@ledgerhq/live-cli", "dev:lld": "pnpm turbo start --no-daemon --filter=ledger-live-desktop", "dev:lld:debug": "DEV_TOOLS=1 LEDGER_INTERNAL_ARGS=--inspect ELECTRON_ARGS=--remote-debugging-port=8315 pnpm turbo start --no-daemon --filter=ledger-live-desktop", "dev:llm": "pnpm turbo start --no-daemon --filter=live-mobile", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e28b97b0223c..39048e409f53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -147,6 +147,9 @@ importers: '@ledgerhq/hw-app-btc': specifier: workspace:^ version: link:../../libs/ledgerjs/packages/hw-app-btc + '@ledgerhq/hw-ledger-key-ring-protocol': + specifier: workspace:^ + version: link:../../libs/hw-ledger-key-ring-protocol '@ledgerhq/hw-transport': specifier: workspace:^ version: link:../../libs/ledgerjs/packages/hw-transport @@ -162,6 +165,9 @@ importers: '@ledgerhq/hw-transport-node-speculos': specifier: workspace:^ version: link:../../libs/ledgerjs/packages/hw-transport-node-speculos + '@ledgerhq/ledger-key-ring-protocol': + specifier: workspace:^ + version: link:../../libs/ledger-key-ring-protocol '@ledgerhq/live-common': specifier: workspace:^ version: link:../../libs/ledger-live-common