0

I've got a encryption/decryption class that I'm using cross platform. I'm using the same class on both server and client. I encrypt a file on a Linux server, then decrypt on either a Linux or Windows client. I have no problems when decrypting on Linux, but when I transfer the file to Windows and try to decrypt, I get the following exception:

ValueError: Input strings must be a multiple of 16 in length

My first thought is that it is caused by the different filesystems, and any characters that are used to create the padding. Here is my class code:

class FileSec:
    def __init__(self):

        # File chunk size
        self.chunk_size = 64*1024

    # Encrypt file with OpenSSL
    def encrypt(self, infile, outfile, key):
        if not infile or not os.path.isfile(infile):
            return False
        if not outfile or os.path.isfile(outfile):
            return False
        if not key:
            return False

        # Encrypt the file
        iv        = ''.join(chr(random.randint(0, 0xFF)) for i in range(16))
        encryptor = AES.new(key, AES.MODE_CBC, iv)
        filesize  = os.path.getsize(infile)
        with open(infile, 'rb') as ifh:
            with open(outfile, 'wb') as ofh:
                ofh.write(struct.pack('<Q', filesize))
                ofh.write(iv)
                while True:
                    chunk = ifh.read(self.chunk_size)
                    if len(chunk) == 0:
                        break
                    elif len(chunk) % 16 != 0:
                        chunk += ' ' * (16 - len(chunk) % 16)
                    ofh.write(encryptor.encrypt(chunk))
        return True

    # Decrypt file with OpenSSL
    def decrypt(self, infile, outfile, key):
        if not infile or not os.path.isfile(infile):
            return False
        if not outfile or os.path.isfile(outfile):
            return False
        if not key:
            return False

        # Decrypt the file
        with open(infile, 'rb') as ifh:
            origsize  = struct.unpack('<Q', ifh.read(struct.calcsize('Q')))[0]
            iv        = ifh.read(16)
            decryptor = AES.new(key, AES.MODE_CBC, iv)
            with open(outfile, 'wb') as ofh:
                while True:
                    chunk = ifh.read(self.chunk_size)
                    if len(chunk) == 0:
                        break
                    ofh.write(decryptor.decrypt(chunk))
                ofh.truncate(origsize)
        return True

http://pastebin.com/Dvf6nUxH

I'm using code adapted from here: http://eli.thegreenplace.net/2010/06/25/aes-encryption-of-files-in-python-with-pycrypto/

Anyone have any suggestions on how I can modify this class to work cross-platform?

djtaylor
  • 1
  • 2

2 Answers2

0

myfile.read(x) reads any amount up to x bytes; it is not guaranteed to return all x.

Note that it will always return at least one until the file is empty, so it is possible to wrap this in a loop, and then join the returned strings.

Veedrac
  • 58,273
  • 15
  • 112
  • 169
  • If I'm understanding correctly, 'ifh.read(16)' is not guaranteed to read 16 bytes, but up to 16 bytes, which makes sense and could be causing the exception. But from what I can tell, in the encryption method, it takes the last chunk of data written, and pads it to 16 bytes, ensuring that the encrypted file size is in increments of 16 bytes. As for returning False vs exceptions, I'm calling this class internally, and testing for True/False values. This is part of an API, so I log any errors and return an HTTP response with the error. – djtaylor Jun 15 '14 at 02:15
0

Closing this one. Turns out the problem has nothing to do with the encryption/decryption function, but with an extra byte being tacked on to the encrypted file when I transfer it to the Windows machine, causing the exception.

djtaylor
  • 1
  • 2