Skip to content

ifduyue/xxtea

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

310 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

XXTEA implemented as a Python extension module, licensed under 2-clause BSD.

The XXTEA algorithm takes a 128-bit key and operates on an array of 32-bit integers (at least 2 integers), but it doesn't define the conversions between bytes and array. Due to this reason, many XXTEA implementations out there are not compatible with each other.

In this implementation, the conversions between bytes and array are taken care of by longs2bytes and bytes2longs. A non-standard 4-byte block PKCS#7 padding is used to make sure that the input bytes are padded to a multiple of 4-byte (the size of a 32-bit integer) and at least 8-byte long (the size of two 32-bit integers, which is required by the XXTEA algorithm). As a result of these measures, you can encrypt not only texts, but also any binary bytes of any length.

Note

This implementation uses a non-standard 4-byte block PKCS#7 padding instead of the conventional 8-byte or 16-byte block. For inputs shorter than 4 bytes, a non-standard hack pads an extra 4 bytes (producing pad values 5–8) to satisfy XXTEA's 2-word minimum. This means the output is NOT compatible with other XXTEA implementations. Pass padding=False for raw XXTEA (requires data length ≥ 8 and multiple of 4).

Installation

$ pip install xxtea -U

Usage

This module provides four functions: encrypt(), decrypt(), encrypt_hex(), and decrypt_hex(), plus an XXTEA type for reusable cipher objects.

>>> import os
>>> import xxtea
>>> import binascii
>>>
>>> key = os.urandom(16)  # Key must be a 16-byte string.
>>> s = b"xxtea is good"
>>>
>>> enc = xxtea.encrypt(s, key)
>>> dec = xxtea.decrypt(enc, key)
>>> s == dec
True
>>>
>>> hexenc = xxtea.encrypt_hex(s, key)
>>> hexenc
b'7ad85672d770fb5cf636c49d57e732ae'
>>> s == xxtea.decrypt_hex(hexenc, key)
True
>>>
>>> binascii.hexlify(enc) == hexenc
True

XXTEA Type

The XXTEA type holds a 16-byte key, rounds, and padding setting, so you can encrypt and decrypt multiple times without passing them each call.

>>> from xxtea import XXTEA
>>>
>>> cipher = XXTEA(key, padding=False, rounds=128)
>>> cipher
<xxtea.XXTEA object at 0x...>
>>>
>>> enc = cipher.encrypt(b'12345678')
>>> cipher.decrypt(enc)
b'12345678'
>>>
>>> hexenc = cipher.encrypt_hex(b'12345678')
>>> cipher.decrypt_hex(hexenc)
b'12345678'

rounds defaults to 0 (auto), padding defaults to True. rounds=0 means 6 + 52 / n, where n is the number of 32-bit words in the data. They are stored on the object and used by every encrypt(), decrypt(), encrypt_hex(), and decrypt_hex() call:

>>> c = XXTEA(key)                          # rounds=0, padding=True
>>> c = XXTEA(key, rounds=64)         # override rounds
>>> c = XXTEA(key, padding=False)     # disable padding
>>> c = XXTEA(key, padding=False, rounds=42)

encrypt_hex() and decrypt_hex() operate on ciphertext in a hexadecimal representation. They are exactly equivalent to:

>>> hexenc = binascii.hexlify(xxtea.encrypt(s, key))
>>> s == xxtea.decrypt(binascii.unhexlify(hexenc), key)
True

Padding

Padding is enabled by default, using a non-standard 4-byte block PKCS#7 scheme. The pad byte value is 4 - (len(data) & 3) (range 1–4), plus an extra 4 bytes when the input is shorter than 4 bytes to meet XXTEA's 2-word minimum (producing pad values 5–8).

Because padding always adds at least one byte, encrypting an 8-byte input produces a 12-byte ciphertext. This is incompatible with other XXTEA implementations that use a standard block size or skip padding altogether. Use padding=False for raw, unpadded XXTEA.

>>> xxtea.encrypt_hex('', key)
b'd63256eb59134f1f'
>>> xxtea.decrypt_hex(_, key)
b''
>>> xxtea.encrypt_hex(' ', key)
b'97009bd24074a7a5'
>>> xxtea.decrypt_hex(_, key)
b' '

You can disable padding by setting padding parameter to False. In this case data will not be padded, so data length must be a multiple of 4 bytes and must not be less than 8 bytes. Otherwise ValueError will be raised:

>>> xxtea.encrypt_hex('', key, padding=False)
ValueError: Data length must be a multiple of 4 bytes and must not be less than 8 bytes
>>> xxtea.encrypt_hex('xxtea is good', key, padding=False)
ValueError: Data length must be a multiple of 4 bytes and must not be less than 8 bytes
>>> xxtea.encrypt_hex('12345678', key, padding=False)
b'64f4e969ba90d386'
>>> xxtea.decrypt_hex(_, key, padding=False)
b'12345678'

Rounds

By default xxtea manipulates the input data for 6 + 52 / n rounds, where n denotes how many 32-bit integers the input data can fit in. We can change this by setting rounds parameter.

Do note that the more rounds it is, the more time will be consumed. rounds must fit in a 32-bit unsigned integer; values exceeding 2**32 - 1 raise OverflowError.

>>> import xxtea
>>> import string
>>> data = string.digits
>>> key = string.ascii_letters[:16]
>>> xxtea.encrypt_hex(data, key)
b'5b80b08a5d1923e4cd992dd5'
>>> 6 + 52 // ((len(data) + (4 - 1)) // 4)  # 4 means 4 bytes, size of a 32-bit integer
23
>>> xxtea.encrypt_hex(data, key, rounds=23)
b'5b80b08a5d1923e4cd992dd5'
>>> xxtea.encrypt_hex(data, key, rounds=1024)
b'1577bbf28c43ced93bd50720'

Catching Exceptions

When calling these functions, a ValueError, TypeError, or OverflowError may be raised:

>>> import xxtea
>>>
>>> def try_catch(func, *args, **kwargs):
...     try:
...         func(*args, **kwargs)
...     except Exception as e:
...         print(e.__class__.__name__, ':', e)
...
...
...
>>> try_catch(xxtea.decrypt, '', key='')
ValueError : Need a 16-byte key.
>>> try_catch(xxtea.decrypt, '', key=' '*16)
ValueError : Invalid data, data length is not a multiple of 4, or less than 8.
>>> try_catch(xxtea.decrypt, ' '*8, key=' '*16)
ValueError : Invalid data, illegal PKCS#7 padding. Could be using a wrong key.
>>> try_catch(xxtea.decrypt_hex, ' '*8, key=' '*16)
TypeError : Non-hexadecimal digit found
>>> try_catch(xxtea.decrypt_hex, 'abc', key=' '*16)
TypeError : Odd-length string
>>> try_catch(xxtea.decrypt_hex, 'abcd', key=' '*16)
ValueError : Invalid data, data length is not a multiple of 4, or less than 8.
>>> try_catch(xxtea.encrypt, b'x', b'k'*16, rounds=2**32)
OverflowError : rounds value too large
>>> try_catch(XXTEA, key=b'short')
ValueError : Need a 16-byte key.
>>> try_catch(XXTEA, key=b'k'*16, rounds=2**32)
OverflowError : rounds value too large

About

Python extension module xxtea

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors