From b699aefaf6e6a13180d24f5028fa9fddd6b2191c Mon Sep 17 00:00:00 2001 From: Jennifer Hasegawa <5481259+jhaaaa@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:50:33 -0700 Subject: [PATCH 1/4] first pass --- .../xip-31/message-backup-providers-flow.mmd | 15 + .../xip-31/message-backup-providers-flow.png | Bin 0 -> 82915 bytes XIPs/assets/xip-31/message-history-actors.mmd | 19 + XIPs/assets/xip-31/message-history-actors.png | Bin 0 -> 57263 bytes XIPs/xip-31-message-history.md | 435 ++++++++++++++++++ 5 files changed, 469 insertions(+) create mode 100644 XIPs/assets/xip-31/message-backup-providers-flow.mmd create mode 100644 XIPs/assets/xip-31/message-backup-providers-flow.png create mode 100644 XIPs/assets/xip-31/message-history-actors.mmd create mode 100644 XIPs/assets/xip-31/message-history-actors.png create mode 100644 XIPs/xip-31-message-history.md diff --git a/XIPs/assets/xip-31/message-backup-providers-flow.mmd b/XIPs/assets/xip-31/message-backup-providers-flow.mmd new file mode 100644 index 0000000..20207e7 --- /dev/null +++ b/XIPs/assets/xip-31/message-backup-providers-flow.mmd @@ -0,0 +1,15 @@ +sequenceDiagram + Participant B as Backup Requester + Participant S as Backup Storage Provider + Participant N as XMTP Network + Participant M as Message Backup Provider + B->>N: Send a MessageHistoryBackupRequest to each potential Message Backup Provider + M->>N: Check for MessageHistoryBackupRequest + N-->>M: Receive MessageHistoryBackupRequest + M->>S: Upload backup file + M->>N: Send MessageHistoryBackupResponse + B->>N: Check for MessageHistoryBackupResponse + N-->>B: Receive response + B->>S: Request file from URL in response + S-->>B: Receive file + B-->>B: Decrypt and load file into database \ No newline at end of file diff --git a/XIPs/assets/xip-31/message-backup-providers-flow.png b/XIPs/assets/xip-31/message-backup-providers-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..51d14dd7e79915782868b7a74134f7e6f61d57cb GIT binary patch literal 82915 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 literal 0 HcmV?d00001 diff --git a/XIPs/assets/xip-31/message-history-actors.mmd b/XIPs/assets/xip-31/message-history-actors.mmd new file mode 100644 index 0000000..14f02cf --- /dev/null +++ b/XIPs/assets/xip-31/message-history-actors.mmd @@ -0,0 +1,19 @@ +classDiagram +class R["Backup Requester"]{ + The new installation requesting missing messages + Responsible for selecting a Backup Storage Provider and obtaining authorization + Creates MessageHistoryBackupRequests + Reads MessageHistoryBackupResponses +} +class M["Message Backup Provider"]{ + An existing installation that has access to message history + Reads MessageHistoryBackupRequests + Creates MessageHistoryBackupResponses + Uploads Message Backup Files to the pre-selected storage provider + Responsible for generating one-time encryption keys +} +class S["Backup Storage Provider"]{ + A cloud service with durable storage + Stores encrypted backups for a few days + Responsible for authenticating requests to reduce abuse +} \ No newline at end of file diff --git a/XIPs/assets/xip-31/message-history-actors.png b/XIPs/assets/xip-31/message-history-actors.png new file mode 100644 index 0000000000000000000000000000000000000000..03b10a74fc20033948d69511ef897b939f14acce GIT binary patch literal 57263 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 ZpB4yC_Il{S*H2l%#{y|>l7~1F{vW!*IadGx literal 0 HcmV?d00001 diff --git a/XIPs/xip-31-message-history.md b/XIPs/xip-31-message-history.md new file mode 100644 index 0000000..851dda6 --- /dev/null +++ b/XIPs/xip-31-message-history.md @@ -0,0 +1,435 @@ +--- +xip: 31 +title: Message History request system +description: A proposed system for transferring message history between installations as part of XMTP V3. +author: Nicholas Molnar (@neekolas), Scott Tuddman (@tuddman) +status: Review +type: Standards +category: Core +created: 2024-03-19 +--- + +## Abstract + +This XIP proposes a system for transferring message history between app installations as part of XMTP V3. + +## Motivation + +The [XMTP V3 Protocol Specification](https://github.com/xmtp/libxmtp/blob/main/TECHNICAL_OVERVIEW.md) creates a new XMTP identity for each installation associated with a blockchain account instead of sharing a single set of keys across all installations like V1/V2. New installations will receive all messages sent to a blockchain account after they are created, but V3 does _not_ specify how to receive messages sent before installation creation. Having conversation history sync between devices is a popular feature of V1 and V2 of the XMTP protocol and there is clear demand from developers for this to be possible as part of V3. + +There are a number of cases where message history synchronization is desirable: + +- User installs App A and starts engaging in conversations. Later, they install App B on the same device and want access to their existing conversations and messages. +- User installs App A and starts engaging in conversations. Later, they install App B (or another copy of App A) on a second device and desire access to their existing conversations and messages. +- User installs App A and starts engaging in conversations. They lose access to the device App A was installed on and purchase a new device. They then need to restore their previous conversations and messages to the new device. +- User installs App A and starts engaging in conversations. App A is later compromised, and they need to migrate their messages and conversations to a different app. + +It's also worth noting that there are many use cases for XMTP where message history synchronization is not necessary or desirable. + +- A bot that replies to messages from users. Many bots don't care about messages sent before they were created. +- A service that sends notifications to a list of subscribers via XMTP. +- An app that allows users to talk to customer support needs access to only the newly created conversation with the support agent. +- A marketplace app that wants to allow buyers and sellers to communicate about a particular item. These would only be new conversations and may not care about preexisting messages. + +### Design goals + +1. Message history synchronization should not compromise the Forward Secrecy or Post Compromise Security properties of XMTP V3. We expect the majority of consumer apps using XMTP to have some form of message history syncing enabled. +1. Message history synchronization should be opt-in for developers. +1. It should be simple for developers to implement message history synchronization correctly. Implementation work should be focused on app-specific UX choices, with the SDK responsible for core transport and security decisions. +1. Compatibility between apps is expected. Apps should be able to share history irrespective of operating system, SDK version, or device type (web vs. mobile vs. server). +1. Even if developers of some popular apps choose to not support message history synchronization, it should be simple for users to opt enable themselves. + +## Specification + +This specification offers the user two modes of message backup, which accommodate different use cases: + +- Remote Message Backups +This is the lowest friction solution, provided the blockchain account has access to another existing installation. + +- Backup Account Files +This is an emergency solution for cases where the user has lost access to all of their XMTP apps (for example, if they lost the only device they have used to connect to XMTP). + +### Actors + +There are three types of actors in this specification. The first two types of actors are XMTP Clients, and the implementation required to fulfill the responsibilities of both roles would be built into `libxmtp`. + +- Backup requesters +These are XMTP Clients that are missing message history and would like to receive a backup. + +- Message Backup Providers +These are XMTP Clients that have a message history and are capable of sending it to a requester. + +- Backup Storage Provider +This is a remote service responsible for temporarily storing backup files, and is only needed for Remote Message Backups. + +![Graphic describing the three types of actors in this specification: Backup Requesters, Message Backup Providers, and Backup Storage Providers|690x101, 100%](./assets/xip-31/message-history-actors.png) + +### Remote Message Backups + +Remote message backups work by having Backup Requesters send a special message type (`MessageHistoryBackupRequest`) across the XMTP network to all other installations signed by the same blockchain account as the sender (Message Backup Providers). + +Upon receipt of these messages, Message Backup Providers should display a prompt to the user asking whether they consent to share their message history with the requesting installation. The prompt should display the Verification PIN from the request, which will also be displayed in the Backup Requester's app to ensure the user approves the correct request. Some apps may choose to force the user to enter the PIN rather than display it as an additional layer of security. + +Upon approval, the app will convert its local database into a standard Message History Backup File, encrypt it, and upload it to the Backup Storage Provider specified in the `MessageHistoryBackupRequest`. + +For mobile apps already handling push notifications, `MessageHistoryBackupRequest`s would become a special case of push notification handling. + +#### Flow for Backup Requesters + +1. Get a list of all other installations associated with their blockchain account. Ignore any installations that have been revoked +2. Generate a `MessageHistoryBackupRequest` for each installation (see below for Protobuf spec) and store in the local database +3. Send the `MessageHistoryBackupRequest` messages to the normal inbound messaging topic for each installation +4. Wait for a response from any of the Message Backup Providers +5. For each `MessageHistoryBackupResponse` + + 5a. Ensure there is a `MessageHistoryBackupRequest` with a matching `requestId` stored in the database. If not, ignore. + + 5b. Ensure the `backupUrl` is on the same host as the requested `backupStorageProviderUploadUrl`. If not, ignore. + + 5c. Download the file from the `backupUrl` and decrypt using the credentials provided in the `MessageHistoryBackupResponse`. If the hash of the downloaded file does not match the hash in the `MessageHistoryBackupResponse`, abort. + + 5d. Load each message into the local database, ignoring any duplicate messages. + + 5e. Delete the `MessageHistoryBackupResponse` and all associated credentials. + + 5f. Set the status of the `MessageHistoryBackupRequest` to `Applied`. + +#### Flow for Message Backup Providers + +1. Receive a `MessageHistoryBackupRequest` as part of the normal message receiving flow. +1. Retrieve the contact for the installation that sent the message and ensure it has not been revoked. If revoked, ignore. +1. Ensure the installation that sent the message has a contact signed by the same blockchain account as the current user. If not, ignore. +1. Convert all messages in the local database into a Message Backup File (maybe we want to chunk here?) +1. Generate ephemeral encryption key, salt, and nonce. Encrypt the file using these keys. +1. Upload the file to the `backupStorageProviderUploadUrl` from the request. +1. Reply to the message with a `MessageHistoryBackupResponse` containing the encryption details, the hash of the backup file, and the `backupUrl` provided by the Backup Storage Provider. +1. Delete the `MessageHistoryBackupRequest`, the local database dump, and all the encryption keys. + +#### End-to-end flow + +![Sequence diagram illustrating the end-to-end flow for message backup providers|690x410](./assets/xip-31/message-backup-providers-flow.png) + +### Backup Account Files + +Backup account files are designed as a solution for users who have lost access to all of their XMTP apps. To create a Backup Account File, an app will generate a new set of XMTP keys and register the installation on the XMTP network. The installation contact published to the network will be set with a type of `backup`, which instructs all well-behaved clients to disregard any messages from this installation other than `MessageHistoryBackupResponse`s. This means that a compromised backup installation cannot be used to send messages on the user's behalf, which makes these files a less valuable target for attack. + +Once a backup account's contact has been published to the network, their installation messaging topic will receive a copy of all new messages sent to the associated blockchain account. + +The newly generated XMTP keys are encrypted and downloaded to the user's device. They may be encrypted by either a passphrase or a wallet signature. Users or apps may decide to store the Backup Account File in a private cloud storage provider (iCloud, Google Drive, etc.). Or the file could simply be stored on-device with the risk of loss if the device was inaccessible. + +It is recommended, but not required, that apps provide an easy method to generate a Backup Account File. + +We may want to consider making a backup client's contact bundle signed by the identity key of the installation that created it instead of a wallet. This removes the need for an additional wallet signature. Ownership could still be determined by following the chain of signatures back to the original wallet. This would make revocation more complicated. We would want to revoke the backup account alongside the parent account, but it should be possible. We would have to be careful to ensure that backups could still be restored even if the main account was compromised. + +#### Restoring from a Backup Account File directly + +A client app can allow a user to restore from a Backup Account File directly using the following steps: + +1. Load the Backup Account File from disk. +1. Create a temporary client instance with a new location for the database. +1. Call `receive` on the temporary client instance to download all messages stored on the network. +1. Convert all messages into a Message Backup File. +1. Import the Message Backup File into the main account's database. +1. Delete the database associated with the temporary client instance and the Message Backup File. + +While some apps may choose to support Backup Account Files directly, we can also offer means to convert Backup Account Files into Remote Message Backups. This allows developers to support all possible backup scenarios simply by supporting the more common Remote Message Backups. + +#### Converting a Backup Account File into a Remote Message Backup + +XMTP Labs should create a simple web app to convert Backup Account Files into Remote Message Backups. In this app, a user could import their Backup Account File (with the file never leaving their machine) and create a temporary client instance with that backup file. The user would be presented with any outstanding `MessageHistoryBackupRequests` and select any backup requests they want to fulfill. The client would download all unread messages from the network and proceed with the regular Message Backup Provider flow for Remote Message Backups. All data would be cleared from the client as soon as the operation was completed. + +This same web app could be used to create Backup Account Files for cases where a user's preferred XMTP app does not support creating Backup Account Files. This means every user on the XMTP network can create a Backup Account File if desired. + +### Backup Storage Provider + +A Backup Storage Provider is a simple HTTP service with two endpoints. Anyone can implement a Backup Storage Provider. It is up to the Backup Requester app to choose the Backup Service Provider for their app. + +These are the required APIs for a minimal Backup Storage Provider: + +`POST /backups`: +Request body would contain the file as multipart/form-data. + +Example response: + +```json +{ + "downloadUrl": "https://backupproviderdomain.com/backups/some-long-unguessable-download-id" +} +``` + +`GET /backups/$DOWNLOAD_ID`: +Returns the uploaded file matching the ID + +It would be the responsibility of the Backup Storage Provider to authenticate requests to `/backups` and mitigate abuse. Uploaded files would only need to be stored for maybe 72 hours before they could be safely purged, as backups are meant to be temporary storage. We could also just delete the file after it had been downloaded once. + +XMTP Labs would provide a reference implementation of a Backup Storage Provider. + +I am also proposing that XMTP Labs run a Backup Storage Provider as a free public good for the next two years, at which point this functionality would become part of an ecosystem of third-party gateway service providers. + +### Changes to the v3 protocol buffers + +#### [Message Protos](https://github.com/xmtp/proto/blob/xmtpv3/proto/v3/message_contents/message.proto) + +```proto +// The decrypted message contents of any message on the installation's messaging topic +message PadlockMessagePayload { + EdDsaSignature header_signature = 1; + oneof contents { + DirectMessage direct_message = 2; + MessageHistoryBackupRequest message_history_backup_request = 3; + MessageHistoryBackupResponse message_history_backup_response = 4; + } +} + +// The decrypted contents of a MessageHistoryBackupRequest +message MessageHistoryBackupRequest { + string request_id = 1; + int32 verification_pin = 2; + string upload_url = 3; +} + +message MessageHistoryBackupResponse { + string request_id = 1; + string backup_url = 2; + // TBD on exact parameter definitions. Want to have flexibilty to change key types later + EncryptionParameters encryption_parameters = 3; + bytes backup_file_hash = 4; + int64 expiration_time_ns = 5; +} + +message DirectMessage { + string convo_id = 1; + bytes content_bytes = 2; +} +``` + +#### [Contact protos](https://github.com/xmtp/proto/blob/xmtpv3/proto/v3/message_contents/public_key.proto) + +```proto + +enum ContactRole { + UNKNOWN_CONTACT_ROLE = 0; + FULL = 1; + BACKUP = 2; +} + +message VmacInstallationPublicKeyBundleV1 { + VmacAccountLinkedKey identity_key = 1; + VmacInstallationLinkedKey fallback_key = 2; + // NEW FIELD + ContactRole contact_role = 3; +} + +// A wrapper for versions of the installation contact bundle to allow +// upgradeability +message InstallationContactBundle { + oneof version { + VmacInstallationPublicKeyBundleV1 v1 = 1; + } +} +``` + +### Changes to `libxmtp` + +```rust +impl Client { + ... + /** + Methods for Backup Requesters + **/ + pub fn requestRemoteMessageHistoryBackup(&self) -> Result, ClientError> { + // Create and send a MessageHistoryBackupRequest to all other installations associated with the current blockchain account + ... + } + + pub fn getRemoteMessageHistoryBackupRequestStatus(&self, requestId: String) -> Result { + // Get the status of a pending backup request + ... + } + + pub fn applyRemoteMessageHistoryBackup(&self, requestId: String) -> Result<(), BackupApplyError> { + // Applies the following steps: + // 1. Look for the matching MessageHistoryBackupResponse and MessageHistoryBackupRequest in the database + // 2. If either are not found, return `ResponseNotFound` + // 3. Download the backup from the URI specified in the MessageHistoryBackupResponse + // 4. Decrypt the backup using the keys specified in the MessageHistoryBackupResponse + // 5. Run loadMessageBackupFile with the decrypted file + // 6. Delete the MessageHistoryBackupResponse from the local database, removing all sensitive key material + // 7. Set the MessageHistoryBackupRequest status to Applied + ... + } + + pub fn loadMessageBackupFile(&self, file: std::fs::File) -> Result<(), BackupApplyError> { + // Read from the file line by line and add each message to the database. If message already exists, skip it and move on to the next line + } + + /** + Methods for Message Backup Providers + **/ + pub fn listInboundBackupRequests(&self) -> Result, ClientError> { + // Return all pending backup requests. + ... + } + + pub fn respondToBackupRequest(&self, requestId: String, approve: bool) -> Result<(), BackupCreateError> { + // Perform the following steps: + // 1. If approve is false, delete the MessageHistoryBackupRequest from the local database and return + // 2. Dump all messages from the database into a correctly encoded MessageHistoryBackupFile format + // 3. Generate a random encryption key, nonce, and salt + // 4. Encrypt the file using the encryption parameters from step 2. + // 5. Upload the file to the provided `backupStorageProviderUploadUrl` + // 6. Send a message over the XMTP network containing the encryption parameters and the `backupUrl` returned from the backup storage provider + // 7. Delete the backup request from the database and return + ... + } + + /** + Methods for Backup Account Files + **/ + pub fn createBackupAccountFile(&self, path: String) -> Result<(), BuckupCreateError> { + // Create a file with a new account's private keys and return the contents of the file + ... + } + + pub fn importFromBackupAccountFile(&self, path: String) -> Result<(), BackupApplyError> { + // Load the file from the path, spin up a temporary client with the account keys, download all messages, and create a new Message Backup File + // Technically we could simplify this to just load the messages directly into the database without the intermediate file, but I like the idea of reducing surface area and re-using as much code as possible from the remote option + ... + } +} + +pub struct MessageHistoryBackupRequest { + pub requestId: String, + pub verificationPin: i16, // A four digit PIN that can be displayed in both the Backup Requester app and the Backup Provider app to ensure the user is responding to the correct backup request + pub backupStorageProviderUploadUrl: String, + pub status: BackupRequestStatus + ... +} + +pub struct MessageHistoryBackupResponse { + pub requestId: String, + pub verificationPin: i16, + backupUrl: String, + encryptionKey: Vec, + nonce: Vec, + salt: Vec, + backupFileHash: Vec, + expirationTimeNs: u8 +} + +pub enum BackupRequestStatus { + Pending, + Expired, + Applied, + Failed +} + +pub enum BackupApplyError { + ResponseNotFound, + DecryptionFailed, + ValidationFailed, +} + +pub enum BackupCreateError { + RequestNotFound, + InvalidRequest, + NetworkError, + EncryptionError +} +``` + +## Rationale + +There are many other potential solutions to message portability listed below. I am proposing this particular solution because it solves the key user problems stated above with the minimal amount of developer friction. Many of the alternatives require substantial cooperation from app developers, who may care more about shipping new features than supporting competing apps. Developers have strong incentives to receive backups but limited incentives to provide them. + +### Alternatives considered + +#### Use the XMTP Network as a Backup Storage Provider + +Instead of storing backups in a remotely hosted file and serving via HTTP, messages would be divided into chunks and stored on the XMTP network on a special topic only known to the participants in the backup message exchange. + +##### XMTP Network Advantages + +- No new infrastructure required. Everything stays on the XMTP network + +##### XMTP Network Disadvantages + +- The XMTP network was not designed for storage of large files. The network currently has a 1MB per message limit so backups would be divided across potentially hundreds of chunks. +- Costly as the network decentralizes and fees are added to the network. Given that alternative storage providers can be used in the current proposal, the benefits to decentralization are limited. + +I could get on board with this proposal if others felt strongly that we shouldn't create new infrastructure. While backups are an odd fit for the XMTP transport network, it would work in the short term and be easier to bootstrap. + +#### Use a QR code instead of an XMTP message + +Following in the footsteps of Signal, message history synchronization could be achieved as a synchronous exchange of data between clients. + +A QR code displayed in the Message Backup Provider could establish an encrypted channel with the Backup Requester. This model follows a flow similar to WalletConnect. Deep links would be used for app -> app transfers. + +Messages would be encrypted using an [N](https://noiseexplorer.com/patterns/X/) or [X](https://noiseexplorer.com/patterns/X/) pattern Noise handshake, with the bootstrap information encoded in the QR for out-of-band transmission. This channel would be private and ephemeral, offering the best protection for backups. + +##### QR Code Advantages + +- Does not require any additional installations to be added to the user's account +- Ensures the Backup Requester and Message Backup Provider are in the same room (QR code) or on the same device (deep link) + +##### QR Code Disadvantages + +- QR codes are most appropriate for cases where there isn't an authenticated and private communication channel between parties. That isn't the problem here. We already have a solution for authenticated and secure messaging between two installations as part of V3. +- All participating apps would have to be able to handle the QR code flow. This requires more complicated front-end designs that every app needs to implement, as well as access to a camera in some cases. This could be enough to discourage developers from implementing their side of the flow. +- Would require mobile app developers to handle XMTP deep links for app -> app transfer. This gets complicated when there are multiple potential apps as backup providers. The Backup Requester would need to be aware of all possible Backup Provider apps and the user would have to select the right one. +- Would need to be rethought for webapp -> server transfer +- Would need to be rethought for webapp -> webapp transfers where QR code scanning and deep-linking are not possible. Maybe something closer to oAuth. + +#### Trusted Backup Service + +This approach is similar to the Backup Account File, but the backup account would be run on a server and listen for new messages continuously. Messages could be added to an archive in real-time, and backups could be served to any app that provided appropriate authentication over HTTP or XMTP protocols. + +##### Trusted Service Advantages + +- Continuous real-time backup of all messages after the service is created. Backups would continue even if all of a user's devices were lost or compromised. +- No need to involve any other apps in the backup process. The user would have complete control over where their backups were stored +- Users could self-host trusted services + +##### Trusted Service Disadvantages + +- The service provider could read all messages as they come off the network. Even if they were encrypted at rest, there is a significant risk of compromise. +- More costly to run than the Backup Account File since it requires server resources to be continuously polling/streaming from the network. +- Multi-tenant providers could be legally compelled to provide access to users' messaging history or otherwise spy on users. +- Self-hosting with a high level of security would be a high bar and might be prohibitive for all but the most committed users. +- Nothing is stopping users from doing this under any scenario. The trusted server would just be a client. If there is demand for this, someone will build it regardless. + +## Backward compatibility + +Given that V3 is brand new, there is no risk of backward incompatible changes with the initial release of this feature. + +Caution should be taken in the design of the Message Backup File format to ensure that it is flexible and compatible with other potential changes. Any fields that may have changing data formats should be self-describing and versioned for backward/forward compatibility. + +## Reference implementation + +Message History Server: [https://github.com/xmtp/xmtp-message-history-server](https://github.com/xmtp/xmtp-message-history-server) + +LibXMTP implementation: [https://github.com/xmtp/libxmtp/blob/main/xmtp_mls/src/groups/message_history.rs](https://github.com/xmtp/libxmtp/blob/main/xmtp_mls/src/groups/message_history.rs) + +## Security considerations + +### Risks and drawbacks to Remote Message Backups + +- Developers of the Message Backup Provider could implement the consent flow in a way that confuses users into accepting backup requests from malicious apps. PIN verification may not be implemented across all providers +- A malicious Message Backup Provider could choose to censor messages in the backup or add spoofed messages since the messages in the backup are unauthenticated. +- Bugs in the validation of requests in Message Backup Providers would be very, very bad. This would need to be very well-tested code since a compromise here would effectively be a 0-click exploit of someone's XMTP account. + +### Risks and drawbacks to Backup Account Files + +- Because the account stored in a Backup Account File never sends messages, all messages sent to the account will be encrypted using a one-time prekey with no ratcheting. A single ratchet step could happen each time a backup was created since the account is briefly online, although this would require the user to replace their Backup Account File with a new one containing the current ratchet state. +- Accounts in the Backup Account File will only be online during backup restoration. This means there are few opportunities to generate new one-time prekeys. This may be mitigated by creating a very large number of one-time prekeys as part of creation. +- This approach relies on the XMTP network allowing messages to remain in pre-delivery storage indefinitely. While that works today, in future iterations of the network, this may be cost-prohibitive. +- Apps creating Backup Account Files would need to handle interacting with the device filesystem or cloud storage providers. For mobile apps, this may require extra permissions. +- If Backup Account Files were accessed and their encryption keys were compromised, the attacker would have access to all historical messages. + +### Risks and drawbacks to Backup Storage Providers + +- Offering free storage on the internet has the potential for abuse. Requests to this service should be rate-limited or otherwise protected against attack. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From c7de18f292210322ff2199c4c4db25cbd5f417f2 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Mon, 9 Sep 2024 18:00:03 -0500 Subject: [PATCH 2/4] Update XIPs/xip-31-message-history.md Co-authored-by: J-Ha <5481259+jhaaaa@users.noreply.github.com> --- XIPs/xip-31-message-history.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XIPs/xip-31-message-history.md b/XIPs/xip-31-message-history.md index 851dda6..ce8280a 100644 --- a/XIPs/xip-31-message-history.md +++ b/XIPs/xip-31-message-history.md @@ -37,7 +37,7 @@ It's also worth noting that there are many use cases for XMTP where message hist 1. Message history synchronization should be opt-in for developers. 1. It should be simple for developers to implement message history synchronization correctly. Implementation work should be focused on app-specific UX choices, with the SDK responsible for core transport and security decisions. 1. Compatibility between apps is expected. Apps should be able to share history irrespective of operating system, SDK version, or device type (web vs. mobile vs. server). -1. Even if developers of some popular apps choose to not support message history synchronization, it should be simple for users to opt enable themselves. +1. Even if developers of some popular apps choose to not support message history synchronization, it should be simple for users to opt-in to enable it for themselves. ## Specification From f5d089f4fba3c1713ac0338018818b6b838f8913 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Tue, 10 Sep 2024 15:17:19 -0500 Subject: [PATCH 3/4] XIP-31 updates --- XIPs/xip-31-message-history.md | 256 +++++++++++++++------------------ 1 file changed, 116 insertions(+), 140 deletions(-) diff --git a/XIPs/xip-31-message-history.md b/XIPs/xip-31-message-history.md index ce8280a..db15014 100644 --- a/XIPs/xip-31-message-history.md +++ b/XIPs/xip-31-message-history.md @@ -44,23 +44,23 @@ It's also worth noting that there are many use cases for XMTP where message hist This specification offers the user two modes of message backup, which accommodate different use cases: - Remote Message Backups -This is the lowest friction solution, provided the blockchain account has access to another existing installation. + This is the lowest friction solution, provided the blockchain account has access to another existing installation. - Backup Account Files -This is an emergency solution for cases where the user has lost access to all of their XMTP apps (for example, if they lost the only device they have used to connect to XMTP). + This is an emergency solution for cases where the user has lost access to all of their XMTP apps (for example, if they lost the only device they have used to connect to XMTP). ### Actors There are three types of actors in this specification. The first two types of actors are XMTP Clients, and the implementation required to fulfill the responsibilities of both roles would be built into `libxmtp`. - Backup requesters -These are XMTP Clients that are missing message history and would like to receive a backup. + These are XMTP Clients that are missing message history and would like to receive a backup. - Message Backup Providers -These are XMTP Clients that have a message history and are capable of sending it to a requester. + These are XMTP Clients that have a message history and are capable of sending it to a requester. - Backup Storage Provider -This is a remote service responsible for temporarily storing backup files, and is only needed for Remote Message Backups. + This is a remote service responsible for temporarily storing backup files, and is only needed for Remote Message Backups. ![Graphic describing the three types of actors in this specification: Backup Requesters, Message Backup Providers, and Backup Storage Providers|690x101, 100%](./assets/xip-31/message-history-actors.png) @@ -84,15 +84,13 @@ For mobile apps already handling push notifications, `MessageHistoryBackupReques 5a. Ensure there is a `MessageHistoryBackupRequest` with a matching `requestId` stored in the database. If not, ignore. - 5b. Ensure the `backupUrl` is on the same host as the requested `backupStorageProviderUploadUrl`. If not, ignore. + 5b. Download the file from the `backupUrl` and decrypt using the credentials provided in the `MessageHistoryBackupResponse`. - 5c. Download the file from the `backupUrl` and decrypt using the credentials provided in the `MessageHistoryBackupResponse`. If the hash of the downloaded file does not match the hash in the `MessageHistoryBackupResponse`, abort. + 5c. Load each message into the local database, ignoring any duplicate messages. - 5d. Load each message into the local database, ignoring any duplicate messages. + 5d. Delete the `MessageHistoryBackupResponse` and all associated credentials. - 5e. Delete the `MessageHistoryBackupResponse` and all associated credentials. - - 5f. Set the status of the `MessageHistoryBackupRequest` to `Applied`. + 5e. Set the status of the `MessageHistoryBackupRequest` to `Applied`. #### Flow for Message Backup Providers @@ -102,7 +100,7 @@ For mobile apps already handling push notifications, `MessageHistoryBackupReques 1. Convert all messages in the local database into a Message Backup File (maybe we want to chunk here?) 1. Generate ephemeral encryption key, salt, and nonce. Encrypt the file using these keys. 1. Upload the file to the `backupStorageProviderUploadUrl` from the request. -1. Reply to the message with a `MessageHistoryBackupResponse` containing the encryption details, the hash of the backup file, and the `backupUrl` provided by the Backup Storage Provider. +1. Reply to the message with a `MessageHistoryBackupResponse` containing the encryption details and the `backupUrl` provided by the Backup Storage Provider. 1. Delete the `MessageHistoryBackupRequest`, the local database dump, and all the encryption keys. #### End-to-end flow @@ -136,7 +134,7 @@ While some apps may choose to support Backup Account Files directly, we can also #### Converting a Backup Account File into a Remote Message Backup -XMTP Labs should create a simple web app to convert Backup Account Files into Remote Message Backups. In this app, a user could import their Backup Account File (with the file never leaving their machine) and create a temporary client instance with that backup file. The user would be presented with any outstanding `MessageHistoryBackupRequests` and select any backup requests they want to fulfill. The client would download all unread messages from the network and proceed with the regular Message Backup Provider flow for Remote Message Backups. All data would be cleared from the client as soon as the operation was completed. +Ephemera should create a simple web app to convert Backup Account Files into Remote Message Backups. In this app, a user could import their Backup Account File (with the file never leaving their machine) and create a temporary client instance with that backup file. The user would be presented with any outstanding `MessageHistoryBackupRequests` and select any backup requests they want to fulfill. The client would download all unread messages from the network and proceed with the regular Message Backup Provider flow for Remote Message Backups. All data would be cleared from the client as soon as the operation was completed. This same web app could be used to create Backup Account Files for cases where a user's preferred XMTP app does not support creating Backup Account Files. This means every user on the XMTP network can create a Backup Account File if desired. @@ -146,60 +144,54 @@ A Backup Storage Provider is a simple HTTP service with two endpoints. Anyone ca These are the required APIs for a minimal Backup Storage Provider: -`POST /backups`: +`POST /upload`: Request body would contain the file as multipart/form-data. Example response: ```json { - "downloadUrl": "https://backupproviderdomain.com/backups/some-long-unguessable-download-id" + "downloadUrl": "https://backupproviderdomain.com/files/some-long-unguessable-download-id" } ``` -`GET /backups/$DOWNLOAD_ID`: +`GET /files/$DOWNLOAD_ID`: Returns the uploaded file matching the ID -It would be the responsibility of the Backup Storage Provider to authenticate requests to `/backups` and mitigate abuse. Uploaded files would only need to be stored for maybe 72 hours before they could be safely purged, as backups are meant to be temporary storage. We could also just delete the file after it had been downloaded once. +It would be the responsibility of the Backup Storage Provider to authenticate requests to `/upload` and mitigate abuse. Uploaded files would only need to be stored for maybe 72 hours before they could be safely purged, as backups are meant to be temporary storage. We could also just delete the file after it had been downloaded once. -XMTP Labs would provide a reference implementation of a Backup Storage Provider. +Ephemera would provide a reference implementation of a Backup Storage Provider. -I am also proposing that XMTP Labs run a Backup Storage Provider as a free public good for the next two years, at which point this functionality would become part of an ecosystem of third-party gateway service providers. +I am also proposing that Ephemera run a Backup Storage Provider as a free public good for the next two years, at which point this functionality would become part of an ecosystem of third-party gateway service providers. ### Changes to the v3 protocol buffers #### [Message Protos](https://github.com/xmtp/proto/blob/xmtpv3/proto/v3/message_contents/message.proto) ```proto -// The decrypted message contents of any message on the installation's messaging topic -message PadlockMessagePayload { - EdDsaSignature header_signature = 1; - oneof contents { - DirectMessage direct_message = 2; - MessageHistoryBackupRequest message_history_backup_request = 3; - MessageHistoryBackupResponse message_history_backup_response = 4; - } -} - -// The decrypted contents of a MessageHistoryBackupRequest -message MessageHistoryBackupRequest { - string request_id = 1; - int32 verification_pin = 2; - string upload_url = 3; +// Initiator or new installation id requesting a history will send a request +message MessageHistoryRequest { + // Unique identifier for each request + string request_id = 1; + // Ensures a human is in the loop + string pin_code = 2; } -message MessageHistoryBackupResponse { - string request_id = 1; - string backup_url = 2; - // TBD on exact parameter definitions. Want to have flexibilty to change key types later - EncryptionParameters encryption_parameters = 3; - bytes backup_file_hash = 4; - int64 expiration_time_ns = 5; +// Pre-existing installation id capable of supplying a history sends this reply +message MessageHistoryReply { + // Must match an existing request_id from a message history request + string request_id = 1; + // Where the messages can be retrieved from + string url = 2; + // Generated input 'secret' for the AES Key used to encrypt the message-bundle + MessageHistoryKeyType encryption_key = 3; } -message DirectMessage { - string convo_id = 1; - bytes content_bytes = 2; +// Key used to encrypt the message-bundle +message MessageHistoryKeyType { + oneof key { + bytes chacha20_poly1305 = 1; + } } ``` @@ -232,89 +224,88 @@ message InstallationContactBundle { ### Changes to `libxmtp` ```rust -impl Client { - ... - /** - Methods for Backup Requesters - **/ - pub fn requestRemoteMessageHistoryBackup(&self) -> Result, ClientError> { - // Create and send a MessageHistoryBackupRequest to all other installations associated with the current blockchain account - ... - } - - pub fn getRemoteMessageHistoryBackupRequestStatus(&self, requestId: String) -> Result { - // Get the status of a pending backup request - ... - } - - pub fn applyRemoteMessageHistoryBackup(&self, requestId: String) -> Result<(), BackupApplyError> { - // Applies the following steps: - // 1. Look for the matching MessageHistoryBackupResponse and MessageHistoryBackupRequest in the database - // 2. If either are not found, return `ResponseNotFound` - // 3. Download the backup from the URI specified in the MessageHistoryBackupResponse - // 4. Decrypt the backup using the keys specified in the MessageHistoryBackupResponse - // 5. Run loadMessageBackupFile with the decrypted file - // 6. Delete the MessageHistoryBackupResponse from the local database, removing all sensitive key material - // 7. Set the MessageHistoryBackupRequest status to Applied - ... - } - - pub fn loadMessageBackupFile(&self, file: std::fs::File) -> Result<(), BackupApplyError> { - // Read from the file line by line and add each message to the database. If message already exists, skip it and move on to the next line - } - - /** - Methods for Message Backup Providers - **/ - pub fn listInboundBackupRequests(&self) -> Result, ClientError> { - // Return all pending backup requests. - ... - } - - pub fn respondToBackupRequest(&self, requestId: String, approve: bool) -> Result<(), BackupCreateError> { - // Perform the following steps: - // 1. If approve is false, delete the MessageHistoryBackupRequest from the local database and return - // 2. Dump all messages from the database into a correctly encoded MessageHistoryBackupFile format - // 3. Generate a random encryption key, nonce, and salt - // 4. Encrypt the file using the encryption parameters from step 2. - // 5. Upload the file to the provided `backupStorageProviderUploadUrl` - // 6. Send a message over the XMTP network containing the encryption parameters and the `backupUrl` returned from the backup storage provider - // 7. Delete the backup request from the database and return - ... - } - - /** - Methods for Backup Account Files - **/ - pub fn createBackupAccountFile(&self, path: String) -> Result<(), BuckupCreateError> { - // Create a file with a new account's private keys and return the contents of the file - ... - } - - pub fn importFromBackupAccountFile(&self, path: String) -> Result<(), BackupApplyError> { - // Load the file from the path, spin up a temporary client with the account keys, download all messages, and create a new Message Backup File - // Technically we could simplify this to just load the messages directly into the database without the intermediate file, but I like the idea of reducing surface area and re-using as much code as possible from the remote option - ... - } -} - -pub struct MessageHistoryBackupRequest { - pub requestId: String, - pub verificationPin: i16, // A four digit PIN that can be displayed in both the Backup Requester app and the Backup Provider app to ensure the user is responding to the correct backup request - pub backupStorageProviderUploadUrl: String, - pub status: BackupRequestStatus - ... +pub enum MessageHistoryError { + #[error("pin not found")] + PinNotFound, + #[error("pin does not match the expected value")] + PinMismatch, + #[error("IO error: {0}")] + IO(#[from] std::io::Error), + #[error("Serialization/Deserialization Error {0}")] + Serde(#[from] serde_json::Error), + #[error("AES-GCM encryption error")] + AesGcm(#[from] aes_gcm::Error), + #[error("reqwest error: {0}")] + Reqwest(#[from] reqwest::Error), + #[error("storage error: {0}")] + Storage(#[from] StorageError), + #[error("type conversion error")] + Conversion, + #[error("utf-8 error: {0}")] + UTF8(#[from] std::str::Utf8Error), + #[error("client error: {0}")] + Client(#[from] ClientError), + #[error("group error: {0}")] + Group(#[from] GroupError), + #[error("request ID of reply does not match request")] + ReplyRequestIdMismatch, + #[error("reply already processed")] + ReplyAlreadyProcessed, + #[error("no pending request to reply to")] + NoPendingRequest, + #[error("no reply to process")] + NoReplyToProcess, + #[error("generic: {0}")] + Generic(String), + #[error("missing history sync url")] + MissingHistorySyncUrl, + #[error("invalid history message payload")] + InvalidPayload, + #[error("invalid history bundle url")] + InvalidBundleUrl, } -pub struct MessageHistoryBackupResponse { - pub requestId: String, - pub verificationPin: i16, - backupUrl: String, - encryptionKey: Vec, - nonce: Vec, - salt: Vec, - backupFileHash: Vec, - expirationTimeNs: u8 +// client methods for message history +impl Client { + // Get the sync group for the client + pub fn get_sync_group(&self) -> Result {} + + // Enable message history sync for the client + pub async fn enable_history_sync(&self) -> Result<(), GroupError> {} + + // Send a history request to the network + pub async fn send_history_request(&self) -> Result<(String, String), MessageHistoryError> {} + + // Send a history reply to the network + pub async fn send_history_reply( + &self, + contents: MessageHistoryReply, + ) -> Result<(), MessageHistoryError> {} + + // Get the latest pending history request + pub async fn get_pending_history_request( + &self, + ) -> Result, MessageHistoryError> {} + + // Reply to the latest pending history request + pub async fn reply_to_history_request( + &self, + ) -> Result {} + + // Get the latest history reply + pub async fn get_latest_history_reply( + &self, + ) -> Result, MessageHistoryError> {} + + // Process the latest history reply + pub async fn process_history_reply(&self) -> Result<(), MessageHistoryError> {} + + // Verify a PIN for a history request + pub fn verify_pin( + &self, + request_id: &str, + pin_code: &str, + ) -> Result<(), MessageHistoryError> {} } pub enum BackupRequestStatus { @@ -323,19 +314,6 @@ pub enum BackupRequestStatus { Applied, Failed } - -pub enum BackupApplyError { - ResponseNotFound, - DecryptionFailed, - ValidationFailed, -} - -pub enum BackupCreateError { - RequestNotFound, - InvalidRequest, - NetworkError, - EncryptionError -} ``` ## Rationale @@ -357,8 +335,6 @@ Instead of storing backups in a remotely hosted file and serving via HTTP, messa - The XMTP network was not designed for storage of large files. The network currently has a 1MB per message limit so backups would be divided across potentially hundreds of chunks. - Costly as the network decentralizes and fees are added to the network. Given that alternative storage providers can be used in the current proposal, the benefits to decentralization are limited. -I could get on board with this proposal if others felt strongly that we shouldn't create new infrastructure. While backups are an odd fit for the XMTP transport network, it would work in the short term and be easier to bootstrap. - #### Use a QR code instead of an XMTP message Following in the footsteps of Signal, message history synchronization could be achieved as a synchronous exchange of data between clients. From 83f8cfed334e5f50aa7557529125198866c8c591 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Tue, 10 Sep 2024 15:18:26 -0500 Subject: [PATCH 4/4] Remove question from XIP-31 --- XIPs/xip-31-message-history.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XIPs/xip-31-message-history.md b/XIPs/xip-31-message-history.md index db15014..0a92aca 100644 --- a/XIPs/xip-31-message-history.md +++ b/XIPs/xip-31-message-history.md @@ -97,7 +97,7 @@ For mobile apps already handling push notifications, `MessageHistoryBackupReques 1. Receive a `MessageHistoryBackupRequest` as part of the normal message receiving flow. 1. Retrieve the contact for the installation that sent the message and ensure it has not been revoked. If revoked, ignore. 1. Ensure the installation that sent the message has a contact signed by the same blockchain account as the current user. If not, ignore. -1. Convert all messages in the local database into a Message Backup File (maybe we want to chunk here?) +1. Convert all messages in the local database into a Message Backup File 1. Generate ephemeral encryption key, salt, and nonce. Encrypt the file using these keys. 1. Upload the file to the `backupStorageProviderUploadUrl` from the request. 1. Reply to the message with a `MessageHistoryBackupResponse` containing the encryption details and the `backupUrl` provided by the Backup Storage Provider.