oxen-io / session-protocol-docs

Documentation for Session
https://getsession.org/
5 stars 3 forks source link

Isolate the concepts of "friends" and "sessions"; automatically accept session reqeusts by default #4

Open msgmaxim opened 4 years ago

msgmaxim commented 4 years ago

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.

Mikunj commented 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
msgmaxim commented 4 years ago

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.

neuroscr commented 4 years ago

What are the security/privacy trade-offs here? Can we safely send these prekeys without ratcheting/signal protocol?

Mikunj commented 4 years ago

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.

nielsandriesse commented 4 years ago

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.

msgmaxim commented 4 years ago

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.

Mikunj commented 4 years ago

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)

Mikunj commented 4 years ago

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.

Message sending

This might not be applicable to mobile so the general principles might need to be adopted instead. session_establish_sender-Message sending

Message receiving

session_establish_sender-Message receive

Session Request expiry checking

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. session_establish_sender-Expire checking

nielsandriesse commented 4 years ago

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.

nielsandriesse commented 4 years ago

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.

nielsandriesse commented 4 years ago

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.

Mikunj commented 4 years ago

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.

nielsandriesse commented 4 years ago

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.

Mikunj commented 4 years ago

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:

End Session message should use FRIEND_REQUEST envelope type

The 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.

When receiving any message

If not group chat message or not receipt or not typing message
    If FRS is sent
        FRS = friends
        send FR accept
        sync contact

When receiving a data message

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

When receiving a null message

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.

When receiving a SESSION_REQUEST flag in data message

We 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

New System Assumptions

Session Request

Content: preKeyBundle + nullMessage

Session Established

Content: nullMessage

Cases

old to new

In the above, it is up to Bob to manually whitelist Alice so that they can view the messages.

new to old

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.

old to old

The old system shouldn't be affected with these new changes.

Mikunj commented 4 years ago

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.

Blocked list

We need to sync across our blocked list to all of our devices.

Contact Sync

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.

Pro

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.

Con

It would be stored on the file server but it's encrypted anyway so i don't think it should matter

Multi device

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.