TBDのWeb5 JS SDKとSSI SDKを試す(前編)

(上のアイキャッチ画像の無断転載・流用を禁止します。)


こんにちは。GMOグローバルサイン・ホールディングスCTO室で分散型IDの研究をしている開発者の神沼@t_kanumaです。

これまでのSSIに関する拙著では主にHyperledger Indy/Ariesを試したり、その中でW3CとDIFの標準に触れたり、また軽くですがOIDFが持つ標準であるOpenID4VCI/VPにも触れてきました。今回は新たに、これまでと同様W3CとDIFの標準に則るOSSで、TBD社が開発するWeb5に触れてみた結果を共有します。

Web5が他と異なるのは、なんと言ってもDIFの標準であるDecentralized Web Nodeを実装している点だと思います。まだTechnical Preview版ということですが今回はこれを試してみます。(備考としてWeb5の進捗率が公開されています。)


(2024/1追記)この記事の内容から深掘りする形で以下のスライドを作成しました。ご興味ございましたらご参照ください。

Web5の(浅い)全体像


1. 三部構成

この記事のシリーズは前中後編で構成されます。それぞれ以下の内容です。

  1. 前編: サンプルアプリのコードを読み、動かしてみて、細かいところは抜きに全体感を把握する。
  2. 中編: Decentralized Appを実装してみる。
  3. 後編: SSI SDKの機能を使ってVC発行/検証を試す。

まず今回の前編では全体の概要を把握するために、GitHubにてTBDが提供するサンプルアプリを覗きます。詳しくは後述しますが、Web5はWeb5 JS SDKSSI SDKの2つのSDKを持ちます。サンプルアプリには2種類があり、1つはVueで作られているTODOアプリで、こちらはWeb5 JS SDKのみを使ったDecentralized Appです。もう1つはReactで作られているWeb5 JS SDKとSSI SDK両方を使ったVCの発行と検証をするアプリです。

中編では、Web5のコンテキストでいうところのCentralized AppをDecentralized Appに変える事を試みます。筆者自前の何かと色々試すときに使っている以下のメモアプリがあります。このアプリはバックエンドがAWSサーバレス環境でGraphQL APIのSPAです。これをDecentralized Appに変えることができるのか、異なるDID/DWNを持つ他者のデータに対する読み書きとそのパーミッションの設定、これがDWNの肝になる部分だと考えますが、それがワークするのかどうかを試します。(現状、こういったことができそうだと想定しています。)

Centralized App

Decentralized App

後編では、Web5/SSI SDK両方を使い、以下のような環境をイメージし、Trust Triangleがワークするか、すなわちIssuer/Verifier/Holderと3つのDWNを立てた状態でVCの発行と検証ができるのかどうかを試します。(現状、こういったことができそうだと想定しています。)

2. Decentralized Web Node


[出展] DIF Decentralized Web Node

DWNの詳細は、DIFの仕様とWeb5の開発者向けドキュメント(特にその中のLearn及びBuildメニュー)をご参照ください。

以下、個人的にポイントになると思う事です。

  1. 任意のデータのストレージとして利用でき、その目的がTrust Triangleの実現だけではない。
    比較対象としてHyperledger AriesのAgentでは、自身のAPIを通したControllerからの指示の結果として、VCやDIDの秘密鍵、他AgentとのVC発行検証のやり取りのデータなどを自身に保管していると考えます。つまりはVC発行やVP検証といった所謂Trust Triangleを達成するためだけにその目的が基本的には閉じている格好です。一方、DWNは任意の目的のデータストレージとして利用できます。そして、そのデータを使うDecentralizedなアプリケーションを開発できるということだと思います。

  2. Entityが2種類のNodeを持つ。
    比較対象としてHyperledger AriesのAgentでは、各Entity(e.g. Alice, Bob)に対しAgentは1つで、企業や団体の場合はCloud型を、個人の場合はEdge型(e.g. モバイルアプリ)のAgentを使うのが通例だと考えます。ただDIDCommのP2P通信の中でEdge型はグローバルIPを持たず、それを遠因としてMediatorというメッセージリレー専用のAgentを使います。
    DWNでは言うなれば1つのEntityが最低1つのCloud型Agent(Remote Node)と最低1つのEdge型Agent(Local Node)を持つと言えると思います。そしてLocalとRemoteはデータが同期されていることに加えて、Remote NodeはLocal NodeのMediatorとしても機能すると言えると思います。

3. Web5 OSSの全体像

Remote DWN

Web5のSDKはLocal Nodeを作ります。Remote Nodeは別途、自分で建てたり誰かの環境に相乗りする必要があるようです。Remote DWNの実装もTBDが開発しOSSとして公開しています。

リポジトリ: https://github.com/TBD54566975/dwn-server

以下のように、Web5 JS SDKのConfigでどこのRemote Nodeを使うのかを指定できます。Tech Preview版の現在は、デフォルトではTBDのRemote Nodeを使うようです。

const { web5, did: myDid } = await Web5.connect({
  techPreview: {
    dwnEndpoints: ["https://dwn.your-domain.org/"],
  },
});

出展: https://github.com/TBD54566975/web5-js

ドキュメントを読むとこのOSSはマルチテナントに対応しているようで、1つのNodeで複数のDIDに対応できるようです。(前述のTBDのRemote Nodeもそのように運用されていると考えます。)

またYouTubeのSSI Meetupチャンネル内の動画
Web5: Open to Build – Block/TBDでTBDの分散型アイデンティティの責任者の方が以下について述べています。

  • Remote NodeにはDIDの秘密鍵を置かないこと。
  • オプションでDID(の公開鍵)でデータの暗号化ができること。
  • LocalとRemote Nodeの位に差はなくマスターレスなシステムであること。

Local/Remote Nodeと各OSSの関係をまとめると以下の図のようになるでしょうか。
中編にてDecentralized Appを作る際に、自分専用のRemote Nodeを建てたいと思います。

SDK/APIの関係整理


出展: TBD Doc – API Reference

上図のように4つのSDK/APIがあります。依存関係を調べた結果、左右で互いに独立しており2つに大別できると思います。左側はDWNをベースにDecentralized Appを作るためのSDKで、右側がTrust Triangleを形成し、VC発行や検証をするためのモノです。以下、それぞれについて見ていきます。

左側: Web5 JS SDK

以下、GitHubリポジトリとNPMパッケージへのリンクです。

  1. https://github.com/TBD54566975/web5-js
  2. https://www.npmjs.com/package/@tbd54566975/web5

いくつかポイントと思うことを羅列します。

  • Web5はTypeScriptで書かれており、Node.js、ブラウザ、React Native、Electron環境で動かせるようです。
  • 上記2で依存関係を確認すると、上図のDWN JS SDKの他、TBDが開発する他のいくつかのパッケージに依存していることがわかります。またDIFが開発するION Toolsにも依存しています。このことからWeb5 JS SDKは抽象度の高いパッケージだということがわかります。
  • 上記1のAPI Documentを確認すると、DIDメソッドとしてdid:ionとdid:keyをサポートしていることがわかります。did:keyの使い所がわかりませんが、did:keyがエンドポイント情報を持たないメソッドだということを考えると、DWNをコントロールして、他者との通信のために使うメソッドとしては、以下で強調されている通り、did:ionということになるでしょうか。

Storing DIDs on ION (a Layer 2 DID network that runs on top of Bitcoin)
is a preferred design decision for the implementation of Web 5.
ION is a decentralized replacement for DNS for identity identifiers, so there
are no authorities, coordinators, tokens, or other centralized bottleneck.
出展: What is Web5?(TBD)

右側: SSI SDK

以下、GitHubリポジトリとGoのパッケージのへのリンクです。

いくつかポイントと思うことを羅列します。

  • SSI Service APIはSSI SDKをラップしていて、AriesにおけるACA-PyやAFJ ExtensionのようにREST APIをexposeします。
  • SSI SDKはGoで実装されていて、上記のNo.3のリポジトリでWASM化でき、それによりブラウザや他のJavaScript環境で動作させることができるようです。
  • SSI SDKが実装するW3CとDIFの標準が、上記No.1と2に記載されています。
  • SSI SDKはWeb5 JS SDKとは独立しているため、それ単体で機能するわけですが、サンプルアプリの1つではWASM化されたSSI SDKをWeb5 JS SDKと合わせて使うことで、VCを発行し、それをDWNに保管するという実装になっています。詳細は後述します。

オープンスタンダードまとめ

ToIP Technology StackにWeb5が実装するオープンスタンダードを当てはめてみると以下の形になるかと考えます。

注目すべきはSSI SDKにdid:ionが無いことです。did:ionの目的は前述した通りで、VCとVPの署名を用途にはしないものと考えます。DIDメソッドのラインナップを見ると、例えばIssuerによるVC署名にはdid:webを、HolderによるVP署名にはdid:keyを使うケースが想定できます。

4. Decentralized Appのサンプルを覗く

4-1. Web5 JS SDKを利用するTODOアプリ

リポジトリ: https://github.com/TBD54566975/incubating-web5-labs/tree/main/todo-app

(1) 全体像

後述の内容を踏まえて筆者が想定する全体像です。

(2) UI

シンプルなTODOアプリです。

(3) Chrome Developer Toolで確認

(3)-i. Local DWNの場所

結論から述べると、Decentralized Appの形態がブラウザの場合、Local Nodeは以下のキャプチャの通り、ブラウザ内のIndexed DB(実装がKVSであるLevelDB)の模様です。(Web5のドキュメントを読んでいてもIn-browser DWNという表現が出てきますが、つまりはこういうことだと思います。)

Web5 JS SDKが依存するDWN JS SDKのドキュメント内のArchitecture図を見ても、DWNのストレージにLevelDBを採用していることがわかります。

(3)-ii. DWNをコントロールするDIDとResolver

(おそらく一度Local NodeとDIDを生成した後のWebアプリへの初回アクセス時に)Remote DWNに繋ぐのが1つ目的だと思いますが、以下のようにIONのDIDをResolveしに行っています。レスポンスとしてDID Documentを取得しており、その中でService Endpointを見るとRemote DWNが前述した通りTechnical Preview版のデフォルトとしてTBDが運用するモノであることがわかります。

このDIDはMethod Specific IdentifierからDID DocumentをResolveできるLong-Form DIDであることがわかります。Long-Formの場合、DID DocumentはIONノードと、その背後に位置するBitcoinとIPFSに永続化する必要がありません。SDKのコードを読むと、Tech Preview版のためかIONノードに永続化しに行っていないように見えます。具体的には、https://github.com/TBD54566975/web5-js/blob/v0.7.11/packages/dids/src/did-ion.ts にてhttps://github.com/decentralized-identity/ion-tools/blob/main/src/did.js を利用しDIDを作りますが、この時にIONにDIDの各種オペレーション(Createなど)を要求するdid.jsのgenerateRequest(..)メソッドを呼び出してはいません。Long-FormのMethod Specific Identifierを作るに留まっています。

Resolveのリクエスト

レスポンス

DIFのUniversal ResolverからもこのDIDをResolveすることができました。(前述したようにIONに永続化していないため、Short-Formによる問い合わせでは404 Not Foundの結果に終わります。)

またWeb5 JS SDKは、マイクロソフトが運用するResolver(もしくはIONノード自体)にアクセスしている模様です。

(3)-iii. Remote Nodeとの同期?

1秒間隔程度で、2つのRemote Nodeに対しPOSTでポーリングしていることがわかります。Remote Nodeと同期しているということでしょうか?

リクエスト

注目すべきは以下のリクエストヘッダーの”Dwn-Request”です。
DWN仕様の9. Messagesのサンプルデータにかなり近しい内容だと思います。
targetとして先のDIDが記載されており、Remote Node側でのデータ格納の振り分けに使われるのかと考えます。またauthorization.payloadとauthorization.signatures.protectedのデコードした値を下記に記載します。先のDIDで署名されてRemote Nodeに送られているかと考えます。

Dwn-Request

{
   "jsonrpc":"2.0",
   "id":"4bee3fed-8453-41d6-bf39-21a0d587778b",
   "method":"dwn.processMessage",
   "params":{
      "target":"did:ion:EiB9xVg-zxXthAusSC6fFLDKmgdeweDYucHEkBzzJQUgew:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJhdXRoeiIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJBWFBlVzRrT3JNd2VsSzVjRWdHaEp4bUd4VFBfeWFUTk9yT2lkc2ZERGxVIiwieSI6IkRXS0RJcmNiNVl3a2JaZ1YxWkk4a0xfSG5tcEs0MUFSRllCekMweVA5UDQifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6Ikpzb25XZWJLZXkyMDIwIn0seyJpZCI6ImVuYyIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJqX2pvakFXOFdXRV82TjJaaDhTYTRRZUN5WlVFdmh5V29HVDByaHM1OFVVIiwieSI6Ikk0NDgwdjllcnEyb2ZLWU1QSnFqYWJ3aGUzSzVYQUFFZmVhcGNQdHpWNlkifSwicHVycG9zZXMiOlsia2V5QWdyZWVtZW50Il0sInR5cGUiOiJKc29uV2ViS2V5MjAyMCJ9XSwic2VydmljZXMiOlt7ImlkIjoiZHduIiwic2VydmljZUVuZHBvaW50Ijp7Im1lc3NhZ2VBdXRob3JpemF0aW9uS2V5cyI6WyIjYXV0aHoiXSwibm9kZXMiOlsiaHR0cHM6Ly9kd24udGJkZGV2Lm9yZy9kd24xIiwiaHR0cHM6Ly9kd24udGJkZGV2Lm9yZy9kd240Il0sInJlY29yZEVuY3J5cHRpb25LZXlzIjpbIiNlbmMiXX0sInR5cGUiOiJEZWNlbnRyYWxpemVkV2ViTm9kZSJ9XX19XSwidXBkYXRlQ29tbWl0bWVudCI6IkVpQ3hTWXNaWjNvYXpXMFVuVUo0LW1hQjRFMDNWdFhMenVjdjBMT1A4N2N5SlEifSwic3VmZml4RGF0YSI6eyJkZWx0YUhhc2giOiJFaUQ4MVhld2FWTE5nNkM0M2YxcW4zX0RXRTdWQmFIbTFRWkNpQ1VxMlVrTUtBIiwicmVjb3ZlcnlDb21taXRtZW50IjoiRWlCX2hhT1BNMWpUSFFkQUs3UmxnUUpzR3NsZE5BV3ZOT0pUU2x2RThmRU0wUSJ9fQ",
      "message":{
         "descriptor":{
            "interface":"Events",
            "method":"Get",
            "watermark":"01H62VCKE0CHC821C1B6JK5J7D"
         },
         "authorization":{
            "payload":"eyJkZXNjcmlwdG9yQ2lkIjoiYmFmeXJlaWdxZGE2cmN5aWlicXpvb2duZXR1ZDNvbHRneHBhYWlic2R0cHR2dm82ZHhnb2R0cWdvanEifQ",
            "signatures":[
               {
                  "protected":"eyJhbGciOiJzZWNwMjU2azEiLCJraWQiOiJkaWQ6aW9uOkVpQjl4VmctenhYdGhBdXNTQzZmRkxES21nZGV3ZURZdWNIRWtCenpKUVVnZXc6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSnlaWEJzWVdObElpd2laRzlqZFcxbGJuUWlPbnNpY0hWaWJHbGpTMlY1Y3lJNlczc2lhV1FpT2lKaGRYUm9laUlzSW5CMVlteHBZMHRsZVVwM2F5STZleUpqY25ZaU9pSnpaV053TWpVMmF6RWlMQ0pyZEhraU9pSkZReUlzSW5naU9pSkJXRkJsVnpSclQzSk5kMlZzU3pWalJXZEhhRXA0YlVkNFZGQmZlV0ZVVGs5eVQybGtjMlpFUkd4Vklpd2llU0k2SWtSWFMwUkpjbU5pTlZsM2EySmFaMVl4V2trNGEweGZTRzV0Y0VzME1VRlNSbGxDZWtNd2VWQTVVRFFpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4wc2V5SnBaQ0k2SW1WdVl5SXNJbkIxWW14cFkwdGxlVXAzYXlJNmV5SmpjbllpT2lKelpXTndNalUyYXpFaUxDSnJkSGtpT2lKRlF5SXNJbmdpT2lKcVgycHZha0ZYT0ZkWFJWODJUakphYURoVFlUUlJaVU41V2xWRmRtaDVWMjlIVkRCeWFITTFPRlZWSWl3aWVTSTZJa2swTkRnd2RqbGxjbkV5YjJaTFdVMVFTbkZxWVdKM2FHVXpTelZZUVVGRlptVmhjR05RZEhwV05sa2lmU3dpY0hWeWNHOXpaWE1pT2xzaWEyVjVRV2R5WldWdFpXNTBJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYU3dpYzJWeWRtbGpaWE1pT2x0N0ltbGtJam9pWkhkdUlpd2ljMlZ5ZG1salpVVnVaSEJ2YVc1MElqcDdJbTFsYzNOaFoyVkJkWFJvYjNKcGVtRjBhVzl1UzJWNWN5STZXeUlqWVhWMGFIb2lYU3dpYm05a1pYTWlPbHNpYUhSMGNITTZMeTlrZDI0dWRHSmtaR1YyTG05eVp5OWtkMjR4SWl3aWFIUjBjSE02THk5a2QyNHVkR0prWkdWMkxtOXlaeTlrZDI0MElsMHNJbkpsWTI5eVpFVnVZM0o1Y0hScGIyNUxaWGx6SWpwYklpTmxibU1pWFgwc0luUjVjR1VpT2lKRVpXTmxiblJ5WVd4cGVtVmtWMlZpVG05a1pTSjlYWDE5WFN3aWRYQmtZWFJsUTI5dGJXbDBiV1Z1ZENJNklrVnBRM2hUV1hOYVdqTnZZWHBYTUZWdVZVbzBMVzFoUWpSRk1ETldkRmhNZW5WamRqQk1UMUE0TjJONVNsRWlmU3dpYzNWbVptbDRSR0YwWVNJNmV5SmtaV3gwWVVoaGMyZ2lPaUpGYVVRNE1WaGxkMkZXVEU1bk5rTTBNMll4Y1c0elgwUlhSVGRXUW1GSWJURlJXa05wUTFWeE1sVnJUVXRCSWl3aWNtVmpiM1psY25sRGIyMXRhWFJ0Wlc1MElqb2lSV2xDWDJoaFQxQk5NV3BVU0ZGa1FVczNVbXhuVVVwelIzTnNaRTVCVjNaT1QwcFVVMngyUlRobVJVMHdVU0o5ZlEjZGlkOmlvbjpFaUI5eFZnLXp4WHRoQXVzU0M2ZkZMREttZ2Rld2VEWXVjSEVrQnp6SlFVZ2V3OmV5SmtaV3gwWVNJNmV5SndZWFJqYUdWeklqcGJleUpoWTNScGIyNGlPaUp5WlhCc1lXTmxJaXdpWkc5amRXMWxiblFpT25zaWNIVmliR2xqUzJWNWN5STZXM3NpYVdRaU9pSmhkWFJvZWlJc0luQjFZbXhwWTB0bGVVcDNheUk2ZXlKamNuWWlPaUp6WldOd01qVTJhekVpTENKcmRIa2lPaUpGUXlJc0luZ2lPaUpCV0ZCbFZ6UnJUM0pOZDJWc1N6VmpSV2RIYUVwNGJVZDRWRkJmZVdGVVRrOXlUMmxrYzJaRVJHeFZJaXdpZVNJNklrUlhTMFJKY21OaU5WbDNhMkphWjFZeFdrazRhMHhmU0c1dGNFczBNVUZTUmxsQ2VrTXdlVkE1VURRaWZTd2ljSFZ5Y0c5elpYTWlPbHNpWVhWMGFHVnVkR2xqWVhScGIyNGlYU3dpZEhsd1pTSTZJa3B6YjI1WFpXSkxaWGt5TURJd0luMHNleUpwWkNJNkltVnVZeUlzSW5CMVlteHBZMHRsZVVwM2F5STZleUpqY25ZaU9pSnpaV053TWpVMmF6RWlMQ0pyZEhraU9pSkZReUlzSW5naU9pSnFYMnB2YWtGWE9GZFhSVjgyVGpKYWFEaFRZVFJSWlVONVdsVkZkbWg1VjI5SFZEQnlhSE0xT0ZWVklpd2llU0k2SWtrME5EZ3dkamxsY25FeWIyWkxXVTFRU25GcVlXSjNhR1V6U3pWWVFVRkZabVZoY0dOUWRIcFdObGtpZlN3aWNIVnljRzl6WlhNaU9sc2lhMlY1UVdkeVpXVnRaVzUwSWwwc0luUjVjR1VpT2lKS2MyOXVWMlZpUzJWNU1qQXlNQ0o5WFN3aWMyVnlkbWxqWlhNaU9sdDdJbWxrSWpvaVpIZHVJaXdpYzJWeWRtbGpaVVZ1WkhCdmFXNTBJanA3SW0xbGMzTmhaMlZCZFhSb2IzSnBlbUYwYVc5dVMyVjVjeUk2V3lJallYVjBhSG9pWFN3aWJtOWtaWE1pT2xzaWFIUjBjSE02THk5a2QyNHVkR0prWkdWMkxtOXlaeTlrZDI0eElpd2lhSFIwY0hNNkx5OWtkMjR1ZEdKa1pHVjJMbTl5Wnk5a2QyNDBJbDBzSW5KbFkyOXlaRVZ1WTNKNWNIUnBiMjVMWlhseklqcGJJaU5sYm1NaVhYMHNJblI1Y0dVaU9pSkVaV05sYm5SeVlXeHBlbVZrVjJWaVRtOWtaU0o5WFgxOVhTd2lkWEJrWVhSbFEyOXRiV2wwYldWdWRDSTZJa1ZwUTNoVFdYTmFXak52WVhwWE1GVnVWVW8wTFcxaFFqUkZNRE5XZEZoTWVuVmpkakJNVDFBNE4yTjVTbEVpZlN3aWMzVm1abWw0UkdGMFlTSTZleUprWld4MFlVaGhjMmdpT2lKRmFVUTRNVmhsZDJGV1RFNW5Oa00wTTJZeGNXNHpYMFJYUlRkV1FtRkliVEZSV2tOcFExVnhNbFZyVFV0Qklpd2ljbVZqYjNabGNubERiMjF0YVhSdFpXNTBJam9pUldsQ1gyaGhUMUJOTVdwVVNGRmtRVXMzVW14blVVcHpSM05zWkU1QlYzWk9UMHBVVTJ4MlJUaG1SVTB3VVNKOWZRI2F1dGh6In0",
                  "signature":"HRzJtmFCRi7HpU1K7WzZ9INhk0DsMHqT5bVO88wJEEgrvEbGzIzqcvCfvzpURLuHIuoj6m1ehlHMDA1Cv37jeg"
               }
            ]
         }
      }
   }
}

authorization.payload

{
  "descriptorCid": "bafyreigqda6rcyiibqzoognetud3oltgxpaaibsdtptvvo6dxgodtqgojq"
}

authorization.signatures.protected

{
  "alg": "secp256k1",
  "kid": "did:ion:EiB9xVg-zxXthAusSC6fFLDKmgdeweDYucHEkBzzJQUgew:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJhdXRoeiIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJBWFBlVzRrT3JNd2VsSzVjRWdHaEp4bUd4VFBfeWFUTk9yT2lkc2ZERGxVIiwieSI6IkRXS0RJcmNiNVl3a2JaZ1YxWkk4a0xfSG5tcEs0MUFSRllCekMweVA5UDQifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6Ikpzb25XZWJLZXkyMDIwIn0seyJpZCI6ImVuYyIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJqX2pvakFXOFdXRV82TjJaaDhTYTRRZUN5WlVFdmh5V29HVDByaHM1OFVVIiwieSI6Ikk0NDgwdjllcnEyb2ZLWU1QSnFqYWJ3aGUzSzVYQUFFZmVhcGNQdHpWNlkifSwicHVycG9zZXMiOlsia2V5QWdyZWVtZW50Il0sInR5cGUiOiJKc29uV2ViS2V5MjAyMCJ9XSwic2VydmljZXMiOlt7ImlkIjoiZHduIiwic2VydmljZUVuZHBvaW50Ijp7Im1lc3NhZ2VBdXRob3JpemF0aW9uS2V5cyI6WyIjYXV0aHoiXSwibm9kZXMiOlsiaHR0cHM6Ly9kd24udGJkZGV2Lm9yZy9kd24xIiwiaHR0cHM6Ly9kd24udGJkZGV2Lm9yZy9kd240Il0sInJlY29yZEVuY3J5cHRpb25LZXlzIjpbIiNlbmMiXX0sInR5cGUiOiJEZWNlbnRyYWxpemVkV2ViTm9kZSJ9XX19XSwidXBkYXRlQ29tbWl0bWVudCI6IkVpQ3hTWXNaWjNvYXpXMFVuVUo0LW1hQjRFMDNWdFhMenVjdjBMT1A4N2N5SlEifSwic3VmZml4RGF0YSI6eyJkZWx0YUhhc2giOiJFaUQ4MVhld2FWTE5nNkM0M2YxcW4zX0RXRTdWQmFIbTFRWkNpQ1VxMlVrTUtBIiwicmVjb3ZlcnlDb21taXRtZW50IjoiRWlCX2hhT1BNMWpUSFFkQUs3UmxnUUpzR3NsZE5BV3ZOT0pUU2x2RThmRU0wUSJ9fQ#did:ion:EiB9xVg-zxXthAusSC6fFLDKmgdeweDYucHEkBzzJQUgew:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJhdXRoeiIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJBWFBlVzRrT3JNd2VsSzVjRWdHaEp4bUd4VFBfeWFUTk9yT2lkc2ZERGxVIiwieSI6IkRXS0RJcmNiNVl3a2JaZ1YxWkk4a0xfSG5tcEs0MUFSRllCekMweVA5UDQifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6Ikpzb25XZWJLZXkyMDIwIn0seyJpZCI6ImVuYyIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJqX2pvakFXOFdXRV82TjJaaDhTYTRRZUN5WlVFdmh5V29HVDByaHM1OFVVIiwieSI6Ikk0NDgwdjllcnEyb2ZLWU1QSnFqYWJ3aGUzSzVYQUFFZmVhcGNQdHpWNlkifSwicHVycG9zZXMiOlsia2V5QWdyZWVtZW50Il0sInR5cGUiOiJKc29uV2ViS2V5MjAyMCJ9XSwic2VydmljZXMiOlt7ImlkIjoiZHduIiwic2VydmljZUVuZHBvaW50Ijp7Im1lc3NhZ2VBdXRob3JpemF0aW9uS2V5cyI6WyIjYXV0aHoiXSwibm9kZXMiOlsiaHR0cHM6Ly9kd24udGJkZGV2Lm9yZy9kd24xIiwiaHR0cHM6Ly9kd24udGJkZGV2Lm9yZy9kd240Il0sInJlY29yZEVuY3J5cHRpb25LZXlzIjpbIiNlbmMiXX0sInR5cGUiOiJEZWNlbnRyYWxpemVkV2ViTm9kZSJ9XX19XSwidXBkYXRlQ29tbWl0bWVudCI6IkVpQ3hTWXNaWjNvYXpXMFVuVUo0LW1hQjRFMDNWdFhMenVjdjBMT1A4N2N5SlEifSwic3VmZml4RGF0YSI6eyJkZWx0YUhhc2giOiJFaUQ4MVhld2FWTE5nNkM0M2YxcW4zX0RXRTdWQmFIbTFRWkNpQ1VxMlVrTUtBIiwicmVjb3ZlcnlDb21taXRtZW50IjoiRWlCX2hhT1BNMWpUSFFkQUs3UmxnUUpzR3NsZE5BV3ZOT0pUU2x2RThmRU0wUSJ9fQ#authz"
}

レスポンス

(3)-iv. データ書き込み時の振る舞い

Chrome Developer ToolでなぜかPayloadのタブが出てこず、Bodyの内容がわかりませんが、レスポンスを見るとRemote Nodeに対し書き込みが行われていることがわかります。(ヘッダーのDwn-Requestの内容は上記と同様でした。)

レスポンス

{
    "jsonrpc": "2.0",
    "id": "f5b35f6c-62e5-4cb1-9865-6da5bf6e7a75",
    "result": {
        "reply": {
            "status": {
                "code": 200,
                "detail": "OK"
            },
            "messages": [
                {
                    "messageCid": "bafyreidgqjszoqqj3worf6c3gnc5yutbc3soktrcuqtv2o2peknyb6rkwa",
                    "message": {
                        "recordId": "bafyreicdbwx3ohpmtroxt3hgrk6t5q6yu5jt6nuuio5dubjpylcdkld54i",
                        "descriptor": {
                            "method": "Write",
                            "schema": "http://some-schema-registry.org/todo",
                            "dataCid": "bafkreihixbdrqpxa2l3kbez2twl2ibdv5l2grr3rh4r64m7zth7nyh7juy",
                            "dataSize": 54,
                            "interface": "Records",
                            "dataFormat": "application/json",
                            "dateCreated": "2023-07-24T04:38:32.016504Z",
                            "dateModified": "2023-07-24T04:38:32.016504Z"
                        },
                        "authorization": {
                            "payload": "eyJyZWNvcmRJZCI6ImJhZnlyZWljZGJ3eDNvaHBtdHJveHQzaGdyazZ0NXE2eXU1anQ2bnV1aW81ZHVianB5bGNka2xkNTRpIiwiZGVzY3JpcHRvckNpZCI6ImJhZnlyZWliajQ1dmVhbWp2d2czNDR3eXU1bDZjbzNwYnlhcndjNWJ6cmdldHk1dzVsZGVwaDJndWJpIn0",
                            "signatures": [
                                {
                                    "protected": "eyJhbGciOiJzZWNwMjU2azEiLCJraWQiOiJkaWQ6aW9uOkVpQjl4VmctenhYdGhBdXNTQzZmRkxES21nZGV3ZURZdWNIRWtCenpKUVVnZXc6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSnlaWEJzWVdObElpd2laRzlqZFcxbGJuUWlPbnNpY0hWaWJHbGpTMlY1Y3lJNlczc2lhV1FpT2lKaGRYUm9laUlzSW5CMVlteHBZMHRsZVVwM2F5STZleUpqY25ZaU9pSnpaV053TWpVMmF6RWlMQ0pyZEhraU9pSkZReUlzSW5naU9pSkJXRkJsVnpSclQzSk5kMlZzU3pWalJXZEhhRXA0YlVkNFZGQmZlV0ZVVGs5eVQybGtjMlpFUkd4Vklpd2llU0k2SWtSWFMwUkpjbU5pTlZsM2EySmFaMVl4V2trNGEweGZTRzV0Y0VzME1VRlNSbGxDZWtNd2VWQTVVRFFpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4wc2V5SnBaQ0k2SW1WdVl5SXNJbkIxWW14cFkwdGxlVXAzYXlJNmV5SmpjbllpT2lKelpXTndNalUyYXpFaUxDSnJkSGtpT2lKRlF5SXNJbmdpT2lKcVgycHZha0ZYT0ZkWFJWODJUakphYURoVFlUUlJaVU41V2xWRmRtaDVWMjlIVkRCeWFITTFPRlZWSWl3aWVTSTZJa2swTkRnd2RqbGxjbkV5YjJaTFdVMVFTbkZxWVdKM2FHVXpTelZZUVVGRlptVmhjR05RZEhwV05sa2lmU3dpY0hWeWNHOXpaWE1pT2xzaWEyVjVRV2R5WldWdFpXNTBJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYU3dpYzJWeWRtbGpaWE1pT2x0N0ltbGtJam9pWkhkdUlpd2ljMlZ5ZG1salpVVnVaSEJ2YVc1MElqcDdJbTFsYzNOaFoyVkJkWFJvYjNKcGVtRjBhVzl1UzJWNWN5STZXeUlqWVhWMGFIb2lYU3dpYm05a1pYTWlPbHNpYUhSMGNITTZMeTlrZDI0dWRHSmtaR1YyTG05eVp5OWtkMjR4SWl3aWFIUjBjSE02THk5a2QyNHVkR0prWkdWMkxtOXlaeTlrZDI0MElsMHNJbkpsWTI5eVpFVnVZM0o1Y0hScGIyNUxaWGx6SWpwYklpTmxibU1pWFgwc0luUjVjR1VpT2lKRVpXTmxiblJ5WVd4cGVtVmtWMlZpVG05a1pTSjlYWDE5WFN3aWRYQmtZWFJsUTI5dGJXbDBiV1Z1ZENJNklrVnBRM2hUV1hOYVdqTnZZWHBYTUZWdVZVbzBMVzFoUWpSRk1ETldkRmhNZW5WamRqQk1UMUE0TjJONVNsRWlmU3dpYzNWbVptbDRSR0YwWVNJNmV5SmtaV3gwWVVoaGMyZ2lPaUpGYVVRNE1WaGxkMkZXVEU1bk5rTTBNMll4Y1c0elgwUlhSVGRXUW1GSWJURlJXa05wUTFWeE1sVnJUVXRCSWl3aWNtVmpiM1psY25sRGIyMXRhWFJ0Wlc1MElqb2lSV2xDWDJoaFQxQk5NV3BVU0ZGa1FVczNVbXhuVVVwelIzTnNaRTVCVjNaT1QwcFVVMngyUlRobVJVMHdVU0o5ZlEjZGlkOmlvbjpFaUI5eFZnLXp4WHRoQXVzU0M2ZkZMREttZ2Rld2VEWXVjSEVrQnp6SlFVZ2V3OmV5SmtaV3gwWVNJNmV5SndZWFJqYUdWeklqcGJleUpoWTNScGIyNGlPaUp5WlhCc1lXTmxJaXdpWkc5amRXMWxiblFpT25zaWNIVmliR2xqUzJWNWN5STZXM3NpYVdRaU9pSmhkWFJvZWlJc0luQjFZbXhwWTB0bGVVcDNheUk2ZXlKamNuWWlPaUp6WldOd01qVTJhekVpTENKcmRIa2lPaUpGUXlJc0luZ2lPaUpCV0ZCbFZ6UnJUM0pOZDJWc1N6VmpSV2RIYUVwNGJVZDRWRkJmZVdGVVRrOXlUMmxrYzJaRVJHeFZJaXdpZVNJNklrUlhTMFJKY21OaU5WbDNhMkphWjFZeFdrazRhMHhmU0c1dGNFczBNVUZTUmxsQ2VrTXdlVkE1VURRaWZTd2ljSFZ5Y0c5elpYTWlPbHNpWVhWMGFHVnVkR2xqWVhScGIyNGlYU3dpZEhsd1pTSTZJa3B6YjI1WFpXSkxaWGt5TURJd0luMHNleUpwWkNJNkltVnVZeUlzSW5CMVlteHBZMHRsZVVwM2F5STZleUpqY25ZaU9pSnpaV053TWpVMmF6RWlMQ0pyZEhraU9pSkZReUlzSW5naU9pSnFYMnB2YWtGWE9GZFhSVjgyVGpKYWFEaFRZVFJSWlVONVdsVkZkbWg1VjI5SFZEQnlhSE0xT0ZWVklpd2llU0k2SWtrME5EZ3dkamxsY25FeWIyWkxXVTFRU25GcVlXSjNhR1V6U3pWWVFVRkZabVZoY0dOUWRIcFdObGtpZlN3aWNIVnljRzl6WlhNaU9sc2lhMlY1UVdkeVpXVnRaVzUwSWwwc0luUjVjR1VpT2lKS2MyOXVWMlZpUzJWNU1qQXlNQ0o5WFN3aWMyVnlkbWxqWlhNaU9sdDdJbWxrSWpvaVpIZHVJaXdpYzJWeWRtbGpaVVZ1WkhCdmFXNTBJanA3SW0xbGMzTmhaMlZCZFhSb2IzSnBlbUYwYVc5dVMyVjVjeUk2V3lJallYVjBhSG9pWFN3aWJtOWtaWE1pT2xzaWFIUjBjSE02THk5a2QyNHVkR0prWkdWMkxtOXlaeTlrZDI0eElpd2lhSFIwY0hNNkx5OWtkMjR1ZEdKa1pHVjJMbTl5Wnk5a2QyNDBJbDBzSW5KbFkyOXlaRVZ1WTNKNWNIUnBiMjVMWlhseklqcGJJaU5sYm1NaVhYMHNJblI1Y0dVaU9pSkVaV05sYm5SeVlXeHBlbVZrVjJWaVRtOWtaU0o5WFgxOVhTd2lkWEJrWVhSbFEyOXRiV2wwYldWdWRDSTZJa1ZwUTNoVFdYTmFXak52WVhwWE1GVnVWVW8wTFcxaFFqUkZNRE5XZEZoTWVuVmpkakJNVDFBNE4yTjVTbEVpZlN3aWMzVm1abWw0UkdGMFlTSTZleUprWld4MFlVaGhjMmdpT2lKRmFVUTRNVmhsZDJGV1RFNW5Oa00wTTJZeGNXNHpYMFJYUlRkV1FtRkliVEZSV2tOcFExVnhNbFZyVFV0Qklpd2ljbVZqYjNabGNubERiMjF0YVhSdFpXNTBJam9pUldsQ1gyaGhUMUJOTVdwVVNGRmtRVXMzVW14blVVcHpSM05zWkU1QlYzWk9UMHBVVTJ4MlJUaG1SVTB3VVNKOWZRI2F1dGh6In0",
                                    "signature": "IAz24oFpdMQA7SzH_QVHC7XiRGWTIUnAyBUqoulm8_8sjMfYQTDRw3CKdYXMkqxr-zW8t042D1LjTqOdg4i3gg"
                                }
                            ]
                        }
                    },
                    "encodedData": "eyJjb21wbGV0ZWQiOmZhbHNlLCJkZXNjcmlwdGlvbiI6Iuiyt-OBhOeJqeOBq-ihjOOBjyJ9"
                }
            ]
        }
    }
}

上記レスポンス内のencodedData

{
  "completed": false,
  "description": "買い物に行く"
}

4-2. Web5 SDKとSSI SDKを併用して、VC発行検証するアプリ

リポジトリ: https://github.com/TBD54566975/incubating-web5-labs/tree/main/dwn-verifiable-credentials-demo

このアプリはReact製で、Web5 JS SDKとWASM化されたSSI SDKを併用して使います。SSI SDKの持つインターフェイスは以下の通りです。

// src/LoadWasm/wasmTypes.d.tsより
export interface Window {
  Go: any;
  createDIDKey: ()=>any
  parseJWTCredential: (jwt: string)=>any
  createVerifiableCredential: (issuerDID: string, issuerDIDPrivateKey: string, subjectJSON: string)=>any
  verifyJWTCredential: (vcJWT: string, publicKeyBase58: string)=>any
}

またこのアプリは機能として、UIから以下を行うことができます。
DWNをベースにSSI SDKをどのように扱えば良いのかを学べると思います。

  • VC(LDP-VC or JWT-VC)の発行
  • DWN(Local Node)へのVCの保管(Remote Nodeは無し)
  • VCの検証を行う

またこのアプリは、Issuer/Holder/Verifierとして一人三役を演じます。

  • Holder: DWNが紐づくdid:ionを持ちます。
  • Issuer:
    • VCに署名するためのDIDとして、デフォルト値はMethod Specific Identifierから公開鍵を導出できるdid:key(とその秘密鍵)が設定されています。
    • UIから、SSI SDKがサポートするメソッドの任意のDIDに変更できます。
    • DIF Credential Manifestに則るVC発行の機能はサンプルアプリ上は無いようです。これから実装されるのか、前述のインターフェイスに取り込んでいないのかはわかっていません。
  • Verifier
    • VPではなくVCを検証するようです。
    • DIF Presentation Exchangeに則るVP作成と検証の機能はこのサンプルアプリ上は無いようです。これから実装されるのか、前述のインターフェイスに取り込んでいないのかはわかっていません。

UIの設定とVC発行の様子

デフォルト設定で発行される(Proof部分を除く)JWT VC

{
  "iss": "did:key:z6Mkuxr8y6uyaze1UoXXGHhuZBcuon8yiNEh12Hh85V8hEx4",
  "jti": "adc6ddbd-c934-438b-88ff-b9b94e9f194d",
  "nbf": 1690180779,
  "sub": "did:ion:EiAyhxEhU87xYa1Efi6WfNIVflSFas8-th3uNlmRLJhuaw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJDd0lWQk85WXROOUxhOUhFY3FoS2lQWjQzM2tqLW50YUpIWUFVVGRWTTI0IiwieSI6IkZFME0xalpqeGtHWmZoZWFoYm9GSGxZRHB5cnJaRFN5X01HOW43b01lR2sifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6Ikpzb25XZWJLZXkyMDIwIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlDcWFPWi1Md1BmQ1piYjdwTXpDQW42QjhjWVRyVDRfX0ZIUFFhb1BvWnBtdyJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQS1ubFNkZDhVQXBRQ2ZSc1UySmtZeUFxZ0ZFelZIWGlkMTFXazZzSnNFWFEiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJFT3ktR0lZeW9BNHJnOTJrMlFRTzd2b2k2UU85QkoyNWNiZmFrTHBlU1FnIn19",
  "vc": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1"
    ],
    "id": "adc6ddbd-c934-438b-88ff-b9b94e9f194d",
    "type": [
      "VerifiableCredential"
    ],
    "issuer": "did:key:z6Mkuxr8y6uyaze1UoXXGHhuZBcuon8yiNEh12Hh85V8hEx4",
    "issuanceDate": "2023-07-24T15:39:39+09:00",
    "credentialSubject": {
      "id": "did:ion:EiAyhxEhU87xYa1Efi6WfNIVflSFas8-th3uNlmRLJhuaw:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljS2V5cyI6W3siaWQiOiJrZXktMSIsInB1YmxpY0tleUp3ayI6eyJjcnYiOiJzZWNwMjU2azEiLCJrdHkiOiJFQyIsIngiOiJDd0lWQk85WXROOUxhOUhFY3FoS2lQWjQzM2tqLW50YUpIWUFVVGRWTTI0IiwieSI6IkZFME0xalpqeGtHWmZoZWFoYm9GSGxZRHB5cnJaRFN5X01HOW43b01lR2sifSwicHVycG9zZXMiOlsiYXV0aGVudGljYXRpb24iXSwidHlwZSI6Ikpzb25XZWJLZXkyMDIwIn1dfX1dLCJ1cGRhdGVDb21taXRtZW50IjoiRWlDcWFPWi1Md1BmQ1piYjdwTXpDQW42QjhjWVRyVDRfX0ZIUFFhb1BvWnBtdyJ9LCJzdWZmaXhEYXRhIjp7ImRlbHRhSGFzaCI6IkVpQS1ubFNkZDhVQXBRQ2ZSc1UySmtZeUFxZ0ZFelZIWGlkMTFXazZzSnNFWFEiLCJyZWNvdmVyeUNvbW1pdG1lbnQiOiJFaUJFT3ktR0lZeW9BNHJnOTJrMlFRTzd2b2k2UU85QkoyNWNiZmFrTHBlU1FnIn19",
      "web5OG": true
    }
  }
}

5. 理解できていないこと、疑問に思うこと

  • Web5の肝となると考える、Protocolsについて
  • LocalとRemote Node間でのメッセージ送受信は必ずHTTPSで行う必要があるのか?それともセキュアな通信はDWNが保証できるのか?DWNがメッセージに暗号化と署名を施せるのか?(DWNの仕様、前述のDWN Serverのドキュメント、サンプルアプリでの振る舞いを総合的に見ると釈然としない部分)
  • ブラウザ、モバイル、デスクトップそれぞれの環境でのLocal Nodeにおける、DIDの秘密鍵の管理手法について
  • Protocolsによるアクセスコントロールとデータ暗号化の関連性。暗号化したデータは他者と共有できるのか?それとも自分専用のモノになるのか?
  • ION DIDの秘密鍵も複数のLocal Node間で同期(複製)されるのか?
  • 既存のRemote Nodeに対して新たにLocal Nodeを足すときにどう認証するのか?逆にDID Documentに新たにRemote Nodeを足したときはどう同期するのか?

このあたりはDecentralized AppやTrust Triangleを実装する上ではSDKに任せている部分であり、把握してなくても実現できそうではありますが、説明責任を果たすために理解したいと思う部分です。今後DWNの仕様やSDKのコードを読み、アプリコードを書いて動かしながら学んでいきたいと思います。

おわりに

細かい部分までの理解が及んでおらず疑問点は多々残りますが、全体像は少し見えてきた気がします。

W3CとDIFの標準に則るOSSの種類、選択肢が増えたということは、SSI/DID/VC分野のアプリ開発者としてとてもありがたいと思います。

どなたかのお役に立てたならば幸いです。中編に続きます。