SSI/VCモデルとHyperledger Indy/Ariesを使ったシステム構築実証[後編]

SSI

はじめに

GMOグローバルサイン・ホールディングスCTO室でデジタルアイデンティティーの研究開発をしている神沼(かぬま)です。

この記事は、SSI/VCモデルとHyperledger Indy/Ariesを使ったシステム構築実証[前編]の後編になります。

後編では、私が行った小さなPoC、テーマ「年齢認証」でHyperledger Indy、Ariesを使い、実際にシステムを構築・開発できるか?について、アプリケーションのデモ動画を含めご説明します。

PoC

目的

前述の通り、Hyperledger Indy、Ariesを使い、実際にシステムを構築・開発できるか?を実証します。

テーマ

年齢認証をテーマにします。

コンビニ、スーパー、酒屋などのレジに置いて、お酒を購入する顧客が20才以上であることを認証します。

3つの登場人物は以下の通りです。

  1. アリス(Holder)
    • スーパーマーケットでお酒を買う顧客です。
    • Agentの形態はスマホアプリです。
  2. GMO GlobalSign Holdings、以下GSHD(Issuer)
    • アリスの買い物より前に、アリスからのWebフォーム申請を審査して年齢を含んだCredentialを発行します。
    • Agentの形態はWebアプリです。
  3. ボブマーケットと店員ボブ(Verifier)
    • アリスからのProofの提示を受け、20才以上であるか、そしてProofが正当なものか検証を行うスーパーマーケットです。
    • Agentの形態はWebアプリです。
    • ボブマーケットのレジには、アリスとのやりとりを始めるためのQRコードが置いてあります。


Daniel H Hardman,CC BY-SA 4.0,link

フロー(エージェント間のやり取りの流れ)

3部構成になります。

フェーズ1: Issuer/Holder間の招待と接続

  1. アリスはGSHDのWebフォームで所定の情報を入力、送信する。
  2. GSHDはフォーム情報の真正性を審査してOKであれば、招待コードをアリスにメールで送信する。
  3. アリスはスマホアプリを開いて招待コードを入力し、GSHDに送信する。
  4. GSHDは招待コードを受理して、Credential発行のオファーをアリスに出す。
ポイント
  • 1及び2は、VCモデルの外で行われます。今回はメール認証を使っています。
  • 4からVCモデルの中に入ります。ここからDIDComm Protocolによる通信が開始されます。

フェーズ2: IssuerからHolderへのCredential発行

  1. オファーを受けたアリスは、スマホアプリからGSHDに対しCredential発行の要求をする。
  2. アリスの要求に対し、GSHDはWebアプリからCredentialを発行する。
  3. アリスはスマホアプリで発行されたCredentialを確認する。
ポイント
  • 発行の要求、及び発行は、ユーザ操作がトリガーとなります。
    • これは私がそのように設計したためです。
    • 後述の”Proof提示・検証フェーズ”とは、対照的になっています。

フェーズ3: Holder/Verifier間の接続とProofの提示・検証

  1. ボブはWebアプリ(ブラウザ)上に接続のためのQRコードを表示する。
  2. アリスはブラウザ上のQRコードをスマホアプリで読み取る。
    • 読み取ったと同時にバックグラウンドで以下が行われる。
      • アリスAgentは読み取った招待コードからボブAgentと接続する。
      • ボブAgentはアリスAgentと接続後、Proofの要求をする。
      • アリスAgentは、CredentialからProofを作成し、ボブAgentに送信する。
      • ボブAgentは、Proofの提示を受け検証を行なう。
  3. ボブは、Webアプリ上で検証結果を確認する。
ポイント
  • Proofの要求及び検証は、ユーザ視点ではバックグラウンドで一気に行われます。
    • これは私がそのように設計したためです。
    • 前述の”Credential発行フェーズ”とは、対照的になっています。

備考

以下は全て設計次第です

  • 上記1.招待・接続フェーズは、HolderではなくIssuer/Verifierから開始しましたがHolderから開始することもできます。
  • 上記2.Credential発行フェーズは、Issuerのofferから開始しましたが、Aries ProtocolとしてはProposalという形でHolderから開始することもできます。
  • 上記3.Proof提示・検証フェーズは VerifierのRequestから開始しましたが、Aries ProtocolとしてはProposalという形でHolderから開始することもできます。

PoCシステム構成

システム構成概略図

概要

  • 主にAWS上でシステムを構築しました。
  • 緑がHolder、青がIssuer、赤がVerifierのコンポーネントになります。
  • 大きな丸が1つのEC2インスタンスになります。その中にある7つの四角はそれぞれがDockerコンテナになります。
    • 内4つは、Indyネットワークを構成するノードになります。
    • 内3つは、各AgentのAriesフレームワーク(aca-py)になります。

Indyネットワーク

4つのノードそれぞれがDockerコンテナとしてパッケージングされているvon-networkを利用しました。コマンド1つで簡単にネットワークを構築できます。Dockerに加えてDocker Compose環境が必要となります。

aca-py

  • aca-pyはその名の通り、Pythonプロセスです。
  • aca-py間の点線は、DIDComm Protocolによる通信を意味します。
  • aca-pyは、SQLiteを内蔵しており、そこにDIDの秘密鍵そしてHolderに関してはCredentialを保管しています。
  • aca-pyで用意されているスクリプトでDockerコンテナをデプロイします。ここでは、起動パラメータの精査と設定が大きな仕事になります。以下にパラメーターをいくつか例示します。
    • アクセスするIndyネットワークのURL
    • 自身のDIDComm ProtocolによるEndpointのURL、及びイベントハンドラー(Webhook)のURL
    • Walletのストレージタイプ(メモリ、SQLite、PostgreSQL)

アプリケーション/イベントハンドラー

  • 各AgentのイベントハンドラーにはAPI GatewayとLambdaを利用し、Pythonで実装しています。
  • 前編で述べた通り、aca-pyではアプリの言語、実行環境を開発側が自由に決められます。

アプリケーション/UI周り

  • Issuer、VerifierのWebアプリはNext.jsで実装しています。
    • 静的ファイルのホスティング先としてAmplifyを利用しています。
  • 主にaca-pyの状態とUIの状態を同期させる目的で、リアルタイムでアプリと同期できるGoogle FirebaseのCloud Firestoreを利用しています。
  • HolderのスマホアプリはReact Nativeで実装しています。
    • 今回は技術的、スケジュール的な都合上、Holderのスマホアプリにaca-pyを利用しました。しかし、aca-pyは本来サーバーサイド専用のフレームワークになります。このやり方ではCredentialと秘密鍵はサーバ側に保管され、現実的には個人ではなくサービスプロバイダーが管理する形になってしまいます。これはSSIの考え方に合っていません。Credentialと秘密鍵はスマートフォンに保管されるのが基本形であるため、aries-mobileagent-xamarinなどモバイルアプリ専用のフレームワークを使うのが本来の姿です。
  • 前編で述べた通り、aca-pyではアプリの言語、実行環境を開発側が自由に決められます。

その他

  • Holder-Issuer間のVCモデル外での招待コードのメール認証にLambdaを利用し、Node.jsで実装しています。
  • aca-pyのAPI EndpointはHTTPSでなくHTTPです。WebアプリからのMixed Contentを回避するため、HTTPS化をするリバースプロキシとしてCloudFrontを利用しています。

フローと照らし合わせたポイント

  • イベントハンドラーがイベントを受け取った際の処理は以下の2つに大別できます。
    • Cloud Firestoreに状態を書き込み、次のプロトコル推進をユーザに任せる。
    • イベントを受けとったハンドラー自身が、すぐさまaca-pyの所定のAPI Endpointを呼び出し、プロトコルを進める。
  • Holder-Issuer間はイベントをUIまで返しユーザに操作を委ねる場合と、イベントハンドラー間で、すなわちユーザから見るとバックグラウンドでAries Protocolを進める場合があります。
  • Holder-Verifier間は、接続から検証までをイベントハンドラー間で行います。ユーザから見るとバックグラウンドで一気に行われます。

DIDを中心とした情報

デモの前に、フローを始める前とその最中におけるDIDを中心とした情報を見ていきます。

Issuerの準備:Indy上の4つのDID Document

フローを始める前に、GSHD(Issuer)は自身が発行するCredentialに対応する4つのDID DocumentをIndy上に作成しておく必要があります。

  1. TYPE:NYM
    • NYMはpseudonym(作家のペンネーム、筆名)の略だと推測します。
    • NYMの値がGSHDのPublic DIDをBase58でエンコードしたものです。
    • Role:ENDORSERは、IssuerのIndy上でのRoleです。
    • VerkeyはこのDIDに紐づく公開鍵です。値はBase58でエンコードされたものです。
    • 詳細は省きますが、このレコードはIndyの別ロール、Trustee(このネットワークの創始者、運営者)またはSteward(ネットワークに参加しているトランザクション検証ノード)により作成されます。
  2. TYPE:ATTRIB
    • GSHD(Issuer)のDIDComm ProtocolによるEndpoint情報を持ちます。
    • このレコードはaca-pyが自身の起動時に作成します。詳細は省きますが、起動パラメータにseedを指定します。これは擬似乱数のseedのようなもので、Trustee/Stewardが作成するNYMレコード、すなわちDIDとその公開鍵ペアの素になるものだと推測します。そうすると、IssuerはTrustee/Stewardからこのseedをセキュアな経路で受け取る必要が出てきます。このseedをパラメータに設定することで、IssuerはIndy上のDIDを所有することが認められるのだと思います。
  3. TYPE:SCHEMA
    • Credentialのスキーマ定義です。
    • このPoCではnameとageという2つのClaimを持っていることがわかります。
    • このレコードは、NYM/ATTRIBレコードを作成後、aca-py API経由でGSHD(Issuer)が作成します。
  4. TYPE:CRED_DEF
    • Credential Definitionになります。ここでは以下を宣言します。
      • ”私GSHDはIssuerとしてこのDIDを使い、このスキーマに則り、各Claimごとにこれらの公開鍵に対応する秘密鍵で署名して、Credentialを発行します。”
    • このレコードは、NYM/Endopoint/Schemaレコードを記録後に、aca-py API経由でGSHD(Issuer)が作成します。
    • Schemaの各Claimごとの署名に対応する公開鍵が記録されています。Verifierはそれを利用しProofの検証をします。

フロー内でのDID関連情報

ここでは、このPoCの各フェーズにおいて、アプリがaca-py APIから取得できる実際の値をサンプルとして5つ列挙します。

  • Agent間で接続を確立した後のConnection情報
    • この接続における自身と相手のPairwise DID、接続ID、状態などを見れます。
    {
          "request_id": "b8a1d47a-20e6-414d-87ae-d1e541974efa",
          "invitation_mode": "once",
          "initiator": "external",
          "routing_state": "none",
          "my_did": "AbejbTQ86GPKM9PVg3KrP3",
          "created_at": "2020-09-24 05:32:24.609925Z",
          "their_did": "NPBWhzn5MTic5W6YCKwmUy",
          "their_label": "agify-isser-acapy",
          "connection_id": "cf185942-cb4a-4567-adf3-1752ec29f091",
          "state": "active",
          "updated_at": "2020-09-24 05:32:25.826680Z",
          "invitation_key": "9gkERW9PQ2URngCEXrDF2LM1tabD1kj9fg9GWSs4z7or",
          "accept": "auto"
    }
  • 各AgentがWalletに持つDIDの一覧
    • 自身のDIDとその公開鍵(verkey)が見て取れます。wallet_onlyはPairwise DIDであることを意味していると考えます。
{
      "did": "2Bzip889RLZ57kxe2NZnrP",
      "verkey": "ec5HdZddLJAvj82qKLN1HKYiiMjKaynMroDTqkL39v3",
      "posture": "wallet_only"
    },
    {
      "did": "2Hmzbu7pKSWCQoxbyziL6p",
      "verkey": "hkouN5JpSQxYKLMzggq8Sy8ADivrLEzJxxbqUjuYs63",
      "posture": "wallet_only"
    }
  • HolderがWalletに持つCredential
    • attrsからCredentialの各Claimの値が見てとれます。
    • schema_id及びcred_def_idから、Indy上のどのSchema、Credential Definitonに基づいたモノであるかがわかります。
{
          "referent": "credential_sample",
          "attrs": {
            "age": "34",
            "name": "Taro Tanaka"
          },
          "schema_id": "Vc7MmqrjX1mj1ertBeA6vP:2:agify:1.0",
          "cred_def_id": "Vc7MmqrjX1mj1ertBeA6vP:3:CL:87:default",
          "rev_reg_id": null,
          "cred_rev_id": null
    }
  • IssuerがHolderに送信するCredential発行オファーの内容抜粋
    • 平たく言うと、”Holderさん、このschema_id、cred_def_idのCredential発行しません?”になります。
    {
          "auto_offer": false,
          "schema_id": "Vc7MmqrjX1mj1ertBeA6vP:2:agify:1.0",
          "comment": "Offer on cred def id Vc7MmqrjX1mj1ertBeA6vP:3:CL:87:default"
          },
          "trace": false,
          "credential_exchange_id": "6fc6ec52-a21f-4f27-a600-683ba4bd6c5d",
          "initiator": "self",
          "auto_remove": false,
          "connection_id": "9b324014-5807-49b8-b680-dc76d5e6786c",
          "credential_offer": {
            "schema_id": "Vc7MmqrjX1mj1ertBeA6vP:2:agify:1.0",
            "cred_def_id": "Vc7MmqrjX1mj1ertBeA6vP:3:CL:87:default",
          },
          "state": "offer_sent",
    }
  • VerifierがHolderに送信するProof要求の内容抜粋
    • 平たく言うと、”Holderさん、このIssuerが署名したnameとageデータでProofを作ってね。ゼロ知識証明の条件は、age>=20だよ。”になります。
    • ここで重要な点は、ProofがDIDと疎結合になっていることです。
      • これにより複数のIssuerが発行した複数のCredentialから必要なClaimを組み合わせてProofを作れます。
      • Issuerが各Claimごとに署名しているのはこのためです。
    {
          "state": "request_sent",
          "presentation_request": {
            "name": "age schema",
            "requested_attributes": {
              "0_name_uuid": {
                "name": "name",
                "restrictions": [
                  {
                    "issuer_did": "Vc7MmqrjX1mj1ertBeA6vP"
                  }
                ]
              }
            },
            "requested_predicates": {
              "0_age_GE_uuid": {
                "name": "age",
                "p_type": ">=",
                "p_value": 20,
                "restrictions": [
                  {
                    "issuer_did": "Vc7MmqrjX1mj1ertBeA6vP"
                  }
                ]
              }
            }

デモ

以下の動画をご確認ください。流れはフローと照らし合わせてご覧ください。

「フェーズ1: 招待・接続」 & 「フェーズ2: Credential発行」

  • Holder
    • Webフォームの内容について、実運用を仮定すると運転免許証のコピーなどが必要になると思われますが、このPoCではこの通りです。
    • ”クレデンシャル”ボタンから発行されたCredentialを確認できます。
    • ”履歴”ボタンは、過去のProof提示履歴を表示します。
    • 画面右上のボタンはIssuer及びVerifierからの通知を表示します。
  • Issuer
    • メニュー”申請一覧”は、このアプリのメインの画面になります。ここにHolderからのフォーム申請、Credential発行要求がリアルタイムで表示、更新されます。
    • メニュー”接続一覧”は、過去に行なった接続とその状態の情報を表示します。
    • メニュー”DIDDocs”は、自身のIndy上のCredentialのスキーマ情報を表示します。

フェーズ3: Proof提示・検証

  • Holder
    • VerifierのQRコードを読み取るのみのため、動画は割愛します。
  • Verifier
    • メニュー”検証”は、更新するたびに変わるQRコードを表示します。検証結果を画面に緑丸(成功)か赤丸(失敗)でリアルタイムで表示します。
    • メニュー”履歴”は、過去の検証の結果を表示します。
    • メニュー”DIDDocs”は、IssuerのIndy上のCredentialのスキーマ情報を表示します。

総括

実証結果

フローを完遂し、年齢が20才以上であればVerifierの検証が成功し、20才未満であれば検証が失敗することを確認できました。以上から、テーマ「年齢認証」でHyperledger Indy、Ariesを使い、実際にシステムを構築・開発できることを実証できました。

考察

開発の容易性について

前編で述べた通り、以下のことはAriesフレームワークが隠蔽してくれます。

  • DIDの生成とAgent間のDIDComm Protocolでのセキュアな通信
  • Indyへのアクセス(情報格納・取得)
  • Credentialの発行、署名、保存
  • Proofの提示、検証(ゼロ知識証明含む)
  • Walletの管理
  • アプリへのイベント通知、アプリからのリクエスト受付

そのため、Webの知識・技術があればフレームワークが提供する高レベルなAPIを使うことでSSIアプリケーション・システムを構築できます。これはSSIを広く浸透させる、スケールさせるための作りだと思います。

Indyについては、自前でブロックチェーンネットワークを始めたり、既存ネットワークに参加してトランザクション検証ノードを運用する場合は、根本から理解が必要になると思われます。

実運用を仮定した場合のシステムの差異について
  • Indyの環境
    • 今回の環境はあくまでも開発用のネットワークになります。 実運用では、ノードごとに別々の企業・団体によって厳格なガバナンスの元、管理・運用されるモノです。
    • SSIによるソリューションが目的であれば、現実的には前述のSovrinのような既存環境を使うことになると思います。
  • 各Agent間のネットワーク
    • aca-py間のDIDComm Protocolについて、今回はLAN内での通信になりましたが、実運用では各aca-pyがGIPを持ちインターネット越しに通信することになります。
  • WalletのDB
    • aca-pyは軽量なRDBであるSQLiteを内蔵しており今回はそれを使いましたが、Postgresqlを使うことも出来ます。
    • 本番運用の信頼性を考えるとPostgresql化するのが望ましいと思います。
  • Holderアプリのフレームワーク
    • 前述の理由から、aca-pyではなくaries-mobileagent-xamarinを使うことになります。
  • ルーティングエージェントの用意
    • インターネットの仕組み上、スマホエージェントはそれだけではP2Pで他エージェントと通信できるわけではありません。ルーティング処理専門の別エージェントをインターネット上に用意する必要があります。これはGoogleのGCM/FCM、AppleのAPNsのような存在で、個人ではなく、例えばスマホアプリを提供するサービスプロバイダなどが用意する必要があります。またこの部分は完全に非中央集権型にできない点になります。アプリ利用者はプロバイダーが通信内容を盗聴したり悪用しないことを信頼する必要があります。
  • aca-pyコンテナのクラスタ化
    • 今回は各aca-pyのコンテナは1つでしたが、スケーラビリティとアベイラビリティーを考えて、KubernetesやAWS ECSなどでクラスタ化することになると思います。
既存のパラダイムとの関係について

Agentが互いに接続するための招待コードをVCモデル外で送る必要がありました(今回はメール認証)。また、スマホアプリではルーティングエージェントを提供するサービスプロバイダが必要になります。これらのことから、既存のID管理のパラダイムとは共存していくものであり、それを一気に塗り替えるものではないと考えます。

今後の深堀

簡単に私が今後、深堀していく内容を列挙します。

  • Credential失効の検証
  • Ariesフレームワーク隠蔽部分とIndy周りについて公式ドキュメントやソースコード解析から理解を深める。
  • 前述の実運用を仮定したシステム環境に現環境を近づける。

最後に

以上でこの記事を終わります。

私の次回の記事では、”ゼロ知識証明”について、その概念モデルと離散対数問題を例に取った説明を投稿する予定です。

最後までお読みいただき、ありがとうございました。

参考サイト

  • edX
    • Introduction to Hyperledger Sovereign Identity Blockchain Solutions:Indy,Aries&Ursa
    • Becoming a Hyperledger Aries Developer

謝辞

今回のPoCを私と共同開発してくれた(JavaScript周りの全部を開発してくれた)yushimatenjinこと弊社グローバルサービスプロバイダー部の羽賀にBig Thanksを送ります。