0

I found some code which I want to incorporate into my Python encryption program. It should encrypt the files in the code's same directory, and I want it to target a directory. But, it's written in Python 2 and when I change around some code to fit Python 3, I get the following error:

Traceback (most recent call last):
  File "/home/pi/Desktop/Projects/FyleCript/Dev Files/encryption.py", line 77, in <module>
    encrypt(SHA256.new(password).digest(), str(Tfiles))
  File "/usr/lib/python3/dist-packages/Crypto/Hash/SHA256.py", line 88, in new
    return SHA256Hash().new(data)
  File "/usr/lib/python3/dist-packages/Crypto/Hash/SHA256.py", line 75, in new
    return SHA256Hash(data)
  File "/usr/lib/python3/dist-packages/Crypto/Hash/SHA256.py", line 72, in __init__
    HashAlgo.__init__(self, hashFactory, data)
  File "/usr/lib/python3/dist-packages/Crypto/Hash/hashalgo.py", line 51, in __init__
    self.update(data)
  File "/usr/lib/python3/dist-packages/Crypto/Hash/hashalgo.py", line 69, in update
    return self._hash.update(data)
TypeError: Unicode-objects must be encoded before hashing

But the code works perfectly in Python 2. I have tried looking for similar questions on SO and Googling, but no help.


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))
       
def allfiles():
        allFiles = []
        for root, subfiles, files in os.walk(os.getcwd()):
                for names in files:
                        allFiles.append(os.path.join(root, names))
 
        return allFiles
 
       
choice = input("Do you want to (E)ncrypt or (D)ecrypt? ")
password = input("Enter the password: ") 

encFiles = allfiles()
 
if choice == "E" or 'e':
        for Tfiles in encFiles:
                if os.path.basename(Tfiles).startswith("(encrypted)"):
                        print("%s is already encrypted" %str(Tfiles))
                        pass
 
                elif Tfiles == os.path.join(os.getcwd(), sys.argv[0]):
                        pass
                else:
                        encrypt(SHA256.new(password).digest(), str(Tfiles))
                        print("Done encrypting %s" %str(Tfiles))
                        os.remove(Tfiles)
 
 
elif choice == "D" or 'd':
        filename = input("Enter the filename to decrypt: ")
        if not os.path.exists(filename):
                print("The file does not exist")
                sys.exit()
        elif not filename.startswith("(encrypted)"):
                print("%s is already not encrypted" %filename)
                sys.exit()
        else:
                decrypt(SHA256.new(password).digest(), filename)
                print("Done decrypting %s" %filename)
                os.remove(filename)
 
else:
        print("Please choose a valid command.")
        sys.exit()

Can anyone help me with this problem? I have used a Python 2 to 3 tool, but it still didn't work.

Also, could you please fix the directory problem? It isn't necessary, but I would like it.


EDIT: I have replaced str with bytes and bytearray but it returns the same error.

18
  • Did you try using bytes instead of str? Python 2 strings are just arrays of bytes; Python 3 strings are Unicode, and as the error message says, they need to be encoded into bytes before doing binary things like encryption. Commented Jul 15, 2018 at 17:22
  • @TomZych So, replace all the str with bytes. Commented Jul 15, 2018 at 17:24
  • Your example does not seem to fulfill minimal verifiable criteria. Commented Jul 15, 2018 at 17:24
  • 1
    You have implicit strings too, e.g. IV. I don’t have this package on my computer, can’t experiment easily. I recommend you look at the docs and make sure you’re passing a valid type to encrypt and anything else that gives an error, then work backwards until all the types are correct. Note that bytes is immutable; use bytearray when you need something mutable. Commented Jul 15, 2018 at 17:32
  • 1
    No thanks, I’m not that invested in your problem. Commented Jul 15, 2018 at 17:45

1 Answer 1

3

Your 'password' variable is a string, but SHA256.new expects bytes (to allow for unicode, for example). That you need bytes is listed in the Crypto.Hash.SHA256 documentation.

The solution is to encode the password to bytes before hashing. This is almost literally what the error message says (if you know that all strings in python 3 are unicode objects):

TypeError: Unicode-objects must be encoded before hashing

The solution is for example to encode with utf8 :

SHA256.new(password.encode('utf8')).digest()
Sign up to request clarification or add additional context in comments.

2 Comments

This works but there's a new error: ValueError: IV must be 16 bytes long
Please start a new question if you want to ask about this, and make sure you provide details and show what you have tried already.

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.