28 KiB
eip | title | author | discussions-to | status | type | category | created | requires |
---|---|---|---|---|---|---|---|---|
1484 | Digital Identity Aggregator | Anurag Angara <anurag.angara@gmail.com>, Andy Chorlian <andychorlian@gmail.com>, Shane Hampton <shanehampton1@gmail.com>, Noah Zinsmeister <noahwz@gmail.com> | https://github.com/ethereum/EIPs/issues/1495 | Draft | Standards Track | ERC | 2018-10-12 | 191 |
Simple Summary
A protocol for aggregating digital identity information that's broadly interoperable with existing, proposed, and hypothetical future digital identity standards.
Abstract
This EIP proposes an identity management and aggregation framework on the Ethereum blockchain. It allows entities to claim an identity via a singular Identity Registry
smart contract, associate this Identity
with Ethereum addresses in a variety of meaningful ways, and use it to interface with smart contracts. This enables arbitrarily complex identity-related functionality. Notably (among other features) this proposal is DID compliant, can natively support ERC-725 and ERC-1056 identities, and lays a robust foundation for meta-transactions.
Motivation
Emerging identity standards and related frameworks proposed by the Ethereum community (including ERCs/EIPs 725, 735, 780, 1056, etc.) define and instrumentalize digital identity in a variety of ways. As existing approaches mature, new standards emerge, and isolated, non-standard approaches to identity develop, managing multiple identities will become increasingly burdensome and involve the unnecessary duplication of work.
The proliferation of on-chain identity solutions can be traced back to the fact that each codifies a notion of identity and links it to specific aspects of Ethereum (claims protocols, per-identity smart contracts, signature verification schemes, etc.). This proposal eschews that approach, instead introducing a protocol layer in between the Ethereum network and individual identity applications. This solves identity management and interoperability challenges by enabling any identity-driven application to leverage an un-opinionated identity management protocol.
Definitions
-
Identity Registry
: A single smart contract which is the hub for allIdentities
. The primary responsibility of theRegistry
is to enforce a global namespace forIdentities
, which are individually denominated by Ethereum Identification Numbers (EINs). -
Identity
: A data structure containing all the information relevant to a particular identity. They are denominated by EINs (incrementinguint
s), which are unique but uninformative. -
Associated Address
: An Ethereum address publicly associated with anIdentity
. In order for an address to become anAssociated Address
for anIdentity
, theIdentity
must produce signed messages from the candidate address and an existingAssociated Address
indicating this intent. Identities can remove anAssociated Address
by producing an appropriately signed message indicating intent to disassociate itself from theIdentity
. An address may only be anAssociated Address
for oneIdentity
at any given time. -
Provider
: An Ethereum address (typically but not by definition a smart contract) authorized to add and removeAssociated Addresses
,Providers
, andResolvers
fromIdentities
who have authorized theProvider
to act on their behalf.Providers
exist to facilitate user adoption, and make it easier to manageIdentities
. -
Resolver
: A smart contract containing arbitrary information pertaining toIdentities
. 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 simple attestation structures or more sophisticated financial dApps, social media dApps, etc. EachResolver
added to anIdentity
makes theIdentity
more informative. -
Recovery Address
: An Ethereum address (either an account or smart contract) that can be used to recover lost identities as outlined in the Recovery section. -
Poison Pill
: In the event of irrecoverable control of anIdentity
, thePoison Pill
offers a contingency measure to permanently disable theIdentity
. It removes allAssociated Addresses
andProviders
while preserving theIdentity
(and optionally,Resolvers
). Evidence of the existence of theIdentity
persists, while control over theIdentity
is nullified.
Specification
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 called Resolvers
. This allows an atomic entity, the Identity
, to be resolvable to abstract data structures, the Resolvers
. Resolvers
recognize Identities
by any of their Associated Addresses
, or by their EIN
.
The protocol revolves around claiming an Identity
and managing Associated Addresses
and Resolvers
. Identities delegate much of this responsibility to one or more Providers
. Provider
smart contracts or addresses may add and remove Resolvers
and Providers
indiscriminately, but may only add and remove Associated Addresses
with the appropriate permissions.
Identity Registry
The Identity Registry
contains functionality to create new Identities
and for existing Identities
to manage their Providers
, Associated Addresses
, and Resolvers
. It is important to note that this registry fundamentally requires transactions for every aspect of building out an Identity
. However, recognizing the importance of accessibility to dApps and identity applications, we empower Providers
to build out Identities
on the behalf of users, without requiring users to pay gas costs. An example of this pattern, often referred to as a meta transactions, can be seen in the reference implementation.
Due to the fact that multiple addresses can be associated with a given identity (though not the reverse), Identities
are denominated by EIN
s. This uint
identifier can be encoded in QR format or transformed into more user-intuitive formats, such as a string
, in registries at the Provider
or Resolver
level.
Address Management
The address management function consists of trustlessly connecting multiple user-owned Associated Addresses
to an Identity
. It does not give special status to any particular Associated 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, or identifiers
and delegates
as denominated in ERC-1056. This allows a user to access common identity data from multiple wallets while still:
- retaining flexibility to interact with contracts outside of their identity
- 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 signatures - one from an address already within the registry and one from the address to be claimed. Importantly, the transaction need not come from the original user, which allows third parties (entities, governments, etc.) to bear the overhead of creating core identities. To prevent a compromised Associated Address
from unilaterally removing other Associated Addresses
, removal of an Associated Address
also requires a signature from the address to be removed.
All signatures required in ERC-1484 are designed per the ERC-191 v0 specification. To avoid replay attacks, all signatures must include a timestamp within a rolling lagged window of the current block.timestamp
. For more information, see this best practices document in the reference implementation.
Provider Management
While the protocol allows users to directly call identity management functions, it also aims to be more robust and future-proof by allowing Providers
, typically arbitrary smart contracts, to perform identity management functions on a user's behalf. A Provider
set by an identity can perform address management and resolver management functions by passing a user's EIN
in function calls. In order to prevent Identities
from adding an initial Provider
that does not implement the functionality to add other Providers
(this leaving the Identity
'stuck'), identities may add Providers
directly from the Identity Registry
.
Resolver Management
A Resolver
is any smart contract that encodes information which resolves to an Identity
. We remain agnostic about the specific information that can be encoded in a resolver and the functionality that this enables. The existence of resolvers is primarily what makes this ERC an identity protocol rather than an identity application. Resolvers
resolve abstract data in smart contracts to an atomic entity, the Identity
.
Recovery
The specification includes a Recovery Address
for instances when users lose control over an Associated Address
. Upon Identity
creation, the public Recovery Address
is passed as a parameter by the creator. Recovery functionality is triggered in three scenarios:
1. Changing Recovery Address: If a recovery key is lost, a Provider
can triggerRecoveryAddressChangeFor through a Provider
. To prevent malicious behavior from someone who has gained control of an Associated Address
or Provider
and is changing the Recovery Address
to one under their control, this action triggers a 14 day challenge period during which the old Recovery Address
may reject the change. If the Recovery Address
does not reject the change within 14 days, the Recovery Address
is changed. Old Recovery Address
reject the change by can dispute the change request by calling triggerRecovery.
2. Recovery: Recovery occurs when a user recognizes that an Associated Address
or the Recovery Address
belonging to the user is lost or stolen. In this instance a Recovery Address
must call triggerRecovery. This removes all Associated Addresses
and Providers
from the corresponding Identity
and replaces them with an address passed in the function call. The Identity
and associated Resolvers
maintain integrity. The user is now responsible for adding the appropriate un-compromised addresses back to their Identity
.
Importantly, the Recovery Address
can be a user-controlled wallet or another address such as a multisig wallet or smart contract. This allows for arbitrarily sophisticated recovery logic! This includes the potential for recovery to be fully compliant with standards such as DID.
3. Poison Pill
The Recovery scheme offers considerable power to a Recovery Address
; accordingly, the Poison Pill
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 Recovery Address
and triggers recovery, any address removed in the Recovery
process can call triggerPoisonPill within 14 days to permanently disable 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 Resolver
or Provider
layers.
Alternative Recovery Considerations
We considered many possible alternatives when devising the Recovery process outlined above. We ultimately selected the scheme that was most un-opinionated, modular, and consistent with the philosophy behind the Associated Address
, Provider
, and Resolver
components. Still, we feel that it is important to highlight some of the other recovery options we considered, to provide a rationale as to how we settled on what we did.
High Level Concerns
Fundamentally, a Recovery scheme needs to be resilient to a compromised address taking control of a user's Identity
. A secondary concern is preventing a compromised 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.
Alternative 1: Nuclear Option
This approach would allow any Associated Address
to destroy an Identity
whenever another Associated Address
is compromised. While this may seem severe, we strongly considered it because this ERC is an identity protocol, not an identity application. This means that though a user's compromised Identity
is destroyed, they should still have recourse to whatever restoration mechanisms are available in each of their actual identities at the Resolver
and/or Provider
level. We ultimately dismissed this approach for two main reasons:
- It is not robust in cases where a user has only one
Associated Address
- It would increase the frequency of recovery requests to identity applications due to its unforgiving nature.
Alternative 2: Unilateral Address Removal via Providers
This would allow Providers
to remove an Associated Address
without a signature from said address. This implementation would allow Providers
to include arbitrarily sophisticated schemes for removing a rogue address - for instance, multi-sig requirements, centralized off-chain verification, user controlled master addresses, deferral to a jurisdictional contract, and more. To prevent a compromised Associated Address
from simply setting a malicious Provider
to remove un-compromised addresses, it would have required a waiting period between when a Provider
is set and when they would be able to remove an Associated Address
. 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 also considered including a flag that would allow a user to decide whether or not a Provider
may remove Associated Addresses
unilaterally. Ultimately, we concluded that only allowing removal of Associated Addresses
via the Recovery Address
enables equally sophisticated recovery logic while separating the functionality from Providers
, leaving less room for users to relinquish control to potentially flawed implementations.
Rationale
We find that at a protocol layer, identities should not rely on specific claim or attestation structures, but should instead be a part of a trustless framework upon which arbitrarily sophisticated claim and attestation structures may be built.
The main criticism of existing identity solutions is that they're overly restrictive. We aim to limit requirements, keep identities modular and future-proof, and remain un-opinionated regarding any functionality a particular identity component may have. This proposal gives users the option to interact on the blockchain using an arbitrarily robust Identity
rather than just an address.
Implementation
The reference implementation for ERC-1484 may be found in hydrogen-dev/ERC-1484.
identityExists
Returns a bool
indicating whether or not an Identity
denominated by the passed EIN
exists.
function identityExists(uint ein) public view returns (bool);
hasIdentity
Returns a bool
indicating whether or not the passed _address
is associated with an Identity
.
function hasIdentity(address _address) public view returns (bool);
getEIN
Returns the EIN
associated with the passed _address
. Throws if the address is not associated with an EIN
.
function getEIN(address _address) public view returns (uint ein);
isAssociatedAddressFor
Returns a bool
indicating whether or not the passed _address
is associated with the passed EIN
.
function isAssociatedAddressFor(uint ein, address _address) public view returns (bool);
isProviderFor
Returns a bool
indicating whether or not the passed provider
has been set by the passed EIN
.
function isProviderFor(uint ein, address provider) public view returns (bool);
isResolverFor
Returns a bool
indicating whether or not the passed resolver
has been set by the passed EIN
.
function isResolverFor(uint ein, address resolver) public view returns (bool);
getIdentity
Returns the recoveryAddress
, associatedAddresses
, providers
and resolvers
of the passed EIN
.
function getIdentity(uint ein) public view
returns (address recoveryAddress, address[] associatedAddresses, address[] providers, address[] resolvers);
createIdentity
Creates an Identity
, setting the Provider
to the msg.sender
. Returns the EIN
of the new Identity
.
function createIdentity(address recoveryAddress, address provider, address[] resolvers) public returns (uint ein);
Triggers event: IdentityCreated
createIdentityDelegated
Preforms the same logic as createIdentity
, but is called by a Provider
. This function requires a signature for the associatedAddress
to confirm their consent.
function createIdentityDelegated(
address recoveryAddress, address associatedAddress, address[] resolvers,
uint8 v, bytes32 r, bytes32 s, uint timestamp
) public returns (uint ein);
Triggers event: IdentityCreated
addAssociatedAddressDelegated
Adds the addressToAdd
to the EIN
of the approvingAddress
. Requires signatures from both the approvingAddress
and the addressToAdd
.
function addAssociatedAddressDelegated(
address approvingAddress, address addressToAdd, uint8[2] v, bytes32[2] r, bytes32[2] s, uint[2] timestamp
) public;
Triggers event: AssociatedAddressAdded
removeAssociatedAddressDelegated
Removes the addressToRemove
from its associated EIN
. Requires a signature from the addressToRemove
.
function removeAssociatedAddressDelegated(
address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp
) public;
Triggers event: AssociatedAddressRemoved
addProviders
Adds an array of Providers
to the Identity
of the msg.sender
.
function addProviders(address[] providers) public;
Triggers event: ProviderAdded
addProvidersFor
Preforms the same logic as addProviders
, but is called by a Provider
.
function addProvidersFor(uint ein, address[] providers) public;
Triggers event: ProviderAdded
removeProviders
Removes an array of Providers
from the Identity
of the msg.sender
.
function removeProviders(address[] providers) public;
Triggers event: ProviderRemoved
removeProvidersFor
Preforms the same logic as removeProviders
, but is called by a Provider
.
function removeProvidersFor(uint ein, address[] providers) public;
Triggers event: ProviderRemoved
addResolversFor
Adds an array of Resolvers
to the passed EIN
. This must be called by a Provider
.
function addResolversFor(uint ein, address[] resolvers) public;
Triggers event: ResolverAdded
removeResolversFor
Removes an array of Resolvers
from the passed EIN
. This must be called by a Provider
.
function removeResolversFor(uint ein, address[] resolvers) public;
Triggers event: ResolverRemoved
triggerRecoveryAddressChangeFor
Initiates a change in the current recoveryAddress
for a given EIN
.
function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) public;
Triggers event: RecoveryAddressChangeTriggered
triggerRecovery
Triggers EIN
recovery from the current recoveryAddress
, or the old recoveryAddress
if changed within the last 2 weeks.
function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s, uint timestamp) public;
Triggers event: RecoveryTriggered
triggerPoisonPill
Triggers the poison pill on an EIN
. This renders the Identity
permanently unusable.
function triggerPoisonPill(uint ein, address[] firstChunk, address[] lastChunk, bool clearResolvers) public;
Triggers event: IdentityPoisoned
Events
IdentityCreated
MUST be triggered when an Identity
is created.
event IdentityCreated(
address indexed initiator, uint indexed ein,
address recoveryAddress, address associatedAddress, address provider, address[] resolvers, bool delegated
);
AssociatedAddressAdded
MUST be triggered when an address is added to an Identity
.
event AssociatedAddressAdded(
address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress
);
AssociatedAddressRemoved
MUST be triggered when an address is removed from an Identity
.
event AssociatedAddressRemoved(address indexed initiator, uint indexed ein, address removedAddress);
ProviderAdded
MUST be triggered when a provider is added to an Identity
.
event ProviderAdded(address indexed initiator, uint indexed ein, address provider, bool delegated);
ProviderRemoved
MUST be triggered when a provider is removed.
event ProviderRemoved(address indexed initiator, uint indexed ein, address provider, bool delegated);
ResolverAdded
MUST be triggered when a resolver is added.
event ResolverAdded(address indexed initiator, uint indexed ein, address resolvers);
ResolverRemoved
MUST be triggered when a resolver is removed.
event ResolverRemoved(address indexed initiator, uint indexed ein, address resolvers);
RecoveryAddressChangeTriggered
MUST be triggered when a recovery address change is triggered.
event RecoveryAddressChangeTriggered(
address indexed initiator, uint indexed ein, address oldRecoveryAddress, address newRecoveryAddress
);
RecoveryTriggered
MUST be triggered when recovery is triggered.
event RecoveryTriggered(
address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress
);
IdentityPoisoned
MUST be triggered when an Identity
is poisoned.
event IdentityPoisoned(address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset);
Solidity Interface
interface ERC1484 {
// Identity View Functions /////////////////////////////////////////////////////////////////////////////////////////
function identityExists(uint ein) external view returns (bool);
function hasIdentity(address _address) external view returns (bool);
function getEIN(address _address) external view returns (uint ein);
function isAssociatedAddressFor(uint ein, address _address) external view returns (bool);
function isProviderFor(uint ein, address provider) external view returns (bool);
function isResolverFor(uint ein, address resolver) external view returns (bool);
function getIdentity(uint ein) external view
returns (address recoveryAddress, address[] associatedAddresses, address[] providers, address[] resolvers);
// Identity Management Functions ///////////////////////////////////////////////////////////////////////////////////
function createIdentity(address recoveryAddress, address provider, address[] resolvers) external returns (uint ein);
function createIdentityDelegated(
address recoveryAddress, address associatedAddress, address[] resolvers,
uint8 v, bytes32 r, bytes32 s, uint timestamp
) external returns (uint ein);
function addAssociatedAddressDelegated(
address approvingAddress, address addressToAdd, uint8[2] v, bytes32[2] r, bytes32[2] s, uint[2] timestamp
) external;
function removeAssociatedAddressDelegated(
address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp
) external;
function addProviders(address[] providers) external;
function addProvidersFor(uint ein, address[] providers) external;
function removeProviders(address[] providers) external;
function removeProvidersFor(uint ein, address[] providers) external;
function addResolversFor(uint ein, address[] resolvers) external;
function removeResolversFor(uint ein, address[] resolvers) external;
// Recovery Management Functions ///////////////////////////////////////////////////////////////////////////////////
function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) external;
function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s) external;
function triggerPoisonPill(uint ein, address[] firstChunk, address[] lastChunk, bool clearResolvers) external;
// Events //////////////////////////////////////////////////////////////////////////////////////////////////////////
event IdentityCreated(
address indexed initiator, uint indexed ein,
address recoveryAddress, address associatedAddress, address provider, address[] resolvers, bool delegated
);
event AssociatedAddressAdded (address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress);
event AssociatedAddressRemoved (address indexed initiator, uint indexed ein, address removedAddress);
event ProviderAdded (address indexed initiator, uint indexed ein, address provider, bool delegated);
event ProviderRemoved (address indexed initiator, uint indexed ein, address provider, bool delegated);
event ResolverAdded (address indexed initiator, uint indexed ein, address resolvers);
event ResolverRemoved (address indexed initiator, uint indexed ein, address resolvers);
event RecoveryAddressChangeTriggered(
address indexed initiator, uint indexed ein, address oldRecoveryAddress, address newRecoveryAddress
);
event RecoveryTriggered(
address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress
);
event IdentityPoisoned (address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset);
}
Backwards Compatibility
Identities
established under this standard consist of existing Ethereum addresses; accordingly, there are no backwards compatibility issues. Deployed, non-upgradeable smart contracts that wish to become Resolvers
for Identities
will need to write wrapper contracts that resolve addresses to EIN
-denominated Identities
.
Additional References
Copyright
Copyright and related rights waived via CC0.