Permalink
Branch: master
Find file Copy path
ea419f3 Feb 8, 2019
1 contributor

Users who have contributed to this file

256 lines (182 sloc) 9.11 KB

Universal DID Operations

Markus Sabadello (markus@danubetech.com), Nader Helmy (nader.helmy@danubetech.com), Vienna, 8th February 2019

Introduction

Decentralized Identifiers (DIDs) have seen increasing adoption across a wide number of distributed ledger ecosystems and blockchains. This is in large part due to our ability to effectively communicate by resolving these DIDs. The process of obtaining a DID Document associated with a particular DID is outlined in the DID Resolution spec.

A blockchain-agnostic implementation of the spec is hosted at the Decentralized Identity Foundation and is fully open-sourced. The Universal Resolver can be found at its website and also runs locally or remotely through an API. It currently supports DIDs on Sovrin, BTCR, uPort, Jolocom, Veres One, ERC-725, Blockstack, IPFS, and DNS via a number of community-contributed drivers built on top of the Universal Resolver.

Now that we can universally resolve a DID, how can we do the same with the entire DID lifecycle? All DID methods commonly share 4 operations: Create, Resolve, Update, and Revoke. We can envision a counterpart to the Resolver, called a Registrar, that contains these additional DID operations.

DID Resolution is relatively straightforward because there is no authentication required and thus no keys involved. In addition, it is an atomic operation and can easily be done over the web. Creating a DID, updating its DID Document and revoking a DID's secret will require the same abstraction layer.

Abstract Interface

create()


create(method, options) -> state, metadata

create(method, options, did-doc) -> state, metadata

create(method, options, did-doc, wallet) -> state, metadata


To create a DID, we specify where we want it created, with optional parameters for registering a DID Document and storing keys.


method

  • sov, btcr, v1, ...

options

  • mainnet or testnet or other...
  • seed

did-doc

  • entire new DID Document

wallet

  • storage for generated private keys
  • storage of existing keys
  • e.g. text file, wallet API endpoint, local wallet, etc.

update()


update(identifier, options, wallet, did-doc) -> state, metadata

update(identifier, options, wallet, did-doc-operation) -> state, metadata


To update a DID with a new DID Document, we verify ownership over the DID and submit any requested changes.


did-doc

  • entire new DID Document, to replace the previous one

did-doc-operation

  • incremental update to the existing DID Document, e.g.:
  • add-service
  • remove-service
  • add-publickey
  • remove-publickey

revoke()


revoke(identifier, options, wallet) -> state, metadata


To revoke a DID, we verify ownership over the DID.

checkOperation()


checkOperation(jobid) -> state, metadata

checkOperation(identifier) -> state, metadata


At any time, we can query the Registrar to find the state of a previous DID operation, or the last known state of the identifier.


jobid

  • returned as part of the state object
  • generated by the Registrar

state

state

  • finished
    • DID
    • wallet {optional}
  • action
    • jobid
    • actiontype (e.g. send tokens to wallet)
  • wait
    • jobid
    • waittype (e.g. wait for confirmation on chain)
  • fail
    • error message

metadata

metadata

  • operation metadata
    • duration
  • method metadata
    • method-specific hash
    • token balance

The Registrar has four states: finished, failed, wait, and action. This flow diagram indicates the responses a typical user can expect. The Registrar has four states: finished, failed, wait, and action. This flow diagram indicates the responses a typical user can expect.

Architecture

In order to implement a library or tool that supports the above interfaces for creating, updating, and revoking DIDs in a method-agnostic way, we can imagine a similar architecture as we have built for the Universal Resolver, i.e. using a set of drivers that perform method-specific operations. Accordingly, we can call this library or tool a Universal Registrar.

Some architectural questions that apply to the resolve() operation also apply to other operations, e.g.:

  • Is the abstract interface implemented as a library that can be integrated locally into an application or service, or is the abstract interface exposed by a remote service and used via HTTP or another binding?
  • How do method-specific drivers interact with the DID's target system? For example, do they have direct access to a blockchain full node?
  • What are implications of the above questions for trust and security?

Unlike the resolve() operation however, the other operations create(), update(), and revoke() are more challenging and therefore raise additional architectural questions, since they typically involve the use of secrets such as private keys, and write operations to the DID's target system:

  • Where are secrets generated? Are a DID's private keys generated by the driver, or by the client that uses the Universal Registrar?
  • Where are secrets stored? Are a DID's private keys stored in a wallet held by the driver, or by the client that uses the Universal Registrar?
  • Where are the identifiers generated? Does the client generate the identifier (the DID) that gets created, or does this happen entirely inside the driver? Note that e.g. in the "btcr" DID method, the DID only becomes known at the end of the creation process, not at the beginning.

The Universal Registrar interface can be configured to generate & store keys in various locations, e.g. in the client, in each method-specific driver, or in the Registrar's cloud wallet. The Universal Registrar interface can be configured to generate & store keys in various locations, e.g. in the client, in each method-specific driver, or in the Registrar's cloud wallet.

HTTP Binding

The abstract interface above can be implemented and deployed in the form of bindings to different protocols, such as simple HTTP POST operations, with inputs and outputs encoded as JSON.

For example, the operations above can be deployed at the following endpoints:

https://uniregistrar.io/1.0/create
https://uniregistrar.io/1.0/update
https://uniregistrar.io/1.0/revoke

Examples

create() did:sov

REQUEST

create("did:sov", { "network": "stn" })

RESPONSE

state

{
  "state": "finished",
  "identifier": "did:sov:stn:888G8onFVhEP3kVCipXvey",
  "wallet": {
    "seed": "ceiusFJbi5z1Fs3vOj7HKIGcCblb84pl"
  }
}

metadata

{
  "network": "stn",
  "poolVersion": 2,
  "submitterDid": "WRfXPg8dantKVubE3HX8pw"
}

create() did:btcr

REQUEST

create("did:btcr", { "network": "stn" })

RESPONSE

state

{
  "state": "wait",
  "jobId": "cd86ca7a-4ae5-40ed-8187-99b5484415e3",
  "wait": "confirmingtransaction",
  "waitTime": "1800000"
}

metadata

{
  "chain": "TESTNET",
  "transactionHash": "42e74f2530c452cae0fac7495d4143fffac8784dec1f00f22a4b2196b28fa4da",
  "balance": 0.31018803,
  "changeAddress": "n3mR6awpt4D1yfCwkVReKXbbpPosfz569r"
}

WAIT 30 MINUTES ...

REQUEST

checkOperation("cd86ca7a-4ae5-40ed-8187-99b5484415e3")

RESPONSE

state

{
  "state": "finished",
  "identifier": "did:btcr:xk7m-czu9-qq8c-djqs",
  "wallet": {
    "privateKeyWif": "cSaQH1A2v9b56DTTtMobTvxLJ3Z4yun2urNhVYLTnn7jRi3wtaBZ",
    "privateKeyHex": "95086c356343a6cb56f186f3cbb5791a2acd2cd4a34063416a94d01fd51af768"
  }
}

metadata

{
  "chain": "TESTNET",
  "transactionHash": "1810eb9000a43eee466af6f159fad9d8423fb4b8912e55d9fc155de388b66cd3",
  "blockHeight": 1456107,
  "blockIndex": 47
}

Other Topics and Future Ideas

  • Indy A2A binding to communicate with the Universal Resolver / Registrar
    • With further development of Indy Agents and other interoperable Agent protocols there will be new opportunities for utilization of the DID infrastructure included in the Resolver & Registrar. These APIs could be used in a broader set of consumer-facing services and applications, integrated within development environments, and/or built into decentralized production systems.
  • Special considerations about certain DID methods (e.g. peer)
    • Off-ledger DIDs or public key DIDs should be supported as they become more defined protocols. The abstract interface is compatible with those types of DIDs, and additional drivers can be built to support their functionality.
  • Wallet Developments
    • As implementations of Identity Wallets are developed and created, there will be new extensions and interfaces to improve on security and usability. These developments are theoretically compatible with the abstract interface outlined here, with few if any modifications. The Universal Registrar should be updated as browser extensions, hardware wallets, cloud wallets, etc. become a reality.