Open msgmaxim opened 4 years ago
One advantage of this is that by splitting sessions from friend requests, our core application logic won't touch the signal protocol. All we would need to do is implement a mechanism which handles the passing of a pre key bundle to another user and ensuring a session is setup before messages are sent.
A rough implementation of this would be:
< A wants to send a message to B>
Do we have a session with B?
- No? Send out a session request and put the original message on hold (save it to db or something)
- Yes? Send the message
< B receives session request from A>
Process the pre key bundle
Send out a session request complete message (or any message encrypted with the new session)
< A received encrypted message from B>
Do we have pending messages?
- Yes? Start sending them out
Forgot to mention that, unlike Signal, we can actually do this since we don't have a central server for prekeys. In our case we can generate as many as we want on the fly without worrying that they will run out.
What are the security/privacy trade-offs here? Can we safely send these prekeys without ratcheting/signal protocol?
What are the security/privacy trade-offs here?
We didn't discuss much in detail but the security would remain the same as what we have right now. Privacy also wouldn't be breached because the friend request system would still remain in place. The downside is that someone could potentially spam us with session requests but we can add a throttle on the receiving end as well as giving the user the option to block.
Maybe a downside would be that a user would be able to tell when you came online once you respond to a session request.
Can we safely send these prekeys without ratcheting/signal protocol?
We currently do this with friend requests anyway.
Creating an abstraction that takes care of the session / friend request logic sounds like a very sensible thing to do :) . Also, auto-accepting session requests seems fine to me.
There currently *is* a logical separation between friend requests and session requests by the way. It's that session requests are only ever used in the context of closed groups and friend requests are only ever used in the context of one-to-one chats. Not saying this is a good setup but it's at least something of a distinction.
friend requests are only ever used in the context of one-to-one chats
Multidevice uses friend requests, where a session request would be more appropriate.
I propose building an abstraction with an API that would allow exchaning (regular as well as "control") messages between any two pubkeys, without the caller having to worry about establishing a session first.
With this proposal, wouldn't our friend request system become redundant? Currently our friend requests boil down to the following:
I listed it as a whitelist because a user has to manually 'accept' or 'decline' a friend request. Only once they have 'accepted' can they 'send' or 'receive' (since accepting will process the preKeyBundle, a secure channel can be opened) a secure message.
If we split apart session establishment from it then only the whitelist functionality of the friend request will remain. This might be an advantage as it would simplify the system down a lot but the disadvantage would be that the friend request system would basically be scraped (good and bad i guess)
I've made general rough overview diagrams of the process we can use for setting up sessions with devices. Here we assume messages belong to the following 3 categories, SessionRequest
(formerly FriendRequest
), MediumGroup
and EncryptedMessage
.
We also use timestamps as a way to determine which session requests to process. This is because we don't want to process old requests causing session out of sync.
I haven't really thought about what to do when we get session out of sync just yet.
This might not be applicable to mobile so the general principles might need to be adopted instead.
This is needed in the case where we send out session request but don't get a reply and our message expires on the storage server.
Multidevice uses friend requests, where a session request would be more appropriate.
That's "one-to-one" in the sense of one user talking to one other user. I agree we should be using session requests to establish sessions with linked devices though.
Something to think about is how this is going to work from a UI perspective. When user A doesn't yet have a session with user B and the message they're trying to send gets queued, do we show it as sent and later mark it as failed if, after the session has been established and the actual message gets sent, it failed to send? This could be confusing to the user (imagine your messages show up as sent, but next time you open the app they're suddenly not sent). On the other hand I don't think we can show it as pending to the user if they don't have a session yet. That's exposing a technical detail to the user that they shouldn't know about, and you'd likely end up with confused users waiting for their messages to send and then getting frustrated because they're stuck on pending.
Another thing is backwards / cross-platform compatibility. This system will have to play well with existing desktop clients, and also with mobile. On mobile it seems less necessary to implement this refactor so it'll likely be a while before we do it. In the meantime things need to work well.
Something to think about is how this is going to work from a UI perspective. When user A doesn't yet have a session with user B and the message they're trying to send gets queued, do we show it as sent and later mark it as failed if, after the session has been established and the actual message gets sent, it failed to send? This could be confusing to the user (imagine your messages show up as sent, but next time you open the app they're suddenly not sent). On the other hand I don't think we can show it as pending to the user if they don't have a session yet. That's exposing a technical detail to the user that they shouldn't know about, and you'd likely end up with confused users waiting for their messages to send and then getting frustrated because they're stuck on pending.
Yep this is something we'll have to discuss. We could show some sort of message saying to the user that an encrypted channel is being setup before message is sent or something
Another thing is backwards / cross-platform compatibility. This system will have to play well with existing desktop clients, and also with mobile. On mobile it seems less necessary to implement this refactor so it'll likely be a while before we do it. In the meantime things need to work well.
Well i really think if we implement this on one platform then it should be done on all platforms otherwise we'll start having big problems with backward compatibility.
Well i really think if we implement this on one platform then it should be done on all platforms otherwise we'll start having big problems with backward compatibility.
I'd like to do it eventually on all platforms because I agree it's a good approach, but on mobile it's not necessary to do this refactor to reach a stable and reliable app and that's the overarching goal right now. There are other things that are higher priority on mobile that'll have to happen first, so this refactor on desktop will have to take into account the fact that mobile may not implement this system for a while.
I've thought about the proposed changes that i mentioned above and realised that we will need to make changes to the way we handle friend requests and session requests. Currently we rely on the envelope type being FRIEND_REQUEST
to inform us wether a given message was a friend request or not. This reliance on the envelope type needs to be removed so that we can safely split session from friend request.
The following changes need to be made:
FRIEND_REQUEST
envelope typeThe reason for this is that in the new protocol, an end session message will just become a SESSION_REQUEST
which will have the end session flag data attached to it.
If not group chat message or not receipt or not typing message
If FRS is sent
FRS = friends
send FR accept
sync contact
Here we're using the assumption that friend request messages will always have some kind of text content. If we use this definition then we can simplify the friend request logic down a bit to go inline with the removal of it in the new protocol.
if text or attachment and not group message
if FRS is none or expired
FRS = received
This logic has a dependence on envelope type because in the new protocol, FRIEND_REQUEST
envelope type will be renamed to SESSION_REQUEST
envelope type.
Thus it's the same as saying if we got a session request then send back a session establish.
if envelope type is friend request
send back session established message
In the new protocol this condition on the null message shouldn't exist.
SESSION_REQUEST
flag in data messageWe always accept a session request even if it wasn't from a group member.
This will be deprecated in the new protocol and be replaced by SESSION_REQUEST
envelope type instead.
if message is session request
send session established message
return - don't process any more
Content: preKeyBundle + nullMessage
Content: nullMessage
In the above, it is up to Bob to manually whitelist Alice so that they can view the messages.
The above system will still allow Alice to manually accept friend requests.
The downside is that Bob will be able to message Alice continuously so we'll need a way to handle that case.
The old system shouldn't be affected with these new changes.
Friend requests will also need to be updated with this proposal. We can thoroughly simplify the current system by removing them and going with the classic blacklist approach.
The blacklist or blocking system already exists in session so we don't need to re-implement it. It is also backwards compatible as we only need to remove the friend request system.
We need to sync across our blocked list to all of our devices.
Contact syncing should pass all the users who you have a session with that are not blocked.
NOTE: This isn't essential but can be added into session in the future. It might be best to maybe use the system that Signal uses which is where they concatenate all the contacts, encrypt them and upload them to the file server. Then the message just contains an attachment pointer which can be downloaded and processed.
This has both pros and cons.
There isn't a limit to the amount of contacts you need to sync. Currently because of storage server limits, we have to split the contact sync message into chunks and send them off. This would allow us to send off only one message.
It would be stored on the file server but it's encrypted anyway so i don't think it should matter
When you receive a message you need to check to see if any of the linked devices of the sender is in your blocked list. If they are then we should ignore.
Originally having a session with a pubkey was tied up to the friend request logic. We then introduced a concept of a "session request", which would let two pubkeys establish a session without explicitly being friends. This was necessary for small closed groups as members of each group need to have pairwise sessions with each other even if they don't already have a private conversation. (The concept of a "conversation" is also annoyingly tied up to having a session, but this is probably out of scope of this proposal.) However, there is still no clear separation between the two, for example, multidevice still uses "friend requests" to establish a session between linked devices (and thus the "Please accept to enable messages to be synced across devices" message). I believe these are very different concepts and they should be isolated.
I propose building an abstraction with an API that would allow exchaning (regular as well as "control") messages between any two pubkeys, without the caller having to worry about establishing a session first. Keeping the session logic separate from everything else should greatly recude the complexity of the codebase leading to fewer corner cases (bugs) and faster development of new features.
I also propose accepting session requests automatically, which is already done for members of the same closed group. I believe the friend/not-friend relationship logic should be handled on the level above encryption. Say if we are not friends, the application will just not display any message, and if the user is banned, the messages could be dropped without even hitting the databse. Giving someone we don't like the ability to encrypt messages for us is not really that much. The only downside I see is that one might spam a user with session requests, but you can already do almost the same amount of damage with just friend requests. The latter might even be more annoying to the user since it will clutter the UI.
This proposal should line up well with the refactoring of the sending/receiving pipelines in Desktop codebase.