Hyperledger Indy・Ariesによる分散型IDアプリケーション開発ガイド

3. 実装編

TIS Blockchain Promotion Office
42 min readSep 7, 2021

目次

  1. はじめに
  2. Aries(ACA-Py)を使用したアプリ開発ガイド
    開発全体の流れ
    接続確立
    Credential発行
    Presentation検証
  3. おわりに

1. はじめに

◆ この記事について

TIS Blockchain推進室では、Blockchainユースケースの1つとして分散型アイデンティティ(以下「分散型ID」)に関する技術検証やリサーチを行っています。

前回の記事では、環境構築編としてAries-CloudAgent-Python(ACA-Py)での開発環境をローカルPC上に構築するための手順を紹介しました。今回は、ACA-Pyを使用したアプリの開発方法について説明します。ACA-Pyを使用した場合の開発全体の流れを説明し、個々のステップについて具体的なAPIの呼び出し順序や、引数として与えるJSONの内容について解説していきます。

2. Aries(ACA-Py)を使用したアプリ開発ガイド

◆ 開発全体の流れ

ACA-Pyを使用してアプリケーションを開発するには、以下の3つを行います。

  1. VON-networkを起動し、DIDを登録する
  2. ACA-Pyを起動する
  3. REST-APIを呼び出す処理を実装するA CA-Pyに用意されているREST-APIを呼び出す

※1、2は環境構築編で実施しているため、本記事では3から説明します。

ACA-Pyでは、Credential発行、Presentation検証などのやり取りについて、それぞれの開始から終わりまでを一連のレコードとして内部のDBに記録しています。各やり取りのステップが進むごとにそのレコードのステータスが更新され、「{最初のステータス}」から始まり「{最後のステータス}」になると終了し、あとは参照されるだけになります。終了したやり取りの参照が不要であれば、「{最後のステータス}」まで進んだレコードは自動的に削除する、という設定も可能です。

Hyperledger IndyではCredential Offerなどを自分で管理しDBに登録しておく必要がありますが、ACA-Pyではその必要はなく効率化されています。

ACA-PyのAPIはやり取りの種類ごとにURLがグループ化されており、その構成は以下のようになっています。

表1_APIのカテゴリとURLグループ

※ Swaggerを見るとこの他にも様々なURL群がありますが、それらは開発途上のものだったり、Credential発行などの「分散型IDのコア機能」ではなかったりするため、今回は使いません。

ACA-Pyを使用したアプリ開発の流れについては以下となります。

<アプリのフロー(図)>

※VON-network、ACA-Pyが起動済みの前提

図1_Ariesでのアプリのフロー

ここから、開発におけるAPI実装のポイントについて、Swaggerを利用して解説していきます。

ACA-Pyとやり取りするJSONの項目の内、キーとなるもの(IDとなるもの)をまとめます。内容についてはこの後の説明で触れていきます。

表2_APIのカテゴリとAPIパターン

※ なお、これらのID項目の値はentity間で異なります。たとえば、接続確立の一連の流れにおいて、inviterとinviteeは互いにconnectionをやり取りしますが、inviterとinviteeでconnection_idは異なります。

◆ 接続確立

< 前提 >
ここ以降、ACA-Pyを2台立てています([Holder]/[Issuer兼Verifier])

< 接続確立を行う2者の状態遷移 >
ACA-Pyでは、接続したい2者がそれぞれinviterとinviteeに分かれ、以下の5つの状態に沿って接続確立を行います。

表3_ACA-Pyでの接続状態一覧

なお、Issuer/Holderといった役割がinviter/inviteeの役割に影響することはありません(どちらがinviter/inviteeになってもよい)

/connections
はじめに、「/connections」を実行してみます。
これは、このAgent内に保存されている接続の一覧を取得するAPIになります。

※Swaggerの使い方について
API名の部分をクリックすると詳細情報が展開され、「Try it out」を押すとパラメータの設定や実行ボタン等が展開されます。「Execute」のボタンを押すことでAPIを実行でき、その下に結果が表示されます。

まだどのAgentとも接続を確立していないため、接続一覧では何も表示されません。この状態が「null」の状態となります。

/connections/create-invitation
ここから実際に他Agentとの接続を確立していきます。接続のプロセスを開始するために、まずはinviter側で「/connections/create-invitation」を実行し、「invitation」を発行します。「invitation」は接続を始めるための招待メッセージとなり、inviteeがこれを元にConnection Requestを発行することで次のステップへと進みます。

なお、APIを実行する際、元々入っているbodyは消してしまって空JSONの状態にしてください。

APIのレスポンスとして、invitationメッセージのJSONが表示されます。

これにより、ACA-Py1(実行した側)にconnectionのレコードが1件生成され、stateが「invited」として登録されます。「/connections」を実行することで確認が可能です。
※ JSON中では「invitation」となっていますが、Ariesのプロトコル上は「invited」のため、こちらで進めていきます(これらの言葉の違いに意味はありません)。

/connections/receive-invitation
もう片方(ACA-Py2)で、「/connections/receive-invitation」を実行します。これはACA-Py1の発行したinvitationメッセージを受け取るためのAPIで、パラメータにinvitationの情報を入力します。なお、ここでは「@id」、「label」、「recipientKeys」、「serviceEndpoint」の4項目のみ指定します(必須項目群)。その他の3項目(「did」、「imageUrl」、「routingKeys」)はここでは使用しないため、削除してください。

実行すると、ACA-Py2の側にもconnectionのレコードが生成されます。

/connections/{conn_id}/accept-invitation
次に、ACA-Py2側で「/connections/{conn_id}/accept-invitation」を実行します。これは受け取ったinvitationメッセージを元にConnection Requestを発行するAPIで、接続確立のステップを次に進めるものとなります。

パラメータの「conn_id」に、「connection_id」の値を入力します。その他のパラメータについてはここでは使用しないため、無視して構いません。

実行すると、invitationを元にConnection Requestが発行され、ACA-Py1へと送信されてstateが「request」に進みます。

/connections/{conn_id}/accept-request
次に、ACA-Py1側で「/connections/{conn_id}/accept-request」を実行します。これはConnection Requestを受け入れるためのAPIで、これが完了すると「接続確立」となります。

パラメータの「conn_id」に、「connection_id」の値を入力します。その他のパラメータについては無視して構いません。「connection_id」は、ACA-Py1とACA-Py2で値が異なります。それぞれのAgentで「/connections」を実行して、connection_idの値を確認してから実施してください。

実行すると、Connection Requestを元にConnection Responseが発行され、ACA-Py2へと送信されてstateが「response」に進みます。

この状態になると接続が確立し、Credential発行などを行えるようになります。
※なお、今後この相手との初回通信時(厳密には初めてメッセージを受信した時)に、stateが「active」に更新されます。(「active」はAriesプロトコル上での「complete」を指します)

< 接続確立のAPI呼び出し順序まとめ >
接続確立を行うには、1~4までを順に呼び出します。

表4_接続確立で使用するAPIまとめ

< 開発時のTips >
ACA-Pyの起動時に以下のオプションを加えることで、上記のAPI呼び出しを一部省略できます。

どちらも付けると、実質的に「1. /connections/create-invitation」 → 「2. /connections/receive-invitaion」だけで接続を確立できます。

表5_接続確立用のオプション

◆ Credential発行

< Credential発行におけるIssuer/Holderの状態遷移 >
Credential発行は、IssuerとHolderの2者間で行います。AriesのプロトコルにおいてCredential発行に関する状態に名前はつけられていませんが、ACA-Py上では以下のように状態遷移します。

Issuer側の取る状態

表6_Credential発行 におけるIssuerの状態一覧

Holder側の取る状態

表7_Credential発行におけるHolderの状態一覧

これとは別に、Issuerは事前に「Credential Schema」、「Credential Definition」の2つを登録しておく必要があるため、ここから説明を始めます。

/schemas
まずIssuer側のACA-Pyで、「/schemas」を実行します。これはCredential Schemaを登録するためのAPIで、パラメータにはSchemaを表すJSONを入力します(ここでは学生証(「学籍番号」、「名前」、「年齢」、「住所」)とします)。

実行すると、Verifiable Data RegistryにSchemaが登録され、誰もが参照できるようになります。

schemas/{schema_id}
登録されているSchemaを参照するには、「/schemas/{shcmeaschema_id}」を実行します。この際、SchemaのIDが必要になるため、登録時のレスポンスに表示されているSchemaIDを使用するか、「/schemas/created」を実行して「自分の登録したSchemaのID一覧」を確認します。

SchemaのIDは、このように構成されています。

/credential-definitions
Credential Definitionを登録するために、「/credential-definitions」を実行します。

パラメータのJSONはほぼ初期値で大丈夫ですが、「schema_id」の値を先ほど登録したSchemaのIDに変更し、「support_revocation」はfalseとしてください。

実行すると、Verifiable Data RegistryにCredential Definitionが登録され、誰もが参照できるようになります。

※ Credential Definitionの確認手順はSchemaの際と同じパターンのため、説明は割愛します。

/issue-credential/send-offer
ここから、実際にCredential発行のステップに入っていきます。
まず、Issuer側で「/issue-credential/send-offer」を実行し、Holderに対してCredential Offerを送信します。

{
// Holder(宛先)とのconnectionID
"connection_id": "765b49e7-ee31-45cd-a285-a52c3879bdb6",
// 発行したい種類のCredentialのDefinitionID
"cred_def_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
// 発行したいCredentialの内容
"credential_preview": {
// 「Credentialの内容である」というメタ情報。常に固定。
"@type": "issue-credential/1.0/credential-preview",
// 発行予定Credentialの本体部分(項目と値のセット)
"attributes": [{
"name": "student_id",
"value": "gakuseki_0001"
},
{
"name": "name",
"value": "TIS Tarou"
},
{
"name": "age",
"value": "23"
},
{
"name": "address",
"value": "東京都江東区豊洲2-2-1 豊洲ベイサイドクロスタワー"
}
]
}
}

イメージ

/issue-credential/records
「/issue-credential/send-offer」を実行すると、IssuerからHolderにCredential Offerが送信され、そのレコードがお互いに作成されます。

「/issue-credential/records」を実行すると、内容を確認できます。
※ 非常に巨大なJSONになることと、人間に読めない情報が多いため、一部削っています。

// トップレベルは「"results"の項目だけを持ったJSONオブジェクト」の形で返される
{
// Credential発行の流れのレコードがJSONArrayとして返される(1件につきオブジェクト1つの形)
"results": [{
"credential_offer_dict": {
// 「credential offerである」というメタ情報。常に固定
"@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/offer-credential",
// 内部で使用されている情報。 Credential発行の流れには使用しない
"@id": "1e4e24c9-488c-461a-8385-9fc4432b6550",
/*

*/
"credential_preview": {
"@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview",
// 発行予定Credentialの本体部分(項目と値のセット)
"attributes": [{
"name": "student_id",
"value": "gakuseki_0001"
},
{
"name": "name",
"value": "TIS Tarou"
},
{
"name": "age",
"value": "23"
},
{
"name": "address",
"value": "東京都江東区豊洲2-2-1 豊洲ベイサイドクロスタワー"
}
]
}
},
"thread_id": "1e4e24c9-488c-461a-8385-9fc4432b6550",
// このレコードにおける、自分の役割。 "issuer" か "holder" のどちらかになる
"role": "issuer",
// 使用するSchemaのID
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"created_at": "2021-06-30 07:27:51.876969Z",
"credential_proposal_dict": {
/*

*/
},
// 使用するCredential DefinitionのID
"credential_definition_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
// [重要]Credential発行の流れにおける現在の状態
"state": "offer_sent",
"credential_offer": {
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"cred_def_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
/*

*/
},
"nonce": "580736787401286449861491"
},
"updated_at": "2021-06-30 07:27:51.876969Z",
"auto_offer": false,
// このやり取りの相手とのConnectionID(宛先)
"connection_id": "765b49e7-ee31-45cd-a285-a52c3879bdb6",
// [重要]このレコードを特定するためのID。 発行完了するまで使用する
"credential_exchange_id": "bd14f589-dc05-407f-8fc4-fcb0d9cae9c0",
"initiator": "self"
}]
}

次に、Holder側で「/issue-credential/records」を実行し、Issuerから送られてきたCredential Offerの内容を確認します。

後続のAPIで「credential_exchange_id」が必要になるものの、Issuer側とHolder側で「credential_exchange_id」の値が異なるためです。

{
"results": [{
"thread_id": "1e4e24c9-488c-461a-8385-9fc4432b6550",
// Holder側では「holder」になる
"role": "holder",
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"initiator": "external",
// Credential発行の流れにおける現在の状態(Issuer側とは状態の名前が異なる)
"state": "offer_received",
"credential_proposal_dict": {
/*

*/
},
"connection_id": "b0972772-c14a-4b8a-b0ae-cc07c21869c6",
"auto_issue": false,
"auto_remove": true,
// このレコードを特定するためのID。 Issuer側とは値が異なる
"credential_exchange_id": "8caf1873-4af9-4436-a8f0-e7b318b4342b",
"updated_at": "2021-06-30 07:27:51.978430Z",
"credential_offer": {
/*

*/
},
"auto_offer": false,
"trace": false,
"credential_definition_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
"created_at": "2021-06-30 07:27:51.978430Z"
}]
}

/issue-credential/{cred_ex_id}/send-request
credential_exchange_idの値を確認したら、Holder側で「/issue-credential/records/{cred_ex_id}/send-request」を実行します。このAPIは、Issuerに対してCredential Requestを送信します。パラメータの「cred_ex_id」に、「credential_exchange_id」の値を入力します。

Credential Requestが送信され、Holder側でのレコードのステータスは「request_sent」になります。

{
"results": [{
"thread_id": "1e4e24c9-488c-461a-8385-9fc4432b6550",
"role": "holder",
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"initiator": "external",
"state": "request_sent",
"credential_proposal_dict": {
/*

*/
},
"connection_id": "b0972772-c14a-4b8a-b0ae-cc07c21869c6",
/*

*/
"credential_request_metadata": {
/*

*/
},
"auto_offer": false,
"trace": false,
"credential_definition_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
"created_at": "2021-06-30 07:27:51.978430Z",
"credential_request": {
/*

*/
}
}]
}

/issue-credential/records/{cred_ex_id}/issue
次に、Issuer側で「/issue-credential/records/{cred_ex_id}/issue」を実行し、Credentialを発行します。パラメータの「comment」には人間が読める形式のコメント(= 普通の文章)を入れることができますが、ここでは使用しません。

cred_ex_idには「/issue-credential/records/」で取得できるcredential_exchange_idを指定します。

{
"results": [{
// この時点でのIssuer側の状態は「request_received」
"state": "request_received",
"credential_exchange_id": "bd14f589-dc05-407f-8fc4-fcb0d9cae9c0",
/*
その他略
*/
}]
}

Credentialが発行され、Issuer側の状態が「credential_issued」になります。なお、この時点ではまだ「Credentialを発行してHolderへと送信した」だけであり、完了はしていません。HolderがCredentialを受け取ることで、はじめて「完了」となります。

{
"results": [{
// Issuerの状態が「credential_issued」になる
"state": "credential_issued",
"credential_exchange_id": "bd14f589-dc05-407f-8fc4-fcb0d9cae9c0",
/*
その他略
*/
}]
}

/issue-credential/records/{cred_ex_id}/store
次に、Holder側で「/issue-credential/records/{cred_ex_id}/store」を実行します。これはIssuerから発行されたCredentialを自身のKMSに保存するAPIで、これを呼ぶことで一連の流れが完了します。パラメータの「credential_id」に文字列を与えると、KMS内でのCredentialのIDをここで指定できます。
※ここでは空JSONにして実行します。

パラメータの「cred_ex_id」に、「credential_exchange_id」の値を入力します。

{
"results": [{
// この時点でのHolderの状態は「credential_received」
"state": "credential_received",
"credential_exchange_id": "8caf1873-4af9-4436-a8f0-e7b318b4342b",
}]
}

実行すると、CredentialがKMSに保存され、状態が「credential_acked」になります。

{
"thread_id": "1e4e24c9-488c-461a-8385-9fc4432b6550",
"role": "holder",
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"initiator": "external",
// Credentialを受け入れた状態(完了状態)
"state": "credential_acked",
// CredentialのID。 /~/storeのパラメータを与えると、ここを任意の文字列にできる。デフォルトはランダム
"credential_id": "7a9f9d16-3566-4745-ae9a-371c96339d4e",
// 実際のCredential本体
"raw_credential": {
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"cred_def_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
"rev_reg_id": null,
"values": {
"student_id": {
"raw": "gakuseki_0001",
"encoded": "45150731180998508235491498897709506499412006914375532912224364466611866828364"
},
"name": {
"raw": "TIS Tarou",
"encoded": "757970805717603451143451529533166489648021599786965151150806695577522801777"
},
"age": {
"raw": "23",
"encoded": "23"
},
"address": {
"raw": "東京都江東区豊洲2-2-1 豊洲ベイサイドクロスタワー",
"encoded": "48950248484431008549422275442718118364692519431618915967659434696251913457722"
}
},
"signature": {
/*

*/
},
"signature_correctness_proof": {
/*

*/
},
"rev_reg": null,
"witness": null
},
/*

*/
}

/credentials
Holderで「/credentials」を実行すると、保存されているCredentialの中で、このPresentation Requestに使用できるCredentialを取得します。

CredentialのIDは「referent」という項目になります。

< Credential発行のAPI呼び出し順序まとめ >
Credential発行を行うには、1~4までを順に呼び出します。

表8_Credential発行のステップごとに呼び出すAPI一覧

< 開発時のTips >
ACA-Pyの起動時に以下のオプションを加えることで、上記のAPI呼び出しを一部省略できます。どちらも付けると、実質的に「1. /issue-credential/send-offer」 → 「2. /issue-credential/records/{cred_ex_id}/send-request」だけでCredentialを発行できます。

表9_Credential発行用のオプション

◆ Presentation検証

Presentation検証は、VerifierとHolderの2者間で行います。AriesのプロトコルにおいてPresentation検証に関する状態に名前はつけられていませんが、ACA-Py上では以下のように状態遷移します。

Verifier側の取る状態

表10_Presentation検証におけるVerifierの取る状態

Holder側の取る状態

表11_Presentation検証におけるHolderの取る状態

/present-proof/send-request
Verifier側のACA-Pyで、「/present-proof/send-request」を実行します。これはPresentation Requestを送るためのAPIで、パラメータにはPresentation Requestとして開示を要求するJSONを入力します。

パラメータとして与えるJSON

{
// HolderとのConnectionID
"connection_id": "9049045d-3341-4f83-9edf-9f2f5bb6dcdb",
"proof_request": {
// Presentation Requestの名前。Presentation Requestの種類ごとにつけるとよい
"name": "presentation-request001",
// 10ケタの数字。0始まりだとエラーになる
"nonce": "1234567890",
// Credentialの項目を開示してもらう部分
"requested_attributes": {
// Presentation上の項目名。自由につけられる
"Presentationの項目名1": {
// 開示してほしいCredentialの項目名。これは実際のCredentialの項目名と一致する必要がある
"name": "student_id"
},
"Presentationの項目名2": {
"name": "name"
},
"Presentationの項目名3": {
"name": "address"
}
},
// Credentialの項目が特定の条件を満たすかどうかを問う部分。 ここでは「18歳以上か」を問う
"requested_predicates": {
"条件式名1": {
// この条件式で参照する項目名(実際のCredentialの項目名と一致する必要がある)
"name": "age",
// 「age >= 〇〇」 となること
"p_type": ">=",
// 「age >= 18」となること
"p_value": 18
}
},
"version": "1.0"
}
}

/present-proof/records
実行すると、VerifierからHolderに対してPresentation Requestが送信され、レコードが作られます。「/present-proof/records」を実行すると、内容を確認できます。

※非常に巨大なJSONになることと、人間に読めない情報が多いため、一部削っています。

{
"results": [{
// [重要]このレコードの状態。Presentation-Requestを送った直後
"state": "request_sent",
"updated_at": "2021-07-02 02:04:14.045544Z",
"connection_id": "765b49e7-ee31-45cd-a285-a52c3879bdb6",
"presentation_request_dict": {
/*

*/
},
// このレコードにおける自分の役割。 "verifier" か "prover" のどちらかになる
"role": "verifier",
// [重要]このレコードを特定するためのID。 検証完了するまで使用する
"presentation_exchange_id": "dd9c9f98-d8fa-40cc-b08e-c48fe3c6659e",
"auto_present": false,
"initiator": "self",
"thread_id": "fa9ff0dc-7de0-4e87-afa9-5d73abf610da",
"created_at": "2021-07-02 02:04:14.045544Z",
"trace": false,
// Presentation Requestの本体部分
"presentation_request": {
"name": "presentation-request001",
"nonce": "1234567890",
"version": "0.1",
// Credential項目の開示要求をした部分
"requested_attributes": {
"student_id": {
"name": "student_id"
},
"name": {
"name": "name"
},
"address": {
"name": "address"
}
},
// Credential項目が条件式を満たすか問い合わせた部分
"requested_predicates": {
"is_seizin": {
"name": "age",
"p_type": ">=",
"p_value": 18
}
}
}
}]
}

presentation_exchange_idの値を確認するため、Holder側でも「/present-proof/records」を実行します。

{
"results": [{
// このレコードを特定するためのID。 Verifier側の値とは異なる
"presentation_exchange_id": "af0d8386-76d7-4fe2-b112-66b666cee07e",
// このレコードにおける自分の役割。 "verifier" か "prover"のどちらかになる
"role": "prover",
// このレコードの状態。 Presentation Requestを受け取った直後
"state": "request_received",
/*
その他略
*/
}]
}

/present-proof/records/{pres_ex_id}/credentials
presentation_exchange_idの値を確認したら、Holder側で「/present-proof/records/{pres_ex_id}/credentials」を実行します。これは指定したPresentation Requestに対して使用できるCredential一覧を取得するAPIで、後続のPresentation作成APIの準備として必要となります。

// トップレベルはJSON配列で返ってくる。    JSONオブジェクト + "results" の形ではないので注意
[
// オブジェクト1つがCredential1つ(+関連情報)に対応する
{
// Credential情報
"cred_info": {
// CredentialID。これを後で使用する
"referent": "7a9f9d16-3566-4745-ae9a-371c96339d4e",
"attrs": {
"age": "23",
"address": "東京都江東区豊洲2-2-1 豊洲ベイサイドクロスタワー",
"name": "TIS Tarou",
"student_id": "gakuseki_0001"
},
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"cred_def_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
"rev_reg_id": null,
"cred_rev_id": null
},
"interval": null,
// このCredentialが、Presentation Requestのどの項目に対して使用できるか
"presentation_referents": [
// ここにはPresentation Requestの項目が並んでいる(requested_attributes と requested_predicates)
"student_id",
"is_seizin",
"name",
"address"
]
}
]

/present-proof/records/{pres_ex_id}/send_presentation
次に、Holder側で「/present-proof/records/{pres_ex_id}/send_presentation」を実行します。このAPIは、VerifierのPresentation Request に対してPresentationを送信します。

パラメータの「pres_ex_id」に、「presentation_exchange_id」の値を入力し、JSONは以下のように指定します。

{
"requested_attributes": {
// Presentation Requestの項目と一致させること
"student_id": {
// 「/present-proof/records/{pres_ex_id}/credentials」で確認したCredentialID
"cred_id": "7a9f9d16-3566-4745-ae9a-371c96339d4e",
// この項目の生データをVerifierに開示するかのフラグ。 falseにすると、ハッシュのみが渡される
"revealed": true
},
"name": {
"cred_id": "7a9f9d16-3566-4745-ae9a-371c96339d4e",
"revealed": true
},
"address": {
"cred_id": "7a9f9d16-3566-4745-ae9a-371c96339d4e",
"revealed": false
}
},
"requested_predicates": {
"is_seizin": {
"cred_id": "7a9f9d16-3566-4745-ae9a-371c96339d4e"
}
},
// 自分(Holder)からVerifierに対して伝えたい項目(今回は割愛)。指定の仕方はIndy編をご覧ください。
"self_attested_attributes": {}
}

実行すると、PresentationがVerifierへと送信され、Holder側の状態は「presentation_sent」に変わります。

{
"results": [{
"presentation_exchange_id": "af0d8386-76d7-4fe2-b112-66b666cee07e",
// Presentationを送信した状態
"state": "presentation_sent",
/*
その他略
*/
}]
}

Verifier側で「/present-proof/records」を実行すると、状態が「presentation_received」に変わっていることが分かります。

{
"results": [{
"presentation_exchange_id": "dd9c9f98-d8fa-40cc-b08e-c48fe3c6659e",
// Presentationを受信した状態
"state": "presentation_received",
/*
その他略
*/
}]
}

/present-proof/records/{pres_ex_id}/verify-presentation
Verifier側で「/present-proof/records/{pres_ex_id}/verify-presentation」を実行すると、Holderから受け取ったPresentationを検証できます。パラメータの「pres_ex_id」に、「presentation_exchange_id」の値を入力します。

実行すると、Presentationの内容が正しいものかVerifiable Data Registryへの問い合わせが行われ、検証結果が返されます。レコードの状態は「verified」へと変わり、Presentation検証の一連の流れが終了します。

{
// 検証済みの状態
"state": "verified",
// 検証結果。 失効済みのCredentialを使用した場合や改ざんがあった場合などは、ここがfalseになる
"verified": "true",
"presentation": {
"proof": {
/*

*/
},
// Holderから送られてきたpresentation情報
"requested_proof": {
// 生の値を開示している項目
"revealed_attrs": {
"student_id": {
"sub_proof_index": 0,
"raw": "gakuseki_0001",
"encoded": "45150731180998508235491498897709506499412006914375532912224364466611866828364"
},
"name": {
"sub_proof_index": 0,
"raw": "TIS Tarou",
"encoded": "757970805717603451143451529533166489648021599786965151150806695577522801777"
}
},
"self_attested_attrs": {},
// 生の値を開示していない項目 ※ここにはハッシュもないが、実体は上部で省略している「proof」内にある
"unrevealed_attrs": {
"address": {
"sub_proof_index": 0
}
},
"predicates": {
"is_seizin": {
"sub_proof_index": 0
}
}
},
// SchemaやDefinitionのID群
"identifiers": [{
"schema_id": "NUNVFLaabHKhZLfw9MzVi4:2:gakuseisyou:2.0",
"cred_def_id": "NUNVFLaabHKhZLfw9MzVi4:3:CL:13:default",
"rev_reg_id": null,
"timestamp": null
}]
},
"updated_at": "2021-07-02 04:54:26.523694Z",
"connection_id": "765b49e7-ee31-45cd-a285-a52c3879bdb6",
"presentation_request_dict": {
/*

*/
},
"presentation_exchange_id": "dd9c9f98-d8fa-40cc-b08e-c48fe3c6659e",
"presentation_request": {
/*

*/
},
/*
その他略
*/
}

< Presentation検証のAPI呼び出し順序まとめ >
Presentation検証を行うには、1~5までを順に呼び出します。

表12_Presentation検証のステップごとに呼び出すAPI一覧

< 開発時のTips >
ACA-Pyの起動時に以下のオプションを加えることで、上記のAPI呼び出しを一部省略できます。

表13_Presentation検証用のオプション

4. おわりに

分散型ID、およびHyperledger Ariesについて、全3回に分けて紹介してきました。Hyperleder Indy/Aries では今回ご紹介したACA-Py以外にも複数のフレームワークとAPIが提供されています。基本的なWebアプリケーションの開発知識があれば、これらのAPIを利用する事で比較的容易にSSIアプリケーションを開発する事ができます。

さらに、StreetCred社の提供する「Trinsic」や、Evernym社の提供する「Verity」といった、サードパーティの強力な開発プラットフォームもあるため、要件や開発期間に応じてこれらの活用も検討すると良いかと思います。

なお今回の記事は、弊社が技術検証として観光をテーマとしたデモアプリを開発した際の知見を元に執筆しました。デモアプリについて詳しく知りたい方は、TIS/Blockchain推進室(bc_prom@ml.tis.co.jp)までお問い合わせください。

本シリーズの内容がHyperledger Indy/Ariesの調査、開発の一助となれば幸いです。

Hyperledger Indy・Ariesによる分散型IDアプリケーション開発ガイド
1. 概要編
2. 環境構築編
3. 実装編

お問い合わせ: bc_prom@ml.tis.co.jp
記:TIS Blockchain Promotion Office (Hiromichi Abe)
Thanks to Takahiro Uchidate and Hideki Nakachi.

--

--