Simple encrypted web chat. Powered by socket.io, the web cryptography API. This project is an example of how client side encryption works and how you can integrate it as a chat service.
Liberty server is a Node.js application that requires redis.
The Liberty web client is written in JavaScript with React JS and Redux.
Copy .env.dist
files in server/
and client/
directories without the .dist
extensions and adapt them to your needs.
You need Redis in order to make the server works. A simple way to achieve this, if you have docker, is to execute the following command:
docker run --name liberty-redis --rm -p 6379:6379 -d redis redis-server --appendonly yes
Alternatively, you can select the memory STORE_BACKEND
instead of redis
in your server .env
file to avoid Redis use.
Install dependencies
$ yarn
Start server and client
$ yarn setup
$ yarn dev
Just run the following:
$ docker-compose up
This will automatically create the default .env
files, and run redis for you.
Create server and client production builds
$ yarn build
Start server
$ yarn start
Build it.
$ docker build --tag liberty.io:latest .
Then run it. Example:
$ docker run --name liberty.io --env STORE_HOST=redis://redis.host:6379 liberty.io
You are able to use any of the enviroment variables available in server/.env.dist
and client/.env.dist
. The defaults are available in Dockerfile
Please report any security issues to [email protected]
.
liberty uses a combination of asymmetric encryption (RSA-OAEP), symmetric session keys (AES-CBC) and signing keys (HMAC) for security.
Here's an overview of a chat between Uttam and Amit (also applies to group chats):
- Amit creates a room and immediately creates a public/private key pair (RSA-OAEP).
- Uttam joins the room and also creates a public/private key pair. He is sent Amit's public key and he sends Amit his public key.
- When Amit goes to send a message, three things are created: a session key (AES-CBC), a signing key (HMAC SHA-256) and an initialization vector (used in the encryption process).
- Amit's message is encrypted with the session key and initialization vector, and a signature is created using the signing key.
- The session key and signing key are encrypted with each recipient's public key (in this case only Uttam, but in a group chat multiple).
- The encrypted message, initialization vector, signature, encrypted session key and encrypted signing key are sent to all recipients (in this case just Uttam) as a package.
- Uttam receives the package and decrypts the session key and signing key using her private key. She decrypts the message with the decrypted session key and vector, and verifies the signature with the decrypted signing key.
Group chats work the same way because in step 5 we encrypt keys with everyone's public key. When a message is sent out, it includes encrypted keys for everyone in the room, and the recipients then pick out the ones for them based on their user ID.
Liberty does not provide any guarantee that the person you're communicating with is who you think they are. Authentication functionality may be incorporated in future versions.
Liberty encodes documents into base64 using btoa and is encrypted the same way chat messages are.
- When a file is "uploaded", the document is encoded on the client and the server recieves the encrypted base64 string.
- The server sends the encrypted base64 string to clients in the same chat room.
- Clients recieving the encrypted base64 string then decrypts and decodes the base64 string using atob.
The default transferable file size limit is 4MB, but can be changed in .env
file with the REACT_APP_MAX_FILE_SIZE
variable.
Liberty uses socket.io to transmit encrypted information using secure WebSockets (WSS).
Rooms are stored in memory on the server until all participants have left, at which point the room is destroyed. Only public keys are stored in server memory for the duration of the room's life.
Chat history is stored in each participant's browser, so it is effectively erased (for that user) when their window is closed.