Develop with pleasure!

福岡でCloudとかBlockchainとか。

IONのDIDのリカバリーと無効化操作をアンカリングしてみる

techmedia-think.hatenablog.com

techmedia-think.hatenablog.com

ときたので、残りのRecoverおよびDeactivate操作をアンカリングしてみる。

Recover操作

Recoverは、Update Keyの更新とRecovery Key自体の更新および、DID State Patcheで定義したDID Documentに対する変更を適用する操作になる。DID Documentの更新という意味ではUpdate操作と似ているけど、Update Keyを紛失したり盗まれたとしても、Recovery Keyがあれば(制御下にあれば)、Recover操作によるDIDの更新で、DIDの管理が引き続き可能。一方、Recovery Keyを紛失してしまうと、既存のUpdate KeyによりDIDの更新だけは引き続き可能だけど、盗まれてしまうとDIDの制御権を奪われることになる。そういう意味でRecover Keyの管理の方が重要度が高い。

実際にRecover操作で、DID Documentを更新してみよう。手順は↓

  1. 新しいRecovery Keyを生成
  2. 新しいUpdate Keyを生成
  3. DID Documentに対する変更を定義したDID State Patcheを作成する。今回は新しい署名鍵を生成し、現在のDIDの署名鍵を新しい鍵に置き換えるreplaceアクションを適用。
  4. 2のUpdate Keyのコミットメントと、3のPatchからRecovery Operation Delta Objectを作成。
  5. 4のデータのハッシュと現在のRecovery Keyのデータ、新しいRecovery KeyのコミットメントからRecovery Operation Signed Data Objectを作成。
  6. 5のデータに対して、現在のRecovery Keyで署名したJWSを生成。
  7. JWSデータを使って、Core Proof Fileを作成し、IPFSにアンカリングする。
  8. 4のDeltaオブジェクトを使って、Chunk Fileを作成し、IPFSにアンカリングする。
  9. 8のChunk FileのURIを含む、Provisional Index Fileを作成し、IPFSにアンカリングする。
  10. 9のProvisional Index FileのURIと、7のCore Proof FileのURI、現在のDIDにコミットされているRecovery Keyのコミットメントに対応するReveal ValueからCore Index Fileを作成し、IPFSにアンカリングする。
  11. Core Index FileのURIからAnchor Stringを生成し、それをBitcoinにアンカリングする。

Update操作との違いは、

  • プルーフはProvisional Proof Fileではなく、Core Proof Fileにセット。
  • Recover操作によるDID Documentの変更の差分(delta)や、更新に使用する鍵のReveal Valueは、Provisional Index FileではなくCore Index Fileに配置。

あたりかな。この辺りのデータの配置先をUpdateとRecover/Deactivateでそれぞれ分離するメリットってどこにあるんだろう?同じファイルや方法で扱えた方がシンプルで便利だと思うんだけど。

以下、実際にRecover操作をCASにアンカリングしたコード↓

require 'sidetree'

Sidetree::Params.network = Sidetree::Params::Network::TESTNET

# DIDのsuffix
did_suffix = "EiBRrmEha_Q30GieEwLB-XM8CZd_b49dQ7znhaBxfAHTsQ"

# 現在設定されているRecovery Key
recovery_key =
  Sidetree::Key.new(
    private_key:
      45_036_086_779_068_677_123_348_498_811_216_758_728_966_810_636_876_259_204_257_311_947_782_243_979_294
  )
# 新しいRecovery Keyを生成
new_recovery_key = Sidetree::Key.generate

# 新しいUpdate Keyを生成
new_update_key = Sidetree::Key.generate

# 新しい署名鍵を生成
new_signing_key = Sidetree::Key.generate(id: "signing-key")

# 置き換えるDID Documentを生成
document = Sidetree::Model::Document.new(public_keys: [new_signing_key])
delta =
  Sidetree::Model::Delta.new(
    [document.to_replace_patch],
    new_update_key.to_commitment
  )
# JWSを生成して現在のRecovery Keyで署名
claim = {
  recoveryKey: recovery_key.to_jwk.normalize,
  recoveryCommitment: new_recovery_key.to_commitment,
  deltaHash: delta.to_hash
}
jws = Sidetree::Util::JWS.sign(claim, recovery_key)

ipfs = Sidetree::CAS::IPFS.new

# Core Proof Fileを作成
core_proof_file = Sidetree::Model::CoreProofFile.new(recover_proofs: [jws])
core_proof_file_uri = ipfs.write(core_proof_file.to_compress)

# Recover Operationを作成
recover_op =
  Sidetree::OP::Recover.new(
    did_suffix,
    delta,
    jws,
    recovery_key.to_reveal_value
  )

# Chunk Fileを作成
chunk_file =
  Sidetree::Model::ChunkFile.create_from_ops(recover_ops: [recover_op])
chunk_file_uri = ipfs.write(chunk_file.to_compress)

# Provisional Index Fileを作成
provisional_index_file =
  Sidetree::Model::ProvisionalIndexFile.new(
    chunks: [Sidetree::Model::Chunk.new(chunk_file_uri)]
  )
provisional_index_file_uri = ipfs.write(provisional_index_file.to_compress)

# Core Index Fileを作成
core_index_file =
  Sidetree::Model::CoreIndexFile.new(
    provisional_index_file_uri: provisional_index_file_uri,
    core_proof_file_uri: core_proof_file_uri,
    recover_ops: [recover_op]
  )
core_index_file_uri = ipfs.write(core_index_file.to_compress)

anchor_str =
  Sidetree::Util::AnchoredDataSerializer.serialize(1, core_index_file_uri)

Anchor Stringは、

ion:1.QmT2UBzXXsk4gjmUPYvFkQLXgZayfEUq5hpUEGjVRFphgt

で、アンカリングしたBitcoinトランザクションは、8ed80cf58094d12c4fca6dd9fb2ae136278e78afbd65fa108a5db961f762f7ae。ブロックに取り込まれると、IONのResolverでもDID Documentの更新が確認できる↓

DID Document(署名鍵)に加えて、Recovery Commitment、Update Commitmentが更新されている。

Deactivate操作

Deactivate操作は、Recovery Keyを使ってDID Documentを無効化する操作になる。

手順は、他の操作よりシンプルで、

  1. 無効化対象のDIDのSuffixとRecovery KeyでDeactivate Operation Signed Data Objectを作成
  2. 1のデータに対して、現在のRecovery Keyで署名したJWSを生成。
  3. JWSデータを使って、Core Proof Fileを作成し、IPFSにアンカリングする。
  4. 3のCore Proof FileのURI、現在のDIDにコミットされているRecovery Keyのコミットメントに対応するReveal ValueからCore Index Fileを作成し、IPFSにアンカリングする。
  5. Core Index FileのURIからAnchor Stringを生成し、それをBitcoinにアンカリングする。

DID Documentの更新がないので、Provisional Index FileやChunk Fileが不要になる。

require 'sidetree'

Sidetree::Params.network = Sidetree::Params::Network::TESTNET

# 現在設定されているRecovery Key
recovery_key =
  Sidetree::Key.new(
    private_key:
      22_121_807_773_082_901_989_880_604_296_336_998_182_556_647_810_161_279_933_493_862_174_847_957_422_871
  )

# JWSを生成して現在のRecovery Keyで署名
claim = {
  didSuffix: did_suffix,
  recoveryKey: recovery_key.to_jwk.normalize
}
jws = Sidetree::Util::JWS.sign(claim, recovery_key)

ipfs = Sidetree::CAS::IPFS.new

# Core Proof Fileを作成
core_proof_file = Sidetree::Model::CoreProofFile.new(deactivate_proofs: [jws])
core_proof_file_uri = ipfs.write(core_proof_file.to_compress)

# Deactivate Operationを作成
deactivate_op =
  Sidetree::OP::Deactivate.new(
    did_suffix,
    jws,
    recovery_key.to_reveal_value
  )

# Core Index Fileを作成
core_index_file =
  Sidetree::Model::CoreIndexFile.new(
    core_proof_file_uri: core_proof_file_uri,
    deactivate_ops: [deactivate_op]
  )
core_index_file_uri = ipfs.write(core_index_file.to_compress)

anchor_str =
  Sidetree::Util::AnchoredDataSerializer.serialize(1, core_index_file_uri)

Anchor Stringは、

ion:1.QmWmJ5HWVvga46CcGkem7JTxMZtJrVtsuQDaPmRf3Pi54V

で、アンカリングしたBitcoinトランザクションは、1c18fc397aebd5c1ee0d12a973d8e9fb01d2c2945bf965508480efc422e78a23。ブロックに取り込まれると、IONのResolverでもDID Documentが無効化されたことが分かる(DID Documentの鍵やserviceが削除されている)↓

ということで、Sidetreeに定義された一連の操作(Create / Update / Recover / Deactivate)を一通りやってみた。

ブロックチェーンにアンカリングしたデータを使ってオフチェーンプロトコルを動作させるというコンセプトはやっぱり面白い。ブロックチェーンで担保するのはデータの適用順序だけ、後はオフチェーンのコントラクトやプロトコルで解決する。