user269867
user269867

Reputation: 3972

python 3 encrypt and decrypt image using AES

I am using AES to encrypt and decrypt the image. I have inherited the code so please guide me if you guys see something wrong. I am trying to understand and fix the code.

    chunk_size = 64*1024
    output_file = filename+".enc"
    file_size = bytes(os.path.getsize(filename))
    IV = get_random_bytes(16)

    encryptor = AES.new(key, AES.MODE_CBC, IV)
    with open(filename, 'rb') as inputfile:
        with open(output_file, 'wb') as outf:
            outf.write(file_size)
            outf.write(IV)
            while True:
                chunk = bytes(inputfile.read(chunk_size))
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                   chunk = chunk + bytes(16 - len(chunk)%16)
                outf.write(encryptor.encrypt(chunk))

My image is 66 kb which comes around - 67584 bytes. Considering AES works with 16 bytes this code should error out but it generate the encrypted file. When I try to decrypt using

def decrypt(key, filename):
        chunk_size = 64*1024
        output_file = filename[:-4]
        with open(filename, 'rb') as inf:
            filesize = bytes(inf.read(16))
            IV = inf.read(16)
            decryptor = AES.new(key, AES.MODE_CBC, IV)
            with open(output_file, 'wb') as outf:
                while True:
                    chunk = inf.read(chunk_size)
                    print(len(chunk))
                    if len(chunk)==0:
                        break
                    outf.write(decryptor.decrypt(chunk))
                outf.truncate(filesize)```

I get an error like below

TypeError: an integer is required (got type bytes)

and when I type chunk in bytes I get the following error

Input strings must be a multiple of 16 in length

I am confused how can I fix error "multiple of 16 in length" when my filesize on the console for source is shown as 65536.

Upvotes: 4

Views: 5059

Answers (3)

bibi 21000
bibi 21000

Reputation: 1

You can take a look at aesfile : https://github.com/bibi21000/AesFile

To encrypt :

import aesfile

with open(source, 'rb') as fin, aesfile.open(destination, mode='wb', aes_key=key) as fout:
    while True:
        data = fin.read(7777)
        if not data:
            break
        fout.write(data)

And decrypt :

import aesfile

with aesfile.open(source, mode='rb', aes_key=key) as fin, open(destination, 'wb') as fout :
    while True:
        data = fin.read(8888)
        if not data:
            break
        fout.write(data)

Or you can compress (with zstd) and encrypt in one pass, simply replace

import aesfile

with

import aesfile.zstd

Look at https://github.com/bibi21000/CofferFile/blob/main/BENCHMARK.md for performance impact.

Upvotes: 0

Manav Sengupta
Manav Sengupta

Reputation: 111

Well I am not able to verify the given code since I dont know your exact need or usage or the idea of the implementation, but if you want to see a similar code I wrote about how to encrypt and decrypt the images using python by AES encryption, to get the crux idea(you may then be able to tweak and get your code working according to your needs, or use mine if you just want what it does)

You may consider a step by step blog walkthrough I wrote a while back it might help(Recommended before using the code for better understanding of the code) How to Encrypt and Decrypt Images using Python and pycryptodome

However if you just need the code and its dependencies you may as well fork it from its official github repo CrypImg

Note: You should get the neccessary modules installed before using, you may get them in the requirements.txt given along with the code in the github repo above.

I know I was not able to directly solve your problem, but just because I myself got it working after a lot of challenges I wanted to try to help if it matches what you need so you may get it working as well.

My code:

#Importing Stuff
from Crypto.Cipher import AES
import io
import PIL.Image
from tkinter import *
import os

#Private Stuff
key = b'Key of length 16' #Todo Enter a Key(Like a password only) Here of Length 16 (Both Key and ivb required keep both safely and securely)
iv = b'ivb of length 16' #Todo Enter a ivb (Like a password only) Here of Length 16 (Both Key and ivb required keep both safely and securely)




#Encrypting Image
def encrypt_image():
    global key,iv,entry_for_folder
    file_path=str(entry_for_folder.get())
    if(file_path=="" or file_path[0]==" "):
        file_path=os.getcwd()
    files=[]
    # r=root, d=directories, f = files
    for r, d, f in os.walk(file_path):
        for file in f:
            if((('.JPG' in file) or ('.jpg' in file)) and ('.enc' not in file)):
                files.append(os.path.join(r, file))
    for file_name in files:
        input_file = open(file_name,"rb")
        input_data = input_file.read()
        input_file.close()

        cfb_cipher = AES.new(key, AES.MODE_CFB, iv)
        enc_data = cfb_cipher.encrypt(input_data)

        enc_file = open(file_name+".enc", "wb")
        enc_file.write(enc_data)
        enc_file.close()


#Decrypting Image
def decrypt_image():
    global key,iv,entry_for_folder
    file_path = str(entry_for_folder.get())
    if (file_path == "" or file_path[0] == " "):
        file_path = os.getcwd()
    files = []
    # r=root, d=directories, f = files
    for r, d, f in os.walk(file_path):
        for file in f:
            if '.enc' in file:
                files.append(os.path.join(r, file))
    for file_name in files:
        enc_file2 = open(file_name,"rb")
        enc_data2 = enc_file2.read()
        enc_file2.close()

        cfb_decipher = AES.new(key, AES.MODE_CFB, iv)
        plain_data = (cfb_decipher.decrypt(enc_data2))

        imageStream = io.BytesIO(plain_data)
        imageFile = PIL.Image.open(imageStream)
        if('.jpg' in file_name):
            imageFile.save((file_name[:-8])+".JPG")
        elif('.JPG' in file_name):
            imageFile.save((file_name[:-8])+".jpg")




#Tkinter Stuff

root=Tk()

root.title("Simple AES Encryption and Decryption of JPG Images")

folder_directory_label=Label(text="Enter the Folder Directory")
folder_directory_label.pack()

entry_for_folder=Entry(root)
entry_for_folder.pack()


encrypt=Button(text="Encrypt All",command=encrypt_image)
encrypt.pack()
label=Label(text="Leave Blank for Current Working Directory")
label.pack()
decrypt=Button(text="Decrypt All",command=decrypt_image)
decrypt.pack()



root.mainloop()

This above app code is Licensed under MIT LICENSE AGREEMENT

Before Encryption

and After Encryption(leave the directory entry blank for the encrypting/decrypting all images recursively in the current working directory else enter a folder directory)

After Encryption

Hope it Helps a little.

------------------Update-------------------------

The Code is updated for more convenient usage, please check the github repo Cryptimg For the latest version of the code.

Upvotes: 0

Topaco
Topaco

Reputation: 49525

The file size is stored improperly. To store the file size in the first 16 bytes (as it's presumably intended with regard to the decrypt method, although 16 bytes are actually too large) in big endian order, replace in the encryption:

file_size = bytes(os.path.getsize(filename))

with

file_size = os.path.getsize(filename).to_bytes(16, byteorder='big')

and in the decryption:

filesize = bytes(inf.read(16))

with

filesize = int.from_bytes(inf.read(16), byteorder='big')

With these changes, encryption and decryption work as intended.

Note: You use a Zero padding variant for padding and store the file size (probably only) to remove the padding after decryption. There is a more efficient method, PKCS7 padding. Here the information how many bytes to remove is already included in the padding itself. So the file size does not have to be stored (at least not to remove the padding). In addition, padding is also supported in PyCryptodome by the methods pad and unpad.

Upvotes: 4

Related Questions