dmk-business-logic について
Ledger Device Management Kit(DMK)を実装する開発者向けのリファレンススキルです。API 設計の概念、デバイス操作、通信上の制約、署名、安全性に関わる挙動を解説します。
主な機能
- 01クリア署名とブラインド署名の概念
- 02セキュアチャネルの要件
- 03デバイスアクションとコマンド
- 04セッションのライフサイクルと通信方式
- 05導出パスの指針
- 06デバイスの真正性確認
活用シーン
- 01デバイスアクションとコマンドの選択
- 02トランザクション署名フローの設計
- 03Ledger 接続セッションの管理
- 04チェーンの導出パス設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ledgerhq/ agent-skills --skill dmk-business-logicインストール後、Claude Code で/dmk-business-logicを入力して読み込まれたか確認します。
詳細
DMK のビジネスロジックと概念
このスキルでは、DMK API の設計上の決定に至った背景(ドメインモデル、セキュリティ上の制約、トレードオフ)を説明し、開発者が呼び出し方だけでなく、その理由も理解できるようにします。また、実装中に用語や概念の確認が必要になった際、ledger-dmk-implementation/SKILL.md によってコネクターとして読み込まれます。
Clear Signing と Blind Signing
意味
Blind signing では、ユーザーに Ledger の画面上で生の 16 進数バイト列が表示されます。ユーザーが確認できない、長くて判読不能な文字列です。内容を実質的に確認できないまま、承認を求められます。
Clear Signing では、デバイスの画面に、人が読めるトランザクションの詳細(送信先アドレス、金額、トークン名、コントラクト名、手数料)が表示されます。ボタンを押す前に、実際に承認する内容を確認できます。
重要な理由
デバイスの画面は、唯一の信頼できる表示画面です。侵害されたホスト(マルウェア、フィッシングサイト)は、ブラウザーやデスクトップの UI 上にユーザーが見たいものを何でも表示できます。デバイスの画面は偽装できません。Clear Signing によってデバイスの画面が意味のあるものになります。Blind Signing では画面が役に立ちません。
DMK での仕組み
DMK は Clear Signing がなくても完全に機能します。Clear Signing は必須要件ではなく、機能を強化するものです。Ethereum で有効にするには、次のようにします。
@ledgerhq/context-moduleは必ずインストールする必要があります。ETH signer kit の必須 peer dependency であり、デバイスが人に読めるフィールドを表示するために必要なメタデータ(ABI、トークン情報、NFT の詳細)の取得を担います。originTokenは、SignerEthBuilderに渡す任意のパートナートークンです。指定しなくても signer は機能しますが、デバイスには生の 16 進数が表示されます。実行時エラーは発生せず、エクスペリエンスは気づかないうちに Blind Signing に劣化します。トークンを取得するには、Ledger のパートナープログラムに登録してください:https://developers.ledger.com/docs/clear-signing/for-wallets。
// Clear Signing enabled (requires partner token):
new SignerEthBuilder({ dmk, sessionId, originToken: "your-partner-token" }).build();
// Blind signing — works but users see raw hex on device:
new SignerEthBuilder({ dmk, sessionId }).build();
Bitcoin と Solana では、Clear Signing はアプリレベルで処理されます。originToken は不要ですが、デバイス上の Ledger アプリがそのトランザクションの種類に対応している必要があります。
開発者向けチェックリスト
originTokenは任意です。まずはこれを使わずに統合してリリースし、本番環境で Clear Signing を有効にする準備ができたら Ledger のパートナープログラムに登録してください- ETH signer kit を使用する場合は、開発中も必ず
@ledgerhq/context-moduleをインストールしてください getAddress()のフローでは、必ずcheckOnDevice: trueを使用してください。これは、受取フローにおける Clear Signing に相当するアドレス検証です
セキュアチャネル
概要
セキュアチャネルは、Ledger デバイスと Ledger の HSM(ハードウェアセキュリティモジュール)バックエンド間の、暗号化され認証された接続です。バックエンドはこの接続を通じて、ローカルでは実行できない特権操作をデバイス上で実行できます。
必要な操作
| 操作 | セキュアチャネルが必要か |
|---|---|
GenuineCheckDeviceAction | はい |
ListInstalledAppsDeviceAction | はい |
InstallAppDeviceAction | はい |
UninstallAppDeviceAction | はい |
| すべての signer 操作(署名、アドレス取得) | いいえ |
OpenAppDeviceAction | いいえ |
仕組み
- DMK は Ledger の HSM(
wss://manager.live.ledger.com)への WebSocket 接続を開きます - デバイスと HSM が相互認証のハンドシェイクを行います
- デバイスの画面に
AllowSecureConnectionが表示され、ユーザーに接続の許可を求めます。ユーザーは実際に操作して接続を承認する必要があります - 承認されると、そのデバイスが再起動するまでセッションが信頼されます
実際の影響
- インターネット接続が必要です。 オフラインではセキュアチャネル操作に失敗し、ローカルでの代替手段はありません。
デバイスアクションとコマンド
2 段階の API
DMK は、デバイスとのやり取りに 2 つのレベルを提供します。
コマンド(dmk.sendCommand())は、低レベルの単一 APDU の往復処理です。デバイスに 1 つの命令を送信し、結果を同期的に返します。ユーザーとのやり取りは処理されないため、それ以外のすべては呼び出し側の責任です。
デバイスアクション(dmk.executeDeviceAction()、または署名者キットのメソッド)は、高レベルでオーケストレーションされたフローです。ロック解除の検出、アプリの起動、ユーザー確認のプロンプト、再試行、終了状態の通知まで、一連の処理全体を単一のオブザーバブルを通じて扱います。
使い分け
| ユースケース | 使用するもの |
|---|---|
| トランザクションへの署名、アドレスの取得 | 署名者キットのデバイスアクション(署名者ビルダー経由) |
| 正規品チェック、アプリのインストール/アンインストール | 適切な DeviceAction クラスを指定した dmk.executeDeviceAction() |
| ユーザー確認 UI を伴うアプリの起動 | dmk.executeDeviceAction() 経由の OpenAppDeviceAction |
| ファームウェアバージョンの確認、現在のアプリ名の取得 | sendCommand() — シンプルで、ユーザーとのやり取りは不要です |
| カスタム/未対応のチェーン、raw APDU | ApduBuilder を指定した sendCommand() — 署名者キットがない場合に限ります |
デバイスアクションがある理由
デバイスアクションがなければ、開発者はそれぞれ以下を再実装する必要があります。
- デバイスのロック解除を検出して待機する
- アクティブなアプリを検出して切り替える
ConfirmOpenAppプロンプトを処理する- ステータスワードのエラーをユーザー向けメッセージに変換する
- タイムアウトとキャンセルを管理する
デバイスアクションは、これらすべてをカプセル化します。返されるオブザーバブルは、UI プロンプトに直接対応する状態を通知します。UserInteractionRequired.SignTransaction は「ユーザーにプロンプトを表示する」、DeviceActionStatus.Completed は「完了したので、出力を取り出す」を意味します。
セッションと SessionId
セッションとは
セッションは、特定の Ledger デバイスとのアクティブな USB/BLE 接続を表します。dmk.connect() によって作成され、dmk.disconnect() によって破棄されます。sessionId は、DMK インスタンス内でセッションを識別する不透明な文字列です。
セッションではないもの
- チェーンごとのものではありません。 同じ
sessionIdを、再接続せずに ETH、BTC、SOL、その他の署名者で使用できます。 - 認証ではありません。 署名操作のたびに、デバイスは引き続きユーザーに確認を求めます。セッションは単なるトランスポートハンドルです。
- ページの読み込みをまたいで永続するものではありません。 ブラウザーを更新すると、セッションは破棄されます。
セッションの有効期間
セッションは、次のいずれかが起きるまで有効です。
dmk.disconnect({ sessionId })が呼び出される- USB ケーブルが抜かれる(セッション状態が
Disconnectedになる) - ブラウザーのタブが閉じられる
既存の sessionId を使って再接続しようとしないでください。Disconnected になったら、startDiscovering() から新しいフローを開始してください。
セッションリフレッシャー
DMK は、セッションの状態(現在のアプリ、デバイスの状態)を最新に保つため、定期的にデバイスをポーリングします。これがセッションリフレッシャーです。複数のタブで 2 つのインスタンスが同じ物理デバイスを共有する場合、ポーリングの競合を避けるために無効にしてください。
dmk.connect({ device, sessionRefresherOptions: { isRefresherDisabled: true } });
トランスポート
抽象化されるもの
トランスポート層は、ホストと Ledger デバイス間の物理通信プロトコルを処理します。DMK の API はトランスポートにかかわらず同一で、初期化方法だけが異なります。
| トランスポート | プロトコル | 環境 |
|---|---|---|
| WebHID | USB HID、64 バイトフレーム | ブラウザー(Chromium のみ) |
| WebBLE | Bluetooth GATT | ブラウザー |
| Node-HID | ネイティブアドオン経由の USB HID | Node.js、Electron |
| React Native HID | USB HID | React Native |
| React Native BLE | Bluetooth | React Native |
| Speculos | エミュレーターへの TCP ソケット | 開発/CI のみ |
ブラウザーの制約
WebHID には、以下が必要です。
- Chromium ベースのブラウザー(Chrome、Edge、Brave)— Firefox と Safari は WebHID をサポートしていません
- HTTPS または localhost — 通常の HTTP では WebHID がブロックされます
- ユーザーの操作 —
startDiscovering()とconnect()は、ボタンクリックなどの操作から呼び出す必要があります。ページ読み込み時に呼び出した場合、通知なく失敗します。
DMK インスタンスごとに 1 つのトランスポート
各 DeviceManagementKitBuilder は 1 つ以上のトランスポートを受け取ります。実際には、1 つを使用してください。DMK は登録された順にトランスポートを試します。
派生パス
派生パスとは
派生パスは、デバイスに保存されたルートシードから派生するキーを指定するインデックスの並びです。同じシードフレーズを指定した場合、同じパスからは常に同じキーが生成されます。一方、異なるパスからは、実行時エラーが発生することなく、まったく異なるキーが生成されます。
ユーザー入力ではなく、開発者が定数として定義する理由
デバイスの観点では、「誤ったパス」はありません。どのパスからも有効なキーが生成されます。ユーザーが 1 つでも異なるセグメントを含むパスを入力すると、別のアドレスが生成されます。そのアドレスに送金された資金は、正しいパスが分からない限りアクセスできません。これは、気づかれないまま取り返しのつかない事態につながるミスです。
パスは、ユーザーが使用するウォレットの標準に合わせて、アプリケーションコード内に定数としてハードコードする必要があります。
パス形式のルール
m/接頭辞は付けません。DerivationPathUtils.splitPathは/区切りのセグメントを解析します。先頭のmは有効なセグメントではありません。parseInt("m")はNaNを返し、「invalid number provided」というエラーが発生します。"m/44'/60'/0'/0/0"ではなく"44'/60'/0'/0/0"を使用してください。- ハード化されたセグメントには
'の接尾辞を使用します(例:44')。これはインデックスに0x80000000を加算する省略表記です。 - アカウントとインデックスは別の概念です。
accountはウォレットを選択します(ほとんどのユーザーは0を使用します)。indexは、そのウォレット内のアドレスを選択します。
チェーンごとの標準パス
| チェーン | Ledger Live のパス | 備考 |
|---|---|---|
| Ethereum | 44'/60'/0'/0/0 | BIP44、アカウント 0、インデックス 0 |
| Bitcoin Native SegWit | 84'/0'/0' | BIP84、ウォレットポリシーに渡します |
| Solana | 44'/501'/0'/0' | コミュニティ標準 |
| Cosmos | 44'/118'/0'/0/0 | BIP44 |
一覧にないチェーンについては、SLIP-0044 でコインタイプが定義されています(github.com/satoshilabs/slips/blob/master/slip-0044.md)。
BOLOS
BOLOS(Blockchain Open Ledger Operating System)は、現在は Ledger OS と呼ばれる、Ledger デバイスのオペレーティングシステムです。アプリが開かれておらず、デバイスがホーム画面(ダッシュボード)にある場合、state.currentApp.name は "BOLOS" を返します。
アプリが開かれていないことを検出する方法は次のとおりです。
const isOnDashboard =
state.currentApp.name === "BOLOS" || state.currentApp.name === "Dashboard";
署名キットのデバイスアクションを使用する場合、アプリの切り替えは自動的に処理されるため、これを明示的に検出する必要はありません。操作前に UX フィードバックを表示する場合(「アプリが開かれていません。続行すると適切なアプリが開きます」など)に役立ちます。
正規品チェック
正規品チェックでは、実物の Ledger デバイスが Ledger 製の正規ハードウェアであり、偽造品ではないことを確認します。デバイスに、対応する証明書が Ledger のルート CA によって署名された秘密鍵の所有を証明させ、セキュアチャネルを通じて検証します。
output.isGenuine: boolean を返します。
使用する場面: 企業向けの鍵管理のセットアップや、デバイスの初回オンボーディングフローなど、処理を続行する前にデバイスの真正性を確認する必要がある、高セキュリティのフローで使用します。通常の署名操作では必要ありません。
証明されないこと: 製造後にデバイスが改ざんされていないことや、シードフレーズが正しくバックアップされていることは証明されません。
Ledger Wallet / Ledger Live
Ledger Live は Ledger Wallet の旧名称です。どちらも同じデスクトップおよびモバイルアプリケーションを指します。ドキュメント、スキルファイル、SDK の参照ではいずれかの名称が使われることがありますが、同じものとして扱ってください。
デバイスアクション(OpenAppDeviceAction、InstallAppDeviceAction、UninstallAppDeviceAction)で使用するアプリ名は、Ledger のアプリカタログに表示される名称(Ledger Wallet に表示される名称と同じ)と完全に一致させる必要があります。タイプミスや大文字・小文字の違いがあると、何が問題だったのか分からないまま、無言で失敗するか、分かりにくいエラーが発生します。