| eip | TBD |
|---|---|
| title | Digital Identity Aggregator |
| author | Anurag Angara <anurag.angara@gmail.com>, Andy Chorlian <andychorlian@gmail.com>, Shane Hampton <shanehampton1@gmail.com>, Noah Zinsmeister <noahwz@gmail.com> |
| discussions-to | https://github.com/ethereum/EIPs/issues/TBD |
| status | Draft |
| type | Standards Track |
| category | ERC |
| created | 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'sprimary responsibility is enforcing a global namespace for identities, which are individually denominated by a uniquestringbetween 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 Addressfor anIdentity, theIdentitymust produce:- a signed message from the candidate address indicating intent to associate itself with the
Identity - a signed message from an existing
Associated Addressof theIdentityindicating 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 yourIdentitywe have implemented the ability to "nuke" your theIdentity. When this happens, allassociated addresses,resolversandproviderswill be removed and theIdentitywill 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 removeResolversandAssociated Addressesfrom theIdentitiesof users who have authorized theProviderto 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.