0

This is a continuation of this question. Please do not mark this question as a duplicate for it has a different error I need to fix.

TL;DR for the link:

So I was asking about a Unicode error in python for my encryption program and someone told me to just encode the password in utf-8 and it worked.


Now I have a second problem, where it says that the IV isn't 16 bytes, so I checked this by putting print(len(IV)) after the IV and after running 3 tests it only returns 16 once, in the middle of the printed characters, and the end character is greater than 16 (eg: 37, 35, 28, etc.).

How could I fix this so that the IV always returns 16 bytes?

Full error:

Traceback (most recent call last):
  File "/home/pi/Desktop/Projects/FyleCript/Dev Files/encryption.py", line 77, in <module>
    encrypt(SHA256.new(password.encode('utf-8')).digest(), str(Tfiles))
  File "/home/pi/Desktop/Projects/FyleCript/Dev Files/encryption.py", line 17, in encrypt
    encryptor = AES.new(key, AES.MODE_CBC, IV)
  File "/usr/lib/python3/dist-packages/Crypto/Cipher/AES.py", line 94, in new
    return AESCipher(key, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/Crypto/Cipher/AES.py", line 59, in __init__
    blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
    self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long

Code:

def encrypt(key, filename):
        chunksize = 64 * 1024
        outFile = os.path.join(os.path.dirname(filename), "(encrypted)"+os.path.basename(filename))
        filesize = str(os.path.getsize(filename)).zfill(16)
        IV = ''

        for i in range(16):
                IV += chr(random.randint(0, 0xFF))

        encryptor = AES.new(key, AES.MODE_CBC, IV)

        with open(filename, "rb") as infile:
                with open(outFile, "wb") as outfile:
                        outfile.write(filesize)
                        outfile.write(IV)
                        while True:
                                chunk = infile.read(chunksize)

                                if len(chunk) == 0:
                                        break

                                elif len(chunk) % 16 !=0:
                                        chunk += ' ' *  (16 - (len(chunk) % 16))

                                outfile.write(encryptor.encrypt(chunk))


def decrypt(key, filename):
        outFile = os.path.join(os.path.dirname(filename), os.path.basename(filename[11:]))
        chunksize = 64 * 1024
        with open(filename, "rb") as infile:
                filesize = infile.read(16)
                IV = infile.read(16)

                decryptor = AES.new(key, AES.MODE_CBC, IV)

                with open(outFile, "wb") as outfile:
                        while True:
                                chunk = infile.read(chunksize)
                                if len(chunk) == 0:
                                        break

                                outfile.write(decryptor.decrypt(chunk))

                        outfile.truncate(int(filesize))

Any help would be appreciated.

3
  • 1
    The first thing I noticed: if choice == "E" or 'e' will always be true. Instead do if choice in ( "E", 'e') Commented Jul 16, 2018 at 13:06
  • We don't need to see your entire program, just the minimal amount required to reproduce the issue. There's a lot that can be trimmed out. Commented Jul 16, 2018 at 13:20
  • Do not use os.random() when doing crypto, use Crypto.Random() instead. Check this answer to see how to do it properly, it also shows differences between Python 2.x and 3.x. Commented Jul 16, 2018 at 13:29

2 Answers 2

3

Well, let's take a look at what IV may consist of:

IV = ''

for i in range(16):
    IV += chr(random.randint(0, 0xFF))

Let's see how many bytes a character from range(0, 0xff) consumes:

>>> [len(chr(i).encode()) for i in range(0, 0xff)]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

And this is the source of the problem: you're assuming each character is one byte long, but that's not the case.

You can generate a random IV of N bytes with the following code:

import os

N = 16
IV = os.urandom(N)

Another issue in your code is that you're opening all your files in 'rb' mode, which stands for "read binary", but attempting to write to it instances of str, like your IV. That won't work because in this mode you're only allowed to read and write bytes, not str. In my solution for calculating the IV this issue completely disappears.

Sign up to request clarification or add additional context in comments.

8 Comments

So how do I solve the next error: chunk += ' ' * (16 - (len(chunk) % 16)) TypeError: can't concat bytes to str? If I put a b in front of it, the encrypted file says it has bytes, but when i open it in the text editor, the file is blank.
@TrooperZ, you should read about the usage of bytes in Python 3 and how this is different from str. If a text editor shows you something that looks empty, don't trust it and open your file in a hex editor.
But in python 2, it shows odd characters (which means it's encrypted)
What's the size of the newly generated encrypted file? If it's zero bytes, then it is empty indeed.
it's MUCH bigger (a 3.4 kb to 6.8)
|
1

You have not converted your IV string to a byte string. In Python 3 str is not a byte string, but a character string. str is abstracted away from the concept of how characters are represented as bytes.

You'll need to convert your IV variable (and perhaps others, I haven't checked) to be instances of bytes. It's also a bit easier to make your bytestring in Python 3.

random_byte_list = [random.randrange(256) for _ in range(16)]
IV = bytes(random_byte_list)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.