There are many end-to-end encryption protocols in use, with Signal being one of the more popular ones. Aside from being rather complex, I'm not entirely sure Signal is better than some less complex protocols. Giving it some thought, I want to see if the following protocol (something I made up), is a feasible alternative. This protocol may already exist under some known name. If it does, please let me know.
The goal, as for any end-to-end encryption, is to encrypt data at the source location and decrypt it at the target location. Private and public keys are needed whereby the public key is used to encrypt the data and the private key is used to decrypt data. What end-to-end encryption does not support is verification, meaning that you cannot know for certain whether the person sending or receiving the data is who you believe them to be. If the data being sent is just chat messages, there is no real way of knowing whether you are chatting with the real person you believe you are. However, if the data is a live video stream, you can almost be certain you are dealing with the real person, although this can also be circumvented if the person has an identical twin, or the person is an imposter that just happens to look and sound like the real person.
The protocol I am describing here takes into account the need for verification, when it is possible to provide it.
Let's assume that we are dealing with two people: Alice and Bob. Alice wants to initiate a chat discussion with Bob. A backend server is used to act as the middle man that facilitates communication between the two people. Ultimately, the design of the end-to-end encryption protocol is to have the server play a minimal role in the communications. This means that the only data that the server should store are public keys and encrypted chat messages. It needs to store these since it is possible that one of the persons is not online and the chat messages need to be forwarded to them as soon as they go online.
When Alice wants to initiate a chat discussion with Bob, she needs to obtain Bob's public key in order to encrypt the chat message. We'll assume that Bob is about to use the app for the first time and then signs in with the backend. Bob's device generates a public key that gets stored on the server. This public key however contains some encrypted data that identifies Bob's device. Bob could be using a smartphone or a computer. Let's assume he's using a smartphone. The smartphone could have a unique identifier that uniquely identifies it from all other devices in the world. This could be a hardware identifier or even a random number that the app generates. When Bob's app goes to create the public key, it will include this identifier within the key and it will be encrypted. Whenever Bob's device receives an encrypted message from Alice, it can use his private key to decrypt the message and obtain the unique identifier. If the identifier matches with the one from his device, he can be certain that his public key was used to encrypt the message.
Until Bob's public key gets stored on the server, Alice cannot send any chat messages. All Alice would see on her device would be a status message indicating that the connection to Bob is still pending. Once the server has Bob's public key and Alice is online, it will forward the key to Alice. Like Bob, Alice has also created a public key which contains a unique identifier that identifies her device. This key is also stored on the server.
At this point, the initialization for end-to-end encryption is complete. Alica and Bob can now start to send chat messages to each other.
Alice sends a chat message that is encrypted with Bob's public key and sent to the server. The server will then forward it to Bob's device. After decrypting the message, Bob's device verifies that the message contains the unique identifier that belongs to his device. If the identifiers match, then the message had to have been encrypted with Bob's public key.
So the server has sent an encrypted message to Bob indicating who it is from (each person's user ID is used to indicate who they are). But Bob can't really be certain that it really is from the person that the server says it's from. After all, if you had a man-in-the-middle hacker, they could obtain the public key and send a message to Bob using Alice's user ID. To solve for this, we need to take a step back to the initialization phase. When Bob's device received a request to connect to Alice, it began to create a public key and included a unique identifier that got encrypted into the key. But in addition to this device ID, Alice's user ID is also included. So when Alice goes to send a chat message and Bob's device decrypts the message, it can obtain the sender ID. It can then compare the user ID that the server provided with the user ID encrypted in the message. If the two match, it most likely means that the message did originate from the same person. This however does not prevent a man-n-the-middle from obtaining Alice's public key and sending chat messages to Bob using Alice's ID. That's why verification, as described below, is important.
This means effectively that for every person Bob wants to communicate with, a unique public key is used for each person.
There is however the case where anyone can have multiple devices. When a chat message is sent, it needs to be sent to all of their devices. However, since we already decided that each device must include an encrypted device ID within their public key, any additional devices can't use the same public key. If they did, as soon as they decrypt a received message and check the device ID, it will discover that it does not match with their own device ID and the message will be rejected.
One solution to this is to repeat the initialization for each device. In essence, you are treating each device as a unique person, even though two or more devices can belong to the same person. But even though they belong to the same person, for the purpose of strict security, they really should be treated as belonging to completely different people. Even though each device acts like a different person, because the public key contains the encypted user ID, the recipient's device will notice that the user IDs are the same and use this when displaying the message from that person.
This is the basics of the end-to-end encyption. It doesn't offer any verification as to whether the person sending chat messages is the real person. Apps like WhatsApp that use Signal allow two people to compare some unique identifiers stored on each device to allow them to verify that they match up with the identifier on the other person's device. For this to work you need to have both persons compare the code on their device with the code on the other device using their eyes. Since both persons are physically seeing each other and have verified that the codes match, then the codes must be valid. These codes are actually part of the public keys used to send and receive data. If the codes don't match, then it might mean that the person that they thought they were communicating with was not the real person. WhatsApp makes this comparison easy by creating a QR code that the other person can scan with using their device. There's no need to physically read and compare a very lengthy set of numbers.
An alternative way of verifying someone is to use a video stream where both persons see each other and can confirm their identities. Instead of sending a chat message, a video feed is initiated between the two. At the start of the feed from both sides, an encrypted video message is sent from both sides. This doesn't have to be a chat message that is shown to the user. It could be the first video frame encrypted or it could just be some arbitary data indicating that the message is intended for verification purposes. The public key used to encrypt chat messages is used to encrypt this message as well. As soon as both persons see each other, a message pops up on the screen asking whether the person they are looking at is the real person. If the person agrees that the other person is the real person, they can confirm it and that person's public key will be marked as verified. This marking occurs on the device and is stored in cache. Whenever the user ever goes to chat with that person in the future, they would see a checkmark or some "verified" icon next to the person's name indicating that they have been verified.
I'm pretty sure I've overlooked something in all of this and would be grateful for any flaws that can be pointed out.
Related
I know how basic RSA encryption works. When a user registers an account with my app on their device, a pair of private and public keys are generated. The public key is stored on the server, and the private is stored securely on the device.
When a user A wants to send text to another user B, the text is encrypted with user B's public key and then the user B decrypts it with his private key. It's all fine to this point.
However how will we handle a situation, where each user may be logged in to several devices? For instance, let's say my app is available in desktop and mobile. I can't share the private key between the two devices, since they are stored in the device only, and I don't think it is the best practice to share or transfer private keys around in the air.
So what is the best practice then? One solution that comes up to my mind is generating a new pair of keys unique for a device each time a user logs in from a new device. But then how would the user be able to decrypt messages sent from his other devices. I don't know what the best practice or solution is, I hope you guys can enlighten me on this issue.
Thanks in before :)
I will demonstrate my question with an example.
I want to develop a distributed application for renting cars. Since the booking details (BD) can be visible on the blockchain, I want to encrypt them and be accesible only by the intended users. Assume that the car owner and the renter have public key and private key, the car has a key (symmetric encryption) and that the onwer and the renter have agreed beforehand on the BD.
The idea is that the owner will generate an access token, from the BD, which will be published on the blockchain, retrieved by the renter and used by him to enter the car.
My question is how can I encrypt the BD in order to generate the access token while at the same time confidentiality, integrity and availability can be maintained.
One ill-solution that I have thought is that the owner encrypts the BD and their signature with the car's key (in order to generate the access token). Then he publishes the access token to the blockchain and retrieved by the renter. However, how can the renter know that the access token contains the agreed BD? Is it good practice to use a second layer of encryption such that; after the owner generates the access token he encrypts it together with the BD with the renter's public key? In this scenario, when the renter decrypts it, he has two things, an access token (which will be used to enter the car) and the booking details (verify that these are the agreed BD).
Is there a more efficient or elegant way to do this?
Thank you in advance.
I'm currently working on an IRC-Chat and we want to add the option to chat with other people privately (User-To-User) which works fine, but the messages aren't stored, meaning that a user loses all private messages after disconnecting. They also can't message a person once they have disconnected.
All of this isn't the case with channels, where messages are stored for X time, allowing a asynchronous communication.
Is there a way of allowing asynchronous messaging for private User-To-User messages without storing the messages in an extra system? Or is this simply a limitation of IRC?
IRC isn't designed to store messages - it's a feature, not a bug. One way to get around this is to configure the server to 'simulate' past messages (essentially pretending to be the other user to send past messages). To do this, you would need to store messages - however, it would be on the server itself, not on a separate database or a third party.
WhatsApp says even the photos shared on its platform are end-to-end encrypted. When WhatsApp says encrypted I assume the data is encrypted in my device and then sent across to the recipient.
When we are sending a photo for the first time we can see the actual upload happening (if you are in a slow network), but when we forward the same picture to someone else, the upload is not happening.
It happens in an instant.
AFAIK the photo would have been again encrypted with the key pair for the second recipient and then again sent to the second recipient. Since the second upload is not happening is it that the encryption happens in the WhatsApp server? Is WhatsApp decrypting the media file and encrypting it with the new recipient's key when we forward it?
Can it be called end-to-end encryption in that case?
Its probably not unique end to end encryption every time a message is shared. They probably store it like you said in an encrypted format and simply share that. This doesn't actually violate end to end encryption since from your device to the device you are sending to will be encrypted. This is just a guess though.
Could you validate my approach for using Firebase to manage a user notification system?
Basically I want to have user specific channels as well as more general channels which hold notifications. These notifications would appear on an intranet if the user has not viewed them before.
The idea being a server side action will update Firebase endpoints using the REST API either for a specific user or broadcast to everyone. The specific user messages I can easily mark as read and therefore not show them again, its the general broadcast I am struggling slightly with.
I could add a flag(user ID) to the general broadcast to indicate its read but I am concerned about performance as the client would have to check historic broadcast messages for the existence of this flag. I could add a user id to create a new endpoint which should be quicker.
e.g. /notification/general/ - contains the message, this triggers the client which then checks to see if /users/USERID/MessageID exists if it doesnt display the message and create this end point.
Is there something I am missing or is that the best approach?
Are the messages always consumed in-order? If so you could have each client remember the ID of the last message it read in each public channel. You could then use "startAt" on the queue to limit it to only new messages.
If they're not consumed in order, then you'll need some way of storing data about which ones were read and which ones weren't. Perhaps you can have each message get sent out to everyone's personal queue, and then have each user remove read messages.
Since there are already undividual user messages, why not just deliver the broadcasts to everyone individually (think email) rather than trying to store a single copy and figure out who read it.
To reduce bulk, you could store the message content separately, and simply store the ids in a user's queue. Then when they are viewed, you flag them per-user without any additional complexity.
At 100k of users receiving 100 messages a day including broadcasts, with a standard firebase ID (around 20 characters), that comes out to 210,000,000 characters a year (i.e. nothing for a database, and probably still far less than the actual bulk of storing the message body), assuming they never expire and get deleted.