pythonencryptioncryptographyctypestwofish

Twofish encryption in Python


I have a test tool which uses Twofish as the encryption algorithm to encrypt data before sending it to the server. The code is written in C++ and uses Bruce Schneier's optimized C implementation (https://www.schneier.com/code/twofish-optimized-c.zip). I need to port this tool to Python and I am using twofish module (https://pypi.python.org/pypi/twofish/0.3.0). I can encrypt and decrypt strings of 16 char length but for other string lengths it gives an error 'ValueError: invalid block length'.

How can I encrypt & decrypt large data using Python's Twofish module?

>>> from twofish import Twofish
>>> key = binascii.unhexlify('8CACBE276491F6FF4B1EC0E9CFD52E76')
>>> t = Twofish(key)
>>> cipher_text = T.encrypt('deadbeaf12345678')
>>> plain_text = t.decrypt(cipher_text)
>>> plain_text
'deadbeaf12345678'
>>> cipher_text = t.encrypt('deadbeaf12345678hello world 1234')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/twofish.py", line 69, in encrypt
    raise ValueError('invalid block length')
ValueError: invalid block length

Update: I am trying out another solution for this problem. I have created a Windows DLL, twofish.dll from Bruce Schneier's optimized C implementation (https://www.schneier.com/code/twofish-optimized-c.zip). Also, I have exported wrapper functions for encode & decode member functions using __declspec(dllexport).

I am loading this DLL in Python using ctype.CDLL function. The prototype for encode function is:

__declspec(dllexport) int encode(unsigned char *key, unsigned char *in, unsigned int inlen, unsigned char *out, unsigned int outbuflen, unsigned int& outlen)

How should I define the argument types in Python script?

import ctypes
import binascii
import requests

twofish_dll = ctypes.CDLL("twofish.dll")

encode = twofish_dll.encode

f = open('test.xml', 'r')
plain_text = f.read()
f.close()

cipher_text = ctypes.create_string_buffer(8192)
cipher_text_lenght = (ctypes.c_uint)()

KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)

encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))

When executed the above code throws below error:

Traceback (most recent call last):
  File "C:\Data\sepm_test.py", line 21, in <module>
    encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))
TypeError: bytes or integer address expected instead of str instance

Solution

  • I finally solved this problem by compiling Bruce Schneier's optimized C implementation for Twofish (https://www.schneier.com/code/twofish-optimized-c.zip) into a DLL and loading that DLL using ctypes module.

    import ctypes
    import binascii
    import requests
    
    twofish_dll = ctypes.CDLL("twofish.dll")
    
    encode = twofish_dll.encode
    
    f = open('test.xml', 'r')
    plain_text = f.read()
    f.close()
    plain_text_buffer = ctypes.create_string_buffer(str.encode(plain_text))
    plain_text_buffer_length = (ctypes.c_uint)(len(plain_text_buffer))
    
    cipher_text_buffer = ctypes.create_string_buffer(8192)
    cipher_text_buffer_length = (ctypes.c_uint)(len(cipher_text_buffer))
    cipher_text_length = ctypes.c_uint(0)
    
    KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
    key = binascii.unhexlify(KCS)
    
    encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
    encode.restype = ctypes.c_int
    
    encode(ctypes.c_char_p(key), plain_text_buffer, plain_text_buffer_length, cipher_text_buffer, cipher_text_buffer_length, ctypes.pointer(cipher_text_length))