2022年11月6日日曜日

発行済みVerifiable Credentialsの状態確認を行うStatus List 2021の仕様と動作

こんにちは、富士榮です。

今日はちょっとマニアックなところを。(完全に自分向けのメモです)


Microsoft Entra Verified IDは発行済みのVerifiable Credentialsの状態(有効・取り消し済み)を管理するためにDecentralized Identity Foundation(DIF)のStatus List 2021とIdentity Hubという仕様を使っています。

ちなみにその後Identity Hubはdecentralized web nodeという仕様になっているので、もうIdentity Hubの仕様はすでに古いものとなっています。

Decentralized Web Nodeの仕様


基本的にやりたいことはとしては、

  • 発行されたVerifiable Credentialの状態をHolderやVerifierは確認したい
  • ただ、Issuerに状態を問い合わせるのでは従来の集中モデルと変わらずプライバシー保護の観点などからも好ましくない
ということなので、Issuerに問い合わせしなくてもVerifiable Credentialの有効状態を確認できる必要があります。

ということでStatusList2021CredentialというタイプのVerifiable Credentialを発行し、IssuerのIdentity Hubに発行、他のノード等からIssuerが把握することなく閲覧が可能な状態を実現します。
なお、スケーラビリティへの考慮から発行済みVerifiable Credentialsのリストをなるべくコンパクトに保持する必要があり、ビット配列で状態を保持する形の仕様となっています。


今回はHolderやVerifier(主にはこちらが確認することが多いとは思いますが)などVerifiable Credentialを受け取ったエンティティがどのように有効性確認を行うのか?について触れたいと思います。(あくまで現時点でのMicrosoft Entra Verified IDでの実装をベースにしています)

■大まかな流れ

大まかにはこんな流れです。
  1. Verifiable Credentialを受け取る
  2. 受け取ったVC内のCredentialStatusを取得(主に以下2つの値が重要)
    • statusListIndex
    • statusListCredential
  3. Issuer DIDのDID DocumentからIdentity HubのserviceEndpointを取得
  4. 2で取得したstatusListCredentilalからIdentity HubへPOSTすべきBodyを生成、Identity Hubへ投げ込む
  5. Identity HubからstatusList2021CredentialというTypeのVCが返ってくるのでencodedListを取得
  6. 2でとっておいたstatusListIndexでencodedListのオフセットを確認し、ビットが立っていたら取り消し済み、倒れていたら有効として判断

■実際に取得してみる

まずは、VCからCredentialStatusの取得です。これは単純にJWTをパースすればOKです。
{
  "vc": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1"
    ],
    "type": [
      "VerifiableCredential",
      "hogehogeVC"
    ],
    "credentialSubject": {
      "name": "hoge taro",
      "mail": "hoge@exampple.jp"
    },
    "credentialStatus": {
      "id": "urn:uuid:6fa4e682-f427-41cf-b0e5-59179d66fea3?bit-index=5",
      "type": "RevocationList2021Status",
      "statusListIndex": 5,
      "statusListCredential": "did:web:example.jp?service=IdentityHub&queries=W3sibWV0aG9kIjoiQ29...(省略)...jZmZWEzIn1d"
    },
    (以下省略)


大事なのはstatusListIndex、statusListCredentialなかでもqueriesの値です。
queriesはbase64urlエンコードされたjsonなのでデコードすると後からIdentity Hubへ投げ込む値が出てきます。
[
	{
		"method": "CollectionsQuery",
		"schema": "https://w3id.org/vc-status-list-2021/v1",
		"objectId": "6fa4e682-f427-41cf-b0e5-59179d66fea3"
	}
]


では、このデータをIdentity Hubに投げ込んでいきます。
まずはIdentity Hubのエンドポイントを取得する必要があります。statusListCredentialを見ると当該のDIDのserviceの種類がIdentityHubとなっているところにqueriesの値を投げ込むべし、ということが書いてあり、MSの仕様ではIdentityHubへ聞きに行く必要があるということが指し示されているからです。
ということでIssuer DIDをResolveします。これまでもこのBlogで触れてきたuniversal resolverを使ってもいいですし、Microsoftが用意しているdiscoveryエンドポイントを使ってもいいでしょう。今回はMicrosoftのものを使いました。
https://discover.did.msidentity.com/1.0/identifiers/{IssuerのDID}
でGETするとDID Documentが返ってきます。
{
  "id": "did:web:example.jp",
  "@context": [
    "https://www.w3.org/ns/did/v1",
    {
      "@base": "did:web:example.jp"
    }
  ],
  "service": [
    {
      "id": "#linkeddomains",
      "type": "LinkedDomains",
      "serviceEndpoint": {
        "origins": [
          "https://example.jp/"
        ]
      }
    },
    {
      "id": "#hub",
      "type": "IdentityHub",
      "serviceEndpoint": {
        "instances": [
          "https://hub.did.msidentity.com/v1.0/{tenant id}"
        ]
      }
    }
  ],
  (以下省略)

この中のIdentityHubのserviceEndpointの値を取得します。Entra Verified IDの場合、https://hub.did.msidentity.com/v1.0/{Azure ADのテナントID}という構造になっていますので、あらかじめわかっている場合はわざわざresolveしなくてもいいかもしれません。

このエンドポイントに先ほど取得したqueriesの値を含むjsonをPOSTすることでstatusList2021Credentialが取得できます。

まずはPOSTデータの作成です。
構造としてはこんな感じになっています。
{
  "requestId": "c5784162-84af-4aab-aff5-f1f8438dfc33",
  "target": "did:web:example.jp",
  "messages": [
    {
      "descriptor": {
        "method": "CollectionsQuery",
        "schema": "https://w3id.org/vc-status-list-2021/v1",
        "objectId": "6fa4e682-f427-41cf-b0e5-59179d66fea3"
      }
    }
  ]
}

requestIdはuuid-v4形式の値を指定します。単なるリクエストとレスポンスを紐づけるための識別子なので値はなんでもOKです。
targetはissuerのDIDを指定します。
messagesの中のdescriptorに先ほど取得したqueriesの値をセットします。

ということでPOSTすると以下のレスポンスが返ってきます。
{
    "requestId": "c5784162-84af-4aab-aff5-f1f8438dfc33",
    "replies": [
        {
            "messageId": "bafkreicksslcxa7xraqg6luqqfsezkpfalpqdqk6jzealwqxrkv7svu5vy",
            "status": {
                "code": 200
            },
            "entries": [
                {
                    "descriptor": {
                        "method": "CollectionsWrite",
                        "schema": "https://w3id.org/vc-status-list-2021/v1",
                        "dataFormat": "application/vc+jwt",
                        "objectId": "6fa4e682-f427-41cf-b0e5-59179d66fea3",
                        "clock": 0,
                        "cid": "bafkreib5cnsi7kw5ya6otq2lybvwnqlh2ga2gmud7x7t46f4zkkcca6s5y"
                    },
                    "data": "ZXlKaGJHY2lPaUpG...(省略)...IydDNXaWhR"
                }
            ]
        }
    ]
}

大事なのはdataの部分です。
この値はbase64エンコードされたVerifiable CredentialsなのでデコードするといつものeyJ...が出てきます。もちろんこのjwtの署名検証はするとして、中身を確認していきます。
{
  "vc": {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://w3id.org/vc-status-list-2021/v1"
    ],
    "type": [
      "VerifiableCredential",
      "StatusList2021Credential"
    ],
    "credentialSubject": {
      "id": "urn:uuid:6fa4e682-f427-41cf-b0e5-59179d66fea3",
      "type": "RevocationList2021",
      "encodedList": "H4sIAAAAAAAAA-3BAQEAAAgCoPw_qmsOEfgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAhAJmaUnkqGEAAA"
    }
  },
  "jti": "did:web:example.jp?service=IdentityHub&queries=W3sibWV0aG9kIjo3...(省略)...zlkNjZmZWEzIn1d",
  "iss": "did:web:example.jp",
  "sub": "urn:uuid:6fa4e682-f427-41cf-b0e5-59179d66fea3",
  "iat": 1667651604
}

大事なのはencodedListです。
この値はbase64でエンコードされgzipされたビット配列なのでデコードし、確認したいVCのstatusListIndexの値のオフセットの部分のビットの状態を確認します。

例えば、以下のような状態になっている場合は上から下に向けて1-2-4-8-...という形で桁が上がっていくのでこの例だと7番目だけがActive、あとは取り消し済みなのでencodedListは2進数でいうと「...110111111」という形で対応するIndexにあたるビットが立ちます。

ここまでわかればあとは簡単ですね。
statusListIndexに指定された値(上の例だと5)に該当するオフセットの部分をmaskしてANDをとれば当該ビットが立っているのかどうかがわかり、当該のVCの状態が把握できます。


ということで今回はStatus List 2021の仕様とMSの実装について触れました。
(実際はMSのAPIの中でよろしくやってくれるので自前でコードを書く必要はないんですけどね)






2022年11月3日木曜日

Auth0 labのVerifiable Credentialsを試してみる

こんにちは、富士榮です


これまで当BlogでもVerifiable Credentials周りは色々と触ってきましたが、今回はAuth0ラボで公開しているサービスを触ってみます。

こんな感じでCredentialを作れるサービスです。



参考)これまで触れてきたDID/Verifiable Credentials関係のポストの一部

※まぁ、古くはConsensysのuPortなんてのをガリガリ触っていた時期もあるんですが。。。


ということで一通り触ってみましょう。

注)その名の通りラボなのでプロダクションで使うのはやめましょう。

■Auth0ラボのセットアップ

まずはAuth0のラボ環境を作ります。通常のAuth0環境とはことなり、別のテナントを作る必要があります。


にアクセスするとログインが求められるのでアカウントを作るか、自前のAuth0のアカウントでログインしましょう。

■Verifiable Credentialsのテンプレートを作成

左側のナビゲーションメニューから[Credentials]をクリックするとTemplateを定義する画面が開きます。
適当にクレデンシャルの名前などを決めていきます。

一旦はここまでで、残りの見た目(Branding)などは後で設定します。

■Issuance Flowを定義

次はCredentialを発行する際の属性のマッピングなどを定義します。Microsoft Entraの場合はRulesで定義しましたが、Auth0の場合は[Actions]で定義します。
[Actions]から[Flows]を開くとテンプレートとしてVerifiable Credential Issuanceがあるので選択します。

カスタムで適当に属性のマッピングなどを定義します。この辺は当たり前ですがいつものAuth0です。
もちろん、ここで定義したapp_metadataはユーザの属性と一致させる必要がありますので、後でユーザを作成するときに属性を追加するのを忘れないようにします。

最終的にこんな感じにFlowが定義されていればOKです。

■DCRの有効化

Walletをクライアントとして登録する必要があるのですが初めからclient_idが払い出せるような世界じゃないのでDCR(Dynamic Client Registration)を有効にしておきます。
※テナント全体の設定に関わる話なので、慎重に

ナビゲーションから[Settings]、[Advanced]を開くと[OIDC Dynamic Application Registration]があるので有効にします。

■認証定義で3rd Party Clientsを有効化

3rd Party Clientsからの認証要求を有効化することでWalletの様に1st Partyではないアプリケーションからの認証要求に対応できる様にします。
これもいつもの[Authentication]から任意のConnection(今回は面倒なのでDatabase Connectionを使いました)を開き、設定内の[Enable for third party clients]を有効にします。

■ユーザの作成と属性の定義

先ほどFlowを定義した際にも述べましたが属性のマッピングを行うにはユーザに当該の属性がないといけません。
なので、ユーザを作成し、app_metadataに必要となる属性値を定義しておきます。
今回はこんな感じで定義しています。

■Credentialの定義を完成させる
最初にやってもよかったんですが、[Credentials]メニューから先ほど作成したTemplateの見た目の部分にあたる[Branding]を設定します。
と言ってもロゴや背景画像を指定するだけですが。

■いざ、発行

ここまでで設定はおしまいなので、発行してみましょう。
テスト用にブラウザで動くWalletが組み込まれているので、[Credentials]からTemplateを開いたところにある[Try Credential]をクリックするとすぐに使えます。

起動するとRequest Credentialボタンが出てくるのでクリックして発行を要求します。
ログインが求められますので、先ほど作成したユーザを使ってログインします。
属性提供に関する認可が走ります。

Credentialが表示されるので、Add CredentialでWalletにCredentialが格納されます。

■次は検証

CredentialがWalletに入ったら次は提示(Presentation)から検証ですね。
こちらもラボが用意されているので非常に簡単です。
Wallet内のCredentialをクリックすると[Try Presentation Flow]というボタンが出てくるのでクリックするだけです。
クリックするとテスト用のVerifierが起動してくるのでQRコードの下にある[Click here to continue]をクリックするとブラウザ版のWalletへPresentation Requestが行われます。
提示するCredentialを選択して[Present credential]をクリックします。

Verifiable Presentation/Credentialがテストサイトに渡されるので中身が見えます。



と、ここまでです。
非常に簡単です。


VPやVCを軽くみましたが、ld-proofなんですね。
Microsoft Authenticatorとの互換性は今のところなさそうです。もうちょいカスタマイズすれば色々とやれそうなので、引き続き触ってみようかと思います。
なんにせよ、こういう形で選択肢が増えてくるのは良いことですね!