Tuesday, May 1, 2012

pycrypto

Here's a Quick Python post about encryption using the pycrypto module. Much of it is derived from the excellent overview here.

I tried easy_install first, but it hung for some reason, so I just did:

git clone https://github.com/dlitz/pycrypto.git
cd pycrypto
python setup.py build
sudo python setup.py install

Let's try a little DES:

>>> from Crypto.Cipher import DES
>>> des = DES.new('01234567',DES.MODE_ECB)
>>> text = 'hello, world'
>>> c = des.encrypt(text)
Traceback (most recent call last):
  File "", line 1, in 
ValueError: Input strings must be a multiple of 8 in length
>>> len(text)
12
>>> text += '.' * (8 - len(text) % 8)
>>> c = des.encrypt(text)
>>> c
'\xc4\xf56\xad\xbb\xae\xb8\x84\xbf\xb3\xfaH^\xe3\x10\x16'
>>> des.decrypt(c)
'hello, world....'
>>> 

Here is a different mode of DES called CFB (Cipher Feedback). It requires some random bytes, and two instantiations of the object:

>>> from Crypto.Cipher import DES
>>> from Crypto import Random
>>> iv = Random.get_random_bytes(8)
>>> des1 = DES.new('01234567', DES.MODE_CFB, iv)
>>> des2 = DES.new('01234567', DES.MODE_CFB, iv)
>>> text = 'hello, world!'
>>> c = des1.encrypt(text)
>>> c
'\x92\xc6\xc7K=\xb0\xf4\x83A\xfd\xa4\x13e'
>>> des2.decrypt(c)
'hello, world!'

The venerable xor method is also present:

>>> from Crypto.Cipher import XOR
>>> key = '\x00'*4 + '\x01'*4 + '\x10'*4 + '\x11'*4
>>> xor = XOR.new(key)
>>> text = '\x00\x01\x10\x11' * 4
>>> c = xor.encrypt(text)
>>> p = xor.decrypt(c)
>>> text
'\x00\x01\x10\x11\x00\x01\x10\x11\x00\x01\x10\x11\x00\x01\x10\x11'
>>> key
'\x00\x00\x00\x00\x01\x01\x01\x01\x10\x10\x10\x10\x11\x11\x11\x11'
>>> c
'\x00\x01\x10\x11\x01\x00\x11\x10\x10\x11\x00\x01\x11\x10\x01\x00'
>>> p
'\x00\x01\x10\x11\x00\x01\x10\x11\x00\x01\x10\x11\x00\x01\x10\x11'

I adapted one of the scripts from Laurent Luce's post to do encryption on a file. Here is the output (the hex values of the key we generated), and some display of the data using hexdump:

> python script.py 
21 31 e4 25 b1 ab be fb 3d 35 95 f7 8b 67 ba 24
> hexdump -C m.txt
00000000  48 65 6c 6c 6f 2c 20 77  6f 72 6c 64 21 0a        |Hello, world!.|
0000000e
> hexdump -C c.txt
00000000  25 bc 08 25 ae 7c fb c3  3a a3 91 83 6a 34 7c 06  |%..%.|..:...j4|.|
00000010
> hexdump -C p.txt
00000000  48 65 6c 6c 6f 2c 20 77  6f 72 6c 64 21 0a 20 20  |Hello, world!.  |
00000010

script.py
from Crypto import Random
from Crypto.Cipher import DES3
import struct

def encrypt_file(ifn, ofn, chunk_size, key, iv):
    des3 = DES3.new(key, DES3.MODE_CFB, iv)
    with open(ifn, 'r') as in_file:
        with open(ofn, 'w') as out_file:
            while True:
                chunk = in_file.read(chunk_size)
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - len(chunk) % 16)
                out_file.write(des3.encrypt(chunk))
 
def decrypt_file(ifn, ofn, chunk_size, key, iv):
    des3 = DES3.new(key, DES3.MODE_CFB, iv)
    with open(ifn, 'r') as in_file:
        with open(ofn, 'w') as out_file:
            while True:
                chunk = in_file.read(chunk_size)
                if len(chunk) == 0:
                    break
                out_file.write(des3.decrypt(chunk))


SZ = 100
ifn = 'm.txt'
ofn = 'c.txt'
key = Random.get_random_bytes(16)
iv = Random.get_random_bytes(8)

L = [struct.unpack('B',k) for k in key]
L = [hex(t[0])[2:] for t in L]
print ' '.join(L)

encrypt_file(ifn, ofn, SZ, key, iv)
decrypt_file(ofn, 'p.txt', SZ, key, iv)