mypython
mypython

Reputation: 3

pycrypto encrypt/decrypt, losing part of encrypted string when decrypting

I am trying to encrypt/decrypt with pycrypto in python. for the most part things have worked smooth but I am getting an odd problem when decrypting data.I have tried to encrypt/decrypt some jpgs for testing and although they encrypt/decrypt without issue, the decrypted files cannot be opened/are corrupted. To try to find the problem I saved a textfile with a random sentence similar to "test this file for integrity blah blah blah" and it decrypts correctly only after ".... integrity blah blah blah", everything before integrity is still in garbled characters. I'm not that knowledgable on AES, but im assuming that this is an encoding/decoding or padding error.

Here is my code:

#encryption
iv = Random.new().read( AES.block_size)

filePath = input("Path to file for encryption: ")
selFile = open(filePath, 'rb')
getBytes = bytes(selFile.read())

encPW = input("Enter password: ")
hashpw = hashlib.sha256(encPW.encode('UTF-8').digest())

destination = input("Destination path for encrypted file: ")

aes = AES.new(hashpw, AES.Mode_CFB, iv)
encFile = base65.b64encode(aes.encrypt(getBytes))

writetofile = open(destination, 'wb')
writetofile.write(encFile)
writetofile.close()
print("Encryption successful")

#Decryption
iv = Random.new().read( AES.block_size)

filePath = input("Path to file for decryption: ")
selFile = open(filePath, 'rb')
getBytes = bytes(selFile.read())

decPW = input("Enter password: ")
hashdecpw = hashlib.sha256(encPW.encode('UTF-8').digest())

destination = input("Destination path for decrypted file: ")

aes = AES.new(hashdecpw, AES.Mode_CFB, iv)
decFile = aes.decrypt(getBytes)

writetofile = open(destination, 'wb')
writetofile.write(decFile)
writetofile.close()
print("Decryption successful")

Any ideas on what could be causing the loss of the first characters, and preventing me from encrypting/decrypting files correctly?

Upvotes: 0

Views: 1752

Answers (2)

Daniel
Daniel

Reputation: 777

You're generating a new IV for encryption and decryption seperately, which comes to yield such problems. Here's what I recommend doing:

def encrypt(inpath, outpath, password):
    iv = Random.new().read(AES.block_size)
    with open(inpath, "rb") as f:
        contents = f.read()
    # A context manager automatically calls f.close()
    key = pbkdf2.crypt(password, "")
    # See notes

    aes = AES.new(key, AES.Mode_CFB, iv)
    encrypted = aes.encrypt(contents)
    with open(outpath, "wb") as f:
        f.write(iv + b":")
        f.write(encrypted)
    print("Encryption successful")


def decrypt(inpath, outpath, password):
    with open(inpath, "rb") as f:
        contents = f.read()

    iv, encrypted = contents.split(b":")
    key = pbkdf2.crypt(password, "")
    aes = AES.new(key, AES.Mode_CFB, iv)

    decrypted = aes.decrypt(contents)
    with open(outpath, "wb") as f:
        f.write(decrypted)
    print("Decryption successful")

Some notes:

  • An IV is not meant to be secret, so it can be randomly generated once and then written to a file to be used later for decryption (as shown in this example)

  • A hashing algorithm is not strong enough for deriving keys, which is why there are special tools called key derivation algorithms (like PBKDF2 in python). Use those instead!

I have not tested this code myself, so it may not work properly.

Upvotes: 1

Artjom B.
Artjom B.

Reputation: 61892

You have at least three issues:

  • You probably mean hashlib.sha256(encPW.encode('UTF-8')).digest() instead of hashlib.sha256(encPW.encode('UTF-8').digest()) (the closing brace is at the wrong position)

  • You're encoding the ciphertext with Base64 before writing it to a file. You've forgot to decode it after reading it back from the file before decrypting it. For example:

    getBytes = base64.b64decode(bytes(selFile.read()))
    
  • This is the big one: You need the exact same IV during the decryption that you've used for encryption. The IV is not secret, but it needs to be unique for every encryption that you've done with the same key. Commonly the IV is written in front of the ciphertext and read back for decryption.

    #encryption
    encFile = base64.b64encode(iv + aes.encrypt(getBytes))
    
    #decryption
    getBytes = base64.b64decode(bytes(selFile.read()))
    iv = getBytes[:16]
    aes = AES.new(hashdecpw, AES.Mode_CFB, iv)
    decFile = aes.decrypt(getBytes[16:])
    

Upvotes: 1

Related Questions