Encrypted Data over the Network in Python 3 (pyAesCrypt)

What’s going on guys?

I have a short encryption tutorial for you today, which is kind of a continuation from my recursive file encryption post a while back.

In that post we did encryption of multiple files recursively, while this time we’ll be doing in-memory AES encryption over the network.

This tutorial will be limited in scope in the sense that we’ll only send the data one way for now (from client to server); however, the plan is to incorporate it into my python control server series soon.

We’ll be using Python 3 for this tutorial (as one should by now). For the encryption side of things we’re using pyAesCrypt, which can be easily installed with pip by running the following command in a terminal:

python -m pip install pyaescrypt

In the video above, after going through the code and explanations, I also fire up wireshark and show the difference from both a plaintext server/client traffic to the encrypted data we’re implementing here.

Dive Into The Code

Let’s take a look into the code now.

Usually I like coding the server first for most projects, however for encryption, given the nature of encrypting first and then decrypting, I think it makes more sense to start off with the client script:

#!/usr/bin/env python3
import socket
import pyAesCrypt
import io

# Enter IP Address and Port
HOST = 'Enter.Ip.Address.Here'
PORT = 4420
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

# Lets set buffer size
bufferSize = 1024
password = 'wearebackboys'

while True:	
	# Text to send over - binary data to be encrypted
	msg = input('\n> Enter message: ')
	pbdata = str.encode(msg)

	# Encrypting
	fIn = io.BytesIO(pbdata)
	fCiph = io.BytesIO()
	pyAesCrypt.encryptStream(fIn, fCiph, password, bufferSize)

	# Data to send
	dataToSend = fCiph.getvalue()

	print('\n' + str(dataToSend))

	# Initializing socket
	s.sendall(dataToSend)
	s.sendall('EOFX'.encode())
	
	# If quitting
	if msg == 'quit': break

We start off by importing socket, io and pyAesCrypt libraries.

Then the usual socket setup, just make sure to change the IP address and Port to your desired address for the server system.

Next we’ll set the buffer size and password (for encryption). Obviously this password will have to match the one in the server script.

Onto the main loop, we’ll prompt the user for the message to send. Then we’ll encode the byte-like object into a string and begin encryption.

The encryptStream method requires a input variable, then output one, followed by the password and buffer size.

After encrypting we’ll print the ciphertext on screen (optionally) and then just go ahead and send it over to the server.

Note that we’re using the string ‘EOFX’ as a delimiter so the server knows when we’re finished receiving encrypted data and ready to decrypt.

Of course we gotta have a ‘quit’ check to see if we should exit the script.

#!/usr/bin/env python3
import socket
import pyAesCrypt
import io

# Enter IP Address and Port
HOST = '0.0.0.0'
PORT = 4420

# Lets set buffer size
bufferSize = 1024
password = 'wearebackboys'

# Initializing socket
c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c.bind(('0.0.0.0', 4420))
c.listen(1)
s,a = c.accept()
print('\n> Connection from: ' + str(a))

while True:
	# Initializing ciphertext binary stream
	fullData = b''
	fCiph = io.BytesIO()
	fDec = io.BytesIO()

	# Main loop to receive data
	while True:
		data = s.recv(1024)
		try:
			if len(data) < 1 or data.decode() == 'EOFX': break
		except UnicodeDecodeError: fullData = fullData + data
	print('\n' + str(fullData) + '\n')
	# Convert to bytes, get length and seek to beginning
	fCiph = io.BytesIO(fullData)
	ctlen = len(fCiph.getvalue())
	fCiph.seek(0)

	# Decrypt stream
	pyAesCrypt.decryptStream(fCiph, fDec, password, bufferSize, ctlen)
	decrypted = str(fDec.getvalue().decode('ascii'))
	print("> Decrypted message: " + decrypted)
	# Check for quit
	if decrypted == "quit": break

Honestly, there’s not much different in the server script.

We have to make sure the buffer size and password match the client’s.

Inside the main loop, we’ll reset the decryption variables and initialize a second loop to receive data and we’re using a little hack to know when we’re still receiving encrypted data or finished with it.

The ‘little hack’ is the try/except UnicodeDecodeError clause. It occurred to me that we might receive chunks of data that are larger than intended, in which case appending it will render the encrypted data undecipherable.

For this reason we have to check for the ‘EOFX’ delimiter to decrypt the data before merging blocks of data together. I know its not the most elegant solution but it serves the purpose of this tutorial for now. 😉

The rest of the code is basically the same from the client script.

Download Code

You can download the code above or simply copy/paste into your editor.

Hope y’all enjoy this tutorial, we’ll be getting into implementing this encryption into our python control server series soon.

‘Til then, see you next time!

Share: