こんにちは。GMOグローバルサイン・ホールディングスCTO室で分散型IDの研究をしている開発者の神沼@t_kanumaです。
Holder Agent(Wallet)の運用においては、以下の2つが重要になると考えます。
- マスターキー管理
- バックアップ/リストア
この記事では、その一例として、弊社のringoがそれぞれに対し行った方式設計と実装について、デモ動画を交えながら共有したいと思います。
前提
これまでのHolder Agentに関する拙著群と同様に
- Holder Agentの形態はAries Framework JavaScript(v0.3.3)を利用して作ったAndroidアプリです。
- WalletはAgentと一体化しています、すなわちAndroidデバイスの中に、暗号化されたSQLiteとして実装されています。
Agentのソースコードリポジトリはこちらをご参照ください。
マスターキー管理
処理方式
WalletのマスターキーはWallet内のコンテンツを暗号化をするためのKEK(Key Encryption Key)になるわけですが、今回の管理の方式は以下の拙著で述べたそれと同じです。
【Hyperledger Indy/Aries】AFJでReact Native製Holder Agentを作りVCモデルを回す(中編)
掻い摘んで述べれば、Holderがアプリの初期設定で入力するPINコードとUUIDを元にパスフレーズを生成し、それをAndroid Keystore Systemを使い暗号化した上でSharedPreferencesに保管します。そしてアプリ起動時にパスフレーズをロードしてマスターキーを導出し、Walletと接続する仕組みです。
なお、今回はマスターキーのローテーションは実装していませんが、上記の記事にも書いてある通り、AFJにそのメソッドが用意されています。詳細は記事をご参照ください。
アプリ実装
- 初期設定でのPINコードとUUIDを基にしたパスフレーズ生成と保管: SignUp.tsx
- アプリ起動におけるPINコード認証からのWallet接続: SignIn.tsx
デモ
初回起動時のPINコード登録
起動2回目以降の認証
Walletのバックアップとリストア
処理方式
私はWebサービスへのログインパスワードの管理にAndroidアプリの1Passwordを利用しています。私の運用手法としては、クラウドではなくデバイス内にパスワードを保管し、バックアップファイルを定期的にGoogle Driveにアップしています。そして、スマートフォンの買い替えの際などに、バックアップファイルをインポートしています。
今回、ringoが実装した方式も以下の図のように、上記と同じです。
バックアップでは、Holderがリストア用パスフレーズを設定した上で、UIからWalletをローカルストレージにエクスポートします。リストアでは、HolderがUIからローカルストレージ上のバックアップファイルを選択しインポートします。
上記のマスターキー管理に記載した記事の中で述べている通り、AFJはIndy SDKを通してWallet(SQLite)を生成します。(正確に言うとv0.3までのAFJです。詳細はこちらをご参照ください)Indy SDKが上記の方式のため、そこに依存した形になっています。
Indy SDKのWalletエクスポート/インポートの設計はこちらのドキュメントに記述されています。
それによれば、エクスポート/インポート機能は以下の特徴を持つと書かれています。
- Walletはエクスポート時に暗号化される。
- パスフレーズからargon2で暗号鍵が生成される。
- 暗号化のアルゴリズムは、ChaCha20-Poly1305-IETFである。
- インポートはWalletが空の状態である時だけ、可能である。
Wallet自体は上記のマスターキーにより最初から暗号化されて存在します。この内容から読み取れることは、エクスポートの際に、暗号化されたWalletをさらに暗号化するのではないかと考えます。また、インポートに関する記述から、今回はインポート前に一度既存のWalletを削除する実装にしています。
アプリ実装
バックアップ
以下、Walletをバックアップするコードの一部です。
const exportConfig: WalletExportImportConfig = {
path: RNFS.DownloadDirectoryPath + "/wallet.bak",
key: "YourBackupPassphrase",
};
agent?.wallet.export(exportConfig);
AFJにWalletをバックアップするためのメソッドが用意されており、エクスポート先のファイルパスと、リストアで入力するパスフレーズをメソッドに渡すと、現在のWalletが出力されます。今回は上記のコードの通り、ファイルパスを固定にしています。詳しい実装は以下をご参照ください。
リストア
以下、Walletをファイルからリストアするコードの一部です。
// 既存のWalletを削除します。
await agent?.wallet.close();
await agent?.wallet.delete();
// keyは上記の"マスターキー管理"で述べた、マスターキーの導出元となる、PINコードとUUIDから作られるパスフレーズです。
const walletConfig: WalletConfig = {
id: "rn-holder-wallet",
key: "YourPassphraseForWallet",
};
// keyはバックアップ時に設定したパスフレーズです。
// backupFilePathは、HolderがUIから選択したバックアップファイルのパスです。
const importConfig: WalletExportImportConfig = {
path: backupFilePath,
key: "YourBackupPassphrase",
};
await agent?.wallet.import(walletConfig, importConfig);
詳しい実装は以下をご参照ください。
デモ
完遂したストーリーは以下の通りです。
- ringoのスマートフォンにて、AgentアプリのUIから、発行済みのVCを保有するWalletをバックアップする。(バックアップファイルはローカルストレージにエクスポートされる。)
- バックアップファイルを私に受け渡す。
- 私のスマートフォンにてAgentアプリをインストールし、UIからバックアップファイルをインポートする。発行済みのVCと、Proof提示履歴が存在することを確認する。
- 発行済みのVCをVerifierに提示して検証に成功する。
上記No.1のバックアップ
上記No.3のリストア
バックアップファイル選択前
選択中
“ファイルを選択する”ボタンを押下すると、デバイスのファイル一覧が表示されファイルを選択できます。私のデバイス内のプライベートな情報がどうしても写ってしまうため、ここは割愛しています。(また選択後、ファイルパスをUIに表示すればわかりやすかったのですが、できておりません。)
選択後
無事、バックアップとリストアが成功し、そこから発行済みVCの検証が通るところまでを確認できました。
以上でこの記事を終わります。
どなたかのお役に立てたならば幸いです。