Data/setting synchronization
Considering how many (client) platforms Interstellar supports, especially with iOS support coming soon, there's the potential that you could have quite a number of devices with Interstellar installed. It would be useful if we had a method to automatically sync up all the data Interstellar stores between multiple devices.
I imagine it would be similar to a P2P network, but it would sync the data via a coordination server that devices would regularly check for updates from. Of course, all data sent to the server would use E2EE (End-to-end encryption) in order to ensure privacy. As I know I, and likely others, would not want to have to create a whole new account just for data sync, then I would imagine there would be some sort of pairing process that is used when first connecting a device, such as a QR code, or pairing pin. Since there would be no account, there would also be no associated data stored on the coordination server; all the data would be stored on each of your devices, which would need to take care of updating each other. The only thing the coordination server would be doing is relaying messages from each device to the whole group.
Potential Solutions
- One being The Messaging Layer Security (MLS) Protocol (RFC 9420). MLS is a new protocol that was finalized in 2023 and is not really used that widely yet, but it does seem like it would be a great fit for this use case. In essence, it works by having each device derive a "group secret", which can be used to encrypt and decrypt the group messages. It also handles when a new client is added to a group, and when a client is removed from the group (so they can't decode future messages). The one big problem with this protocol is that it is, again, new, which means there are hardly any libraries that have implemented it, barely any documentation, and pretty much no examples. I'll link what resources I have found here: RFC 9420, messaginglayersecurity.rocks, existing implementations, a rust library (OpenMLS) with very extensive documentation, and an explanatory YouTube video. No Dart library exists, though, so that would have to be manually created by us.
- Another option is to use something with much better support, such as ECDH (which the Web Push protocol uses) for key exchanges, and then requiring each device that has an update to send a message to every other device on the network. The difference from MLS is that each message needs to be sent as many times as there are connected devices, whereas MLS needs only one (group) message to be sent.
- There's also the possibility to look at what other E2EE messaging apps use, such as signal. Some apps use what are called sender keys, where the sender key is manually sent to every single device, but normal messages would only need to be sent once, which would be encrypted with the sender key.
Alternative Solutions
The big alternative is that instead of hosting our own coordination server, we could provide support for 3rd-party storage adapters, such as Google Drive, iCloud, etc. Several other apps actually do use this method to sync up settings or data, such as an E2EE syncable notes app. I'm not a fan of this option, though, because it means you'd still have to have an account and set up an external service, which would make the whole thing more complicated.
The Thunder app has a means to sync settings via the Lemmy user settings API. This would not work for us, unfortunately, as it would not be able to sync between different Lemmy accounts (or guest) and would not even work with Mbin or PieFed.
This would be pretty cool. If we want to use OpenMLS there is the flutter_rust_bridge package which can integrate rust into a flutter project so we don't have to roll our own implementation from scratch.
I've looked into using MLS for days now (since opening this issue), and it seems like it may be way too complex for what we need. Using OpenMLS with the rust bridge brings it's own issues/annoyances (needing each build system to have rustup installed, specific changes for different build platforms, etc.), and there are currently no native Dart implementations for MLS. I've studied the RFC and even looked into creating a Dart implementation, but again, the protocol is extremely complex and there's no way I could implement it by myself.
That said, I believe the best direction to go would be to create a custom system using sender keys. In essence, whenever a group is updated, the updating client will generate a new group secret, and will send an encrypted message to each client using their public key, where the message will contain the new group secret. Ensuring the group secret is regularly updated (especially after a device is added or removed) will provide forward secrecy and post compromise security. For normal correspondence (data sync, and not group updates), the shared group secret will be used to encrypt and decrypt messages, and messages are only required to be sent once to the whole group (not each individual client).
I started writing a custom proposal for this, but then I realized it would end up being close to as complicated as MLS would be, especially if we wanted similar features, such as a single shared secret, forward secrecy, post-compromise security, etc. Having a Dart implementation of MLS would be great for not just us, but anybody who wants to write any sort of encrypted messaging app in Flutter would benefit from an MLS implementation. So, anyway, I am at least going to make an attempt to implement MLS, and we'll see where it goes. Maybe it'll work out, maybe it won't 🤷.