Search code examples
pythonencryptioncryptographyaespycrypto

PyCrypto AES suddenly stops decrypting after several lines of processing input


I am attempting to encrypt and decrypt 5-14 MB text files with two Python files, one containing encrypt/decrypt methods and another being a runner for those methods.

Here's the class file:

from Crypto.Cipher import AES
from Crypto import Random
import sys
import codecs
reload(sys)
sys.setdefaultencoding('utf8')
sys.stdout = codecs.getwriter('utf8')(sys.stdout)

class Crypt:
    def __init__(self, encrypted, key):
        self.message = encrypted
        self.key = key

    def encrypt(self):
        iv = Random.new().read(AES.block_size)
        if len(self.message) % AES.block_size != 0:
            self.message += " " * (AES.block_size - (len(self.message) % AES.block_size))

        lenKey = len(self.key)
        if lenKey > 32:
            self.key = self.key[:32]

        cipher = AES.new(self.key, AES.MODE_CFB, iv)
        return iv + cipher.encrypt(self.message)

    def decrypt(self):
        iv, message = (self.message[:AES.block_size], self.message[AES.block_size:])
        if len(message) % AES.block_size != 0:
            message += " " * (AES.block_size - (len(message) % AES.block_size))

        lenKey = len(self.key)
        if lenKey > 32:
            self.key = self.key[:32]

        cipher = AES.new(self.key, AES.MODE_CFB, iv)
        return cipher.decrypt(message).strip()

Here's the runner:

import crypt_classes
from Crypto.Hash import SHA256
import sys
reload(sys)
sys.setdefaultencoding('utf8')

h = SHA256.new()
while True:
    choice = raw_input("Encrypt [1] or decrypt [2]? ").strip()
    if choice != "1" and choice != "2":
        print "Invalid choice, please try again."
        continue
    key = ""
    first = ""
    confirm = ""
    while True:
        first = raw_input("Enter passphrase (>= 32 chars): ").strip()
        # confirm = raw_input("Confirm passphrase: ").strip()
        confirm = first
        if first == confirm and len(confirm) >= 32:
            h.update(first)
            key = h.hexdigest()
            break
        else:
            print "Passphrases did not match or were shorter than 32 characters. Try again."
            continue
    if choice == "1":
        f = open("encrypted.txt", "w")
        h = open("source.txt", "r")
        message = h.read()
        h.close()
        encrypter = crypt_classes.Crypt(message, key)
        e = encrypter.encrypt()
        f.write(e)
        f.close()
        break
    elif choice == "2":
        f = open("encrypted.txt", "r")
        encrypted = f.read()
        f.close()
        decrypter = crypt_classes.Crypt(encrypted, key)
        w = open("decrypted.txt", "w")
        o = decrypter.decrypt()
        w.write(o)
        w.close()
        break

Encryption works fine, but as the file is decrypted and the program ostensibly finishes, only a few lines are shown in the file along with a few miscellaneous characters, like so:

# This data file generated by 23andMe at: Thu May 22 15:47:06 2008
#
# Below is a text version of your data. Fields are TAB-separated
# Each line corresponds to a single SNP.  For each SNP, we provide its identifier 
# (an rsid or an internal id), its lU�

Please comment if any further elaboration is required.

Thank you!


Solution

  • Just use simple functions. I see no need for a class here.

    Taking the hex digest and then truncating makes the effective keylength 128 while you seem to want 256.

    Don't write encrypted data as UTF8. Don't use padding for CFB mode.

    My try (which works on my system); you can put the encrypt and decrypt functions with their imports in a separate file (module), if you like.

    from Crypto.Hash import SHA256
    from Crypto.Cipher import AES
    from Crypto import Random
    import sys
    
    def encrypt_data(data, key):
        iv=Random.new().read(AES.block_size)
        aes=AES.new(key, AES.MODE_CFB, iv)
        return iv+aes.encrypt(data)
    
    def decrypt_data(data, key):
        iv = data[:16]
        data = data[16:]
        aes=AES.new(key, AES.MODE_CFB, iv)
        return aes.decrypt(data)
    
    h = SHA256.new()
    while True:
        choice = raw_input("Encrypt [1] or decrypt [2]? ").strip()
        if choice != "1" and choice != "2":
            print "Invalid choice, please try again."
            continue
        key = ""
        first = ""
        confirm = ""
        while True:
            first = raw_input("Enter passphrase (>= 32 chars): ").strip()
            # confirm = raw_input("Confirm passphrase: ").strip()
            confirm = first
            if first == confirm and len(confirm) >= 32:
                h.update(first)
                key = h.digest()
                break
            else:
                print "Passphrases did not match or were shorter than 32 characters. Try again."
                continue
        if choice == "1":
            f = open("encryptedDNA.txt", "w")
            h = open("genome_Mikolaj_Habryn_20080522154706.txt", "r")
            message = h.read()
           h.close()
           encmessage = encrypt_data(message, key)
           f.write(encmessage)
           f.close()
           break
        elif choice == "2":
            f = open("encryptedDNA.txt", "r")
            encrypted = f.read()
            f.close()
            decrypted = decrypt_data(encrypted,key)
            w = open("decryptedDNA.txt", "w")
            w.write(decrypted)
            w.close()
            break
    

    ~