- LACChain
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.
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.
Each identifier can be controlled by just one ethereum address which can be either an externally owned account or a contract account.
The target system is the one where the DID registry is deployed. In general it can be any EVM compatible network.
- 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
- The method name for the proposed DID registry is "lac1"
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 thedid-type-hex
did-version-bytes
- (2 bytes) - represents the transformation to bytes ofdid-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.
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": []
}
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
To construct a valid DID document, first find all changes in history for an identity:
-
perform an eth_call to changed(address identity) on the DID Registry smart contract to get the latst block where a change occurred.
-
If result is zero return.
-
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 );
- event name is
-
In any case if the mentioned events have the attribute
previousChange
different to zero then go back to step 3 whereblock=<value of previousChange>
-
After building the history of events for an address, interpret each event to build the DID document given the following:
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 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:
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.
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"
}
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)
- 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
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.
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": []
}
}
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.
The resolve method returns an object with the following properties: didDocument, didDocumentMetadata, didResolutionMetadata.
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
andDIDDelegateChanged
it is thechangeTime
attribute. Example:
{
"didDocumentMetadata": {
"versionId": "12090175",
"updated": "2021-03-22T18:14:29Z"
}
}
{
"didResolutionMetadata": {
"contentType": "application/did+ld+json"
}
}
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 theDIDAttributeChanged
andDIDDelegateChanged
it is thechangeTime
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"
}
}
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 theDIDAttributeChanged
andDIDDelegateChanged
it is thechangeTime
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"
}
}
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",
}
}
TBD