Ethereum Lightweight Identity
Simple Summary
A registry for key and attribute management of lightweight blockchain identities.
Abstract
This ERC describes a standard for creating and updating identities with a limited use of blockchain resources. An identity can have an unlimited number of delegates
and attributes
associated with it. Identity creation is as simple as creating a regular key pair ethereum account, which means that it’s free (no gas costs) and all ethereum accounts are valid identities. Furthermore this ERC is fully DID compliant.
Motivation
As we have been developing identity systems for the last couple of years at uPort it has become apparent that the cost of identity creation is a large issue. The previous Identity proposal ERC-725 faces this exact issue. Our requirements when creating this ERC is that identity creation should be free, and should be possible to do in an offline environment (e.g. refugee scenario). However it must also be possible to rotate keys without changing the primary identifier of the identity. The identity system should be fit to use off-chain as well as on-chain.
Definitions
-
Identifier
: a piece of data that uniquely identifies the identity, an ethereum address -
delegate
: an address that is delegated for a specific time to perform some sort of function on behalf of an identity -
delegateType
: the type of a delegate, is determined by a protocol or application higher up Examples:did-jwt
raiden
-
attribute
: a piece of data associated with the identity
Specification
This ERC specifies a contract called EthereumDIDRegistry
that is deployed once and can then be commonly used by everyone.
Identity ownership
By default an identity is owned by itself, meaning whoever controls the ethereum account with that address. The owner can be updated to a new key pair account or to a multisig account etc.
identityOwner
Returns the owner of the given identity.
function identityOwner(address identity) public view returns(address);
changeOwner
Sets the owner of the given identity to another ethereum account.
function changeOwner(address identity, address newOwner) public;
changeOwnerSigned
Same as above but with raw signature.
function changeOwnerSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, address newOwner) public;
Delegate management
Delegates can be used both on- and off-chain. They all have a delegateType
which can be used to specify the purpose of the delegate.
validDelegate
Returns true if the given delegate
is a delegate with type delegateType
of identity
.
function validDelegate(address identity, bytes32 delegateType, address delegate) public view returns(bool);
addDelegate
Adds a new delegate with the given type. validity
indicates the number of seconds that the delegate will be valid for, after which it will no longer be a delegate of identity
.
function addDelegate(address identity, bytes32 delegateType, address delegate, uint validity) public;
addDelegateSigned
Same as above but with raw signature.
function addDelegateSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 delegateType, address delegate, uint validity) public;
revokeDelegate
Revokes the given delegate
for the given identity
.
function revokeDelegate(address identity, bytes32 delegateType, address delegate) public;
revokeDelegateSigned
Same as above but with raw signature.
function revokeDelegateSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 delegateType, address delegate) public;
Attribute management
Attributes contain simple data about the identity. They can be managed only by the owner of the identity.
setAttribute
Sets an attribute with the given name
and value
, valid for validity
seconds.
function setAttribute(address identity, bytes32 name, bytes value, uint validity) public;
setAttributeSigned
Same as above but with raw signature.
function setAttributeSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 name, bytes value, uint validity) public;
revokeAttrubte
Revokes an attribute.
function revokeAttribute(address identity, bytes32 name, bytes value) public;
revokeAttributeSigned
Same as above but with raw signature.
function revokeAttributeSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 name, bytes value) public;
Events
DIDOwnerChanged
MUST be triggered when changeOwner
or changeOwnerSigned
was successfully called.
event DIDOwnerChanged(
address indexed identity,
address owner,
uint previousChange
);
DIDDelegateChanged
MUST be triggered when a change to a delegate was successfully made.
event DIDDelegateChanged(
address indexed identity,
bytes32 delegateType,
address delegate,
uint validTo,
uint previousChange
);
DIDAttritueChanged
MUST be triggered when a change to an attribute was successfully made.
event DIDAttributeChanged(
address indexed identity,
bytes32 name,
bytes value,
uint validTo,
uint previousChange
);
Efficient lookup of events through linked identity events
Contract Events are a useful feature for storing data from smart contracts exclusively for off-chain use. Unfortunately current ethereum implementations provide a very inefficient lookup mechanism. By using linked events that always link to the previous block with a change for the identity, we can solve this problem with much improved performance. Each identity has its previously changed block stored in the changed
mapping.
-
Lookup
previousChange
block for identity -
Lookup all events for given identity address using web3, but only for the
previousChange
block -
Do something with the event
-
Find
previousChange
from the event and repeat
Example code:
const history = []
previousChange = await didReg.changed(identity)
while (previousChange) {
const filter = await didReg.allEvents({topics: [identity], fromBlock: previousChange, toBlock: previousChange})
const events = await getLogs(filter)
previousChange = undefined
for (let event of events) {
history.unshift(event)
previousChange = event.args.previousChange
}
}
Building a DID document for an identity
The primary owner key should be looked up using identityOwner(identity)
. This should be the first of the publicKeys listed. Iterate through the DIDDelegateChanged
events to build a list of additional keys and authentication sections as needed. The list of delegateTypes to include is still to be determined. Iterate through DIDAttributeChanged
events for service entries, encryption public keys and other public names. The attribute names are still to be determined.
Rationale
For on-chain interactions Ethereum has a built in account abstraction that can be used regardless of whether the account is a smart contract or a key pair. Any transaction has a msg.sender
as the verified send of the transaction.
Since each Ethereum transaction has to be funded, there is a growing trend of on-chain transactions that are authenticated via an externally created signature and not by the actual transaction originator. This allows 3rd party funding services or receiver pays without any fundamental changes to the underlying Ethereum architecture. These kinds of transactions have to be signed by an actual key pair and thus can not be used to represent smart contract based Ethereum accounts.
We propose a way of a Smart Contract or regular key pair delegating signing for various purposes to externally managed key pairs. This allows a smart contract to be represented both on-chain as well as off-chain or in payment channels through temporary or permanent delegates.
Backwards Compatibility
All ethereum accounts are valid identities (and DID compatible) using this standard. This means that any wallet provider that uses key pair accounts already supports the bare minimum of this standard, and can implement delegate
and attribute
functionality by simply using the ethr-did
referenced below. As the DID Auth standard solidifies it also means that all of these wallets will be compatible with the DID decentralized login system.
Implementation
ethr-did-registry (EthereumDIDRegistry
contract implementation)
ethr-did-resolver (DID compatible resolver)
ethr-did (javascript library for using the identity)
Deployment
The address for the EthereumDIDRegistry
is 0xdca7ef03e98e0dc2b855be647c39abe984fcf21b
on Mainnet, Ropsten, Rinkeby and Kovan.
Copyright
Copyright and related rights waived via CC0.