eip | title | author | discussions-to | status | type | category | created |
---|---|---|---|---|---|---|---|
TBD |
Digital Identity Aggregator |
Anurag Angara <anurag.angara@gmail.com>, Andy Chorlian <andychorlian@gmail.com>, Shane Hampton <shanehampton1@gmail.com>, Noah Zinsmeister <noahwz@gmail.com> |
Draft |
Standards Track |
ERC |
2018-10-08 |
A protocol for aggregating digital identity information that's broadly interoperable with existing, proposed, and hypothetical future digital identity standards.
This EIP proposes a identity management and aggregation framework on the Ethereum blockchain. It allows users to claim an identity via a singular identity Registry
smart contract, associate it with other Ethereum addresses, and use it to interface with smart contracts providing arbitrarily complex identity-related functionality.
Emerging identity standards and related frameworks proposed by the Ethereum community (including ERCs/EIPs 725, 735, 780, 1056, etc.) define and instrumentalize individuals' digital identities in a variety of ways. As existing approaches mature, new standards emerge, and isolated, non-standard approaches to identity develop, managing multiple identities will becoming increasingly burdensome and involve unnecessary duplication of work.
The proliferation of on-chain identity solutions can be traced back to the fact that each has codified their notion of identity and linked it to specific aspects of Ethereum (smart contracts, signature verification, etc.). This proposal eschews that approach, instead introducing a protocol layer in between the Ethereum network and identity applications. This solves identity management and interoperability challenges by enabling any identity-driven application to leverage an un-opinionated identity management protocol.
-
Registry
: A single smart contract which is the hub for all userIdentities
. TheRegistry's
primary responsibility is enforcing a global namespace for identities, which are individually denominated by a uniquestring
between 3 and 32 bytes long. -
Identity
: The core data structure that constitutes a user's identity. Identities consist of 3 sets of addresses:Associated Addresses
,Providers
, andResolvers
. -
Associated Address
: An Ethereum address publicly associated with anIdentity
. In order for an address to become anAssociated Address
for anIdentity
, theIdentity
must produce:- a signed message from the candidate address indicating intent to associate itself with the
Identity
- a signed message from an existing
Associated Address
of theIdentity
indicating the same.
- a signed message from the candidate address indicating intent to associate itself with the
-
Recovery Address
: An Ethereum address with functionality to recover lost identities as outlined in the Address Recovery section -
Poison Pill
: In the event of irrecoverable control of yourIdentity
we have implemented the ability to "nuke" your theIdentity
. When this happens, allassociated addresses
,resolvers
andproviders
will be removed and theIdentity
will be unusable.
Identities
can remove an Associated Address
by producing a signed message indicating intent to disassociate itself from the Identity
. Signatures are stored in the Registry
to prevent replay attacks.
-
Provider
: An Ethereum address (typically but not by definition a smart contract) authorized to add and removeResolvers
andAssociated Addresses
from theIdentities
of users who have authorized theProvider
to act on their behalf. -
Resolver
: A smart contract containing arbitrary information pertaining to users'Identities
. A resolver may implement an identity standard, such as ERC 725, or may consist of a smart contract leveraging or declaring identifying information aboutIdentities
. These could be financial dApps, social media dApps, etc.
A digital identity in this proposal can be viewed as an omnibus account, containing more information about an identity than any individual identity application could. This omnibus identity is resolvable to an unlimited number of sub-identities. Resolvers recognize identities by any of their associated addresses.
The protocol revolves around claiming an Identity, setting a Provider
and managing associated addresses and resolvers. Users may do so directly, or delegate this responsibility to a Provider
. Provider
smart contracts or addresses may add Resolvers
indiscriminately, but may only add and remove Associated Addresses
with the appropriate permissions.
The Identity Registry contains functionality for a user to establish their core identity and manage their Providers
, Associated Addresses
, and Resolvers
. It is important to note that this registry fundamentally requires transactions for every aspect of building out a user's identity through both resolvers and addresses. Nonetheless, we recognize the importance of global accessibility to dApps and identity applications. Accordingly, we include the option for a delegated identity-building scheme that allows smart contracts called Providers
to build out a user's identity through signatures without requiring users to pay gas costs.
We propose that Identities
be denominated by a string
for user-friendliness instead of identifying individuals by an address. Identifying users by an address awkwardly provides added meaning to their owner address despite all Associated Addresses
commonly identifying an individual. Further, it creates a more complicated user experience in passing their ID to a resolver or third-party. Currently, the only practical way for a user to identify themselves is to copy-and-paste their Ethereum address or to share a QR code. While QR codes are helpful, we do not feel that they should be the sole notion of user-friendliness by which a user may identify themselves.
The address management function consists of trustlessly connecting multiple user-owned Associated Addresses
to a user's Identity
. It does not prescribe any special status to any given identity-managing address, rather leaving this specification to identity applications built on top of the protocol - for instance, management
, action
, claim
and encryption
keys denominated in the ERC 725 standard. This allows a user to access common identity data from multiple wallets while still
- retaining flexibility to interact with contracts outside of their core identity pseudonymously
- taking advantage of address-specific permissions established at the application layer of a user's identity.
Trustlessness in the address management function is achieved through a signature and verification scheme that requires two transactions - one from an address already within the registry and one from the address to be claimed. This logic is implemented in . Importantly, the transaction need not come from the original user, which allows entities, governments, etc to bear the overhead of creating a core identity.
initiateClaim
: hash(addressToClaim, secret, coreID)
delegatedInitiateClaim
: hash(signature, addressToClaim, secret, coreID)
finalizeClaim
: transact with secret
and coreID
removeAddress
: transact from a claimed address with addressToRemove
The resolver management function is similarly low-level. It considers a resolver to be any smart contract that encodes information which resolves to a user's core identity. It does not set a standard for specific information that can be encoded in a resolver, rather remaining agnostic to the nature of information itself.
setResolver
: transact with resolverAddress
delegatedSetResolver
: transact with signature
and resolverAddress
removeResolver
: transact with resolverAddress
The resolver standard is primarily what makes this ERC an identity protocol rather than an identity application. Resolvers resolve data about an atomic entity, the coreID, in the form of arbitrarily complex smart contracts rather than a pre-defined attestation structure.
While the protocol allows for users to directly call identity management functions, it also aims to be more robust and future-proof by allowing arbitrary smart contracts to perform identity management functions on a user's behalf. A provider set by an individual can perform address management and resolver management functions by passing the user's coreID
in function calls.
he specification introduces a Recovery Address
to account for instances of lost user control over an Associated Address
. Upon Identity
creation, the public Recovery Address
is passed as a parameter by a provider. Identity recovery functionality is triggered in three events.
Recovery: Recovery occurs when a user recognizes that an Associated Address
belonging to the user is lost or stolen. A user in this instance needs to call initiateRecovery
from the Recovery Address
. initiateRecovery
removes all Associated Addresses
and Providers
from the corresponding Identity
and replaces them with an address passed in the initiateRecovery
function call. The Identity
and associated Resolvers
maintain integrity. The user is now responsible for adding the appropriate uncompromised addresses back to their Identity
in the Registry
.
Changing Recovery Key: If a recovery key is lost, a provider can initiateRecoveryAddressChange
with a signature from any claimed address. To prevent malicious change of a Recovery Address
from someone who has gained control of an Associated Address
, this occurs over a 14 day challenge period during which the Recovery Address
may reject the change. If the Recovery Address
does not reject the change within 14 days, the Recovery Address
is changed. However, during the fourteen day period, the Recovery Address
can dispute the change request by calling triggerRecovery
in which case all Associated Addresses
are removed, and an Address delegated in the triggerRecovery
call becomes the only Associated Address
for the Identity
.
Trap Card
The Recovery scheme offers a lot of power to a Recovery Address
; accordingly, activateTrapCard
is a nuclear option to combat malicious control over an Identity
when a Recovery Address
is compromised. If a malicious actor compromises a user's RecoveryAddress
and calls triggerRecovery
, any address removed in the Recovery
process can activateTrapCard
to nuke the Identity
. The user would then need to create a new Identity
and would be responsible for engaging in recovery schemes for any Identity Applications built in the application layer.
In devising the Recovery process outlined in this specification, we considered many Recovery options. We ultimately selected the scheme that was most unopinionated and modular to be consistent with the the Resolver
, Associated Address
and Provider
components within the specification. Still, we feel it important to highlight some of the other recovery options considered to provide deeper rationale as to why we arrived at the above scheme.
High Level Concerns
Fundamentally, a Recovery scheme needed to be resilient to a compromised Associated Address
taking control of a user's Identity
. A secondary concern was preventing an Associated Address
from maliciously destroying a user's identity due to off-chain utility, which is not an optimal scenario, but is strictly better than if they've gained control.
Nuclear Option
This approach would allow any Associated Address
to destroy an Identity
whenever another Associated Address
is compromised. While this may seem harsh, we held it in strong consideration due to the fact that ERC[TBD] is a protocol rather than an Identity application. Accordingly, once a user destroyed their compromised Identity
, they would need to use whatever Restoration mechanisms were apparent in each of their actual identities. We ultimately dismissed this approach for two main reasons.
- It would increase the frequency of recovery requests for applications of identity due to the multiple addresses any of which could be compromised.
- It is not robust for instances in which a user has only one
Associated Address
Unilateral Address Removal from a provider
This would allow providers to remove an Associated Address
without a signature from the Associated Address
. To prevent a compromised address from setting a malicious provider to remove uncompromised addresses, this would require a waiting period between when a provider is set and when a provider is able to remove an Associated Address
. This implementation would allow a provider to include arbitrarily sophisticated Recovery schemes - for instance, multi-sig requirements, centralized off-chain verification, user controlled master addresses, deferral to a jurisdictional contract, or more. We dismissed this approach because we felt it placed too high of a burden on Providers
. If a Provider
offered a sophisticated range of functionality to a user, but post-deployment a threat was found in the Recovery logic of the provider, provider-specific infrastructure would need to be rebuilt. We considered including a flag that allows a user to determine if a provider may or may not remove Associated Addresses
; however, we ultimately concluded that breaking out removal of an Associated Address
into a Recovery Address
allows for equally sophisticated recovery logic while separating the functionality from providers and leaving less room for users to relinquish control to poorly implemented providers.
Importantly, the recovery address can be a user-controlled wallet or another address such as a multisig wallet or a smart contract address. This allows for more sophisticated recovery structures that can be compliant with identity standards for recovery such as those laid out by DID
We find that at a protocol layer, identity should contain no claim or attestation structure and should rather simply lay a trustless framework upon which arbitrarily sophisticated claim and attestation structures may lie in conjunction.
The main criticism of an identity layer comes from restrictiveness; we aim to limit requirements to be modular and future-proof without providing any special functionality for any component within the core registry. It simply allows users the option to interact on the blockchain using an arbitrarily robust identity rather than just an address.
Returns a bool
indicating whether or not an Identity
denominated by the passed identity
string exists.
function identityExists(string identity) public view returns (bool);
Returns a bool
indicating whether or not the passed _address
is associated with an Identity
.
function hasIdentity(address _address) public view returns (bool);
Returns the identity
associated with the passed _address
. Throws if no such identity
exists.
function getIdentity(address _address) public view returns (string identity);
Returns a bool
indicating whether or not the passed provider
is assigned to the passed identity
.
function isProviderFor(string identity, address provider) public view returns (bool);
Returns a bool
indicating whether or not the passed resolver
is assigned to the passed identity
.
function isResolverFor(string identity, address resolver) public view returns (bool);
Returns a bool
indicating whether or not the passed _address
is owned by the passed identity
.
function isAddressFor(string identity, address _address) public view returns (bool);
Returns three address
arrays of associatedAddresses
, providers
and resolvers
. All of these arrays represent the addresses associated with the passed identity
.
function getDetails(string identity) public view returns (address[] associatedAddresses, address[] providers, address[] resolvers);
Mints an Identity
with the passed identity
and provider
.
function mintIdentity(string identity, address provider) public;
Preforms the same logic as mintIdentity
, but can be called by a provider
. This function requires a signature for the associatedAddress
to confirm their consent.
function mintIdentityDelegated(string identity, address associatedAddress, uint8 v, bytes32 r, bytes32 s) public;
Adds an array of providers
to the Identity
of the msg.sender
.
function addProviders(address[] providers) public;
Removes an array of providers
from the Identity
of the msg.sender
.
function removeProviders(address[] providers) public;
Adds an array of resolvers
to the passed identity
. This must be called by a provider
.
function addResolvers(string identity, address[] resolvers) public;
Removes an array of resolvers
from the passed identity
. This must be called by a provider
.
function removeResolvers(string identity, address[] resolvers) public;
Adds the addressToAdd
to the passed identity
. Requires signatures from both the addressToAdd
and the approvingAddress
.
function addAddress(string identity, address approvingAddress, address addressToAdd, uint8[2] v, bytes32[2] r, bytes32[2] s, uint salt) public;
Removes an addressToRemove
from the passed identity
. Requires a signature from the addressToRemove
.
function removeAddress(string identity, address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint salt) public;
pragma solidity ^0.4.24;
contract ERCTBD {
event IdentityMinted(string identity, address associatedAddress, address provider, bool delegated);
event ResolverAdded(string identity, address resolvers, address provider);
event ResolverRemoved(string identity, address resolvers, address provider);
event AddressAdded(string identity, address addedAddress, address approvingAddress, address provider);
event AddressRemoved(string identity, address removedAddress, address provider);
struct Identity {
AddressSet.Set associatedAddresses;
AddressSet.Set providers;
AddressSet.Set resolvers;
}
function identityExists(string identity) public view returns (bool);
function hasIdentity(address _address) public view returns (bool);
function getIdentity(address _address) public view returns (string identity);
function isProviderFor(string identity, address provider) public view returns (bool);
function isResolverFor(string identity, address resolver) public view returns (bool);
function isAddressFor(string identity, address _address) public view returns (bool);
function getDetails(string identity) public view returns (address[] associatedAddresses, address[] providers, address[] resolvers);
function mintIdentity(string identity, address provider) public;
function mintIdentityDelegated(string identity, address associatedAddress, uint8 v, bytes32 r, bytes32 s) public;
function addProviders(address[] providers) public;
function removeProviders(address[] providers) public;
function addResolvers(string identity, address[] resolvers) public;
function removeResolvers(string identity, address[] resolvers) public;
function addAddress(string identity, address approvingAddress, address addressToAdd, uint8[2] v, bytes32[2] r, bytes32[2] s, uint salt) public;
function removeAddress(string identity, address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint salt) public;
}
Identities
established under this standard consist of existing Ethereum addresses; accordingly, identity construction has no backwards compatibility issues. Deployed, non-upgradeable smart contracts that wish to become Resolvers
to a user's Identity
will need to write wrapper contracts that resolve addresses to Identities
with getIdentity
.
Copyright and related rights waived via CC0.