From 8204bf3b336b0849387a7f8e819d1dd3ab71cc4e Mon Sep 17 00:00:00 2001 From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com> Date: Mon, 12 Aug 2024 14:11:47 -0700 Subject: [PATCH] edits from forum --- XIPs/xip-46-multi-wallet-identity.md | 131 +++++++++++++++------------ 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/XIPs/xip-46-multi-wallet-identity.md b/XIPs/xip-46-multi-wallet-identity.md index 17687f0..4ded048 100644 --- a/XIPs/xip-46-multi-wallet-identity.md +++ b/XIPs/xip-46-multi-wallet-identity.md @@ -16,13 +16,13 @@ This XIP defines a new identity model for XMTP, where users are represented in t ## Motivation -Within the Ethereum ecosystem, security concerns have led to the widespread use of burner wallets, while UX abstractions have led to the creation of non-portable, embedded wallets inside apps. Increasingly, users are likely to possess multiple wallets, and perhaps even identities outside of an EVM chain. +Within the Ethereum ecosystem, security concerns have led to the widespread use of burner wallets, while UX abstractions have led to the creation of non-portable, embedded wallets inside apps. Increasingly, users are likely to possess multiple wallets and perhaps even identities outside of an EVM chain. -In this world, the user would ideally be contactable via any identity they possess, and be able to authenticate to the same inbox via any of these identities to apps on the XMTP network. +In this world, the user would ideally be contactable via any identity they possess and be able to authenticate to the same inbox via any of these identities to apps on the XMTP network. ![Diagram illustrating the relationship between an Inbox ID and multiple wallet addresses](../assets/xip-46/inbox-id-to-multiple-wallets.png) -To facilitate this, XMTP must establish a binding between an XMTP Inbox ID and multiple wallet addresses, with a public API for retrieving them (described in [Identity log](#identity-log)). In the diagram above, XMTP will resolve the solid lines, whereas the dotted lines will continue to be resolved by the implementing app, outside of XMTP - for example, using services such as Airstack. All wallets sharing an Inbox ID share an inbox. +To facilitate this, XMTP must establish a binding between an XMTP Inbox ID and multiple wallet addresses, with a public API for retrieving them (described in [Identity log](#inbox-log)). In the diagram above, XMTP will resolve the solid lines, whereas the dotted lines will continue to be resolved by the implementing app, outside of XMTP - for example, using services such as Airstack. All wallets sharing an Inbox ID share an inbox. When messaging a user by name, the implementing app will resolve from name to Inbox ID, beginning from the bottom of the tree and ending at the top. @@ -32,11 +32,15 @@ When multiple names are present, the implementing app must define a policy for h ## Specification +### Inbox IDs + +In this model, users of the protocol will be identified by Inbox IDs. An Inbox ID can be treated as an opaque string by applications but is constrained to the hash of the first associated address and a nonce. + ### Key hierarchy and permissions -XMTP does not require that apps have direct access to a user’s wallet keys. In XMTP v3, each app installation generates a separate key-pair (‘installation key’) signed by the wallet which can be used to sign operations on behalf of the user. The installation key is not expected to leave the app installation in which it was created. At the cryptographic layer, MLS messages are exchanged between installation keys rather than wallets. +XMTP does not require that apps have direct access to a user’s wallet keys. In XMTP v3, each app installation generates a separate key-pair (‘installation key’) signed by the wallet, which can be used to sign operations on behalf of the user. The installation key is not expected to leave the app installation in which it was created. At the cryptographic layer, MLS messages are exchanged between installation keys rather than wallets. -This defines a hierarchy where an Inbox ID may be associated with multiple wallets. Each wallet may in turn grant messaging permissions to multiple installation keys, forming a parent-child relationship. Each of these is represented as a member of the Inbox ID. +This defines a hierarchy where an Inbox ID may be associated with multiple wallets. Each wallet may, in turn, grant messaging permissions to multiple installation keys, forming a parent-child relationship. Each of these is represented as a member of the Inbox ID. ```protobuf // The identifier for a member of an Inbox @@ -50,24 +54,30 @@ message MemberIdentifier { The user may perform identity updates, such as adding or removing wallets and installations to an Inbox ID, which are signed by these keys. When a member is associated with an Inbox ID, it is assigned a role. The permissions granted to each role are defined as follows. -| Permission | Associated address | Installation key | Recovery address | -| ----------------------------------------------------------- | ------------------ | ---------------- | ---------------- | -| Is typically a wallet | Yes | No | Yes | -| Addressable for incoming messages | Yes | No | No | -| Can be used to authenticate on new apps | Yes | No | No | -| Used by the app to sign messages | No | Yes | No | -| Can add more associated addresses | Yes | Yes | Yes | -| Can add more installation keys | Yes | No | Yes | -| Can revoke other associated addresses and installation keys | No | No | Yes | -| Can revoke self | Yes | Yes | No | -| Can change the recovery address | No | No | Yes | -| Can be revoked | Yes | Yes | No | -| Is automatically revoked when its parent is revoked | No | Yes | No | +| Permission | Associated address | Installation key | Recovery address | +| --- | --- | --- | --- | +| Is typically a wallet | Yes | No | Yes | +| Addressable for incoming messages | Yes | No | No | +| Can be used to authenticate on new apps | Yes | No | No | +| Used by the app to sign messages | No | Yes | No | +| Can add more associated addresses | Yes | No | Yes | +| Can add more installation keys | Yes | No | Yes | +| Can revoke other associated addresses and installation keys | No | No | Yes | +| Can revoke self | No | No | No | +| Can change the recovery address | No | No | Yes | +| Can be revoked | Yes | Yes | No | +| Is automatically revoked when its parent is revoked | No | Yes | No | In short, all roles are able to associate additional members, which provides similar privileges to their existing privileges, but only the recovery address may remove members, which is needed to recover from compromise. There may only be one recovery address per Inbox ID. The recovery address may choose to relinquish its role and assign it to another address. It is permissible, but not required, for a recovery address to also be an associated address. +The member list of an inbox is expected to have the following properties: + +1. Every added member was bidirectionally approved by an existing member and the newly added member. +2. There is a way to recover control over the inbox if any member other than the recovery address is compromised. +3. Any client can verify that (1) is true, and all clients should see the same member list. + ### Identity updates #### Identity actions @@ -154,7 +164,7 @@ Current time: ${YYYY-MM-DD HH:MM:SS UTC} Subsequently, for each identity action, two lines are appended to the result based on the action and target role. -##### CreateInbox +##### CreateInbox (signing text) ```text - Create inbox @@ -209,14 +219,14 @@ The identity update will have one or more signers. If a signer is an installatio If a signer is a wallet that the user controls, the signing text will be visible to the user and require their explicit acceptance. -For example, an app may expose a UI to associate another wallet to the existing Inbox ID (in other words, an `AddAssociation`). Once the user initiates this action, the `existing_member_signature` will typically be signed by an installation key from the app, while the `new_member_signature` will be signed by the wallet. The latter signature will require the user to explicitly confirm the operation from the new wallet. +For example, an app may expose UI to associate another wallet to the existing Inbox ID (in other words, an `AddAssociation`). Once the user initiates this action, the `existing_member_signature` will typically be signed by an installation key from the app, while the `new_member_signature` will be signed by the wallet. The latter signature will require the user to explicitly confirm the operation from the new wallet. #### Signature formats -These identity updates accept the following signature formats to facilitate signatures from EOA wallets, smart contract wallets, and installation keys using the ECDSA signature format, as well as legacy XMTP v2 keys used for the migration of existing accounts. +These identity updates accept the following signature formats to facilitate signatures from EOA wallets, smart contract wallets, installation keys using the ECDSA signature format, as well as legacy XMTP V2 keys used for the migration of existing accounts. ```protobuf -// RecoverableEcdsaSignature for EIP-191 and v2 signatures +// RecoverableEcdsaSignature for EIP-191 and V2 signatures message RecoverableEcdsaSignature { // 65-bytes [ R || S || V ], with recovery id as the last byte bytes bytes = 1; @@ -240,7 +250,7 @@ message Erc1271Signature { // An existing address on xmtpv2 may have already signed a legacy identity key // of type SignedPublicKey via the 'Create Identity' signature. -// For migration to xmtpv3, the legacy key is permitted to sign on behalf of the +// For migration to xmtpv3, the legacy key is permitted to sign on behalf of the // address to create a matching xmtpv3 installation key. // This signature type can ONLY be used for CreateInbox and AddAssociation // payloads, and can only be used once in xmtpv3. @@ -267,7 +277,7 @@ message Signature { For each `IdentityAction`, signature validation must be performed by constructing the relevant signing text, recovering the signing address(es) via the signature(s), and validating the addresses via the [Identity action processing rules](#identity-action-processing-rules). -For more information about `Erc1271Signature`, see [XIP-44: Smart Contract Wallet Support](https://community.xmtp.org/t/xip-44-smart-contract-wallet-support/627). +For more information about `Erc1271Signature`, see **[XIP-44: Smart Contract Wallet Support](https://community.xmtp.org/t/xip-44-smart-contract-wallet-support/627).** For more information about `LegacyDelegatedSignature`, see [Backward Compatibility](#backward-compatibility). @@ -278,12 +288,7 @@ Identity updates are expected to be published to and retrieved from XMTP nodes. - Identity updates for a given Inbox ID or wallet address are ordered consistently across all nodes (sequential consistency) - Identity updates cannot be omitted, and must be retrievable by all clients within a bounded time frame after commit -The server maintains two logs: - -- An inbox log of updates affecting inboxes -- An address log of updates affecting addresses - -All updates will be published to the relevant inbox log, and then to the address log, provided they are valid. +The server maintains two logs - an inbox log of updates affecting inboxes and an address log of updates affecting addresses. All updates will be published to the relevant inbox log and then to the address log, provided they are valid. #### Inbox log @@ -333,7 +338,7 @@ message GetIdentityUpdatesResponse { The address log is also an append-only, strictly-ordered log. The server is expected to append to the address log only if validation succeeded on the affected inbox log. -However, clients only ever need to know the latest Inbox ID that an address was associated with (modeled in the API description below). Clients do not need to validate the address log, other than validating the inbox log of the latest inbox ID that the address points to. +However, clients only ever need to know the latest Inbox ID that an address was associated with (modeled in the API description below). Clients do not need to validate the address log other than validating the inbox log of the latest inbox ID that the address points to. ```protobuf // Request to retrieve the InboxIDs for the given addresses @@ -370,14 +375,14 @@ Log validation must be performed by clients when: Both nodes and clients must apply the following algorithm during validation: -1. For each Identity Update, process all Identity Actions contained in that update sequentially, according to the [Identity Action Processing Rules](#identity-action-processing-rules). +1. For each Identity Update process, all Identity Actions contained in that update sequentially, according to the **Identity Action Processing Rules** described below. 2. Each Identity Action is applied with the Association State from the previous Identity Action (or the previous Identity Update if it is the first action in an Identity Update) as an input. It must provide an updated Association State as an output. -3. If any Identity Action in an Identity Update fails, the entire Identity Update must fail and the Association State must be reverted to the state resulting from the previous Identity Update. -4. If all Identity Actions in the Identity Update have been applied successfully, gather all the signatures used in all Identity Actions in that Identity Update and add them to the `seen_signatures` HashSet contained in the Association State. This is to protect against replays of signatures used in previous Identity Updates. +3. If any Identity Action in an Identity Update fails, the entire Identity Update must fail, and the Association State must be reverted to the state resulting from the previous Identity Update. +4. If all Identity Actions in the Identity Update have been applied successfully, gather all the signatures used in all Identity Actions in that Identity Update and add them to the `seen_signatures` HashSet contained in the Association State. This is to protect against replays of signatures used in previous Identity Updates #### Identity action processing rules -##### CreateInbox (identity action processing rule) +##### CreateInbox (identity action) - CreateInbox actions must be the first Identity Action in the first Identity Update for an Inbox ID - The `inbox_id` is derived via `SHA256(CONCAT($account_address, $nonce))` @@ -393,9 +398,9 @@ Both nodes and clients must apply the following algorithm during validation: - The `new_member_signature` must be valid and recoverable to the same identifier as specified in the `new_member_identifier` - The `existing_member_signature` must be valid and recoverable to the identity of a current member of the Inbox OR to the recovery address - If the `existing_member_signature` is recoverable only to the recovery address and not to any current members, the signature may not be a Legacy Delegated signature -- If either the `existing_member_signature` or the `new_member_signature` is a Legacy Delegated signature the Inbox must have been created with nonce `0`. -- The existing member type must be allowed to add the new member type, according to the [Allowed Associations](#allowed-associations) table. -- Add the member to the Inbox with its `added_by_member` field set to the `existing_member_identifier` +- If either the `existing_member_signature` or the `new_member_signature` is a Legacy Delegated signature, the Inbox must have been created with nonce 0. +- The existing member type must be allowed to add the new member type, according to the **Allowed Associations Table** below +- Add the member to the Inbox, with its `added_by_member` field set to the `existing_member_identifier` ##### RevokeAssociation @@ -405,9 +410,9 @@ Both nodes and clients must apply the following algorithm during validation: - The `recovery_address_signature` must not be a Legacy Delegated signature - The `revoked_member` must be present in the Association State member list - Remove the `revoked_member` from the Association State’s member list -- Remove any members in the Association State’s member list which are both: of type installation AND have their `added_by_member` field set to the `revoked_member` +- Remove any members in the Association State’s member list that are both of type installation AND have their `added_by_member` field set to the `revoked_member` -##### ChangeRecoveryAddress (identity action processing rule) +##### ChangeRecoveryAddress - A CreateInbox action must have been applied before any ChangeRecoveryAddress actions may be applied - The `existing_recovery_address_signature` must not be present in the Association State’s `seen_signatures` HashSet @@ -417,14 +422,14 @@ Both nodes and clients must apply the following algorithm during validation: #### Allowed Associations -| Existing member type | New member type | -| -------------------- | ---------------------- | -| Wallet | Installation OR Wallet | -| Installation | Wallet | +| Existing Member Type | New Member Type | +| --- | --- | +| Wallet | Installation OR Wallet | +| Installation | Wallet | #### Synchronizing MLS State With Inbox State -Clients in an MLS group must perform a `GetIdentityUpdatesRequest`: +Clients in an MLS group must perform a `GetIdentityUpdatesRequest` : - Periodically, when sending messages - Periodically, when fetching messages @@ -435,6 +440,8 @@ When the list of members in the conversation has changed, clients must publish a Each MLS group must maintain a mapping from `inbox_id` → `last_sequence_id` for all Inboxes present in the group. This mapping will be stored as a Group Context Extension in the MLS state, and is available to all members of the group. +The `last_sequence_id` is the highest Identity Update sequence ID that has been applied to the group for the given `inbox_id`. + The mapping will be structured as: ```protobuf @@ -446,23 +453,23 @@ message GroupMembership { This mapping stores the highest known `sequence_id` for each inbox present in the group. -Today, commits adding or removing members contain a single Proposal (the actual add or remove). To facilitate Inbox IDs we will allow for commits that contain two proposals. +Today, commits adding or removing members contain a single Proposal (the actual add or remove). To facilitate Inbox IDs, we will allow for commits that contain two proposals. 1. An `UpdateGroupMembership` Group Context Extensions proposal to update the `GroupMembership` mapping. 2. The standard MLS proposal to add/remove members ##### Validating an `UpdateGroupMembership` proposal -The `UpdateGroupMembership` proposal contains a new `GroupMembership` object. To evaluate these proposals the new `GroupMembership` object must be compared with the current `GroupMembership` value. +The `UpdateGroupMembership` proposal contains a new `GroupMembership` object. To evaluate these proposals, the new `GroupMembership` object must be compared with the current `GroupMembership` value. For each item in the new mapping: 1. Check to see if the `inbox_id` is already present in the current mapping - 1. If present, the new `sequence_id` must be greater than or equal to the current value - 2. If not present, the creator of the commit must have permission to add new members to the group + 1. If present, the new `sequence_id` must be greater than or equal to the current value + 2. If not present, the creator of the commit must have permission to add new members to the group 2. If the `sequence_id` is higher than the newest record in the client’s local cache of Identity Updates for that `inbox_id`, go to the network and request all updates newer than the newest record - 1. If a record matching the `sequence_id` is not found, retry for up to 1 minute until a matching record is found - 2. If no matching record is found after 1 minute, the commit has failed validation and must be aborted + 1. If a record matching the `sequence_id` is not found, retry for up to 1 minute until a matching record is found + 2. If no matching record is found after 1 minute, the commit has failed validation and must be aborted For each item in the current mapping: @@ -480,7 +487,7 @@ To validate the commit, each member must take the list of added and removed `inb For each inbox that has been added or modified: 1. Generate the Association State for the inbox ID at its previous state by replaying all updates up to the old `sequence_id`. This may be cached by the client. - 1. If the previous `sequence_id` is 0, because the Inbox is newly added, skip this step + 1. If the previous `sequence_id` is 0, because the Inbox is newly added, skip this step 2. Process all Identity Updates between the old `sequence_id` and the new `sequence_id` and compute the final Association State, computing a list of installations that were added and removed between the two states 3. Verify that the list of proposals matches the expected output from step 2. If any additions or removals are missing, or if there are any additional members added, the commit must fail validation and be aborted @@ -489,18 +496,28 @@ For each inbox that has been removed from the `GroupMembership`: 1. Go through the current group membership and make a list of all installations that have credentials referencing that `inbox_id` 2. Verify that the list of proposals removes all members in that list +## Threat Model + +While not exhaustive, these are some of the most important potential attacks this system must mitigate: + +1. A malicious app takes control of a user’s installation key. The app developer then attempts to take over the user’s account and prevent the user from regaining control. +2. A malicious server or client fabricates an identity update and adds an unauthorized installation key to a user’s inbox. +3. A malicious server or client replays a previously valid signature and uses it in a different Identity Update for a different purpose than it was originally intended. +4. A malicious user adds someone else’s address to their inbox. Messages directed to the other person’s inbox land in the malicious user’s inbox. +5. A malicious user adds their own wallet address to someone else’s inbox. They can then authenticate on any app as the other person. + ## Backward Compatibility -Multi-wallet identity could be shipped alongside XMTP v3 on MLS. However, to prevent fragmentation, it should only be enabled once both 1:1 and group chats have migrated to XMTP v3 for a significant portion of the network. +Multi-wallet identity could be shipped alongside XMTP v3 on MLS. However, in order to prevent fragmentation, it should only be enabled once both 1:1 and group chats have migrated to XMTP v3 for a significant portion of the network. An important part of the experience is the flow for users moving from v2 to v3. Ideally, no new wallet signatures are required. -In XMTP v2, users were requested for a ‘Create Identity’ wallet signature used to sign their secp256k1 XMTP identity key. Instead of requesting a new wallet signature in XMTP v3, we can perform a one-time delegation of a wallet signature to this new key, to allow the migrating app to sign for the migration invisibly to the user. +In XMTP v2, users were requested for a ‘Create Identity’ wallet signature used to sign their secp256k1 XMTP identity key. Instead of requesting a new wallet signature in XMTP v3, we can perform a one-time delegation of a wallet signature to this new key to allow the migrating app to sign for the migration invisibly to the user. ```protobuf // An existing address on xmtpv2 may have already signed a legacy identity key // of type SignedPublicKey via the 'Create Identity' signature. -// For migration to xmtpv3, the legacy key is permitted to sign on behalf of the +// For migration to xmtpv3, the legacy key is permitted to sign on behalf of the // address to create a matching xmtpv3 installation key. // This signature type can ONLY be used for CreateInbox and AddAssociation // payloads, and can only be used once in xmtpv3. @@ -514,7 +531,7 @@ The rules for processing this signature are as follows: 1. The recovered address of the ‘Create Identity’ signature in the `delegated_key` is the address that this action is being performed on behalf of. 2. The ‘Create Identity’ signature of the `delegated_key` is implicitly treated as a `CreateInbox` action, with a nonce of 0. - 1. During server-side validation, if the address is already associated with an Inbox ID (has pre-existing [Address Log](https://www.notion.so/Multi-Wallet-Identity-XIP-ba30e6ddb30c45acb2dc4ff9eccae7ee?pvs=21) entries), the whole `IdentityUpdate` is invalid. + 1. During server-side validation, if the address is already associated with an Inbox ID (has pre-existing [Address Log](https://www.notion.so/Multi-Wallet-Identity-XIP-ba30e6ddb30c45acb2dc4ff9eccae7ee?pvs=21) entries), the whole `IdentityUpdate` is invalid. 3. The `delegated_key` may be used to perform a single `AddAssociation` action for a new installation key. -This delegated signing action may only be performed in one `InboxUpdate`. This is enforced via the `seen_signatures` HashSet described in previous sections. +This delegated signing action may only be performed in one `IdentityUpdate`. This is enforced via the `seen_signatures` HashSet described in previous sections.