2011-10-17

File Encryption Utility

I would like to write a small utility in Python to manage some private and confidential information in text format, and save it to a file with encryption.

First I will need a utility class to:
1.  encrypt some plain text and save it to a disk file
2.  open the encrypted file and decrypt it into plaintext

With the help of the Python Cryptography Toolkit, I can do that easily:

# filecrypto.py

import os

from Crypto.Hash import SHA256 as HASH
from Crypto.Cipher import Blowfish as CIPHER

class FileCrypto(object):

    def _create_cipher(self, password):
        h = HASH.new()
        h.update(password)
        key = h.digest()
        h.update(key)
        iv = h.digest()[:8]
        return CIPHER.new(key, CIPHER.MODE_CFB, iv)

    def load(self, filename, password):
        'decrypt a file into a string of plain text'
        if not os.path.isfile(filename):
            return ""
        with open(filename, 'rb') as f:
            ciphertext = f.read()
        cipher = self._create_cipher(password)
        return cipher.decrypt(ciphertext)

    def save(self, filename, password, plaintext):
        'encrypt a string of plain text into a file'
        cipher = self._create_cipher(password)
        ciphertext = cipher.encrypt(plaintext)
        with open(filename, 'wb') as f:
            f.write(ciphertext)


I pick Blowfish as the encryption algorithm because it allows variable key size.  CFB (Cipher FeedBack) mode is used to that block size padding is not necessary.

For basic unit testing, I add the following to the module:

def unittest():
    import random
    import string

    # test 1 - encrypt 1024 printable chars
    filename = "".join( [random.choice(string.hexdigits) for i in range(8)] )
    password = "".join( [random.choice(string.printable) for i in range(256)] )
    message = "".join( [random.choice(string.printable) for i in range(1024)] )
    if os.path.isfile(filename):
        os.remove(filename)
    fc = FileCrypto()
    assert "" == fc.load(filename, password)
    fc.save(filename, password, message)
    assert message == fc.load(filename, password)
    fc = None
    if os.path.isfile(filename):
        os.remove(filename)

    # test 2 - encrypt byte values 0 to 255
    filename = "".join( [random.choice(string.hexdigits) for i in range(8)] )
    password = "".join( [random.choice(string.printable) for i in range(256)] )
    message = "".join( [chr(x) for x in range(256)] )
    if os.path.isfile(filename):
        os.remove(filename)
    fc = FileCrypto()
    assert "" == fc.load(filename, password)
    fc.save(filename, password, message)
    assert message == fc.load(filename, password)
    fc = None
    if os.path.isfile(filename):
        os.remove(filename)

if __name__ == '__main__':
    unittest()

In the next posting, I will build a GUI using this module to encrypt/decrypt a file.

No comments: