# Indy DID Method ## About The Indy DID method specification conforms to the requirements in the DID specification currently published by the W3C Credentials Community Group. For more information about DIDs and DID method specifications, please see the DID Primer and DID Spec. ## Abstract Indy is a public ledger designed specifically and only for privacy-preserving self-sovereign identity. A Hyperledger Indy ledger is designed specifically to enable the use of verifiable credentials, allowing credential issuers to publish data necessary for issuing verifiable credentials and constructing presentations from those verifiable credentials. This specification covers how DIDs on an Indy ledger are managed and the operations for creating, reading, updating, and deleting DIDs. ## Indy Ledger Objects: Glossary Instances of Hyperledger Indy networks persist different kind of (internal) data objects in the ledger. The following section describes those objects. ### NYM A NYM (short for "Verinym") is associated with the Legal Identity of an Identity Owner and is a Hyperledger Indy specific term for a data object, which holds DID data of one concrete identity returned during DID resolution. While a NYM can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-nym) from a Hyplerledger Indy Node by any client, a NYM can only be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#nym) to a Hyperledger Indy network as long as the writing entity possess the proper permissions. A NYM object itself does not conform to the [Decentralized Identifiers (DIDs) Core specification](https://https://www.w3.org/TR/did-core/) scheme but rather includes all DID related data of a single identity and therefore its resolution into a DID document does. Therefore writting a NYM to a Hyperledger Indy instance basically results in writting a DID to the ledger. The author of a NYM write transaction is then the owner of the NYM respectively its embedded DID. When reading NYM transaction from the ledger, clients transform / extract the NYM data into a DID document. Often the terms "NYM" and "DID" are used synonymously, although a "NYM" is "just" Hyperledger Indy's specific way of storing DIDs into the ledger. > TODO: finalize ### ATTRIB A Hyperledger Indy ATTRIB (short for "attribute") object extends a specific DID (respectively the NYM) of its owner with further information (attributes) and can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-attrib) from a Hyplerledger Indy Node by any client. An ATTRIB object can only be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#attrib) to a Hyperledger Indy network by an owner of the DID on that network. > TODO: finalize ### SCHEMA A SCHEMA object is a template that defines a set of attribute(names) which are going to be used by issuers for issuance of [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) within a Hyperledger Indy network. SCHEMAs have a name, version and can be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/transactions.html#schema) to the ledger by any entity with proper permissions. Schemas can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-schema)from a Hyplerledger Indy Node by any client. SCHEMAs define the list of attribute(names) of issued credentials based on a CLAIM_DEF (see below). > TODO: finalize ### CLAIM_DEF A CLAIM_DEF (short for "claim definition") object contains data required for credential issuance as well as credential validation and can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-claim-def) by any Hyperledger Indy client. A CLAIM_DEF object references a SCHEMA, references a DID of the issuer and can be [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#claim-def) by any issuer who intends to issue credentials based on that specific SCHEMA to the ledger and has the proper permissions in doing so. A public key of the issuer is included within the CLAIM_DEF which allows validation of the credentials signed by the issuer's private key. When credentials are issued by using the issuers CLAIM_DEF, the attribute(names) of the SCHEMA have to be used. Revokable Verifiable Credentials require CLAIM_DEFs which also reference a REV_REG_DEF (see below). > TODO: finalize ### REV_REG_DEF A REV_REG_DEF object (short for "revocation registry definition") contains information required for verifiers in order to enable them to verify whether a (revokable) verifiable credential has been revoked by the issuer since issuance. REV_REG_DEFs are only needed for revokable verifiable credentials and are most commonly [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#claim-def) to the ledger by the owner of a CLAIM_DEF immediatly after the CLAIM_DEF has been written. They can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-attrib) from a Hyplerledger Indy Node by any client and are updated in case of the revocation of a credential, which is based on the used CLAIM_DEF. Further details about Hyperledger Indy's revocation process can be found [here](https://hyperledger-indy.readthedocs.io/projects/hipe/en/latest/text/0011-cred-revocation/README.html). ### REV_REG_ENTRY A REV_REG_ENTRY object (short for "revocation registry entry") marks the current status of one or more revokable verifiable credentials ("revoked" or "not revoked") in the ledger in a privacy preserving manner. A REV_REG_ENTRY is [written](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#revoc-reg-entry) by the owner of a REV_REG_DEF respectively the issuer of the credential(s) based on a CLAIM_DEF and its REV_REG_DEF. Any REV_REG_ENTRY condensed with further required information can be [read](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg-delta) by any Hyperledger Indy client. Further details about Hyperledger Indy's revocation process can be found [here](https://hyperledger-indy.readthedocs.io/projects/hipe/en/latest/text/0011-cred-revocation/README.html). ## Differences from `did:sov` Early instances of Indy Networks used the `did:sov` DID Method. The following summarizes the differences between that `did:sov` and `did:indy`. - A `did:indy` DID includes a namespace component that enables resolving a DID to a specific instance of an Indy network (e.g. Sovrin, IDUnion, etc.). - `did:indy` DIDs MUST be self-certifying, and that restriction MUST be enforced by the ledger. - Identifiers for Indy ledger objects other than NYMs are adjusted to contain a namespace component. - The specification includes rules for transforming a NYM into a DIDDoc that meets the DID Core Specification. - An optional NYM data item allows entities to extend the DIDDoc returned from a NYM in arbitrary ways. - The controller can decide whether the DIDDoc will be JSON or JSON-LD. - Before writing a NYM to the ledger, the NYM content is verified to ensure that transforming the NYM to a DIDDoc produces a valid JSON and may include DIDDoc validity checking. - The transformation of a read NYM to a DIDDoc is left to the client of an Indy ledger. - A convention for storing Indy network instance config ("genesis") files in a Hyperledger Indy project GitHub repository ("indy-did-networks") is introduced. ## Target System(s) The `did:indy` DID method applies to all DIDs which are anchored to a Hyperledger Indy Ledger and which comply with these specific conventions. ## Motivation and Assumptions ### Assumption: Number of Indy Instances We are anticipating that there will be at most on the order of "low hundreds" of Indy network instances, with the potential for several (usually 3) subnamespaces per network instance for production, test (staging), and development deployments. This assumption is based on the likelihood of their being some (likely small) number of global Indy instances, and some number of national Indy instances. Assuming at most one per country, we would have around 200-300 total, leading to the anticipated maximum of "low hundreds" of Indy network instances. ### Including a Network-specific Identifier Including a network-specific identifier within an Indy DID identifier enables a "network of networks" capability where an Indy DID can be uniquely resolved to a specific DIDDoc stored on a specific Indy network using a common resolver software component. Given a `did:indy` DID, the network-specific identifier embedded in the DID can be extracted to determine where to send the request to resolve the DID Doc. This enables several useful properties: - Decentralization: Additional instances of networks can be deployed and easily used. - Scalability: Additional DID ledgers can be deployed and DIDs on those ledgers easily referenced. - Fit for purpose: A DID ledger can be deployed and easily used for specific purposes, such as part of a nation's critical infrastructure. - Governance: A DID ledger can be deployed under a specific governance structure appropriate to those writing to that ledger. ### Aligning Indy with the DID Core Specification The DID Indy method specification formalizes the transformation of an Indy ledger object (a NYM) into a DIDDoc as defined in the DID Core specification from W3C, ensuring that identifiers written to and read from Indy ledgers are W3C standard DIDs. ### Resolving a DIDDoc in a single transaction Previous approaches to resolving Indy ledger objects into DIDDocs required the client read one or more ledger objects (notably, NYMs and ATTRIBs) before assembly. This is at best "challenging" for the client, and at worst, extremely slow without specialized ledger support, particularly when a non-current version of the DID is being resolved. A preferred approach is to enable the resolution of a DID via a single read transaction that returns a single object off the ledger, including a state proof for that object. ### Cross-Ledger Object References The NYM controller of all objects on an Indy ledger MUST reside on the same Indy ledger as the object. Thus, the DID (e.g. the Indy NYM) of the Issuer of a verifiable credential type must reside on the same ledger as the CLAIM_DEF, REV_REG_DEF and REV_REG_ENTRY objects for that type of verifiable credential. Note that the constraint above does not apply to a SCHEMA referenced by a CLAIM_DEF, since a CLAIM_DEF may use a SCHEMA written by another NYM. As such, a CLAIM_DEF may reference a SCHEMA on a different Indy ledger. ## Indy DID Method Identifiers The did:indy Method DID identifier has four components that are concatonated to make a DID specification conformant identifier. The components are: - **DID**: the hardcoded string `did:` to indicate the identifier is a DID - **DID Indy Method**: the hardcoded string `indy:` indicating that the identifier uses this DID Method specification. - **DID Indy Namespace**: a string that identifies the name of the primary Indy ledger, followed by a `:`. The namespace string may optionally have a secondary ledger name prefixed by a `:` following the primary name. If there is no secondary ledger element, the DID resides on the primary ledger, else it resides on the secondary ledger. By convention, the primary is a production ledger while the secondary ledgers are non-production ledgers (e.g. staging, test, development) associated with the primary ledger. Examples include, `sovrin`, `sovrin:staging` and `idunion`. - **Namespace Identifier**: a self-certifying identifier unique to the given DID Indy namespace. To be self-certifying the identifier must be derived from the initial verkey associated with the identifier. See the [DID Creation](#Creation) section of this document for the derivation details. The components are assembled as follows: `did:indy:<namespace>:<namespace identifier>` Some examples of `did:indy` DID Method identifiers are: * A DID written to the Sovrin MainNet ledger: * `did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgg` * A DID written to the Sovrin StagingNet ledger: * `did:indy:sovrin:staging:6cgbu8ZPoWTnR5Rv5JcSMB` * A DID on the IDUnion Test ledger: * `did:indy:idunion:test:2MZYuPv2Km7Q1eD4GCsSb6` ## Other Indy Ledger Object Identifiers Indy ledgers may hold object types other than DIDs, and each of the other object types must also be resolvable to a specific Indy network instance. The identifiers for these objects are used in data structures that are exchanged by Indy clients (e.g. Aries Agents)--verifiable credentials, presentation requests, presentations and so on. Transitioning to the `did:indy` DID Method requires transitioning Indy clients/resolvers to use the identifiers defined in this section. ### DID URLs for Indy Object Identifiers The structure of identifiers for all non-DID Indy ledger objects is the following DID URL structure, based on the DID of the object's DID controller: - `<did>/<object-type>/<object-type-identifier>` The components of the DID URL are: - `<did>` the `did:indy` DID of the object-owning controller - `<object-type>` one of `SCHEMA`, `CLAIM_DEF`, `REV_REG_DEF`, `REV_REG_ENTRY`, `ATTRIB` - `<object-type-identifier>` an object type unique identifier defined by Indy by object type. The data returned from resolving such DID URLs is the ledger object and relevant state proof; the same data returned from the Indy Node read object transactions, such as the [GET_SCHEMA](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-schema) transaction, and dependent on the type of the object. The following sections cover each ledger object type, providing: - an example DID URL identifier, - a link to an example object residing on the Sovrin MainNet Indy ledger (courtesy of [indyscan.io](indyscan.io)), - the format of the response when resolving the DID URL, - the pre-`did:indy` identifier for each object, and - notes about the elements of the pre-`did:indy` identifier. #### Schema DID URL: [`did:indy:sovrin:F72i3Y3Q4i466efjYJYCHM/SCHEMA/npdb/4.3.4`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/56495) Response: Same as the Indy Node [GET_SCHEMA Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-schema) Existing identifier: `F72i3Y3Q4i466efjYJYCHM:2:npdb:4.3.4` - `2` is the enumerated object type - `npdb` is the client-defined schema name - `4.3.4` is the client-defined tag for the SCHEMA #### Claim Def: DID URL: [`did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/CLAIM_DEF/npdb`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/56496) Response: Same as the Indy Node [GET_CLAIM_DEF Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-claim-def) Existing identifier: `did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb` - `3` is the enumerated object type - `CL` is the signature type for the claim def, which is `CL` for all claim defs on all existing Indy ledgers - `56495` is the sequence number for the Schema object used by this Claim Def - `npdb` is the client-defined claim def name Note that the DID URL format adds a constraint on the client-defined claim def name from the `did:sov` DID Method. Specifically, the same named claim def can no longer be associated with different SCHEMA objects. #### Revocation Registry Definition: DID URL: [`did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/REV_REG_DEF/npdb/TAG1`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/56497) Response: Same as the Indy Node [GET_REVOC_REG_DEF Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg-def) Existing Identifier: `5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1` - `4` is the enumerated object type - `5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb` is the identifier of the associated Claim Def - `TAG1` is the client-defined rev reg name #### Revocation Registry Entry: DID URL: [did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/REV_REG_ENTRY/npdb/TAG1](https://indyscan.io/tx/SOVRIN_MAINNET/domain/58567) The DID URL resolution response depends on the query parameters used, as follows: - `?versionId=<timestamp>` - Response is the same as the Indy Node [GET_REVOC_REG Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg) - If the parameter is left off the current time is used for the timestamp. - `?from=<timestamp>&to=<timestamp>` - Response is the same as the Indy Node [GET_REVOC_REG_DELTA Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-revoc-reg-delta) - As noted in the transaction documentation, the from parameter is optional. - If the `to` parameter is left off the current time is used for the `to` timestamp. Existing Identifier: `5:5nDyJVP1NrcPAttP3xwMB9:4:5nDyJVP1NrcPAttP3xwMB9:3:CL:56495:npdb:CL_ACCUM:TAG1` - `5` is the enumerated object type - The remainder of the identifier is the identifier for the applicable Revocation Registry #### ATTRIB: DID URL: [`did:indy:sovrin:5nDyJVP1NrcPAttP3xwMB9/ATTRIB/<raw>`](https://indyscan.io/tx/SOVRIN_MAINNET/domain/54743), where `<raw>` is the name of the JSON object that is the value of the `raw` ATTRIB value. In the example linked at the start of this paragraph, the `<raw>` value would be `endpoint`. Response: Same as the Indy Node [GET_ATTRIB Txn](https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get-attrib). Only the `raw` parameter form of the transaction is supported. Existing Identifier: `F72i3Y3Q4i466efjYJYCHM:1:b6bf...d9e` - `1` is the enumerated object type - `b6bf...d9e` is an identifier for the ATTRIB, likely a hash of the content ## Finding Indy Ledgers To connect and read or write from a Hyperledger Indy network instance, a client must have the configuration file (in Indy, called the "gensis" file) for the network. Given a `did:indy` DID (e.g. `did:indy:<namespace>:<namespace identifier>`), the Indy network instance on which the DID resides is known. However, there remains a challenge for the entity interested in resolving the DID&mdash;finding the genesis file for that network instance. The following documents two mechanisms resolvers can use to access required genesis files. ### Static A client that will resolve Hyperledger Indy DIDs can be statically configured to "know" about a set of Indy networks by loading the files on startup. The files would be collected from the node operators in some way by those deploying the client software (e.g. an Aries Wallet). When a static list of Indy networks is used, DIDs from other, organically discovered networks not on the list cannot be resolved by the client. ### Dynamic using GitHub The Hyperledger Indy GitHub repo `indy-did-networks` enables Indy DID network operators to publish their network genesis files in a standard way. Within the repo, the folder "networks" contains a folder per primary network. Within each network folder is the genesis file for the primary network, and folders (containing a corresponding genesis file) for each subspace network. The naming format for the genesis files is: - `pool_transactions_genesis.json` For example, the Sovrin MainNet, StagingNet and BuilderNet genesis files will be in the repo as: - `networks/sovrin/pool_transactions_genesis.json` - `networks/sovrin/staging/pool_transactions_genesis.json` - `networks/sovrin/builder/pool_transactions_genesis.json` The committers to the repo for each network SHOULD include in the folder at least a README.md file with information about the network, plus any additional documents about the ledger instance, such as Governance Framework documents. #### Client Usage A client may retrieve selected network genesis files from the repo to use as their set of static files, as described in the [previous section](#Static). The developers of the clients can monitor the repo for changes to the genesis files that they are using. If a DID is obtained by the client that is from a network not already known by the client, the client MAY look for the unknown (to the client) network in the GitHub repo and decide to use (or not) the associated genesis file to connect to the network. The security policy of the client (and perhaps the user of the client) might give options about handling unknown networks, such as: - Never connect. - Review and connect if permission from client operator granted. - Always connect if the genesis file for the network can be found. #### Repository Maintenance Each contributing network instance operator maintains copies of their genesis files and supporting documents in the GitHub repo by submitting Pull Requests (PRs) to the repo. The community selected repo maintainers are expected to merge PRs with limited review based on their knowledge of the network operators. Their focus is not to provide editorial oversight but only to: - prevent updates to a network's gensis file by other than known operator of the network, and - prevent badly formatted genesis files from being added to the repository. The maintainers are authorized to submit PRs to remove "bad actor" network folders based on notifications from the community and followup verification. #### GitHub Update Disputes Any disputes about the handling of PRs submitted to the repo should be escalated through the Indy Community (via the #indy channel on Hyperledger chat and/or at the [Indy Contributors](https://wiki.hyperledger.org/display/indy/Indy+Contributors+Meeting) call or its successor). If the issue is not resolved at the Indy level, the issue should be escalated to Hyperledger leadership (the Executive Director or the Technical Steering Committee). ## DID Operations ### Creation Creation of a `did:indy` DID is performed by an authorized entity executing a `NYM` ledger transaction on a given Indy network. An Indy NYM transaction includes an identifier (`dest`), an ED25519 verification key (`verkey`) and an optional JSON item (`diddocContent`). The NYM is written to a specific Indy network with a given `namespace`. The following validation MUST be performed prior to executing the transaction to create the DID: - Based on the configured authorization rules of the specific Indy ledger, the transaction may have to be signed by others, such as a Trustee or Endorser. If transaction is not authorized, the transaction MUST be rejected and an error returned to the client. - A `did:indy` DID MUST be self-certifying by having the namespace identifier component of the DID (last element) derived from the initial public key of the DID, as follows: - For an Ed25519 key: Convert into Base58char the first 16 bytes of the 256 bit public key (verkey). - The Indy ledger MUST verify the relationship between the namespace identifier component of the DID and the initial public key (verkey). If the relationship between the data elements fails verification, the transaction MUST be rejected and an error returned to the client. - The NYM transaction requires that the transaction to be written is signed by the DID controller. The ledger MUST verify the signature using the NYM `verkey`. If the signature can not be validated, the transaction MUST be rejected and an error returned to the client. - The Indy ledger MUST check that the data in the NYM produces valid JSON and MUST do a limited DIDDoc validation check prior to writing the NYM object to the ledger. Details of the assembly and verification are [below](#DIDDoc-Assembly-and-Verification). If the DIDDoc validation fails, the transaction MUST be rejected and an error returned to the client. Although the DIDDoc is returned from the DIDDoc assembly and verification process, the DIDDoc is not used further by the ledger. Once the validation checks are completed, the NYM transaction is written to the Indy distributed ledger. If the NYM write operation fails, an error is returned to the client. On successfully writing the transaction to the Indy distributed ledger a success status is returned to the client. #### DIDDoc Assembly and Verification The DIDDoc returned when a `did:indy` DID is resolved is not directly stored in an Indy ledger document. Instead, the DIDDoc must be assembled from data elements in the Indy `NYM` object based on a series of steps. When a NYM is created or updated the ledger MUST assemble the DIDDoc (following the steps) and validate the DIDDoc. As well, an Indy DID resolver will receive from the ledger the NYM from the ledger and the non-validation steps must be followed to assemble the DIDDoc for the resolved DID. Since the assembly validation may change between writing the DID and resolving it (notably, step 3.2), a client resolving a DID need SHOULD NOT perform the validation steps. #### DIDDoc Assembly Steps The following are the steps for assembling a DIDDoc from its inputs. 1. If the `verkey` is `null` the DID has been deactivated, and no DIDDoc is created. Assembly is complete; return a success status. 1. *Note: On creation, the operation would have failed on the "self-certifying DID" validation and not reached this point in the process.* 2. The Indy network instance `namespace`, the NYM`dest` and the NYM `verkey` items are merged into a text template to produce a base DIDDoc. 1. See the template in the [Base DIDDoc Template](#base-diddoc-template) section of this document. 2. If there is no `diddocContent` item in the NYM, assembly is complete; return the DIDDoc and a success status. 3. If the `diddocContent` item is included in the NYM is verified and merged into the DIDDoc. 1. The `diddocContent` item MUST NOT have an `id` item. If found, exit the assembly process, returning an error. 2. If the `diddocContent` item contains a `@context` item, the DIDDoc is assumed to be JSON-LD, and the `@context` element MUST include the current DID Core JSON-LD context. If it does not, exit the assmebly process and return an error. 3. If the `diddocContent` item contains `verificationMethod` and/or `authentication` items, process them as follows. 1. The entries MUST NOT have the same `id` values as those from the NYM-generated DIDDoc. If a matching `id` is found, exit and return an error. 2. Merge the entries into the respective arrays of DIDDoc. 4. Add the other items of the `diddocContent` to the DIDDoc. 4. The resulting DIDDoc text must be valid JSON. If not JSON, exit and return an error. 5. The resulting JSON must be a valid DIDDoc. Perform a `<to be determined>` validation process. If not a DIDDoc, exit and return an error. 6. Return the DIDDoc and a success status. The remainder of this section goes through examples of base DIDDoc template (step 2, above) that is created prior to processing the optional `diddocContent` item, and an example of processing a `diddocContent` item. ##### Base DIDDoc Template The base DIDDoc template is static text that forms a JSON structure. To transform a NYM to its minimal DIDDoc, the Indy network instance's `namespace`, and the NYM values `dest` and `verkey` are inserted into the template as indicated below. ```json= { "id": "did:indy:<namespace>:<dest>", "verificationMethod": [{ "id": "did:indy:<namespace>:<dest>#verkey", "type": "Ed25519VerificationKey2018", "publicKeyBase58": "<verkey>", "controller": "did:indy:<namespace>:<dest>" } ], "authentication": [ "did:indy:<namespace>:<dest>#verkey" ] } ``` Assuming values `sovrin` for the `namespace`, `123456` for `dest` and `789abc` for the `verkey` the resulting JSON DIDDoc would be: ```json= { "id": "did:indy:sovrin:123456", "verificationMethod": [{ "id": "did:indy:sovrin:123456#verkey", "type": "Ed25519VerificationKey2018", "publicKeyBase58": "789abc", "controller": "did:indy:sovrin:123456" } ], "authentication": [ "did:indy:sovrin:123456#verkey" ] } ``` ##### Example Extended DIDDoc Item An example of a NYM's extended DIDDoc handling is provided below. In the example below, the `diddocContent` item adds a DIDcomm Messaging service endpoint to the resolved DIDDoc. Note that in the example, an `@context` item is included in the `diddocContent`, which causes the result DIDDoc to be a JSON-LD document, rather than plain JSON. ```jsonld= "diddocContent" : { "@context" : [ "https://www.w3.org/ns/did/v1", "https://identity.foundation/didcomm-messaging/service-endpoint/v1" ], "serviceEndpoint": [ { "id": "did:indy:sovrin:123456#didcomm", "type": "didcomm-messaging", "serviceEndpoint": "https://example.com", "recipientKeys": [ "#verkey" ], "routingKeys": [ ] } ] } ``` Applying the DIDDoc assembly rules to the example above produces the following assembled, valid JSON-LD DIDDoc: ```jsonld= { "@context": [ "https://www.w3.org/ns/did/v1", "https://identity.foundation/didcomm-messaging/service-endpoint/v1" ], "id": "did:indy:sovrin:123456", "verificationMethod": [{ "id": "did:indy:sovrin:123456#verkey", "type": "Ed25519VerificationKey2018", "publicKeyBase58": "789abc", "controller": "did:indy:sovrin:123456" } ], "authentication": [ "did:indy:sovrin:123456#verkey" ], "serviceEndpoint": [ { "id": "did:indy:sovrin:123456#didcomm", "type": "didcomm-messaging", "serviceEndpoint": "https://example.com", "recipientKeys": [ "#verkey" ], "routingKeys": [] } ] } ``` #### Key Agreement By default there is no key agreement section in an assembled DIDDoc. If the DID Controller wants a key agreement key in the DIDDoc, they must explicitly add it by including it in the `diddocContent`. For an ED25519 verification key, an X25519 key agreement key could be derived from the verkey (by the client), or a new key agreement key can be generated and used. #### The "endpoint" ATTRIB Prior to the definition of this DID Method, a convention on Indy ledgers to associate an endpoint to a NYM involved adding an ATTRIB ledger object with a `raw` value of contain the JSON for a name-value pair of `endpoint` and a URL endpoint, often an IP address. We recommend that anyone that is using that format prior to the availability of the `did:indy` DID Method update their NYM on the ledger to use the `diddocContent` item as soon as possible. If a client retrieves a NYM that has a `diddocContent` data element, the client should assume that the DID Controller has made the ATTRIB (if any) obsolete and the client SHOULD NOT retrieve the ATTRIB associated with the DID. If clients want to continue to retrieve and use the `endpoint` ATTRIB transaction associated with a NYM, we recommend that the endpoint value (along with `namespace` and `dest`) be used as if the following was the `diddocContent` item in the NYM. ``` json "diddocContent" : { "@context" : [ "https://identity.foundation/didcomm-messaging/service-endpoint/v1" ], "serviceEndpoint": [ { "id": "did:indy:<namespace>:<dest>#didcomm", "type": "didcomm-messaging", "serviceEndpoint": "<ENDPOINT>", "recipientKeys": [ "#verkey" ] "routingKeys": [ ] } ] } ``` The DIDDoc produced by the NYM and "endpoint" ATTRIB would be created using the DIDDoc Assembly Rules and using the `diddocContent` from the ATTRIB instead of the NYM item. ### Update Updating a DID using the Indy DID Method occurs when a `NYM` transaction is performed by the NYM's controller (the "owner" of the NYM) using the same identifier (`dest`). The Indy ledger MUST validate the NYM transaction prior to writing the NYM to the ledger. When a NYM is updated, the identifier (`dest`) for the NYM does not change, but other values, including the `verkey` and `diddocContent`, may be changed. This means that (as expected) the DID itself does not change, but the DIDDoc returned by the DID may change. The following validation steps are performed prior to the update being written to the ledger: - Based on the configured authorization rules of the specific Indy ledger, the transaction may have to be signed by others, such as a Trustee or Endorser. If transaction is not authorized, the transaction MUST be rejected and an error returned to the client. - The NYM transaction requires that the transaction to be written is signed by the DID controller using the existing `verkey` and, if a new `verkey` is being provided, the new `verkey`. The ledger MUST verify the signature(s). If the signature(s) cannot be validated, the transaction MUST be rejected and an error returned to the client. - The Indy ledger MUST check that the data in the NYM produces valid JSON and MUST do a limited DIDDoc validation check prior to writing the NYM object to the ledger. Details of the assembly and verification are [below](#DIDDoc-Assembly-and-Verification). If the DIDDoc validation fails, the transaction MUST be rejected and an error returned to the client. Although the DIDDoc is returned from the DIDDoc assembly and verification process, the DIDDoc is not used further by the ledger. Once the validation checks are completed, the NYM update transaction is written to the Indy distributed ledger. If the NYM write operation fails, an error MUST be returned to the client. On successfully writing the update transaction to the Indy distributed ledger a success status is returned to the client. ### Read Reading (resolving) a `did:indy` DID requires finding and connecting to the Indy ledger instance holding the DID, retrieving the NYM associated with the DID, verifying the state proof for the returned NYM, and then assembling the DIDDoc. A client/resolver must perform the following steps to complete the process. 1. Given a `did:indy` DID, extract the `<namespace>` component of the DID. 2. If the namespace (specific Indy instance) is known to the resolver and the resolver is connected to the ledger continue. If not: 1. To read the DIDDoc, the client must get the genesis file for the Indy instance and connect to the ledger. For example, the guidance in the [finding Indy ledgers](#Finding-Indy-Ledgers) can be used to discover previously unknown Indy ledgers. 2. If the client cannot find the Indy network terminate the process and return a "Not Found" status to the caller. 3. If the client chooses not to connect to the Indy network terminate the process and return a "Not Authorized" status to the caller. 3. Once connected, the `GET_NYM` Indy request is used, passing in the `<namespace identifer>` component. 1. If resolving a prior version of the DID, a different call is used in at this point. See the [DID Versions](#DID-Versions) section of this document (below) for more details. 4. If the call fails, terminate the process and return a "Not Found" status to the caller. 5. If a NYM is returned, use the state proof to verify the result. If the verification fails, terminate the process and return a "Not Found" status to the caller. 6. Use the DID `<namespace>` component, the NYM data items `dest`, `verkey`, and (optional) `diddocContent` to assemble the DIDDoc using the [DIDDoc assembly process](#DIDDoc-Assembly-and-Verification) defined earlier in this document. 1. Since the assembly validation was done by the ledger before writing the document, the process should be successful. 7. If the DIDDoc is empty (because the `verkey` is null) return a "Not Found" result, otherwise, return the DIDDoc. #### DID Versions In resolving a `did:indy` DID, the DID resolution query parameters `versionId` and `versionTime` may be used. When used, process to retrieve the NYM from the ledger (step 3 above) is different. If the parameter `versionId` is used, the value must be an Indy ledger `seqno` for the requested NYM on the queried Indy ledger. Instead of using the `GET_NYM` call, the Indy `GET_TXN` call is used, passing in the `seqno`. The result is checked that it is the NYM matching the DID namespace identifier. If so the call is considered to have failed. Either way, the process continues at Step 4. If the parameter `versionTime` is used, the `GET_NYM` transaction is called with the `versionTime` timestamp as an additional parameter. The Indy ledger code tries to find the instance of the requested NYM that was active at that time (using ledger transaction timestamps) and returns it (if found) or the call fails. Either way, the process continues at Step 4. ### Deactivate Deactivtion of a `did:indy` DID is done by setting the NYM verkey to null. Once done, the DIDDoc is not found (per the [DIDDoc Assembly Rules](#diddoc-assembly-rules)) and the NYM cannot be updated again. ## `did:indy` DID Component Syntax The following sections provide the syntax and ABNF for the two variable components of a `did:indy` DID, the namespace and namespace identifier. ### `did:indy` DID Namespace Syntax The `did:indy` DID Namespace component MUST include a primary, human-friendly name of the Indy network instance, MAY include an optional ":" separator and subspace, human-friendly name, and MUST include a trailing ":" separator. The subspace name is used to identify an Indy instance related to the primary instance, such as the primary network's test or development instance. The ABNF for the namespace component is: namespace = namestring (":" namestring) ":" namestring = ALPHA *(ALPHA / DIGIT / "_" / "-") The namespace is set by the operator of the network. Although that could lead to namespace collisions, our [assumption about the expected number of Indy instances](#Assumption-Number-of-Indy-Instances) (low 100s at the most) eliminates that as a concern. ### `did:indy` DID Namespace Identifier Syntax The namespace identifer is an identifier within the namespace of a Hyperledger Indy network that is unique for that namespace. The namespace identifier (NSID) is defined by the following ABNF: NSIDstring = 21*22(base58char) base58char = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / "A" / "B" / "C" / "D" / "E" / "F" / "G" / "H" / "J" / "K" / "L" / "M" / "N" / "P" / "Q" / "R" / "S" / "T" / "U" / "V" / "W" / "X" / "Y" / "Z" / "a" / "b" / "c" / "d" / "e" / "f" / "g" / "h" / "i" / "j" / "k" / "m" / "n" / "o" / "p" / "q" / "r" / "s" / "t" / "u" / "v" / "w" / "x" / "y" / "z" The `NSIDString` is base58 encoded using the Bitcoin/IPFS alphabets of a 16-byte uuid. The encoding uses most alphas and digits, omitting 0 / O / I / l to avoid readability problems. This gives a NSID length of either 21 or 22 characters, and it means that identifiers are case-sensitive and may not be case-normalized, even though the prefix is always lower-case. The namespace identifier MUST be derived from the initial `verkey` for the DID, as outlined in the [Creation](#Creation) section of this document. ## JSON or JSON-LD The choice of whether a DID resolves to a JSON or JSON-LD document is up to the DID Controller. If the NYM object on the ledger for the DID has a `diddocContent` data item, and that data item contains an `@context` item, the DIDDoc will be JSON-LD. Otherwise, the resolved DIDDoc will be JSON. The Indy ledger does not validate the JSON-LD on writing a NYM object to the ledger. It is the responsibility of the DID Controller to ensure that a JSON-LD DIDDoc is valid JSON-LD. ## Security Considerations > To Do: Add a brief summary of Indy (public permissioned, consensus) and the assumptions on which the security considerations are documented -- notably that the DID Controller protect their private key(s). Secure communication to the Indy Ledger uses CurveZMQ to mitigate attacks like eavesdropping, replay, message insertion, deletion, modification, impersonation, and man-in-the-middle. Any vulnerabilities in that protocol will apply to Indy. The following sections decribe how the `did:indy` DID Method adheres to the security considerations outlined in the [DID Core 1.0 specification](https://w3c.github.io/did-core). - A DID method specifications MUST follow all guidelines and normative language provided in RFC3552: Writing Security Considerations Sections for the DID operations defined in the DID method specification. > To Do: Review https://datatracker.ietf.org/doc/html/rfc3552 and ensure documentation is written to spec. - The Security Considerations section MUST document the following forms of attack for the DID operations defined in the DID method specification: eavesdropping, replay, message insertion, deletion, modification, denial of service, storage or network amplification, and man-in-the-middle. Other known forms of attack SHOULD also be documented. - eavesdropping - Doesn't matter -- data written is public - replay - Nonce included -- prevents replay - message insertion - Not possible - deletion - Not possible - modification - Consensus prevents - denial of service - Separate interfaces to prevent loss of node-to-node communications - storage or network amplification - Definition: https://github.com/w3c/did-core/pull/730/files - man-in-the-middle - Authorization prevents > To Do: Document each attack mitigations; add others that might be relevant. - The Security Considerations section MUST discuss residual risks, such as the risks from compromise in a related protocol, incorrect implementation, or cipher after threat mitigation was deployed. > To Do: Identify residual attacks and mitigations - The Security Considerations section MUST provide integrity protection and update authentication for all operations required by Section § 8.2 Method Operations. > To Do: Brief review of the write operations -- DID Controller must sign operation, as needed others may need to sign it. Risk of loss of control if private key for the DID Controller is lost. - If authentication is involved, particularly user-host authentication, the security characteristics of the authentication method MUST be clearly documented. - No user-host authentication is used. The appropriate signatures are needed on write transactions, where the DIDs and verkeys of the signatories must be one the ledger. > To Do: Expand - The Security Considerations section MUST discuss the policy mechanism by which DIDs are proven to be uniquely assigned. - DIDs are self-certifying, derived from the initial verkey of an ED25519 key pair, which makes them extremely likely to be unique. Any attempt to write the same DID would only work if the signature matched (e.g. if the seed to create the DID had been lost so the literal same DID was attempted to be written), which would result in no change to the ledger, and goes against assumption of the DID Controller not protecting their seed/private key. > To Do: Expand - Method-specific endpoint authentication MUST be discussed. Where DID methods make use of DLTs with varying network topology, sometimes offered as light node or thin client implementations to reduce required computing resources, the security assumptions of the topology available to implementations of the DID method MUST be discussed. - No endpoint authentication is used. > To Do: Expand - If a protocol incorporates cryptographic protection mechanisms, the DID method specification MUST clearly indicate which portions of the data are protected and by what protections, and it SHOULD give an indication of the sorts of attacks to which the cryptographic protection is susceptible. Some examples are integrity only, confidentiality, and endpoint authentication. > To Do: Expand - Data which is to be held secret (keying material, random seeds, and so on) SHOULD be clearly labeled. - No ledger data needs to be kept secret. The private keys associated with the public keys written to the ledger must be kept secret. - The seed used for private key generation > To Do: Expand - DID method specifications SHOULD explain and specify the implementation of signatures on DID documents, if applicable. > To Do: Document the signature scheme used to authorize writes to the ledger. - Where DID methods use peer-to-peer computing resources, such as with all known DLTs, the expected burdens of those resources SHOULD be discussed in relation to denial of service. > To Do: Document this for Indy - DID methods that introduce new authentication service types, as described in § 5.4 Services, SHOULD consider the security requirements of the supported authentication protocol. > To Do: Confirm not applicable. ## Privacy Considerations Given that Indy is a publicly readable, immutable ledger, no personally identifiable information, including DIDs where a person is the DID Subject, should be placed on the network. ### DID Core Specification Privacy Considerations The requirements for all DID method specifications when authoring the Privacy Considerations section are: - The DID method specification's Privacy Considerations section MUST discuss any subsection of [Section 5 of RFC6973](https://tools.ietf.org/html/rfc6973#section-5) that could apply in a method-specific manner. The subsections to consider are: surveillance, stored data compromise, unsolicited traffic, misattribution, correlation, identification, secondary use, disclosure, and exclusion. - surveillance - stored data compromise - unsolicited traffic - misattribution - correlation - identification - secondary use - disclosure - exclusion > To Do: Need responses to each section ## Future Directions ### Multiple Signature NYMs While not part of this version of the Indy DID Method specification, the group defining the specification recommends that the Indy `NYM` be extended to support multiple signature scenarios, where the NYM can be updated to include multiple verification keys, and ledger rules defined for authentication of update transactions, such as M signatures of N total signatures are required. Such a change would be reflected in the `verificationMethod` and `authentication` items in the generated DIDDoc. ### Recovery Mechanism While not part of this version of the Indy DID Method specification, the group defining the specification recommends that the Indy `NYM` be extended to support a recovery mechanism, such as defined in [KERI](https://keri.one). To summarize, a recovery mechanism involves the controller creating not only a verification key pair, but also a recovery public/private key pair during create and update operations. The recovery key is added in some form to (in the case of Indy) the NYM. With a recovery mechanism in place, if only the active key becomes compromised, only the controller can rotate to the recovery key. Assuming the recovery private key is maintained in an even more secure location than the active private key, recovery should always be possible. Without a recovery mechanism, a compromised active key can be used to rotate the verification key and take control of the NYM (and DID) from the controller. ### KERI Support Some consideration was given in designing the initial version of the `did:indy` DID method to including support for supporting KERI identifiers as part of the DID method. At the time of completing the initial version of the spec there was not a clear definition of what "support" would mean, and the concept was moved to this section of the specification. ### Cross Registering Indy Ledgers Some consideration was given to the idea of allowing network discovery by registering network configuration data (such as Indy genesis files) for a ledger on other ledgers. With such a capability with Indy, connecting to one ledger would allow the discovery of all other Indy ledgers registered on that ledger. The idea was partially explored in [this document](https://docs.google.com/document/d/1qLCaUiPtFZVNVUkAcLOhkPDPFs-ealTQmmy4HvYYhXQ/edit?usp=sharing), but the design was not completed in time for inclusion. ### Other Signature Schemes Indy implicitly uses the ED25519 Signature scheme for the `verkey`. We recommend that the NYMs be evolved to explcitly state the signature scheme so that other signature key schemes can be used on Indy networks.