13

I already have a working program, but the only thing that doesn't work is the decrypt_file() function I have. I can still copy the encrypted text from the file and put it in my decrypt() function and have it work, but when I try to use my supposed-to-be handy decrypt_file() function it throws an error. Now I know 99.999% sure that my encrypt() and decrypt() functions are fine, but there is something with the bytes and strings conversion when I read and encode the text file that throws an error; I just can't find the hangup. Please help!

My Program:

from Crypto import Random from Crypto.Cipher import AES def encrypt(message, key=None, key_size=256): def pad(s): x = AES.block_size - len(s) % AES.block_size return s + ((bytes([x])) * x) padded_message = pad(message) if key is None: key = Random.new().read(key_size // 8) iv = Random.new().read(AES.block_size) cipher = AES.new(key, AES.MODE_CBC, iv) return iv + cipher.encrypt(padded_message) def decrypt(ciphertext, key): unpad = lambda s: s[:-s[-1]] iv = ciphertext[:AES.block_size] cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = unpad(cipher.decrypt(ciphertext))[AES.block_size:] return plaintext def encrypt_file(file_name, key): f = open(file_name, 'r') plaintext = f.read() plaintext = plaintext.encode('utf-8') enc = encrypt(plaintext, key) f.close() f = open(file_name, 'w') f.write(str(enc)) f.close() def decrypt_file(file_name, key): def pad(s): x = AES.block_size - len(s) % AES.block_size return s + ((str(bytes([x]))) * x) f = open(file_name, 'r') plaintext = f.read() x = AES.block_size - len(plaintext) % AES.block_size plaintext += ((bytes([x]))) * x dec = decrypt(plaintext, key) f.close() f = open(file_name, 'w') f.write(str(dec)) f.close() key = b'\xbf\xc0\x85)\x10nc\x94\x02)j\xdf\xcb\xc4\x94\x9d(\x9e[EX\xc8\xd5\xbfI{\xa2$\x05(\xd5\x18' encrypt_file('to_enc.txt', key) 

The text file I encrypted:

b';c\xb0\xe6Wv5!\xa3\xdd\xf0\xb1\xfd2\x90B\x10\xdf\x00\x82\x83\x9d\xbc2\x91\xa7i M\x13\xdc\xa7' 

My error when attempting decrypt_file:

 Traceback (most recent call last): File "C:\Python33\testing\test\crypto.py", line 56, in <module> decrypt_file('to_enc.txt', key) File "C:\Python33\testing\test\crypto.py", line 45, in decrypt_file plaintext += ((bytes([x]))) * x TypeError: Can't convert 'bytes' object to str implicitly [Finished in 1.5s] 

When I replace line 45 with: plaintext += ((str(bytes([x])))) * x, this is the error I get:

Traceback (most recent call last): File "C:\Python33\testing\test\crypto.py", line 56, in <module> decrypt_file('to_enc.txt', key) File "C:\Python33\testing\test\crypto.py", line 46, in decrypt_file dec = decrypt(plaintext, key) File "C:\Python33\testing\test\crypto.py", line 23, in decrypt plaintext = unpad(cipher.decrypt(ciphertext))[AES.block_size:] File "C:\Python33\lib\site-packages\Crypto\Cipher\blockalgo.py", line 295, in decrypt return self._cipher.decrypt(ciphertext) ValueError: Input strings must be a multiple of 16 in length [Finished in 1.4s with exit code 1] 
5
  • it's the bytes object. That is a built-in type. You didn't define it in the program so it's using a the built-in object. I think you meant plaintext. Commented Dec 31, 2013 at 6:15
  • So are you saying the problem is in line 46? Sorry I'm kinda tired on this one and not really thinking clearly. Commented Dec 31, 2013 at 6:22
  • It says right in the stacktrace the line and the code. Commented Dec 31, 2013 at 6:30
  • Sorry, lines 45 and 46 were some of my own debugging type things so I removed those and replaced the error with my actual error. Commented Dec 31, 2013 at 6:32
  • Okay I also added another important error that I get when I try to fix the bytes thing, unless my 'fix' is incorrect. Commented Dec 31, 2013 at 6:36

2 Answers 2

30

I took a closer look at your code, and saw that there were several problems with it. First one is that the crypto functions with with bytes, not text. So it's better to just keep the data as a byte string. This is done simply by putting a 'b' character in the mode. This way you can get rid of all the encoding and bytes conversion you were trying to do.

I rewrote the whole code also using newer Python idioms. Here it is.

#!/usr/bin/python3 from Crypto import Random from Crypto.Cipher import AES def pad(s): return s + b"\0" * (AES.block_size - len(s) % AES.block_size) def encrypt(message, key, key_size=256): message = pad(message) iv = Random.new().read(AES.block_size) cipher = AES.new(key, AES.MODE_CBC, iv) return iv + cipher.encrypt(message) def decrypt(ciphertext, key): iv = ciphertext[:AES.block_size] cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext[AES.block_size:]) return plaintext.rstrip(b"\0") def encrypt_file(file_name, key): with open(file_name, 'rb') as fo: plaintext = fo.read() enc = encrypt(plaintext, key) with open(file_name + ".enc", 'wb') as fo: fo.write(enc) def decrypt_file(file_name, key): with open(file_name, 'rb') as fo: ciphertext = fo.read() dec = decrypt(ciphertext, key) with open(file_name[:-4], 'wb') as fo: fo.write(dec) key = b'\xbf\xc0\x85)\x10nc\x94\x02)j\xdf\xcb\xc4\x94\x9d(\x9e[EX\xc8\xd5\xbfI{\xa2$\x05(\xd5\x18' encrypt_file('to_enc.txt', key) #decrypt_file('to_enc.txt.enc', key) 
Sign up to request clarification or add additional context in comments.

7 Comments

Awesome! Thanks Keith. I knew that my code was pretty sloppy but I have been piecing together others' working parts and trying to make my own--to no avail. This works perfectly though! I'll go ahead and review the code fully and comment it up so that I won't forget how it all works.
Have you tried with .docx or any kind except .txt files?
I'm skeptical about the plaintext.rstrip(b"\0") in the decrypt function. What if the plaintext ended with null bytes? If I encrypt a file with this code, isn't there a risk of the file being corrupt after decryption?
@keith Is there a way to make the decryption of files faster ? I have mp3 files to be encrypted and decrypted.
@yajantb You could read the source in chunks, rather than suck in the whole thing as this does. this is for small text files.
|
3

In Python 3 (which you are clearly using) the default mode for files you open is text, not binary. When you read from the file, you get strings rather than byte arrays. That does not go along with encryption.

In your code, you should replace:

open(file_name, 'r') 

with:

open(file_name, 'rb') 

The same for when you open the file for writing. At that point, you can get rid of all the various occurrences where you convert from string to binary and vice versa.

For instance, this can go away:

plaintext = plaintext.encode('utf-8') 

1 Comment

Okay I tried this; I replaced ALL of my open() modes with rb and wb respectively and now I get this issue: the encrypt_file function works but when I try to use decrypt_file() it runs fine but the file is empty when I open it up...I'm sorry could you please provide the edits in case I missed something, like Line x: , Line y: , etc. I'm really new to using bytes and encryption so it's all a "bit" confusing--and yes that was a pun...haha

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.