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.
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.