Transferring files securely is a much-needed feature in any development environment. End-to-end encryption ensures that not even the server can read the contents of the data. Sockets are commonly used for network communication but have much more use cases than just sending or receiving data. In this blog, we will see how to use TCP sockets for sending and receiving files in Python.

Understanding Encrypted File Transfer via Sockets:

Encrypted file transfer over sockets in Python is a method of securely sending files from one computer to another using the Python programming language. It involves creating a socket connection between the client and the server and encrypting the file on the client side before sending it to the server. The server then decrypts the file and saves it in the desired location.

The process can be broken down into several steps:

  1. The client establishes a socket connection with the server using the socket module.
  2. The client reads the file to be transferred and encrypts it using a library such as pycrypto or cryptography.
  3. The encrypted file is sent over the established socket connection to the server.
  4. On the server side, the received encrypted file is decrypted using the same encryption library and saved to the desired location.

It’s important to note that the encryption key should be securely exchanged between the client and the server before the transfer. This can be achieved through a secure key exchange protocol such as Diffie-Hellman.

It’s also important to use a strong encryption algorithm such as AES to ensure that the file is secure during transmission. Additionally, it’s also important to handle errors, large file transfers, and key management properly.

Now that we have a grasp on the workings of encrypted file transfer via sockets, let’s proceed to implement it.

Implementation:

For this encrypted file transfer we’re going to use symmetric encryption this basically means we’re going to use the same key for the encryption as for the decryption which is different from something like RSA from asymmetric encryption where we have public and private keys to the public key being used for the encryption the private key being used for the decryption

Before going forward we need to install two python packages:

Pycryptodome:

Pycryptodome is a self-contained Python package of low-level cryptographic primitives that supports both Python 2 and 3 It is a fork of the Pycrypto library and it is considered a drop-in replacement for the deprecated pycrypto library, but with the added benefit of being actively maintained and having fixed several known security vulnerabilities of pycrypto. Pycryptodome provides a collection of cryptographic modules that support various algorithms such as symmetric ciphers, message digests, and public-key cryptosystems. It can be used for tasks such as encrypting and decrypting data, generating and managing cryptographic keys, and performing other cryptographic operations.

To install pycrypto use the following command in your command prompt or terminal:

pip3 install Pycryptodome

TQDN:

TQDN is a Python library that provides a fast, extensible progress bar for loops and tables. It is designed to provide visual feedback on the progress of a loop or iteration and can be used to track the progress of tasks such as file transfers, data processing, and more.

To install TQDN use the following command in your command prompt or terminal:

pip3 install tqdm

Code:

Now here we will create two scripts one for the sender and one for the receiver.

Sender side:

import os
import socket
from Crypto.Cipher import AES

key = b"TheBestRandomKey"
nonce = b"TheBestRandomNce"
# you can create your own key but it has to have 16 bytes or you can also generate it randomly

# now let's create a cipher
cipher = AES.new(key, AES.MODE_EAX, nonce)

# In the following line We are using the TCP protocol for our internet socket because it is a connection-oriented protocol, ensuring that no data is lost.
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Running everything on the local host
client.connect(("localhost", 9999))

# Getting the file size, here we are using a an video file
file_size = os.path.getsize("video.mkv")

# Now we want to open that respected file in bytes mode
with open("video.mkv", "rb") as f:
    data = f.read()

# encrypting the data
encrypted = cipher.encrypt(data)

# We will begin by transferring the file name and extension. Next, we will transfer the file size to display a progress bar on the receiving end. Afterwards, we will send all the data. Lastly, we will send an end tag to indicate the end of the file stream on the receiving end.

client.send("newvide.mkv".encode())
client.send(str(file_size).encode())
client.sendall(encrypted)
client.send(b"<END>")

client.close

Here is the step-by-step summary of the above code:

Step 1: Import the required library
Step 2: Create key
Step 3: Create a cipher
Step 4: Create a TCP socket for internet communication
Step 5: Connect to the local host where the receiving server is hosted
Step 6: Calculate the size of the files to be sent
Step 7: Load data in the form of bytes
Step 8: Encrypt data
Step 9: Send the file name, file size, encrypted file, and an end tag to the receiver to indicate that the file has been received fully.

Receiver side:

import socket

import tqdm
from Crypto.Cipher import AES

key = b"TheBestRandomKey"
nonce = b"TheBestRandomNce"

cipher = AES.new(key, AES.MODE_EAX, nonce)

# Here we are again making a tcp socket but this time we are not going to connect a existing host we are going to host a server ourself
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.bind(("localhost", 9999))
server.listen()

# We are only going to receive only one file. if you want to make it continue, you use a while loop.

client, addr = server.accept()

# As we already know what we are receiving first so we can receive it and store them
file_name = client.recv(1024).decode("utf-8")
print(file_name)
file_size = client.recv(1024).decode("utf-8")
print(file_size)

file = open(file_name, "wb")

done = False
file_bytes = b""

# Creating progress bar
progress = tqdm.tqdm(unit="8", unit_scale=True, unit_divisor=1000, total=int())

# Creating a while loop so the data receiving process doesn't stop after just a single transfer.
while not done:
    data= client.recv(1024)
    if file_bytes[-5:] == b"<END>":
        done = True
    else:
        file_bytes += data
    progress.update(1024)

file.write(cipher.decrypt(file_bytes[:-5]))

# Now let's close the server

file.close()
client.close()
server.close()

Output

Final Words

I hope you’ve enjoyed this brief look at using Python to securely transfer files over a socket. This can be particularly useful when developing a game, as you may want to share data between a client and a server without the need to send it over the public internet. Instead, you can share it via a local network. Thanks for reading!