This is a project we (Shiloh and Ido) have done as part of our university course. It is a client-server application which is similar to a messenger (like Whatsapp or Telegram etc etc...);
It communicates under a tcp connection with End-to-end encryption (E2EE). 🔒
Both the client-side and the server-side are written in Python.🐍
Each user needs to register or reconnect to the server and only then he can send (or read) messages to (from) other users that the server (upon request) sends to the users back.
To achieve End-to-end encryption we utilised the Elliptic-curve Diffie–Hellman (ECDH) key agreement protocol and upon each new message we used it to choose a new AES-CBC key that we later
exchangd the message through.🗝️🔐
Note
For a more in-detail explanation, keep scrolling and watch the following videos🗃️📂:
Tip
We recommend being familiar with the following videos above and of course the more knowledge you have the better🎯.
Here are some of our UML's we created before and during our work on the project - they contain some inaccuracies regarding the final design we went with but it contains some good points to what we thoguht of before and during our work on the project.
The server works by default on port: 5000.
In it’s creation, it generates public and private ECC keys.
It waits for requests from clients (in the form of jsons') in an endless loop; when it receives a request, it deciphers the request and operates based on the request code it extracts from the request.
When created (after user input) it initializes a phone number, an email address and generates public and private ECC keys.
When server is online, and a client (lets name him Shiloh) wants to register.
- Shiloh asks the server for his public key.
- Shiloh sends the server his phone number and email address.
- Server validates phone number and email address.
- Server generates a random 6 figures code and sends it by secure channel to the user.
- Shiloh sends the server his public key.
- The server saves the new user (with all of his credentials) in the database.
- Server sends register success if no error has occurred.
Click here to view the UML we created for the Connection protocol or check waiting messages protocol
when the server is online, and a client (lets name him Ido) wants to connect.
User side - the user has already registered - he has the server’s public key and remembers the secret code the server gave him in the registration process. Server side - the user has already registered, the server has the user’s public key, secret code, phone number and email address.
- Ido generates a shared secret with the server using ECDH (Elliptic-curve Diffie–Hellman) with his private key and the server’s public key.
- Ido generates a random salt and derives a secret key using KDF (key derivation function) on the shared secret with salt.
- Ido generates an AES key and a random iv encrypt the secret code (with CBC mode).
- Ido generates a random iv and wraps the AES key with the KDF derived key and the iv.
- Ido sends to the server a json with all the necessary parameters it needs:
message_dict = {
"code": ProtocolCodes.initConnectionAESExchange.value,
"phone_number": phone_number,
"wrapped_aes_key": base64.b64encode(wrapped_key_data).decode("utf-8"),
"iv_for_wrapped_key": base64.b64encode(iv_for_wrapped_key).decode("utf-8"),
"encrypted_secret_code": base64.b64encode(encrypted_secret_code).decode("utf-8"),
"iv_for_secret": base64.b64encode(iv_for_secret).decode("utf-8"),
"salt": base64.b64encode(salt).decode("utf-8"),
}
- The Server validates the phone number.
- The Server generates the same shared secret with his private key and Ido’s public key and derives the same derived key with the salt using KDF.
- The Server unwraps the wrapped AES key with derived key and iv.
- The Server decrypt the secret code using the AES key and the other iv, then validate the code from the database (where he saved Ido’s credentials).
- If all successful, the Server sends to Ido that the connection is established.
when the server is online, and an online client (let's name him Ido) wants to send a message to another client (let's name him Shiloh).
- Ido sends the server Shiloh’s phone number and asks for Shiloh’s public key.
- The Server validates the phone number and sends Ido as requested Shiloh’s public key.
- Ido repeats steps 1-4 from the last chapter, but instead of using the server’s public key, he uses Shiloh’s public key (and encrypt his message with the AES key).
- Ido sends the server:
message_dict = {
"code": UserSideRequestCodes.SEND_MESSAGE.value,
"sender_public_key": base64.b64encode(user_public_key_pem).decode('utf-8'),
"wrapped_aes_key": base64.b64encode(wrapped_key_data).decode('utf-8'),
"iv_for_wrapped_key": base64.b64encode(iv_for_wrapped_key).decode('utf-8'),
"encrypted_message": base64.b64encode(encrypted_message).decode('utf-8'),
"iv_for_message": base64.b64encode(iv_for_secret).decode('utf-8'),
"salt": base64.b64encode(salt).decode('utf-8'),
}
- The Server saves the message in database under waiting messages for Shiloh.
In order to see the message Shiloh needs to initiate the next chapter, request for waiting messages.
Click here to view the UML we created for the check waiting messages protocol or the Connection protocol
when the server is online, and an online client (let's name him Shiloh) wants to see all of his messages.
- Shiloh asks the server for waiting messages according to his phone number.
- Server checks in database for Shiloh's waiting messages and sends the waiting messages to him (deleting them after viewing once).
- Each message contains the sender's information (along side the public key of the sender, the sender's phone number and more...) Shiloh generates the same shared secret with his private key and the sender’s public key (received from the server with the message) and derives the same derived key with the salt using KDF.
- Shiloh unwraps the wrapped AES key with derived key and iv.
- Shiloh decrypts the message using the AES key and the other iv.
- Shiloh views the messages one by one.
• The server and client utilize Elliptic Curve Diffie-Hellman (ECDH) to establish a shared secret, ensuring that sensitive information (like keys and messages) is exchanged securely over an encrypted channel. • Messages between clients and the server, as well as inter-client communication, are encrypted using AES in CBC mode. This ensures that even if intercepted, the information is unintelligible without the correct decryption key.
• User credentials and sensitive identifiers (e.g., secret code) are hashed using a secure cryptographic hashing algorithm (e.g., SHA-256) before being stored in the database. • Hashing ensures that even if the database is compromised, attackers cannot reverse the hashed data to obtain the original values. Additionally, salt values are used to prevent precomputed attacks like rainbow table attacks. • This practice aligns with the principle of defense in depth, further safeguarding sensitive information.
• By using AES encryption, the integrity of messages is inherently protected since tampering with the ciphertext results in decryption failure or nonsensical plaintext.
• The server validates incoming data such as phone numbers, email addresses, and secret codes during both registration and connection, ensuring the legitimacy and accuracy of the data being processed.
• During registration and connection, the client sends its public key and a securely derived secret code to the server, which cross-verifies it with its stored data to authenticate the user.
• Before sending messages, users request the recipient's public key from the server. This ensures that encryption is directed toward the intended recipient, authenticated by their phone number.
• ECDH key exchange ensures that even if an attacker intercepts the data, they cannot derive the shared secret without access to the private keys.
• The server plays a critical role in verifying and distributing public keys, reducing the risk of an attacker inserting a malicious key.
• Random IVs and salts in key derivation and encryption prevent replay attacks and make it computationally infeasible to predict or replicate keys.
• The use of AES encryption ensures that even if ciphertext is intercepted, it cannot be decrypted without the correct AES key.
Why use ECC-based cryptography (Elliptic Curve Cryptography) instead of the known RSA-based cryptography?
For those wondering why did we use ECC-based encryption instead of RSA-based encryption here are some of our considerations:
ECC provides the same level of security as RSA but with much smaller key sizes. For example, a 256-bit key in ECC offers roughly the same security as a 3072-bit key in RSA. Smaller key sizes mean less computational power is
required, leading to faster encryption/decryption and less memory usage.
Since ECC uses smaller keys for the same level of security, it results in faster computation and reduced bandwidth consumption. This makes ECC especially useful in environments with limited resources, like mobile devices or IoT (Internet of Things) devices. ECC's efficiency in terms of smaller key sizes means that it consumes less power, which is particularly valuable in battery-powered devices
With the increasing need for stronger encryption over time, ECC's efficiency and scalability make it more viable for future cryptographic needs. As computational power grows, RSA requires increasingly larger key sizes, which eventually becomes impractical. ECC, on the other hand, can maintain strong security while using relatively small key sizes.
The underlying mathematical problem behind ECC (the elliptic curve discrete logarithm problem) is considered more difficult to solve compared to RSA's integer factorization problem, especially as key sizes increase. Therefore, ECC is generally seen as more secure for a given key size.
We wanted to use this project to learn new technologies, after both of us already have done a project using RSA-based encryption; we used this project as a way to experiment with integrating new technologies.
Why switch from ECC-based asymmetric encryption algorithm to the AES (Advanced Encryption Standard)?
For those wondering why do we change from the ECC-based asymmetric encryption algorithm to the AES symmetric encryption algorithm upon each new message, we do it because AES is much faster and more efficient for encrypting large
amounts of data. Asymmetric encryption is generally more computationally intensive than symmetric based; In our case we switch from ECC which is an asymmetric encryption algorithm to the AES which is symmetric based.