I am making a chat application where:
- A user can be in a group with multiple other users.
- All messages must be encrypted with end-to-end
I am using firebase-realtime-database
to store data.
What I have
-
Randomly generated secret key for every user
String generateEncryptionKey(int len) { var r = Random(); String randomString = String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89)); return randomString; }
This is probably wrong. I would need clarification if I need to get a derived key
from the user’s password or if this is sufficient.
What I need
- Alice sends a message: I need it to be encrypted with a key that is also accessible by any other participant in the group (
shared-secret key
) - Bob wants to read this message: I need to decrypt this with the
shared-secret key
The shared-secret key
shares something in common with every personal key, right? So the messages encrypted by any of the participants can be decrypted by any other.
How can I generate the shared-secret key
?
Code Blocks Needed
generateKeyPair() {
// Generate a private - public keypair for each user
// ...somehow used to make the `shared-secret key`?
}
generateSharedSecretKey() {
// Saved to database as the group's shared secret key.
// only participants can use it to decrypt messages
}
sendMessage() {
// 1. Encrypt the message (**code needed**)...using
// which combination of keys?
// 2. Save to database (I already handle this)
}
receiveMessage() {
// 1. Read from database (I already handle this)
// 2. Decrypt the message (**code needed**)...using
// which combination of keys?
}
Considerations
- Multiple users (so many keys)
- It doesn’t need to be super secure (just the bare minimum is enough)
- A user can have an account on multiple devices
I have read about the Diffie–Hellman key exchange, key pairs, etc. But I don’t really understand all the concepts as I am quite new.
I would need:
- Clarification on basic end-to-end encryption concepts (in case something in my explanation was wrong)
- Simple code samples of every key generation step
- The code blocks have to work no matter which user is sending a message or receiving. That dynamism is what confuses me. How can you encrypt something taking into account that any of 50 users can read it?
I just want encryption of simple data (strings) with multiple users through a database. Is there is an easier way than end-to-end?
3
Answers
I think if you fully understand end-to-end encryption you will easily do this.
Here is a diagram to explain public/private keys in simple terms:
So for example User A wants to receive a secure message from User B:
Group Chat
First the group admin (or whoever creates the group) must generate a single key for the group. This uses symmetric encryption where a single key is used for decryption and encryption. This key should be shared with all the participants.
When a participant wants to send the message, it is encrypted using this key and sent to the server, which simply retransmits it to the participants inbox.
Since user 2 and 3 already have the shared secret key, they decrypt it client side.
There are many choices for this kind of encryption, you can google any "Symmetric Encryption Algorithm" (Eg: AES) and pick one that is available on pub.dev.
Things to Note:
cloud function or eliminate the server entirely (not recommended) by allowing the client to send the messages to each participant
EDIT:
If you want to encrypt the transport of the Shared Secret Key, you could leverage your existing 1 to 1 architecture and the group admin can send the key to each of the participants by encrypting using the participants public key (which of course they will decrypt on their end). As long as you handle the sendGroupSharedSecretKeyToParticipants() properly on senders side and onReceiveGroupSharedSecretKey() on receivers end, you should be fine. This may complicate things a tad bit (handling of sharing the key differently from a typical chat message) but this would be a simple way to do it.
1 to 1 Chat
Each user can have a private key and a public key, messages are sent by encrypting using the receivers public key and the receiver will decrypt it using their own private key, pretty basic stuff which you’ve already figured out or refer to @Jabbar’s answer https://stackoverflow.com/a/74809596/4481095
You can also try with the crypto Flutter package, so with the help of this package you can create a key which will satisfy your requirement and you can manage for each and every user. You can also check out the below package which is published by the dart.dev team.
Here is a code snippet from the above package also just for convenience.