Examples

Note

This library is for creation of block cipher interfaces only! It does not contain or implement any ciphers by itself.

Implementing a block cipher

0. The TEA block cipher

In this example, the a PEP-272 interface for the TEA block cipher is created. TEA uses 128-bit (16 bytes) keys and 64-bit (8 bytes) blocks.

In Python, an unoptimized version of TEA can be written like following:

import struct

def encrypt_tea(value, key, endian="!", rounds=64):
    v0, v1 = struct.unpack(endian + "2L", value)
    k = struct.unpack(endian + "4L", key)

    # mask is an uint32 helper
    delta, mask, sum = 0x9e3779b9, 0xffffffff, 0

    for i in range(rounds//2):
        sum = (sum + delta) & mask
        v0 = v0 + ( ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>5) + k[1]) ) & mask
        v1 = v1 + ( ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>5) + k[3]) ) & mask

    return struct.pack(endian + "2L", v0, v1)

def decrypt_tea(value, key, endian="!", rounds=64):
    v0, v1 = struct.unpack(endian + "2L", value)
    k = struct.unpack(endian + "4L", key)

    # mask is an uint32 helper
    delta, mask = 0x9e3779b9, 0xffffffff
    sum = (delta * rounds // 2) & mask

    for i in range(rounds//2):
        v1 = v1 - ( ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>5) + k[3]) ) & mask
        v0 = v0 - ( ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>5) + k[1]) ) & mask
        sum = (sum - delta) & mask

    return struct.pack(endian + "2L", v0, v1)

1. Constant definition

Import the pep272-encryption module and set module level constants, as defined per PEP-272:

from pep272_encryption import PEP272Cipher, MODE_ECB, MODE_CBC, \
                                            MODE_CFB, MODE_OFB, \
                                            MODE_CTR

block_size = 8  # 64-bit blocks
key_size = 16   # 128-bit keys

2. The TEACipher class

Subclass the PEP272Cipher class, setting the block size parameter and override encrypt_block and decrypt_block methods:

class TEACipher(PEP272Cipher):
    block_size = block_size

    def encrypt_block(self, key, block, **kwargs):
        return encrypt_tea(block, key,
                           kwargs.get('endian', '!'),
                           kwargs.get('rounds', 64))

    def decrypt_block(self, key, block, **kwargs):
        return decrypt_tea(block, key,
                           kwargs.get('endian', '!'),
                           kwargs.get('rounds', 64))


def new(*args, **kwargs):
    return TEACipher(*args, **kwargs)

3. Complete example

Below is the full example code of all snippets combined: A PEP-272 compliant implementation of the TEA block cipher in pure python, that is interchangeable with other ciphers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import struct

from pep272_encryption import PEP272Cipher
from pep272_encryption import MODE_ECB, MODE_CBC, MODE_CFB, MODE_OFB, \
                              MODE_CTR

block_size = 8  # 64-bit blocks
key_size = 16   # 128-bit keys


def new(*args, **kwargs):
    return TEACipher(*args, **kwargs)


class TEACipher(PEP272Cipher):
    block_size = block_size

    def encrypt_block(self, key, block, **kwargs):
        return encrypt_tea(block, key, 
                           kwargs.get('endian', '!'), 
                           kwargs.get('rounds', 64))

    def decrypt_block(self, key, block, **kwargs):
        return decrypt_tea(block, key, 
                           kwargs.get('endian', '!'), 
                           kwargs.get('rounds', 64))


def encrypt_tea(value, key, endian="!", rounds=64):
    v0, v1 = struct.unpack(endian + "2L", value)
    k = struct.unpack(endian + "4L", key)

    # mask is an uint32 helper
    delta, mask, sum = 0x9e3779b9, 0xffffffff, 0

    for i in range(rounds//2):
        sum = (sum + delta) & mask
        v0 = v0 + ( ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>5) + k[1]) ) & mask
        v1 = v1 + ( ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>5) + k[3]) ) & mask

    return struct.pack(endian + "2L", v0, v1)


def decrypt_tea(value, key, endian="!", rounds=64):
    v0, v1 = struct.unpack(endian + "2L", value)
    k = struct.unpack(endian + "4L", key)

    # mask is an uint32 helper
    delta, mask = 0x9e3779b9, 0xffffffff
    sum = (delta * rounds // 2) & mask

    for i in range(rounds//2):
        v1 = v1 - ( ((v0<<4) + k[2]) ^ (v0 + sum) ^ ((v0>>5) + k[3]) ) & mask
        v0 = v0 - ( ((v1<<4) + k[0]) ^ (v1 + sum) ^ ((v1>>5) + k[1]) ) & mask
        sum = (sum - delta) & mask

    return struct.pack(endian + "2L", v0, v1)

This library can then be used easily:

>>> import tea
>>> cipher = tea.new(b'16-bytes key 123', mode=tea.MODE_OFB, IV=b'\00'*8)
>>> cipher.encrypt(b'123456'*6)
b"k\xaf F\xfb*\xeb\x00'kP\x9c\xc9M\xb99\x1cy\xda\x99\xb1\xf0H\x14\x9c\xae@\xddxe`\x01\x85\xc9p\x85"

Implementing a custom mode of operation

In this example a “xor-encrypt-xor” mode is implemented. It takes two additional keys, that are XORed with the plain- and ciphertext. Otherwise it works like ECB.

The mode of operation is passed to the object’s constructor. In this example 500 is assigned to the MODE_XEX constant.

PEP272Cipher is subclassed with the parameters key1 and key2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from abc import ABC

from pep272_encryption import PEP272Cipher
from pep272_encryption.util import split_blocks, xor_strings

MODE_XEX = 500


class XEXCipher(ABC, PEP272Cipher):
    def __init__(self, key, mode, **kwargs):
        self.key1 = kwargs.pop("key1", b'\x00' * self.block_size)
        self.key2 = kwargs.pop("key2", b'\x00' * self.block_size)

        PEP272Cipher.__init__(self, key, mode, **kwargs)

    def encrypt(self, string):
        if self.mode == MODE_XEX:
            out = []
            for block in split_blocks(string, self.block_size):
                inner = xor_strings(block, self.key1)
                encrypted = self.encrypt_block(self.key, inner, **self.kwargs)
                outer = xor_strings(encrypted, self.key2)
                out.append(outer)
            return b"".join(out)
        else:
            PEP272Cipher.encrypt(self, string)

    def decrypt(self, string):
        if self.mode == MODE_XEX:
            out = []
            for block in split_blocks(string, self.block_size):
                encrypted = xor_strings(block, self.key2)
                inner = self.decrypt_block(self.key, encrypted, **self.kwargs)
                plain = xor_strings(inner, self.key1)
                out.append(plain)
            return b"".join(out)
        else:
            PEP272Cipher.decrypt(self, string)

Implementing a stream cipher

While the PEP272 Interface and this library is designed for block ciphers, it may also be used for implementing stream ciphers.

Below is an implementation of the RC4 cipher.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pep272_encryption import PEP272Cipher, MODE_ECB

block_size = 1
key_size = 0


def new(*args, **kwargs):
    return RC4Cipher(*args, **kwargs)


class RC4Cipher(PEP272Cipher):
    block_size = 1
    key_size = 0

    def __init__(self, key, mode=MODE_ECB, **kwargs):
        if mode != MODE_ECB:
            raise ValueError("Stream ciphers only support ECB mode")

        self.S = list(range(256))
        j = 0
        for i in range(256):
            j = (j + self.S[i] + key[i % len(key)]) % 256
            self.S[i], self.S[j] = self.S[j], self.S[i]

        self.i = self.j = 0

        PEP272Cipher.__init__(self, key, mode, **kwargs)

    def encrypt_block(self, key, block, **kwargs):
        self.i = (self.i + 1) % 256
        self.j = (self.j + self.S[self.i]) % 256
        self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]

        K = self.S[(self.S[self.i] + self.S[self.j]) % 256]
        return bytes([block[0] ^ K])

    def decrypt_block(self, key, block, **kwargs):
        return self.encrypt_block(key, block, **kwargs)


assert RC4Cipher(b'\x01\x02\x03\x04\x05').encrypt(b'\x00'*16) \
       == b"\xb29c\x05\xf0=\xc0'\xcc\xc3RJ\n\x11\x18\xa8"