Skip to content

Latest commit

 

History

History
363 lines (242 loc) · 18.9 KB

DidSpecs.md

File metadata and controls

363 lines (242 loc) · 18.9 KB

LAC1 DID Method Specification

Author

  • LACChain

Introduction

In compliance with the W3C did core specs we propose a new DID method based on ethr did method and the LAC did method. The values added in the proposed DID method are the capability to encode the exact path, in the DID, to resolve the underlying DID registry. Secondly, the resolved DID document allows to specify a time in the past from which the attribute being revoked stops being valid, additionally the underlying DID registry shows transparency by giving anyone the ability to verify every change in regards to that attribute.

Abstract

The following DID method inherits from ethr the creation of off-chain DIDs, but different from this we also encode the on-chain DID registry as well as other relevant params as part of the final DID identifier. Any Ethereum contract account or any ethereum externally Owned Account can be a valid param to comprise a valid DID identifier. Such an identifier doesn't need to be registered on any registry. If attributes or services need to be registered those can be added to the instance of did registry obtained after decoding the DID.

The account abstraction as proposed by the ethr underlying ERC1056 standard is also inherited so any account can delegate the transaction sending to a third party account.

Identifier Controller

Each identifier can be controlled by just one ethereum address which can be either an externally owned account or a contract account.

Target System

The target system is the one where the DID registry is deployed. In general it can be any EVM compatible network.

Additional Advantages to ethr and lac DID methods

  • backwads revocation time support: With this feature the the controller of a DID can not only revoke from the moment the controller is executing the revocation of a key but also can specify the time in the past at which the key is considered revoked, this is especially useful for cases when it is necessary to revoke a key without affecting all cryptographically verifiable statements with that key but just part of them from some specified time. This opens two main considerations:
    • Transparency: Since revocations are logged in the blockchain all changes pertaining any key are perfectly traced and thus transparent.
    • Key compromission: Imagine the case where a key, associated to a DID for some purpose, was compromised. In such case, the controller behind the DID can fully revoke the key and thus affect all cryptographically verifiable statements made with that key; or to avoid affecting all cryptographically verifiable statements, the controller can make a revocation by specfyfing a time in the past when that key stops being valid. For example if it is identified that the compromission ocurred X days ago, then the controller can revoke the key specifying that all cryptographically verifiable statements made earlier than X days ago are still valid. The natural question here is how a verifier relies that some cryptographically verifiable statement was made earlier than X days ago?, well it is a matter that the verifiable statement should address; for example by using a proof of time that can be anchored to a blockchain so the verifier can be sure of the existence of that verifiable statement at that time.
  • ability to directly resolve the DID registry from the DID
  • ability to handle improvements while maintaining backwards compatibility

DID Method Name

  • The method name for the proposed DID registry is "lac1"

Specific identifier definition

Let us assume the DID is did:lac1":base58-specific-identifier, then the base58-specific-identifier is defined as follows:

did = "did:lac1":base58-specific-identifier
base58-specific-identifier = base58(specific-identifier-bytes)

specific-identifier-bytes = payload.push(checksum)
checksum = hashFunction(payload)[0:4] # first 4 bytes of checksum
palyload = Array.concat(<did-version-bytes>, <did-type-bytes>, <data-buffer-bytes>)

Where:

  • did-type-bytes - (2 bytes) - represents the transformation to bytes of the did-type-hex
  • did-version-bytes - (2 bytes) - represents the transformation to bytes of did-version-hex

Note that data-buffer-bytes is variable depending on the did-version-bytes and did-type-bytes.

For example when did-version-hex is 0x0001 (in bytes -> <00 01>) and did-type-hex is 0x0001 (in bytes -> <00 01>) the data-buffer-bytes is defined as follows

Having the capability to define different types and different versions allows to better handle any improvements while also maintaining backwards compatibility.

Underlying DID Registry

DID registry

CRUD Operation Definitions

Create

As long as you have all the required inputs the DID creation process will not require any transaction to the underlying ethereum based network. For example for type "0001" and version "0001" the creation will consist of the following operations as mentioned in its create section

A sucessful create operation will render a value like did:lac1:1iT4aTtv4iMBEvQMtdXtWwK4R3r55paDyDywrGXGUZ4EdeCgkBb4mh1EAHrzY1KwKBia where initially the DID Document will look like:

{
  "@context": "https://www.w3.org/ns/did/v1",
  "id": "did:lac1:1iT4Zoku28ehvub6qrZtEp8VTCmqAjxqU5wFUBz4qCDyR8RkTa8uPdNc1MfAV7fSLd7i",
  "controller": "did:lac1:1iT4Zoku28ehvub6qrZtEp8VTCmqAjxqU5wFUBz4qCDyR8RkTa8uPdNc1MfAV7fSLd7i",
  "verificationMethod": [],
  "authentication": [],
  "assertionMethod": [],
  "keyAgreement": [],
  "capabilityInvocation": [],
  "capabilityDelegation": []
}

Read

Controller Address

Each identifier always has a controller address. By default, it is the same as the identifier address, but the resolver MUST check the read only contract function identityController(address identity) on the deployed DID Registry smart contract.

This controller address MUST be represented in the DID document in the attribute controller and MUST be formated following the guidance as specified in the DID Creation process

Enumerating Contract events to build the DID document

To construct a valid DID document, first find all changes in history for an identity:

  1. perform an eth_call to changed(address identity) on the DID Registry smart contract to get the latst block where a change occurred.

  2. If result is zero return.

  3. For the given block, filter events where

    • event name is DIDAttributeChanged for attributes:
        event DIDAttributeChanged(
            address indexed identity,
            bytes name,
            bytes value,
            uint validTo,
            uint changeTime,
            uint previousChange,
            bool compromised
        )
    • and event name is DIDDelegateChanged for onchain delegates:
        event DIDDelegateChanged(
            address indexed identity,
            bytes32 delegateType,
            address delegate,
            uint validTo,
            uint changeTime,
            uint previousChange,
            bool compromised
        );
  4. In any case if the mentioned events have the attribute previousChange different to zero then go back to step 3 where block=<value of previousChange>

  5. After building the history of events for an address, interpret each event to build the DID document given the following:

Attributes (DIDAttributeChanged)

While any attribute can be emited in the DIDAttributeChanged, for the DID document we currently support adding to each of these sections of the DID document:

Delegate Keys (DIDDelegateChanged)

Delegate keys are Ethereum addresses that can either be general signing keys or optionally also perform authentication.

They are also verifiable from Solidity (on-chain).

When a delegate is added or revoked, a DIDDelegateChanged event is published which MUST be used to update the DID document.

The only 2 delegateTypes that are currently published in the DID document are:

'veriKey' delegate type

Which adds a EcdsaSecp256k1RecoveryMethod2020 to the verificationMethod section of the DID document with the blockchainAccountId(ethereumAddress) of the delegate, and adds a reference to it in the assertionMethod section.

'sigAuth' delegate type

Which adds a EcdsaSecp256k1RecoveryMethod2020 to the verificationMethod section of document and a reference to it in the authentication section.

Note the delegateType is a bytes32 type.

Valid on-chain delegates MUST be added to the verificationMethod array as EcdsaSecp256k1RecoveryMethod2020 entries, with the delegate address listed in the blockchainAccountId property and prefixed with eip155:<chainId>:, according to CAIP10

Example:

{
"id": "did:lac1:1iT5jsMUTRkENt6WspMf5CGJNc9bUxt38urgGGxqaFhrLn4cmsC6XNddWb1pAUfonk33#vm-1",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:lac1:1iT5jsMUTRkENt6WspMf5CGJNc9bUxt38urgGGxqaFhrLn4cmsC6XNddWb1pAUfonk33",
"blockchainAccountId": "eip155:648540:0x95d7723676AE52E71281Bc6868A05dB843aD8410"
}
DID Document versions

Only events with a validTo (measured in seconds) greater or equal to the current time should be included in the DID document. It is also possible to know a DID Document where given a versionTime or versionId (specified the didURL query string)

id properties of entries
  • Attribute or delegate changes that result in verificationMethod entries MUST set the id ${did}#vm-${eventIndex}.
  • Attributes that result in service entries MUST set the id to ${did}#service-${eventIndex}

where eventIndex is the index of the event that modifies that section of the DID document.

Example

  • add key => #vm-1 is added
  • add another key => #vm-2 is added
  • add delegate => #vm-3 is added
  • add service => #service-1 is added
  • revoke first key => #vm-1 gets removed from the DID document; #vm-2 and #delegte-3 remain.
  • add another delegate => #vm-5 is added (earlier revocation is counted as an event)
  • first delegate expires => vm-3 is removed, #vm-5 remains intact

Update

The DID Document may be updated by invoking the relevant smart contract functions as defined by the DID registry. This includes changes to the account owner, adding delegates and adding additional attributes. Please find a detailed description in the DID Registry Documentation

These functions will trigger the respective Ethereum events which are used to build the DID Document for a given account as described in Enumerating Contract Events to build the DID Document.

Note: Extending the validity of some attribute/delegate in the DID Registry will create a new input in the DID Document.

Delete

To revoke a DID, the controller of the DID needs to be set to 0x0. Although, 0x0 is a valid Ethereum address, this will indicate the identity has no controller which is a common approach for invalidation.

changeController(address identity, '0x0');

If there is any other changes to the DID document after such a change, all preexisting keys and services will be considered revoked.

If the intention is to revoke all the signatures corresponding to the DID, this option MUST be used.

The DID resolution result for a deactivated DID has the following shape:

{
  "didDocumentMetadata": {
    "deactivated": true
  },
  "didResolutionMetadata": {
    "contentType": "application/did+ld+json"
  },
  "didDocument": {
    "@context": "https://www.w3.org/ns/did/v1",
    "id": "<the deactivated DID>",
    "verificationMethod": [],
    "assertionMethod": [],
    "authentication": []
  }
}

Security considerations of DID versioning

Applications MUST take precautions when using versioned DID URIs. If a key is compromised and revoked then it can still be used to issue signatures on behalf of the "older" DID URI. The use of versioned DID URIs is only recommended in some limited situations where the timestamp of signatures can also be verified, where malicious signatures can be easily revoked, and where applications can afford to check for these explicit revocations of either keys or signatures. Wherever versioned DIDs are in use, it SHOULD be made obvious to users that they are dealing with potentially revoked data.

Metadata

The resolve method returns an object with the following properties: didDocument, didDocumentMetadata, didResolutionMetadata.

DID Document Metadata

When resolving a DID document that has had updates, the latest update MUST be listed in the didDocumentMetadata.

  • versionId MUST be the block number of the latest update. This can be obtained from the changed state variable by passing the respective address.
  • updated MUST be the ISO date string of the block time of the latest update (without sub-second resolution). In the DIDAttributeChanged and DIDDelegateChanged it is the changeTime attribute. Example:
{
  "didDocumentMetadata": {
    "versionId": "12090175",
    "updated": "2021-03-22T18:14:29Z"
  }
}

DID Resolution metadata

{
  "didResolutionMetadata": {
    "contentType": "application/did+ld+json"
  }
}

Resolving DID URIs with query parameters

versionId query string parameter

This DID method supports resolving previous versions of the DID document by specifying a versionId parameter.

Example: did:lac1:1iT5jsMUTRkENt6WspMf5CGJNc9bUxt38urgGGxqaFhrLn4cmsC6XNddWb1pAUfonk33?versionId=12090175

The versionId is the block number at which the DID resolution MUST be performed. Only DIDAttributeChanged and DIDDelegateChanged events, from DID Registry prior to or contained in this block number are to be considered when building the event history.

updated MUST be the ISO date string of the block time at which the DID resolution MUST be performed (without sub-second resolution). In the DIDAttributeChanged and DIDDelegateChanged it is the changeTime attribute.

If there are any events after that block that mutate the DID, the earliest of them SHOULD be used to populate the properties of the didDocumentMetadata:

  • nextVersionId MUST be the block number of the next update to the DID document.
  • nextUpdate MUST be the ISO date string of the block time of the next update (without sub-second resolution). In the DIDAttributeChanged and DIDDelegateChanged it is the changeTime attribute.

In case the DID has had updates prior to or included in the versionId block number, the updated and versionId properties of the didDocumentMetadata MUST correspond to the latest block prior to the versionId query string param.

Any timestamp comparisons of validTo fields of the event history MUST be done against the versionId block timestamp.

Example: ?versionId=12101682

{
  "didDocumentMetadata": {
  "versionId": "12090175",
  "updated": "2021-03-22T18:14:29Z",
  "nextVersionId": "12276565",
  "nextUpdate": "2021-04-20T10:48:42Z"
  }
}

versionTime query string parameter

This DID method supports resolving previous versions of the DID document by specifying a versionTime parameter.

Example: did:lac1:1iT5jsMUTRkENt6WspMf5CGJNc9bUxt38urgGGxqaFhrLn4cmsC6XNddWb1pAUfonk33?versionTime=2021-05-10T17:00:00Z

The versionTime is the time related to a block number at which the DID resolution MUST be performed. Only DIDAttributeChanged and DIDDelegateChanged events, from DID Registry prior to or contained in that block number are to be considered when building the event history. versionTime parameter MUST be compared with the changeTime parameter in the mentioned events to filter the ones to be considered in the DID document.

updated MUST be the ISO date string of the block time at which the DID resolution MUST be performed (without sub-second resolution). In the DIDAttributeChanged and DIDDelegateChanged it is the changeTime attribute.

If there are any events after that block that mutate the DID, the earliest of them SHOULD be used to populate the properties of the didDocumentMetadata:

  • nextVersionId MUST be the block number of the next update to the DID document.
  • nextUpdate MUST be the ISO date string of the block time of the next update (without sub-second resolution). In the DIDAttributeChanged and DIDDelegateChanged it is the changeTime attribute.

In case the DID has had updates prior to or included in the block number correspondent to the mapped versionTime, the updated and versionId properties of the didDocumentMetadata MUST correspond to the latest block prior to the verstionTime query string param.

Any timestamp comparisons of validTo fields of the event history MUST be done against the timestamp block number mapped fromversionTime or otherwise the prior nearest block timestamp to versionTime where a change occurred.

Example: ?versionTime=2021-05-10T17:00:00Z

{
  "didDocumentMetadata": {
  "versionId": "12090175",
  "updated": "2021-03-22T18:14:29Z",
  "nextVersionId": "12276565",
  "nextUpdate": "2021-06-20T10:48:42Z"
  }
}

forTime query string parameter

Given a forTime query string it defines a timewindow where all returned keys in the DID document were set to be valid since forTime up to the latest change; in plain terms it returns all coincidences where forTime =< validTo for each event DIDAttributeChanged and DIDDelegateChanged from DID Registry

For this case the versionId attribute is a block range starting from the minimum block number where events DIDAttributeChanged and DIDDelegateChanged (from DID Registry) accomplish the condition forTime =< validTo up to the last block number where changes were made to the DID document.

updated MUST be the ISO date string of the block time at which the DID resolution MUST be performed (without sub-second resolution). In the DIDAttributeChanged and DIDDelegateChanged it is the changeTime attribute.

Since the returned DID document will always return the latest authorized keys starting from a certain time (forTime), params nextVersionId and nextUpdate are omitted.

Example: ?forTime=2021-05-10T17:00:00Z

{
  "didDocumentMetadata": {
  "versionId": "11565924-12090175",
  "updated": "2022-03-22T18:14:29Z",
  }
}

Reference Implementations

TBD